From 99cdd7ab93566e1170a81a38f139e47fba47d350 Mon Sep 17 00:00:00 2001 From: delageniere Date: Fri, 15 Sep 2017 11:01:11 +0200 Subject: [PATCH 01/38] closes #167 --- ispyb-bcr-ear/pom.xml | 202 +- ispyb-bcr/pom.xml | 9 +- ispyb-ear/pom.xml | 5 +- ispyb-ejb/pom.xml | 28 +- .../ispyb/common/util/upload/ISPyBParser.java | 7 +- .../biosaxs/services/BiosaxsServices.java | 1 + .../core/experiment/Experiment3Service.java | 3 + .../experiment/Experiment3ServiceBean.java | 705 +- .../core/robot/Robot3ServiceBean.java | 2090 +- .../webservice/ATSASPipeline3ServiceBean.java | 2099 +- .../vos/dataAcquisition/Experiment3VO.java | 1006 +- ispyb-ui/pom.xml | 9 +- .../biosaxs/dataAdapter/BiosaxsActions.java | 1 + .../src/main/webapp/js/ispyb/min/ispyb-bx.js | 42726 ++++++++-------- ispyb-ws/pom.xml | 3 +- 15 files changed, 24447 insertions(+), 24447 deletions(-) diff --git a/ispyb-bcr-ear/pom.xml b/ispyb-bcr-ear/pom.xml index c072129af..cdac8b23b 100644 --- a/ispyb-bcr-ear/pom.xml +++ b/ispyb-bcr-ear/pom.xml @@ -1,112 +1,90 @@ - - 4.0.0 - - ispyb - ispyb-parent - 5.2.0 - - ispyb-bcr-ear - ear - - - - - - ispyb - ispyb-ejb3 - ejb - 5.2.0 - provided - - - - ispyb - ispyb-bcr - 5.0.3 - war - - - - org.apache.httpcomponents - httpclient - 4.4 - - - - - bcr - - - src/main/resources - - - - - - org.apache.maven.plugins - maven-ear-plugin - ${version.ear.plugin} - - - - 6 - true - - lib - - - - - ispyb - ispyb-ejb3 - / - - - - - ispyb - ispyb-bcr - /bcr/bcr - - - - no-version - - - - - org.wildfly.plugins - wildfly-maven-plugin - ${version.wildfly.maven.plugin} - - bcr.ear - false - - - - - + + 4.0.0 + + ispyb + ispyb-parent + 5.2.0 + + ispyb-bcr-ear + ear + + + + + ispyb + ispyb-ejb3 + ejb + 5.2.0 + provided + + + + ispyb + ispyb-bcr + 5.0.3 + war + + + + org.apache.httpcomponents + httpclient + 4.4 + + + + + bcr + + + src/main/resources + + + + + + org.apache.maven.plugins + maven-ear-plugin + ${version.ear.plugin} + + + + 6 + true + + lib + + + + + ispyb + ispyb-ejb3 + / + + + + + ispyb + ispyb-bcr + /bcr/bcr + + + + no-version + + + + + org.wildfly.plugins + wildfly-maven-plugin + ${version.wildfly.maven.plugin} + + bcr.ear + false + + + + + diff --git a/ispyb-bcr/pom.xml b/ispyb-bcr/pom.xml index 915e82467..a3a64ce4b 100644 --- a/ispyb-bcr/pom.xml +++ b/ispyb-bcr/pom.xml @@ -133,7 +133,7 @@ commons-collections commons-collections - 3.2.1 + 3.2.2 com.sun.mail @@ -200,12 +200,17 @@ org.hibernate hibernate-core - 4.0.0.Final + 5.0.10.Final + provided antlr antlr + + dom4j + dom4j + diff --git a/ispyb-ear/pom.xml b/ispyb-ear/pom.xml index 7ce68d16a..ce16ed6b7 100644 --- a/ispyb-ear/pom.xml +++ b/ispyb-ear/pom.xml @@ -15,6 +15,7 @@ ejb 5.2.0 + ispyb ispyb-ui @@ -27,6 +28,7 @@ war 5.2.0 + @@ -58,6 +60,7 @@ ispyb-ejb3 / + ispyb ispyb-ws @@ -69,7 +72,7 @@ ispyb-ui /ispyb - + no-version diff --git a/ispyb-ejb/pom.xml b/ispyb-ejb/pom.xml index 35777fab1..c6632b948 100644 --- a/ispyb-ejb/pom.xml +++ b/ispyb-ejb/pom.xml @@ -53,14 +53,6 @@ provided - - - - - - - @@ -69,16 +61,20 @@ provided - org.hibernate hibernate-core - 4.0.0.Final + 5.0.10.Final + provided antlr antlr + + dom4j + dom4j + @@ -90,13 +86,18 @@ org.jboss.ejb3 jboss-ejb3-ext-api - 2.1.0 + 2.2.0.Final org.jboss.as jboss-as-security 7.1.1.Final + + org.jboss.ws + jbossws-spi + 2.3.1.Final + org.apache.axis @@ -108,11 +109,6 @@ httpclient 4.4 - - org.jboss.ws - jbossws-spi - 2.3.1.Final - jhdf jhdf diff --git a/ispyb-ejb/src/main/java/ispyb/common/util/upload/ISPyBParser.java b/ispyb-ejb/src/main/java/ispyb/common/util/upload/ISPyBParser.java index f6a25a0cd..7038cc588 100644 --- a/ispyb-ejb/src/main/java/ispyb/common/util/upload/ISPyBParser.java +++ b/ispyb-ejb/src/main/java/ispyb/common/util/upload/ISPyBParser.java @@ -73,6 +73,8 @@ public class ISPyBParser extends XLSParser { */ private HSSFWorkbook mWorkbook = null; + + private final String PWD = "ispyb"; private static final Ejb3ServiceLocator ejb3ServiceLocator = Ejb3ServiceLocator.getInstance(); @@ -515,7 +517,7 @@ public void populateExistingShipment(String templateFileName, String populatedTe workbook.setSheetName(s, dewarCode + "_" + puckNumber); HSSFSheet sheet = workbook.getSheetAt(s); - sheet.setProtect(false); + sheet.protectSheet(PWD); // Dewar Code HSSFRow row = sheet.getRow(dewarRow); @@ -599,7 +601,8 @@ public void populateExistingShipment(String templateFileName, String populatedTe cell = row.createCell(shippingDateCol); cell.setCellValue(new HSSFRichTextString(shippingDate)); - sheet.setProtect(true); + //sheet.setProtect(true); + sheet.protectSheet(PWD); puckNumber++; } } diff --git a/ispyb-ejb/src/main/java/ispyb/server/biosaxs/services/BiosaxsServices.java b/ispyb-ejb/src/main/java/ispyb/server/biosaxs/services/BiosaxsServices.java index cb0803db9..0ee508c83 100644 --- a/ispyb-ejb/src/main/java/ispyb/server/biosaxs/services/BiosaxsServices.java +++ b/ispyb-ejb/src/main/java/ispyb/server/biosaxs/services/BiosaxsServices.java @@ -487,6 +487,7 @@ public Integer createHPLC(String proposalCode, String proposalNumber, String nam } Experiment3VO experiment = new Experiment3VO(); + experiment = this.experiment3Service.initPlates(experiment); experiment.setProposalId(proposal.getProposalId()); experiment.setName(name); experiment.setSessionId(sessionId); diff --git a/ispyb-ejb/src/main/java/ispyb/server/biosaxs/services/core/experiment/Experiment3Service.java b/ispyb-ejb/src/main/java/ispyb/server/biosaxs/services/core/experiment/Experiment3Service.java index 29d3ae30b..0dbc0ce7c 100644 --- a/ispyb-ejb/src/main/java/ispyb/server/biosaxs/services/core/experiment/Experiment3Service.java +++ b/ispyb-ejb/src/main/java/ispyb/server/biosaxs/services/core/experiment/Experiment3Service.java @@ -72,5 +72,8 @@ public interface Experiment3Service { public abstract void saveStructure(Structure3VO structure3vo); public List> getExperimentDescription(Integer experimentId); + + public Experiment3VO initPlates(Experiment3VO vo); + } \ No newline at end of file diff --git a/ispyb-ejb/src/main/java/ispyb/server/biosaxs/services/core/experiment/Experiment3ServiceBean.java b/ispyb-ejb/src/main/java/ispyb/server/biosaxs/services/core/experiment/Experiment3ServiceBean.java index 084d8769c..ca577d69d 100644 --- a/ispyb-ejb/src/main/java/ispyb/server/biosaxs/services/core/experiment/Experiment3ServiceBean.java +++ b/ispyb-ejb/src/main/java/ispyb/server/biosaxs/services/core/experiment/Experiment3ServiceBean.java @@ -1,348 +1,357 @@ -/******************************************************************************* - * This file is part of ISPyB. - * - * ISPyB is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * ISPyB is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with ISPyB. If not, see . - * - * Contributors : S. Delageniere, R. Leal, L. Launer, K. Levik, S. Veyrier, P. Brenchereau, M. Bodin, A. De Maria Antolinos - ******************************************************************************************************************************/ - -package ispyb.server.biosaxs.services.core.experiment; - - -import ispyb.server.biosaxs.services.core.ExperimentScope; -import ispyb.server.biosaxs.services.core.proposal.SaxsProposal3Service; -import ispyb.server.biosaxs.services.sql.SQLQueryKeeper; -import ispyb.server.biosaxs.vos.assembly.Macromolecule3VO; -import ispyb.server.biosaxs.vos.assembly.Stoichiometry3VO; -import ispyb.server.biosaxs.vos.assembly.Structure3VO; -import ispyb.server.biosaxs.vos.dataAcquisition.Buffer3VO; -import ispyb.server.biosaxs.vos.dataAcquisition.Experiment3VO; -import ispyb.server.biosaxs.vos.dataAcquisition.Measurement3VO; -import ispyb.server.biosaxs.vos.datacollection.MeasurementTodataCollection3VO; -import ispyb.server.biosaxs.vos.datacollection.SaxsDataCollection3VO; -import ispyb.server.biosaxs.vos.utils.comparator.SaxsDataCollectionComparator; -import ispyb.server.biosaxs.vos.utils.parser.RobotXMLParser; -import ispyb.server.mx.services.ws.rest.WsServiceBean; - -import java.io.File; -import java.util.Arrays; -import java.util.Calendar; -import java.util.Collections; -import java.util.Comparator; -import java.util.GregorianCalendar; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import javax.ejb.EJB; -import javax.ejb.Stateless; -import javax.persistence.EntityManager; -import javax.persistence.NoResultException; -import javax.persistence.PersistenceContext; -import javax.persistence.TypedQuery; - -import org.apache.log4j.Logger; -import org.hibernate.SQLQuery; -import org.hibernate.Session; -import org.hibernate.transform.AliasToEntityMapResultTransformer; - - - -@Stateless -public class Experiment3ServiceBean extends WsServiceBean implements Experiment3Service, Experiment3ServiceLocal { - - private final static Logger log = Logger.getLogger(Experiment3ServiceBean.class); - - - /** - * QUERIES - */ - private String GetExperimentDescriptionByExperimentId = getViewExperimentDescriptionTableQuery() + " where experimentId = :experimentId"; - - - - @PersistenceContext(unitName = "ispyb_db") - private EntityManager entityManager; - - @EJB - private SaxsProposal3Service saxsProposal3Service; - - @Override - public void persist(Experiment3VO transientInstance) { - try { - entityManager.persist(transientInstance); - } catch (RuntimeException re) { - throw re; - } - } - - @Override - public Experiment3VO merge(Experiment3VO detachedInstance) { - try { - Experiment3VO result = entityManager.merge(detachedInstance); - return result; - } catch (RuntimeException re) { - throw re; - } - } - - private String getViewExperimentDescriptionTableQuery(){ - return this.getQueryFromResourceFile("/queries/biosaxs/Experiment3ServiceBean/getViewExperimentDescriptionTableQuery.sql"); - } - - - @Override - public List findByProposalId(int proposalId, ExperimentScope scope) { - StringBuilder ejbQLQuery = Experiment3ServiceBean.getQueryByScope(scope); - ejbQLQuery.append("WHERE experiment.proposalId = :proposalId"); - TypedQuery query = entityManager.createQuery(ejbQLQuery.toString(), Experiment3VO.class).setParameter("proposalId", proposalId); - return query.getResultList(); - } - - @Override - public Experiment3VO findById(Integer experimentId, ExperimentScope scope, Integer proposalId) { - try { - StringBuilder ejbQLQuery = Experiment3ServiceBean.getQueryByScope(scope); - ejbQLQuery.append("WHERE experiment.experimentId = :experimentId "); - // if coming from manager account, proposalId can be null - if (proposalId != null) { - ejbQLQuery.append(" and experiment.proposalId = :proposalId"); - } - TypedQuery query = entityManager.createQuery(ejbQLQuery.toString(), Experiment3VO.class); - query.setParameter("experimentId", experimentId); - if (proposalId != null) { - query.setParameter("proposalId", proposalId); - } - return query.getSingleResult(); - } catch (NoResultException e) { - return null; - } - } - - public static StringBuilder getQueryByScope(ExperimentScope scope){ - StringBuilder ejbQLQuery = new StringBuilder(); - ejbQLQuery.append("SELECT DISTINCT(experiment) FROM Experiment3VO experiment "); - - switch (scope) { - case MINIMAL: - break; - case MEDIUM: - ejbQLQuery.append("LEFT JOIN FETCH experiment.samples samples "); - ejbQLQuery.append("LEFT JOIN FETCH samples.macromolecule3VO macromolecule "); - ejbQLQuery.append("LEFT JOIN FETCH macromolecule.stoichiometry st "); -// ejbQLQuery.append("LEFT JOIN FETCH st.macromolecule3VO "); - ejbQLQuery.append("LEFT JOIN FETCH macromolecule.structure3VOs "); - ejbQLQuery.append("LEFT JOIN FETCH samples.measurements specimens "); - ejbQLQuery.append("LEFT JOIN FETCH experiment.samplePlate3VOs samplePlates "); - ejbQLQuery.append("LEFT JOIN FETCH samplePlates.plategroup3VO "); - ejbQLQuery.append("LEFT JOIN FETCH samplePlates.sampleplateposition3VOs "); - - ejbQLQuery.append("LEFT JOIN FETCH specimens.merge3VOs merges "); - ejbQLQuery.append("LEFT JOIN FETCH specimens.run3VO "); - break; - case PREPARE_EXPERIMENT: - ejbQLQuery.append("LEFT JOIN FETCH experiment.samples samples "); - ejbQLQuery.append("LEFT JOIN FETCH samples.macromolecule3VO "); - ejbQLQuery.append("LEFT JOIN FETCH samples.measurements specimens "); - ejbQLQuery.append("LEFT JOIN FETCH experiment.samplePlate3VOs samplePlates "); - ejbQLQuery.append("LEFT JOIN FETCH samplePlates.plategroup3VO "); - ejbQLQuery.append("LEFT JOIN FETCH samplePlates.sampleplateposition3VOs "); - ejbQLQuery.append("LEFT JOIN FETCH experiment.dataCollections dataCollections "); - ejbQLQuery.append("LEFT JOIN FETCH dataCollections.measurementtodatacollection3VOs "); - break; - default: - break; - } - return ejbQLQuery; - } - - - @Override - public Experiment3VO findByMeasurementId(int measurementId){ - StringBuilder ejbQLQuery = Experiment3ServiceBean.getQueryByScope(ExperimentScope.MEDIUM); - ejbQLQuery.append(" wHERE specimens.measurementId = " + measurementId); - TypedQuery query = entityManager.createQuery(ejbQLQuery.toString(), Experiment3VO.class); - return query.getSingleResult(); - } - - - @Override - public List test(String ejbQL) { - try { - TypedQuery query = entityManager.createQuery(ejbQL, Experiment3VO.class); - return query.getResultList(); - } catch (RuntimeException re) { - log.error("get failed", re); - throw re; - } - } - - @Override - public Experiment3VO findById(Integer experimentId, ExperimentScope scope) { - StringBuilder ejbQLQuery = Experiment3ServiceBean.getQueryByScope(scope); - ejbQLQuery.append("WHERE experiment.experimentId = :experimentId"); - TypedQuery query = entityManager.createQuery(ejbQLQuery.toString(), Experiment3VO.class); - query.setParameter("experimentId", experimentId); - List results = query.getResultList(); - if (results.isEmpty()) { - return null; // handle no-results case - } else { - return results.get(0); - } - } - - - @Override - public List> getExperimentDescription(Integer experimentId) { - Session session = (Session) this.entityManager.getDelegate(); - SQLQuery query = session.createSQLQuery(GetExperimentDescriptionByExperimentId); - query.setParameter("experimentId", experimentId); - return executeSQLQuery(query); - } - - - - /** - * It returns the xml as String of the robot.xml file - * This xml format is used on BsxCube for load sample changer experiments - * - */ - @Override - public String toRobotXML(Integer experimentId, int proposalId, final SaxsDataCollectionComparator... multipleOptions) { - this.setPriorities(experimentId, proposalId, multipleOptions); - return this.toRobotXML(experimentId, proposalId); - } - - @Override - public String toRobotXML(Integer experimentId, int proposalId) { - Experiment3VO experiment = this.findById(experimentId, ExperimentScope.MEDIUM); - List buffers = saxsProposal3Service.findBuffersByProposalId(proposalId); - return RobotXMLParser.toRobotXML(experiment, experiment.getSamplePlate3VOs(), buffers); - } - - - - @Override - public void remove(Experiment3VO experiment) { - Experiment3VO result = entityManager.merge(experiment); - this.entityManager.remove(result); - } - - /** - * Sets the order for each data collection based on SaxsDataCollectionComparator - */ - @Override - public Experiment3VO setPriorities(int experimentId, int proposalId, SaxsDataCollectionComparator[] multipleOptions) { - Experiment3VO experiment = this.findById(experimentId, ExperimentScope.MEDIUM, proposalId); - List dataCollectionList = experiment.getDataCollectionList(); - /** Sort data collections by the priority order **/ - Collections.sort(dataCollectionList, SaxsDataCollectionComparator.compare(experiment, SaxsDataCollectionComparator.getComparator(multipleOptions))); - - int priority = 1; - for (int i = 0; i < dataCollectionList.size(); i++) { - SaxsDataCollection3VO datacollection = dataCollectionList.get(i); - List measurementsToDatacollection = this.getMeasurementToDataCollectionOrdered(datacollection); - for (MeasurementTodataCollection3VO m : measurementsToDatacollection) { - Measurement3VO measurement = experiment.getMeasurementById(m.getMeasurementId()); - measurement.setPriority(priority); - measurement = this.entityManager.merge(measurement); - priority ++; - } - } - return experiment; - - } - /** Sort two measurements by their dataCollectionOrder field **/ - private static Comparator MeasurementTodataCollectionComparatorOrder = new Comparator(){ - public int compare(MeasurementTodataCollection3VO o1, MeasurementTodataCollection3VO o2) { - return o1.getDataCollectionOrder() - o2.getDataCollectionOrder(); - } - }; - - /** Sort a data collection by its dataCollectionOrder field **/ - private List getMeasurementToDataCollectionOrdered(SaxsDataCollection3VO dataCollection){ - Set measurements = dataCollection.getMeasurementtodatacollection3VOs(); - List list = Arrays.asList(measurements.toArray(new MeasurementTodataCollection3VO[measurements.size()])); - Collections.sort(list, MeasurementTodataCollectionComparatorOrder); - return list; - } - - @Override - public void saveStructure(Integer macromoleculeId, String fileName, String filePath, String type, String symmetry, String multiplicity) { - Structure3VO structure = new Structure3VO(); - structure.setMacromoleculeId(macromoleculeId); - structure.setFilePath(filePath); - structure.setName(fileName); - structure.setType(type); - structure.setSymmetry(symmetry); - structure.setMultiplicity(multiplicity); - structure.setCreationDate(GregorianCalendar.getInstance().getTime()); - entityManager.merge(structure); - } - - @Override - public void removeStructure(int structureId) { - Structure3VO structure = this.entityManager.find(Structure3VO.class, structureId); - this.entityManager.remove(structure); - } - - @Override - public Structure3VO findStructureById(int structureId) { - return this.entityManager.find(Structure3VO.class, structureId); - } - - @Override - public void removeStoichiometry(int stoichiometryId) { - Stoichiometry3VO st = this.entityManager.find(Stoichiometry3VO.class, stoichiometryId); - this.entityManager.remove(st); - } - - @Override - public void saveStoichiometry(int macromoleculeId, int hostmacromoleculeId, String ratio, String comments) { - Stoichiometry3VO stoi = new Stoichiometry3VO(); - stoi.setMacromolecule3VOByHostMacromoleculeId(macromoleculeId); - stoi.setHostmacromoleculeId(hostmacromoleculeId); - stoi.setRatio(ratio); - this.entityManager.merge(stoi); - } - -// @Override -// public Macromolecule3VO findMacromoleculeById(Integer macromoleculeId) { -// StringBuilder ejbQLQuery = new StringBuilder(); -// ejbQLQuery.append("SELECT DISTINCT(macromolecule) FROM Macromolecule3VO macromolecule "); -// ejbQLQuery.append("LEFT JOIN FETCH macromolecule.stoichiometry st "); -// ejbQLQuery.append("LEFT JOIN FETCH st.macromolecule3VO "); -// ejbQLQuery.append("LEFT JOIN FETCH macromolecule.structure3VOs "); -// ejbQLQuery.append("WHERE macromolecule.macromoleculeId = :macromoleculeId"); -// TypedQuery query = entityManager.createQuery(ejbQLQuery.toString(), Macromolecule3VO.class); -// query.setParameter("macromoleculeId", macromoleculeId); -// return query.getSingleResult(); -// } - - @Override - public Structure3VO findStructureByFilePathId(String filePath, int experimentId) { -// Experiment3VO experiment = this.findById(experimentId, ExperimentScope.MINIMAL); -// List macromolecules = saxsProposal3Service.findMacromoleculesByProposalId(experiment.getProposalId()); -// for (Macromolecule3VO macromolecule3vo : macromolecules) { -// -// } - return null; - } - - @Override - public void saveStructure(Structure3VO structure3vo) { - this.entityManager.merge(structure3vo); - - } -} +/******************************************************************************* + * This file is part of ISPyB. + * + * ISPyB is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * ISPyB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with ISPyB. If not, see . + * + * Contributors : S. Delageniere, R. Leal, L. Launer, K. Levik, S. Veyrier, P. Brenchereau, M. Bodin, A. De Maria Antolinos + ******************************************************************************************************************************/ + +package ispyb.server.biosaxs.services.core.experiment; + + +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; +import java.util.GregorianCalendar; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.ejb.EJB; +import javax.ejb.Stateless; +import javax.naming.NamingException; +import javax.persistence.EntityManager; +import javax.persistence.NoResultException; +import javax.persistence.PersistenceContext; +import javax.persistence.TypedQuery; + +import org.apache.log4j.Logger; +import org.hibernate.SQLQuery; +import org.hibernate.Session; + +import ispyb.server.biosaxs.services.core.ExperimentScope; +import ispyb.server.biosaxs.services.core.plateType.PlateType3Service; +import ispyb.server.biosaxs.services.core.proposal.SaxsProposal3Service; +import ispyb.server.biosaxs.vos.assembly.Stoichiometry3VO; +import ispyb.server.biosaxs.vos.assembly.Structure3VO; +import ispyb.server.biosaxs.vos.dataAcquisition.Buffer3VO; +import ispyb.server.biosaxs.vos.dataAcquisition.Experiment3VO; +import ispyb.server.biosaxs.vos.dataAcquisition.Measurement3VO; +import ispyb.server.biosaxs.vos.datacollection.MeasurementTodataCollection3VO; +import ispyb.server.biosaxs.vos.datacollection.SaxsDataCollection3VO; +import ispyb.server.biosaxs.vos.utils.comparator.SaxsDataCollectionComparator; +import ispyb.server.biosaxs.vos.utils.parser.RobotXMLParser; +import ispyb.server.mx.services.ws.rest.WsServiceBean; + + + +@Stateless +public class Experiment3ServiceBean extends WsServiceBean implements Experiment3Service, Experiment3ServiceLocal { + + private final static Logger log = Logger.getLogger(Experiment3ServiceBean.class); + + + /** + * QUERIES + */ + private String GetExperimentDescriptionByExperimentId = getViewExperimentDescriptionTableQuery() + " where experimentId = :experimentId"; + + + + @PersistenceContext(unitName = "ispyb_db") + private EntityManager entityManager; + + @EJB + private SaxsProposal3Service saxsProposal3Service; + + @EJB + private PlateType3Service plateType3Service; + + @Override + public void persist(Experiment3VO transientInstance) { + try { + entityManager.persist(transientInstance); + } catch (RuntimeException re) { + throw re; + } + } + + @Override + public Experiment3VO merge(Experiment3VO detachedInstance) { + try { + Experiment3VO result = entityManager.merge(detachedInstance); + return result; + } catch (RuntimeException re) { + throw re; + } + } + + private String getViewExperimentDescriptionTableQuery(){ + return this.getQueryFromResourceFile("/queries/biosaxs/Experiment3ServiceBean/getViewExperimentDescriptionTableQuery.sql"); + } + + + @Override + public List findByProposalId(int proposalId, ExperimentScope scope) { + StringBuilder ejbQLQuery = Experiment3ServiceBean.getQueryByScope(scope); + ejbQLQuery.append("WHERE experiment.proposalId = :proposalId"); + TypedQuery query = entityManager.createQuery(ejbQLQuery.toString(), Experiment3VO.class).setParameter("proposalId", proposalId); + return query.getResultList(); + } + + @Override + public Experiment3VO findById(Integer experimentId, ExperimentScope scope, Integer proposalId) { + try { + StringBuilder ejbQLQuery = Experiment3ServiceBean.getQueryByScope(scope); + ejbQLQuery.append("WHERE experiment.experimentId = :experimentId "); + // if coming from manager account, proposalId can be null + if (proposalId != null) { + ejbQLQuery.append(" and experiment.proposalId = :proposalId"); + } + TypedQuery query = entityManager.createQuery(ejbQLQuery.toString(), Experiment3VO.class); + query.setParameter("experimentId", experimentId); + if (proposalId != null) { + query.setParameter("proposalId", proposalId); + } + return query.getSingleResult(); + } catch (NoResultException e) { + return null; + } + } + + public Experiment3VO initPlates(Experiment3VO vo){ + try { + vo.setPlatetype3VOs(this.plateType3Service.findAll()); + } catch (Exception e) { + e.printStackTrace(); + } + return vo; + } + + public static StringBuilder getQueryByScope(ExperimentScope scope){ + StringBuilder ejbQLQuery = new StringBuilder(); + ejbQLQuery.append("SELECT DISTINCT(experiment) FROM Experiment3VO experiment "); + + switch (scope) { + case MINIMAL: + break; + case MEDIUM: + ejbQLQuery.append("LEFT JOIN FETCH experiment.samples samples "); + ejbQLQuery.append("LEFT JOIN FETCH samples.macromolecule3VO macromolecule "); + ejbQLQuery.append("LEFT JOIN FETCH macromolecule.stoichiometry st "); +// ejbQLQuery.append("LEFT JOIN FETCH st.macromolecule3VO "); + ejbQLQuery.append("LEFT JOIN FETCH macromolecule.structure3VOs "); + ejbQLQuery.append("LEFT JOIN FETCH samples.measurements specimens "); + ejbQLQuery.append("LEFT JOIN FETCH experiment.samplePlate3VOs samplePlates "); + ejbQLQuery.append("LEFT JOIN FETCH samplePlates.plategroup3VO "); + ejbQLQuery.append("LEFT JOIN FETCH samplePlates.sampleplateposition3VOs "); + + ejbQLQuery.append("LEFT JOIN FETCH specimens.merge3VOs merges "); + ejbQLQuery.append("LEFT JOIN FETCH specimens.run3VO "); + break; + case PREPARE_EXPERIMENT: + ejbQLQuery.append("LEFT JOIN FETCH experiment.samples samples "); + ejbQLQuery.append("LEFT JOIN FETCH samples.macromolecule3VO "); + ejbQLQuery.append("LEFT JOIN FETCH samples.measurements specimens "); + ejbQLQuery.append("LEFT JOIN FETCH experiment.samplePlate3VOs samplePlates "); + ejbQLQuery.append("LEFT JOIN FETCH samplePlates.plategroup3VO "); + ejbQLQuery.append("LEFT JOIN FETCH samplePlates.sampleplateposition3VOs "); + ejbQLQuery.append("LEFT JOIN FETCH experiment.dataCollections dataCollections "); + ejbQLQuery.append("LEFT JOIN FETCH dataCollections.measurementtodatacollection3VOs "); + break; + default: + break; + } + return ejbQLQuery; + } + + + @Override + public Experiment3VO findByMeasurementId(int measurementId){ + StringBuilder ejbQLQuery = Experiment3ServiceBean.getQueryByScope(ExperimentScope.MEDIUM); + ejbQLQuery.append(" wHERE specimens.measurementId = " + measurementId); + TypedQuery query = entityManager.createQuery(ejbQLQuery.toString(), Experiment3VO.class); + return query.getSingleResult(); + } + + + @Override + public List test(String ejbQL) { + try { + TypedQuery query = entityManager.createQuery(ejbQL, Experiment3VO.class); + return query.getResultList(); + } catch (RuntimeException re) { + log.error("get failed", re); + throw re; + } + } + + @Override + public Experiment3VO findById(Integer experimentId, ExperimentScope scope) { + StringBuilder ejbQLQuery = Experiment3ServiceBean.getQueryByScope(scope); + ejbQLQuery.append("WHERE experiment.experimentId = :experimentId"); + TypedQuery query = entityManager.createQuery(ejbQLQuery.toString(), Experiment3VO.class); + query.setParameter("experimentId", experimentId); + List results = query.getResultList(); + if (results.isEmpty()) { + return null; // handle no-results case + } else { + return results.get(0); + } + } + + + @Override + public List> getExperimentDescription(Integer experimentId) { + Session session = (Session) this.entityManager.getDelegate(); + SQLQuery query = session.createSQLQuery(GetExperimentDescriptionByExperimentId); + query.setParameter("experimentId", experimentId); + return executeSQLQuery(query); + } + + + + /** + * It returns the xml as String of the robot.xml file + * This xml format is used on BsxCube for load sample changer experiments + * + */ + @Override + public String toRobotXML(Integer experimentId, int proposalId, final SaxsDataCollectionComparator... multipleOptions) { + this.setPriorities(experimentId, proposalId, multipleOptions); + return this.toRobotXML(experimentId, proposalId); + } + + @Override + public String toRobotXML(Integer experimentId, int proposalId) { + Experiment3VO experiment = this.findById(experimentId, ExperimentScope.MEDIUM); + List buffers = saxsProposal3Service.findBuffersByProposalId(proposalId); + return RobotXMLParser.toRobotXML(experiment, experiment.getSamplePlate3VOs(), buffers); + } + + + + @Override + public void remove(Experiment3VO experiment) { + Experiment3VO result = entityManager.merge(experiment); + this.entityManager.remove(result); + } + + /** + * Sets the order for each data collection based on SaxsDataCollectionComparator + */ + @Override + public Experiment3VO setPriorities(int experimentId, int proposalId, SaxsDataCollectionComparator[] multipleOptions) { + Experiment3VO experiment = this.findById(experimentId, ExperimentScope.MEDIUM, proposalId); + List dataCollectionList = experiment.getDataCollectionList(); + /** Sort data collections by the priority order **/ + Collections.sort(dataCollectionList, SaxsDataCollectionComparator.compare(experiment, SaxsDataCollectionComparator.getComparator(multipleOptions))); + + int priority = 1; + for (int i = 0; i < dataCollectionList.size(); i++) { + SaxsDataCollection3VO datacollection = dataCollectionList.get(i); + List measurementsToDatacollection = this.getMeasurementToDataCollectionOrdered(datacollection); + for (MeasurementTodataCollection3VO m : measurementsToDatacollection) { + Measurement3VO measurement = experiment.getMeasurementById(m.getMeasurementId()); + measurement.setPriority(priority); + measurement = this.entityManager.merge(measurement); + priority ++; + } + } + return experiment; + + } + /** Sort two measurements by their dataCollectionOrder field **/ + private static Comparator MeasurementTodataCollectionComparatorOrder = new Comparator(){ + public int compare(MeasurementTodataCollection3VO o1, MeasurementTodataCollection3VO o2) { + return o1.getDataCollectionOrder() - o2.getDataCollectionOrder(); + } + }; + + /** Sort a data collection by its dataCollectionOrder field **/ + private List getMeasurementToDataCollectionOrdered(SaxsDataCollection3VO dataCollection){ + Set measurements = dataCollection.getMeasurementtodatacollection3VOs(); + List list = Arrays.asList(measurements.toArray(new MeasurementTodataCollection3VO[measurements.size()])); + Collections.sort(list, MeasurementTodataCollectionComparatorOrder); + return list; + } + + @Override + public void saveStructure(Integer macromoleculeId, String fileName, String filePath, String type, String symmetry, String multiplicity) { + Structure3VO structure = new Structure3VO(); + structure.setMacromoleculeId(macromoleculeId); + structure.setFilePath(filePath); + structure.setName(fileName); + structure.setType(type); + structure.setSymmetry(symmetry); + structure.setMultiplicity(multiplicity); + structure.setCreationDate(GregorianCalendar.getInstance().getTime()); + entityManager.merge(structure); + } + + @Override + public void removeStructure(int structureId) { + Structure3VO structure = this.entityManager.find(Structure3VO.class, structureId); + this.entityManager.remove(structure); + } + + @Override + public Structure3VO findStructureById(int structureId) { + return this.entityManager.find(Structure3VO.class, structureId); + } + + @Override + public void removeStoichiometry(int stoichiometryId) { + Stoichiometry3VO st = this.entityManager.find(Stoichiometry3VO.class, stoichiometryId); + this.entityManager.remove(st); + } + + @Override + public void saveStoichiometry(int macromoleculeId, int hostmacromoleculeId, String ratio, String comments) { + Stoichiometry3VO stoi = new Stoichiometry3VO(); + stoi.setMacromolecule3VOByHostMacromoleculeId(macromoleculeId); + stoi.setHostmacromoleculeId(hostmacromoleculeId); + stoi.setRatio(ratio); + this.entityManager.merge(stoi); + } + +// @Override +// public Macromolecule3VO findMacromoleculeById(Integer macromoleculeId) { +// StringBuilder ejbQLQuery = new StringBuilder(); +// ejbQLQuery.append("SELECT DISTINCT(macromolecule) FROM Macromolecule3VO macromolecule "); +// ejbQLQuery.append("LEFT JOIN FETCH macromolecule.stoichiometry st "); +// ejbQLQuery.append("LEFT JOIN FETCH st.macromolecule3VO "); +// ejbQLQuery.append("LEFT JOIN FETCH macromolecule.structure3VOs "); +// ejbQLQuery.append("WHERE macromolecule.macromoleculeId = :macromoleculeId"); +// TypedQuery query = entityManager.createQuery(ejbQLQuery.toString(), Macromolecule3VO.class); +// query.setParameter("macromoleculeId", macromoleculeId); +// return query.getSingleResult(); +// } + + @Override + public Structure3VO findStructureByFilePathId(String filePath, int experimentId) { +// Experiment3VO experiment = this.findById(experimentId, ExperimentScope.MINIMAL); +// List macromolecules = saxsProposal3Service.findMacromoleculesByProposalId(experiment.getProposalId()); +// for (Macromolecule3VO macromolecule3vo : macromolecules) { +// +// } + return null; + } + + @Override + public void saveStructure(Structure3VO structure3vo) { + this.entityManager.merge(structure3vo); + + } +} diff --git a/ispyb-ejb/src/main/java/ispyb/server/biosaxs/services/core/robot/Robot3ServiceBean.java b/ispyb-ejb/src/main/java/ispyb/server/biosaxs/services/core/robot/Robot3ServiceBean.java index b05cecc55..60a952618 100644 --- a/ispyb-ejb/src/main/java/ispyb/server/biosaxs/services/core/robot/Robot3ServiceBean.java +++ b/ispyb-ejb/src/main/java/ispyb/server/biosaxs/services/core/robot/Robot3ServiceBean.java @@ -1,1045 +1,1045 @@ -/******************************************************************************* - * This file is part of ISPyB. - * - * ISPyB is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * ISPyB is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with ISPyB. If not, see . - * - * Contributors : S. Delageniere, R. Leal, L. Launer, K. Levik, S. Veyrier, P. Brenchereau, M. Bodin, A. De Maria Antolinos - ******************************************************************************************************************************/ - -package ispyb.server.biosaxs.services.core.robot; - -import ispyb.common.util.Constants; -import ispyb.server.biosaxs.services.core.ExperimentScope; -import ispyb.server.biosaxs.services.core.experiment.Experiment3ServiceLocal; -import ispyb.server.biosaxs.services.core.measurement.Measurement3ServiceLocal; -import ispyb.server.biosaxs.services.core.measurementToDataCollection.MeasurementToDatacollection3ServiceLocal; -import ispyb.server.biosaxs.services.core.plateType.PlateType3ServiceLocal; -import ispyb.server.biosaxs.services.core.proposal.SaxsProposal3ServiceLocal; -import ispyb.server.biosaxs.services.core.samplePlate.Sampleplate3ServiceLocal; -import ispyb.server.biosaxs.services.core.specimen.Specimen3Service; -import ispyb.server.biosaxs.vos.assembly.Macromolecule3VO; -import ispyb.server.biosaxs.vos.dataAcquisition.Buffer3VO; -import ispyb.server.biosaxs.vos.dataAcquisition.Experiment3VO; -import ispyb.server.biosaxs.vos.dataAcquisition.Measurement3VO; -import ispyb.server.biosaxs.vos.dataAcquisition.Specimen3VO; -import ispyb.server.biosaxs.vos.dataAcquisition.plate.PlateGroup3VO; -import ispyb.server.biosaxs.vos.dataAcquisition.plate.Platetype3VO; -import ispyb.server.biosaxs.vos.dataAcquisition.plate.Sampleplate3VO; -import ispyb.server.biosaxs.vos.dataAcquisition.plate.Sampleplateposition3VO; -import ispyb.server.biosaxs.vos.datacollection.MeasurementTodataCollection3VO; -import ispyb.server.biosaxs.vos.datacollection.SaxsDataCollection3VO; - -import java.lang.reflect.Modifier; -import java.util.ArrayList; -import java.util.Calendar; -import java.util.Date; -import java.util.GregorianCalendar; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; - -import javax.ejb.EJB; -import javax.ejb.Stateless; -import javax.persistence.EntityManager; -import javax.persistence.PersistenceContext; - -import org.apache.log4j.Logger; - -import com.google.gson.GsonBuilder; - -@Stateless -public class Robot3ServiceBean implements Robot3Service, Robot3ServiceLocal { - - private final static Logger log = Logger.getLogger(Robot3ServiceBean.class); - - /** The specimen3 service local. */ - @EJB - private Measurement3ServiceLocal measurement3ServiceLocal; - - /** The experiment3 service local. */ - @EJB - private Experiment3ServiceLocal experiment3ServiceLocal; - - /** The macromolecule3 service. */ - @EJB - private SaxsProposal3ServiceLocal proposal3Service; - - /** The sample plate3 service. */ - @EJB - private Sampleplate3ServiceLocal samplePlate3Service; - - /** The plate type3 service. */ - @EJB - private PlateType3ServiceLocal plateType3Service; - - /** The measurement to data collection3 service. */ - @EJB - private MeasurementToDatacollection3ServiceLocal measurementToDataCollection3Service; - - @EJB - private Specimen3Service specimen3Service; - - /** The entity manager. */ - @PersistenceContext(unitName = "ispyb_db") - private EntityManager entityManager; - - /** The now. */ - protected static Calendar NOW; - - /** - * Gets the now. - * - * @return the now - */ - private Date getNow() { - NOW = GregorianCalendar.getInstance(); - return NOW.getTime(); - } - - /** - * Gets the buffers. It returns a hashmap - * - * @param samples - * the samples - * @return the buffers - */ - private HashMap parseBuffers(ArrayList> samples, int proposalId) { - List buffersProposal = proposal3Service.findBuffersByProposalId(proposalId); - Robot3ServiceBean.log.info("----------------- parseBuffers ------------ "); - HashMap bufferNameToBuffer3VO = new HashMap(); - - /** Store the name of the macromolecules **/ - HashMap bufferNameToBufferAcronym = new HashMap(); - - /** - * It is possible that users make a mistake and for the same specimen in the same position they set a different - * buffer's name - */ - HashMap bufferPositionToBufferAcronym = new HashMap(); - - for (HashMap sample : samples) { - if (sample.get("type").equals("Buffer")) { - Buffer3VO buffer = new Buffer3VO(); - buffer.setName(sample.get("macromolecule").trim()); - buffer.setAcronym(sample.get("macromolecule").trim()); - buffer.setComment(sample.get("comments")); - - Robot3ServiceBean.log.info("sample: " + sample.toString()); - Robot3ServiceBean.log.info("buffername: " + sample.get("buffername")); - - String positionCode = this.getSamplePositionKey(sample); - if (bufferPositionToBufferAcronym.get(positionCode) == null) { - bufferNameToBufferAcronym.put(sample.get("buffername").trim(), sample.get("macromolecule").trim()); - bufferPositionToBufferAcronym.put(positionCode, sample.get("macromolecule").trim()); - } else { - Robot3ServiceBean.log.info("Buffers detected in the same position with different macromolecule name"); - log.warn("Buffers detected in the same position with different macromolecule name"); - bufferNameToBufferAcronym.put(sample.get("buffername").trim(), - bufferPositionToBufferAcronym.get(positionCode)); - } - } - } - - for (String bufferName : bufferNameToBufferAcronym.keySet()) { - Buffer3VO bufferExists = this - .getBufferByAcronym(buffersProposal, bufferNameToBufferAcronym.get(bufferName)); - if (bufferExists != null) { - bufferNameToBuffer3VO.put(bufferName, bufferExists); - } else { - Buffer3VO buffer = new Buffer3VO(); - buffer.setAcronym(bufferNameToBufferAcronym.get(bufferName)); - buffer.setName(bufferNameToBufferAcronym.get(bufferName)); - bufferNameToBuffer3VO.put(bufferName, buffer); - } - } - - return bufferNameToBuffer3VO; - } - - /** If the acronym exists in the list the macromolecule is returned **/ - private Macromolecule3VO getMacromoleculeByAcronym(List macromolecules, String acronym) { - for (Macromolecule3VO macromolecule : macromolecules) { - if (macromolecule.getAcronym().toLowerCase().equals(acronym.toLowerCase())) { - return macromolecule; - } - } - return null; - } - - /** If the acronym exists in the list the buffer is returned **/ - private Buffer3VO getBufferByAcronym(List buffers, String acronym) { - for (Buffer3VO buffer : buffers) { - if (buffer.getAcronym().toLowerCase().equals(acronym.toLowerCase())) { - return buffer; - } - } - return null; - } - - /** - * Gets the macromolecules. If there are two with the same name it means that they are the same macromolecules - * - * @param samples - * the samples - * @return the macromolecules - */ - private HashMap parseMacromolecules(ArrayList> samples, - int proposalId) { - List macromoleculesProposalList = proposal3Service.findMacromoleculesByProposalId(proposalId); - Robot3ServiceBean.log.info("----------------- parseMacromolecules ------------ "); - HashMap acronymToMacromolecule3VO = new HashMap(); - /** Store the name of the macromolecules **/ - HashSet macromoleculeAcronyms = new HashSet(); - for (HashMap sample : samples) { - if (sample.get("type").equals("Sample")) { - /** If not macromolecule is defined we use the code **/ - if (sample.get("macromolecule") == null) { - macromoleculeAcronyms.add(sample.get("code").trim()); - } else { - macromoleculeAcronyms.add(sample.get("macromolecule").trim()); - } - } - } - - for (String acronym : macromoleculeAcronyms) { - Macromolecule3VO macromoleculeExists = this.getMacromoleculeByAcronym(macromoleculesProposalList, acronym); - if (macromoleculeExists != null) { - acronymToMacromolecule3VO.put(acronym, macromoleculeExists); - } else { - Macromolecule3VO macromolecule = new Macromolecule3VO(); - macromolecule.setAcronym(acronym); - macromolecule.setName(acronym); - acronymToMacromolecule3VO.put(acronym, macromolecule); - } - } - return acronymToMacromolecule3VO; - } - - private String getSamplePositionKey(HashMap sample) { - return sample.get("plate") + "_" + sample.get("row") + "_" + sample.get("well"); - } - - /** - * Gets the specimens. - * - * @param samples - * the samples - * @param acronymToMacromolecule3VO - * the macromolecules - * @param bufferNameToBuffer3VO - * the buffers - * @param plateIndexToSamplePlate3VO - * the plates - * @return HashMap where String is bufferName for the buffers and position for the Samples - */ - private HashMap getSpecimens(ArrayList> samples, - HashMap acronymToMacromolecule3VO, - HashMap bufferNameToBuffer3VO, HashMap plateIndexToSamplePlate3VO) { - - HashMap specimenIdToSpecimen3VO = new HashMap(); - - /** Looking for positions **/ - for (HashMap sample : samples) { - // String positionCode = sample.get("plate") + "_" + sample.get("row") + "_" + sample.get("well"); - String positionCode = this.getSamplePositionKey(sample); - Specimen3VO specimen = null; - - if (specimenIdToSpecimen3VO.get(positionCode) == null) { - /** Creating new Sample for the experiment **/ - specimen = new Specimen3VO(); - /** Buffer **/ - Buffer3VO buffer = bufferNameToBuffer3VO.get(sample.get("buffername").trim()); - if (buffer == null) { - log.error("Buffer not found with buffer name: " + sample.get("buffername")); - } - specimen.setBufferId(buffer.getBufferId()); - - /** Macromolecule **/ - if (sample.get("type").equals("Sample")) { - Macromolecule3VO macromolecule3VO = acronymToMacromolecule3VO.get(sample.get("macromolecule")); - specimen.setMacromolecule3VO(macromolecule3VO); - } - - /** Position **/ - if (plateIndexToSamplePlate3VO != null) { - Sampleplate3VO plate = plateIndexToSamplePlate3VO.get(sample.get("plate")); - if (plate != null) { - Sampleplateposition3VO position = new Sampleplateposition3VO(); - position.setSamplePlateId(plate.getSamplePlateId()); - position.setRowNumber(Integer.parseInt(sample.get("row"))); - position.setColumnNumber(Integer.parseInt(sample.get("well"))); - position = entityManager.merge(position); - specimen.setSampleplateposition3VO(position); - } - } - /** Specimen properties **/ - specimen.setConcentration(sample.get("concentration")); - specimen.setVolume(sample.get("volume")); -// specimen.setCode(sample.get("macromolecule")); - } else { - /** Updating volume to load **/ - specimen = specimenIdToSpecimen3VO.get(positionCode); - specimen.setVolume(String.valueOf(Integer.parseInt(specimen.getVolume()) - + Integer.parseInt(sample.get("volume")))); - } - - /** it is a buffer we keep the code of the buffername **/ - if (specimen.getMacromolecule3VO() == null) { - specimenIdToSpecimen3VO.put(sample.get("buffername"), specimen); - } else { - specimenIdToSpecimen3VO.put(positionCode, specimen); - } - } - return specimenIdToSpecimen3VO; - } - - /** - * Gets the plates. This should be changed depending on the bm configuration - * - * @param samples - * the samples - * @param storageTemperature - * the storage temperature - * @return the plates - */ -// private HashMap parsePlates(ArrayList> samples, String storageTemperature) { -// /** There is always three Plates **/ -// HashMap platePositionToSamplePlate3VO = new HashMap(); -// -// /** Plate 1 **/ -// Platetype3VO type = this.plateType3Service.findById(1); -// Sampleplate3VO samplePlate_1 = new Sampleplate3VO(); -// samplePlate_1.setStorageTemperature(storageTemperature); -// samplePlate_1.setName(type.getName()); -// samplePlate_1.setSlotPositionRow("1"); -// samplePlate_1.setSlotPositionColumn("1"); -// samplePlate_1.setPlatetype3VO(type); -// platePositionToSamplePlate3VO.put("1", samplePlate_1); -// -// /** Plate 2 **/ -// Platetype3VO type_2 = this.plateType3Service.findById(2); -// Sampleplate3VO samplePlate_2 = new Sampleplate3VO(); -// samplePlate_2.setStorageTemperature(storageTemperature); -// samplePlate_2.setName(type_2.getName()); -// samplePlate_2.setPlatetype3VO(type_2); -// samplePlate_2.setSlotPositionRow("1"); -// samplePlate_2.setSlotPositionColumn("2"); -// platePositionToSamplePlate3VO.put("2", samplePlate_2); -// -// /** Plate 3 **/ -// Platetype3VO type_3 = this.plateType3Service.findById(4); -// Sampleplate3VO samplePlate_3 = new Sampleplate3VO(); -// samplePlate_3.setStorageTemperature(storageTemperature); -// samplePlate_3.setName(type_3.getName()); -// samplePlate_3.setSlotPositionRow("1"); -// samplePlate_3.setSlotPositionColumn("3"); -// samplePlate_3.setPlatetype3VO(type_3); -// platePositionToSamplePlate3VO.put("3", samplePlate_3); -// -// return platePositionToSamplePlate3VO; -// } - - /** - * Parses the specimen. - * - * @param sample - * the sample - * @return the specimen3 vo - */ - private Measurement3VO parseSpecimen(HashMap sample) { - String waittime = sample.get("waittime"); - - /** - * Problem is that from BsxCube there is not any parameters for volume, volume in this case means volumeToLoad - * however from WUI there are both for indicating in buffers specially the volume and the volumeToLoad - * **/ - String volumeToLoad = sample.get("volume"); - if (sample.containsKey("volumeToLoad")) { - volumeToLoad = sample.get("volumeToLoad"); - } - String comments = sample.get("comments"); - String transmission = sample.get("transmission"); - String viscosity = sample.get("viscosity"); - String flow = sample.get("flow"); - String code = sample.get("code"); - - Measurement3VO measurement = new Measurement3VO(); -// measurement.setCode(code); - measurement.setWaitTime(waittime); - measurement.setVolumeToLoad(volumeToLoad); - measurement.setComment(comments); - measurement.setTransmission(transmission); - measurement.setViscosity(viscosity); - measurement.setFlow(Boolean.valueOf(flow)); - - return measurement; - } - - /** - * Creates the measurements. - * - * @param samples - * the samples - * @param sample3vOs - * the sample3v os - * @param experiment - * the experiment - * @param buffers - * the buffers - * @param mode - * the mode - * @param extraFlowTime - * the extra flow time - * @return the list - * @throws Exception - * the exception - */ - public List createMeasurements(ArrayList> samples, - HashMap sample3vOs, Experiment3VO experiment, HashMap buffers, - String mode, String extraFlowTime) throws Exception { - List dataCollections = new ArrayList(); - - /** Creating for measurements **/ - for (HashMap sample : samples) { - String positionCode = sample.get("plate") + "_" + sample.get("row") + "_" + sample.get("well"); - if (sample.get("type").equals("Sample")) { - - Specimen3VO specimen = sample3vOs.get(positionCode); - String exposureTemperature = sample.get("SEUtemperature"); - Measurement3VO measurement = this.parseSpecimen(sample); - measurement.setSpecimenId(specimen.getSpecimenId()); - measurement.setExtraFlowTime(extraFlowTime); - measurement.setExposureTemperature(exposureTemperature); - -// try{ - /** Code should be runNumber otherwise set as acronym + concentration **/ -// if (sample.get("code") != null){ -// measurement.setCode(sample.get("code")); -// } -// else{ -// measurement.setCode(specimen.getMacromolecule3VO().getAcronym() + "_" + specimen.getConcentration()); -// } -// } -// catch(Exception exp){ -// exp.printStackTrace(); -// System.out.println("It has not been possible to set the code to the measurement"); -// } - - /** Buffer **/ - Specimen3VO buffer = sample3vOs.get(sample.get("buffername")); - - if (buffer == null) { - throw new Exception("Buffer not found: " + sample.get("buffername")); - } - - /** MODE: Before and After **/ - if (mode.equals("BeforeAndAfter")) { - for (HashMap sampleBuffer : samples) { - if (sampleBuffer.get("type").equals("Buffer")) { - if (sampleBuffer.get("buffername").equals(sample.get("buffername"))) { - - /** Measurement Before **/ - Measurement3VO measurementBufferBefore = this.parseSpecimen(sampleBuffer); - measurementBufferBefore.setSpecimenId(buffer.getSpecimenId()); - measurementBufferBefore.setExtraFlowTime(extraFlowTime); - measurementBufferBefore.setExposureTemperature(exposureTemperature); - - /** Copying parameters of the sample measurement **/ - /** Update, buffer's parameter may be different **/ - measurementBufferBefore.setVolumeToLoad(measurement.getVolumeToLoad()); - measurementBufferBefore.setTransmission(measurement.getTransmission()); - measurementBufferBefore.setFlow(measurement.getFlow()); - measurementBufferBefore.setViscosity(measurement.getViscosity()); - measurementBufferBefore.setWaitTime(measurement.getWaitTime()); - - /** But not exposure temperature **/ - measurementBufferBefore.setExposureTemperature(measurement.getExposureTemperature()); - - measurementBufferBefore = this.measurement3ServiceLocal.merge(measurementBufferBefore); - buffer.getMeasurements().add(measurementBufferBefore); - - /** Sample **/ - measurement = this.measurement3ServiceLocal.merge(measurement); - specimen.getMeasurements().add(measurement); - - /** After **/ - Measurement3VO specimenBufferAfter = this.parseSpecimen(sampleBuffer); - specimenBufferAfter.setSpecimenId(buffer.getSpecimenId()); - specimenBufferAfter.setExtraFlowTime(extraFlowTime); - specimenBufferAfter.setExposureTemperature(exposureTemperature); - /** Specimen After Code **/ - // specimenBufferAfter.setCode(experiment.getCodeSpecimen(buffer, specimenBufferAfter)); - - /** Copying parameters of the sample measurement **/ - specimenBufferAfter.setVolumeToLoad(measurement.getVolumeToLoad()); - specimenBufferAfter.setTransmission(measurement.getTransmission()); - specimenBufferAfter.setFlow(measurement.getFlow()); - specimenBufferAfter.setViscosity(measurement.getViscosity()); - specimenBufferAfter.setWaitTime(measurement.getWaitTime()); - - specimenBufferAfter.setExposureTemperature(measurement.getExposureTemperature()); - - specimenBufferAfter = this.measurement3ServiceLocal.merge(specimenBufferAfter); - buffer.getMeasurements().add(specimenBufferAfter); - - /** Creating Data Collections **/ - List dataCollectionSpecimens = new ArrayList(); - dataCollectionSpecimens.add(measurementBufferBefore); - dataCollectionSpecimens.add(measurement); - dataCollectionSpecimens.add(specimenBufferAfter); - dataCollections.add(this.createDataCollection(dataCollectionSpecimens, experiment)); - } - } - } - } - - if (mode.equals("After")) { - for (HashMap sampleBuffer : samples) { - if (sampleBuffer.get("type").equals("Buffer")) { - if (sampleBuffer.get("buffername").equals(sample.get("buffername"))) { - - /** Sample **/ - measurement = this.measurement3ServiceLocal.merge(measurement); - - /** After **/ - Measurement3VO measurementBufferAfter = this.parseSpecimen(sampleBuffer); - measurementBufferAfter.setSpecimenId(buffer.getSpecimenId()); - measurementBufferAfter.setExtraFlowTime(extraFlowTime); - measurementBufferAfter.setExposureTemperature(exposureTemperature); - - /** Specimen code **/ - // measurementBufferAfter.setCode(experiment.getCodeSpecimen(buffer, - // measurementBufferAfter)); - measurementBufferAfter = this.measurement3ServiceLocal.merge(measurementBufferAfter); - - /** Creating Data Collections **/ - List dataCollectionSpecimens = new ArrayList(); - dataCollectionSpecimens.add(measurement); - dataCollections.add(this.createDataCollection(dataCollectionSpecimens, experiment)); - } - } - } - } - - if (mode.equals("Before")) { - for (HashMap sampleBuffer : samples) { - if (sampleBuffer.get("type").equals("Buffer")) { - if (sampleBuffer.get("buffername").equals(sample.get("buffername"))) { - Measurement3VO specimenBufferBefore = this.parseSpecimen(sampleBuffer); - specimenBufferBefore.setSpecimenId(buffer.getSpecimenId()); - specimenBufferBefore.setExtraFlowTime(extraFlowTime); - specimenBufferBefore.setExposureTemperature(exposureTemperature); - - /** Specimen Before **/ - // specimenBufferBefore.setCode(experiment.getCodeSpecimen(buffer, - // specimenBufferBefore)); - specimenBufferBefore = this.measurement3ServiceLocal.merge(specimenBufferBefore); - buffer.getMeasurements().add(specimenBufferBefore); - - /** Sample **/ - measurement = this.measurement3ServiceLocal.merge(measurement); - specimen.getMeasurements().add(measurement); - - /** Creating Data Collections **/ - List dataCollectionSpecimens = new ArrayList(); - dataCollectionSpecimens.add(specimenBufferBefore); - dataCollectionSpecimens.add(measurement); - dataCollections.add(this.createDataCollection(dataCollectionSpecimens, experiment)); - } - } - } - } - - if (mode.equals("None")) { - /** Single sample with no data collection **/ - measurement = this.measurement3ServiceLocal.merge(measurement); - specimen.getMeasurements().add(measurement); - - } - } - } - return dataCollections; - } - - /** - * Creates the data collection. - * - * @param specimens - * the specimens - * @param experiment - * the experiment - * @return the saxs data collection3 vo - */ - private SaxsDataCollection3VO createDataCollection(List specimens, Experiment3VO experiment) { - SaxsDataCollection3VO dc = new SaxsDataCollection3VO(); - dc.setExperimentId(experiment.getExperimentId()); - dc = this.entityManager.merge(dc); - - int order = 1; - for (Measurement3VO specimen3vo : specimens) { - MeasurementTodataCollection3VO mc = new MeasurementTodataCollection3VO(); - mc.setDataCollectionId(dc.getDataCollectionId()); - mc.setMeasurementId(specimen3vo.getMeasurementId()); - mc.setDataCollectionOrder(order); - dc.getMeasurementtodatacollection3VOs().add(this.measurementToDataCollection3Service.merge(mc)); - order++; - - } - return dc; - } - - private Specimen3VO isAlreadyCreatedByPosition(Specimen3VO specimen, List specimens) { - if (specimen.getSampleplateposition3VO() != null) { - /** Checking for position of the specimen **/ - for (Specimen3VO existingSpecimen : specimens) { - if (existingSpecimen.getSampleplateposition3VO() != null) { - if (existingSpecimen.getSampleplateposition3VO().getSamplePlateId() - .equals(specimen.getSampleplateposition3VO().getSamplePlateId())) { - if (existingSpecimen.getSampleplateposition3VO().getColumnNumber() == specimen - .getSampleplateposition3VO().getColumnNumber()) { - if (existingSpecimen.getSampleplateposition3VO().getRowNumber() == specimen - .getSampleplateposition3VO().getRowNumber()) { - return existingSpecimen; - } - } - } - } - } - } - return null; - } - - private Experiment3VO createExperimentFromRobotParams(Experiment3VO experiment, - ArrayList> samples, int proposalId, String mode, String storageTemperature, - String extraFlowTime, Boolean optimize) throws Exception { - - try { - ArrayList bufferList = new ArrayList(); - HashMap bufferNameToBuffer3VO = parseBuffers(samples, proposalId); - HashMap bufferIdToBuffer3VO = new HashMap(); - for (String key : bufferNameToBuffer3VO.keySet()) { - Buffer3VO buffer3VO = bufferNameToBuffer3VO.get(key); - buffer3VO.setProposalId(proposalId); - buffer3VO = this.proposal3Service.merge(buffer3VO); - bufferNameToBuffer3VO.put(key, buffer3VO); - bufferIdToBuffer3VO.put(buffer3VO.getBufferId(), buffer3VO); - bufferList.add(buffer3VO); - } - - HashMap acronymToMacromolecule3VO = parseMacromolecules(samples, proposalId); - log.debug("----------------- MACROMOLECULES ------------"); - log.debug(acronymToMacromolecule3VO.toString()); - for (String key : acronymToMacromolecule3VO.keySet()) { - Macromolecule3VO macromolecule3VO = acronymToMacromolecule3VO.get(key); - macromolecule3VO.setProposalId(proposalId); - macromolecule3VO = this.proposal3Service.merge(macromolecule3VO); - acronymToMacromolecule3VO.put(key, macromolecule3VO); - } - - /** There is only one plate group **/ - PlateGroup3VO plateGroup = new PlateGroup3VO(); - plateGroup.setName("BsxCube Group 1"); - plateGroup.setStorageTemperature(storageTemperature); - plateGroup = this.samplePlate3Service.merge(plateGroup); - - /** Plate index is the position in the plate in the sample changer: 1,2,3 for bm29 **/ -// HashMap plateIndexToSamplePlate3VO = parsePlates(samples, storageTemperature); - HashMap plateIndexToSamplePlate3VO = getDefaultSampleChangerPlateconfiguration(storageTemperature); - System.out.println(new GsonBuilder().excludeFieldsWithModifiers(Modifier.PRIVATE).create().toJson(plateIndexToSamplePlate3VO)); - - /** Creating plates **/ - for (String key : plateIndexToSamplePlate3VO.keySet()) { - Sampleplate3VO plate = plateIndexToSamplePlate3VO.get(key); - plate.setExperimentId(experiment.getExperimentId()); - plate.setStorageTemperature(storageTemperature); - plate.setPlategroup3VO(plateGroup); - plate = this.samplePlate3Service.merge(plate); - plateIndexToSamplePlate3VO.put(key, plate); - experiment.getSamplePlate3VOs().add(plate); - } - - log.debug("----------------- buffers ------------"); - log.debug(bufferNameToBuffer3VO.toString()); - - log.debug("----------------- samples ------------"); - log.debug(samples.toString()); - - HashMap specimens = getSpecimens(samples, acronymToMacromolecule3VO, - bufferNameToBuffer3VO, plateIndexToSamplePlate3VO); - log.debug("----------------- specimens ------------"); - log.debug(specimens.toString()); - - HashMap sampleBuffersVOs = new HashMap(); - - List specimensCreated = new ArrayList(); - /** - * Key: For buffers is the buffer name For samples is the position - */ - for (String key : specimens.keySet()) { - Specimen3VO specimen = specimens.get(key); - if (specimen.getSampleplateposition3VO() != null) { - specimen.setSampleplateposition3VO(entityManager.merge(specimen.getSampleplateposition3VO())); - } - specimen.setExperimentId(experiment.getExperimentId()); - - /** - * Feature when samples contains macromolecule and buffername is the backgroundId Before doing the merge - * we have to check that this is not the case: BUFFERNAME PLATE ROW WELL BUFFER 1 1 1 1 BBSA 2 1 1 1 - * BBSA - * **/ - Specimen3VO existingSpecimenAtSamePosition = isAlreadyCreatedByPosition(specimen, specimensCreated); - if (existingSpecimenAtSamePosition == null) { - specimen = specimen3Service.merge(specimen); - specimensCreated.add(specimen); - experiment.getSamples().add(specimen); - } else { - specimen = existingSpecimenAtSamePosition; - } - - specimens.put(key, specimen); - if (specimen.getMacromolecule3VO() == null) { - sampleBuffersVOs.put(bufferIdToBuffer3VO.get(specimen.getBufferId()).getAcronym(), specimen); - } - } - - List dataCollectionList = createMeasurements(samples, specimens, experiment, - bufferNameToBuffer3VO, mode, extraFlowTime); - - /** Set the priorities but also optimize the buffers **/ - if (optimize) { - // measurement3ServiceLocal.optimizeAndPrioritize(experiment, dataCollections, 1); - measurement3ServiceLocal.resetAllPriorities(experiment, dataCollectionList); - measurement3ServiceLocal.optimizeDatacollectionByRemovingDuplicatedBuffers(experiment, - dataCollectionList, 1); - } - return experiment; - } catch (Exception e) { - throw e; - } - - } - - /** - * createExperimentFromRobotParams - * - * Create the macromolecule, buffer, plates, samples, measurement and executes the prioritizer in order to optimize - * the buffers duplicated When a data collection is: buffer1 sample1 buffer1 buffer1 sample2 buffer1 - * - * Because there are two buffer1 together the prioritizer will remove one like: buffer1 sample1 buffer1 sample2 - * buffer1 - * - * @param samples - * ArrayList> containing the hashmaps with all the values required to generate an - * experiment samples = "[{'code': 'Buff1', 'plate': '2', 'enable': True, 'title': 'P2-1:9', - * 'transmission': 20.0, 'viscosity': 'Low', 'recuperate': False, 'SEUtemperature': 4.0, 'flow': False, - * 'comments': 'test', 'volume': 20, 'buffername': 'testot', 'typen': 0, 'waittime': 0.0, - * 'concentration': 0.0, 'type': 'Buffer', 'well': '9', 'row': 1}] - * - * Coming form WUI we add a new parameter called volumeToLoad in order to distinguish both specially for - * buffer measurements - * - * @param proposalId - * the proposal id - * @param mode - * the mode: "BeforeAndAfter", "None", "Before" and "After" - * @param storageTemperature - * the storage temperature - * @param extraFlowTime - * the extra flow time - * @return the experiment3VO - * - * TODO: Mode should replaced by enumeration - */ - @Override - public Experiment3VO createExperimentFromRobotParams(ArrayList> samples, Integer sessionId, - int proposalId, String mode, String storageTemperature, String extraFlowTime, String type, - String sourceFilePath, String name, String comments) throws Exception { - return this.createExperimentFromRobotParams(samples, sessionId, proposalId, mode, storageTemperature, - extraFlowTime, type, sourceFilePath, name, true, comments); - } - - @Override - public Experiment3VO createExperimentFromRobotParams(ArrayList> samples, Integer sessionId, - int proposalId, String mode, String storageTemperature, String extraFlowTime, String type, - String sourceFilePath, String name, Boolean optimize, String comments) throws Exception { - - Experiment3VO experiment = new Experiment3VO(); - experiment.setType(type); - - experiment.setSourceFilePath(sourceFilePath); - experiment.setCreationDate(getNow()); - experiment.setName(name); - experiment.setProposalId(proposalId); - experiment.setSessionId(sessionId); - experiment.setComments(comments); - experiment = this.experiment3ServiceLocal.merge(experiment); - - if (sourceFilePath != null) { - /** Modifying the path if contains __ID__ then we replace __ID__ by the real id of the experiment **/ - experiment.setSourceFilePath(experiment.getSourceFilePath().replace("__ID__", - String.valueOf(experiment.getExperimentId()))); - experiment = this.experiment3ServiceLocal.merge(experiment); - } - - /** Triming all values **/ - for (HashMap hashMap : samples) { - for (String key : hashMap.keySet()) { - /** It may be null when creating template and plate = null **/ - if (hashMap.get(key) != null) { - hashMap.put(key, hashMap.get(key).trim()); - } - } - } - - /** Dealing with macromolecule **/ - for (HashMap hashMap : samples) { - if (!hashMap.containsKey("macromolecule")) { - hashMap.put("macromolecule", hashMap.get("code")); - } - } - - experiment = this.createExperimentFromRobotParams(experiment, samples, proposalId, mode, storageTemperature, - extraFlowTime, optimize); - return experiment; - } - - @Override - public Experiment3VO addMeasurementsToExperiment(int experimentId, int proposalId, - ArrayList> samples) throws Exception { - System.out.println("addMeasurementsToExperiment"); - this.log.info("addMeasurementsToExperiment"); -// Experiment3VO experiment = this.experiment3ServiceLocal.findById(experimentId, ExperimentScope.PREPARE_EXPERIMENT, proposalId); - Experiment3VO experiment = this.experiment3ServiceLocal.findById(experimentId, ExperimentScope.MINIMAL, proposalId); - System.out.println("PREPARE_EXPERIMENT"); - this.log.info("PREPARE_EXPERIMENT"); - /** Triming all values **/ - for (HashMap hashMap : samples) { - for (String key : hashMap.keySet()) { - /** It may be null when creating template and plate = null **/ - if (hashMap.get(key) != null) { - hashMap.put(key, hashMap.get(key).trim()); - } - } - } - - HashMap buffers = this.parseBuffers(samples, proposalId); - for (String key : buffers.keySet()) { - System.out.println(buffers.get(key)); - } - - HashMap macromolecules = this.parseMacromolecules(samples, proposalId); - for (String key : macromolecules.keySet()) { - System.out.println(macromolecules.get(key)); - } - - HashMap specimens = this.getSpecimens(samples, macromolecules, buffers); - for (String key : specimens.keySet()) { - System.out.println(specimens.get(key)); - } - - for (String key : specimens.keySet()) { - Specimen3VO sample = specimens.get(key); - sample.setExperimentId(experiment.getExperimentId()); - sample.setSampleplateposition3VO(null); - sample = specimen3Service.merge(sample); - experiment.getSamples().add(sample); - specimens.put(key, sample); - } - this.createMeasurements(samples, specimens, experiment, buffers, "BeforeAndAfter", "0"); - experiment = this.experiment3ServiceLocal.findById(experimentId, ExperimentScope.MEDIUM, proposalId); - return experiment; - } - - private HashMap getSpecimens(ArrayList> samples, - HashMap macromolecules, HashMap buffers) { - - HashMap sample3VOs = new HashMap(); - - /** Looking for positions **/ - for (HashMap sample : samples) { - String positionCode = sample.get("plate") + "_" + sample.get("row") + "_" + sample.get("well"); - Specimen3VO sample3VO = null; - - if (sample3VOs.get(positionCode) == null) { - /** Creating new Sample for the experiment **/ - sample3VO = new Specimen3VO(); - - /** Buffer **/ - Buffer3VO buffer = buffers.get(sample.get("buffername").trim()); - if (buffer == null) { - log.error("Buffer not found with buffer name: " + sample.get("buffername")); - } - sample3VO.setBufferId(buffer.getBufferId()); - - /** Macromolecule **/ - if (sample.get("type").equals("Sample")) { - Macromolecule3VO macromolecule3VO = macromolecules.get(sample.get("macromolecule")); - sample3VO.setMacromolecule3VO(macromolecule3VO); - } - - /** Sample properties **/ - sample3VO.setConcentration(sample.get("concentration")); - sample3VO.setVolume(sample.get("volume")); -// sample3VO.setCode(sample.get("macromolecule")); - - } else { - /** Updating volume to load **/ - sample3VO = sample3VOs.get(positionCode); - sample3VO.setVolume(String.valueOf(Integer.parseInt(sample3VO.getVolume()) - + Integer.parseInt(sample.get("volume")))); - } - - /** it is a buffer we keep the code of the buffername **/ - if (sample3VO.getMacromolecule3VO() == null) { - sample3VOs.put(sample.get("buffername"), sample3VO); - } else { - sample3VOs.put(positionCode, sample3VO); - } - - } - return sample3VOs; - } - - - /** - * Gets the plates. This should be changed depending on the bm configuration - * - * @param samples - * the samples - * @param storageTemperature - * the storage temperature - * @return the plates - */ - @Override - public HashMap getDefaultSampleChangerPlateconfiguration( String storageTemperature) { - /** There is always three Plates **/ - HashMap platePositionToSamplePlate3VO = new HashMap(); - - /** Plate 1 **/ - Platetype3VO type = this.plateType3Service.findById(1); - Sampleplate3VO samplePlate_1 = new Sampleplate3VO(); - samplePlate_1.setStorageTemperature(storageTemperature); - samplePlate_1.setName(type.getName()); - samplePlate_1.setSlotPositionRow("1"); - samplePlate_1.setSlotPositionColumn("1"); - samplePlate_1.setPlatetype3VO(type); - platePositionToSamplePlate3VO.put("1", samplePlate_1); - - /** Plate 2 **/ - Platetype3VO type_2 = this.plateType3Service.findById(2); - Sampleplate3VO samplePlate_2 = new Sampleplate3VO(); - samplePlate_2.setStorageTemperature(storageTemperature); - samplePlate_2.setName(type_2.getName()); - samplePlate_2.setPlatetype3VO(type_2); - samplePlate_2.setSlotPositionRow("1"); - samplePlate_2.setSlotPositionColumn("2"); - platePositionToSamplePlate3VO.put("2", samplePlate_2); - - /** Plate 3 **/ - Platetype3VO type_3 = this.plateType3Service.findById(4); - Sampleplate3VO samplePlate_3 = new Sampleplate3VO(); - samplePlate_3.setStorageTemperature(storageTemperature); - samplePlate_3.setName(type_3.getName()); - samplePlate_3.setSlotPositionRow("1"); - samplePlate_3.setSlotPositionColumn("3"); - samplePlate_3.setPlatetype3VO(type_3); - platePositionToSamplePlate3VO.put("3", samplePlate_3); - - /** At EMBL Layout is vertical instead of horizontal **/ - if (Constants.SITE_IS_EMBL()){ - samplePlate_1.setSlotPositionRow("1"); - samplePlate_1.setSlotPositionColumn("1"); - samplePlate_2.setSlotPositionRow("2"); - samplePlate_2.setSlotPositionColumn("1"); - samplePlate_3.setSlotPositionRow("3"); - samplePlate_3.setSlotPositionColumn("1"); - } - return platePositionToSamplePlate3VO; - } - - public ArrayList createOrUpdateMacromolecule(ArrayList> samples, int proposalId) throws Exception { - Robot3ServiceBean.log.info("createOrUpdateMacromolecule"); - - ArrayList macromoleculeList = new ArrayList(); - - /** Triming all values **/ - for (HashMap hashMap : samples) { - for (String key : hashMap.keySet()) { - /** It may be null when creating template and plate = null **/ - if (hashMap.get(key) != null) { - hashMap.put(key, hashMap.get(key).trim()); - } - } - } - - HashMap macromolecules = this.parseMacromolecules(samples, proposalId); - for (String key : macromolecules.keySet()) { - Robot3ServiceBean.log.info("createOrUpdateMacromolecule: " + macromolecules.get(key)); - } - - /** Macromolecule **/ - for (HashMap sample : samples) { - if (sample.get("type").equals("Sample")) { - Macromolecule3VO macromolecule3VO = macromolecules.get(sample.get("macromolecule")); - Robot3ServiceBean.log.info("createOrUpdateMacromolecule macromolecule.acronym = " + macromolecule3VO.getAcronym()); - if (macromolecule3VO != null) { - macromolecule3VO.setProposalId(proposalId); - macromolecule3VO = this.proposal3Service.merge(macromolecule3VO); - macromoleculeList.add(macromolecule3VO); - } - } - } - Robot3ServiceBean.log.info("createOrUpdateMacromolecule size = " + macromoleculeList.size()); - - return macromoleculeList; - } - - public ArrayList createOrUpdateBuffer(ArrayList> samples, int proposalId) throws Exception { - Robot3ServiceBean.log.info("createOrUpdateBuffer"); - - ArrayList bufferList = new ArrayList(); - - /** Triming all values **/ - for (HashMap hashMap : samples) { - for (String key : hashMap.keySet()) { - /** It may be null when creating template and plate = null **/ - if (hashMap.get(key) != null) { - hashMap.put(key, hashMap.get(key).trim()); - } - } - } - - HashMap buffers = this.parseBuffers(samples, proposalId); - for (String key : buffers.keySet()) { - Robot3ServiceBean.log.info("createOrUpdateBuffer: key= " + key + " | value= " + buffers.get(key)); - } - - /** Buffer **/ - for (HashMap sample : samples) { - Buffer3VO buffer3VO = buffers.get(sample.get("buffername").trim()); - if (buffer3VO == null) { - log.error("Buffer not found with buffer name: " + sample.get("buffername")); - } - - if (buffer3VO != null) { - buffer3VO.setProposalId(proposalId); - buffer3VO = this.proposal3Service.merge(buffer3VO); - bufferList.add(buffer3VO); - } - } - Robot3ServiceBean.log.info("createOrUpdateBuffer size = " + bufferList.size()); - - return bufferList; - } -} +/******************************************************************************* + * This file is part of ISPyB. + * + * ISPyB is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * ISPyB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with ISPyB. If not, see . + * + * Contributors : S. Delageniere, R. Leal, L. Launer, K. Levik, S. Veyrier, P. Brenchereau, M. Bodin, A. De Maria Antolinos + ******************************************************************************************************************************/ + +package ispyb.server.biosaxs.services.core.robot; + +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Date; +import java.util.GregorianCalendar; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; + +import javax.ejb.EJB; +import javax.ejb.Stateless; +import javax.persistence.EntityManager; +import javax.persistence.PersistenceContext; + +import org.apache.log4j.Logger; + +import com.google.gson.GsonBuilder; + +import ispyb.common.util.Constants; +import ispyb.server.biosaxs.services.core.ExperimentScope; +import ispyb.server.biosaxs.services.core.experiment.Experiment3ServiceLocal; +import ispyb.server.biosaxs.services.core.measurement.Measurement3ServiceLocal; +import ispyb.server.biosaxs.services.core.measurementToDataCollection.MeasurementToDatacollection3ServiceLocal; +import ispyb.server.biosaxs.services.core.plateType.PlateType3ServiceLocal; +import ispyb.server.biosaxs.services.core.proposal.SaxsProposal3ServiceLocal; +import ispyb.server.biosaxs.services.core.samplePlate.Sampleplate3ServiceLocal; +import ispyb.server.biosaxs.services.core.specimen.Specimen3Service; +import ispyb.server.biosaxs.vos.assembly.Macromolecule3VO; +import ispyb.server.biosaxs.vos.dataAcquisition.Buffer3VO; +import ispyb.server.biosaxs.vos.dataAcquisition.Experiment3VO; +import ispyb.server.biosaxs.vos.dataAcquisition.Measurement3VO; +import ispyb.server.biosaxs.vos.dataAcquisition.Specimen3VO; +import ispyb.server.biosaxs.vos.dataAcquisition.plate.PlateGroup3VO; +import ispyb.server.biosaxs.vos.dataAcquisition.plate.Platetype3VO; +import ispyb.server.biosaxs.vos.dataAcquisition.plate.Sampleplate3VO; +import ispyb.server.biosaxs.vos.dataAcquisition.plate.Sampleplateposition3VO; +import ispyb.server.biosaxs.vos.datacollection.MeasurementTodataCollection3VO; +import ispyb.server.biosaxs.vos.datacollection.SaxsDataCollection3VO; + +@Stateless +public class Robot3ServiceBean implements Robot3Service, Robot3ServiceLocal { + + private final static Logger log = Logger.getLogger(Robot3ServiceBean.class); + + /** The specimen3 service local. */ + @EJB + private Measurement3ServiceLocal measurement3ServiceLocal; + + /** The experiment3 service local. */ + @EJB + private Experiment3ServiceLocal experiment3ServiceLocal; + + /** The macromolecule3 service. */ + @EJB + private SaxsProposal3ServiceLocal proposal3Service; + + /** The sample plate3 service. */ + @EJB + private Sampleplate3ServiceLocal samplePlate3Service; + + /** The plate type3 service. */ + @EJB + private PlateType3ServiceLocal plateType3Service; + + /** The measurement to data collection3 service. */ + @EJB + private MeasurementToDatacollection3ServiceLocal measurementToDataCollection3Service; + + @EJB + private Specimen3Service specimen3Service; + + /** The entity manager. */ + @PersistenceContext(unitName = "ispyb_db") + private EntityManager entityManager; + + /** The now. */ + protected static Calendar NOW; + + /** + * Gets the now. + * + * @return the now + */ + private Date getNow() { + NOW = GregorianCalendar.getInstance(); + return NOW.getTime(); + } + + /** + * Gets the buffers. It returns a hashmap + * + * @param samples + * the samples + * @return the buffers + */ + private HashMap parseBuffers(ArrayList> samples, int proposalId) { + List buffersProposal = proposal3Service.findBuffersByProposalId(proposalId); + Robot3ServiceBean.log.info("----------------- parseBuffers ------------ "); + HashMap bufferNameToBuffer3VO = new HashMap(); + + /** Store the name of the macromolecules **/ + HashMap bufferNameToBufferAcronym = new HashMap(); + + /** + * It is possible that users make a mistake and for the same specimen in the same position they set a different + * buffer's name + */ + HashMap bufferPositionToBufferAcronym = new HashMap(); + + for (HashMap sample : samples) { + if (sample.get("type").equals("Buffer")) { + Buffer3VO buffer = new Buffer3VO(); + buffer.setName(sample.get("macromolecule").trim()); + buffer.setAcronym(sample.get("macromolecule").trim()); + buffer.setComment(sample.get("comments")); + + Robot3ServiceBean.log.info("sample: " + sample.toString()); + Robot3ServiceBean.log.info("buffername: " + sample.get("buffername")); + + String positionCode = this.getSamplePositionKey(sample); + if (bufferPositionToBufferAcronym.get(positionCode) == null) { + bufferNameToBufferAcronym.put(sample.get("buffername").trim(), sample.get("macromolecule").trim()); + bufferPositionToBufferAcronym.put(positionCode, sample.get("macromolecule").trim()); + } else { + Robot3ServiceBean.log.info("Buffers detected in the same position with different macromolecule name"); + log.warn("Buffers detected in the same position with different macromolecule name"); + bufferNameToBufferAcronym.put(sample.get("buffername").trim(), + bufferPositionToBufferAcronym.get(positionCode)); + } + } + } + + for (String bufferName : bufferNameToBufferAcronym.keySet()) { + Buffer3VO bufferExists = this + .getBufferByAcronym(buffersProposal, bufferNameToBufferAcronym.get(bufferName)); + if (bufferExists != null) { + bufferNameToBuffer3VO.put(bufferName, bufferExists); + } else { + Buffer3VO buffer = new Buffer3VO(); + buffer.setAcronym(bufferNameToBufferAcronym.get(bufferName)); + buffer.setName(bufferNameToBufferAcronym.get(bufferName)); + bufferNameToBuffer3VO.put(bufferName, buffer); + } + } + + return bufferNameToBuffer3VO; + } + + /** If the acronym exists in the list the macromolecule is returned **/ + private Macromolecule3VO getMacromoleculeByAcronym(List macromolecules, String acronym) { + for (Macromolecule3VO macromolecule : macromolecules) { + if (macromolecule.getAcronym().toLowerCase().equals(acronym.toLowerCase())) { + return macromolecule; + } + } + return null; + } + + /** If the acronym exists in the list the buffer is returned **/ + private Buffer3VO getBufferByAcronym(List buffers, String acronym) { + for (Buffer3VO buffer : buffers) { + if (buffer.getAcronym().toLowerCase().equals(acronym.toLowerCase())) { + return buffer; + } + } + return null; + } + + /** + * Gets the macromolecules. If there are two with the same name it means that they are the same macromolecules + * + * @param samples + * the samples + * @return the macromolecules + */ + private HashMap parseMacromolecules(ArrayList> samples, + int proposalId) { + List macromoleculesProposalList = proposal3Service.findMacromoleculesByProposalId(proposalId); + Robot3ServiceBean.log.info("----------------- parseMacromolecules ------------ "); + HashMap acronymToMacromolecule3VO = new HashMap(); + /** Store the name of the macromolecules **/ + HashSet macromoleculeAcronyms = new HashSet(); + for (HashMap sample : samples) { + if (sample.get("type").equals("Sample")) { + /** If not macromolecule is defined we use the code **/ + if (sample.get("macromolecule") == null) { + macromoleculeAcronyms.add(sample.get("code").trim()); + } else { + macromoleculeAcronyms.add(sample.get("macromolecule").trim()); + } + } + } + + for (String acronym : macromoleculeAcronyms) { + Macromolecule3VO macromoleculeExists = this.getMacromoleculeByAcronym(macromoleculesProposalList, acronym); + if (macromoleculeExists != null) { + acronymToMacromolecule3VO.put(acronym, macromoleculeExists); + } else { + Macromolecule3VO macromolecule = new Macromolecule3VO(); + macromolecule.setAcronym(acronym); + macromolecule.setName(acronym); + acronymToMacromolecule3VO.put(acronym, macromolecule); + } + } + return acronymToMacromolecule3VO; + } + + private String getSamplePositionKey(HashMap sample) { + return sample.get("plate") + "_" + sample.get("row") + "_" + sample.get("well"); + } + + /** + * Gets the specimens. + * + * @param samples + * the samples + * @param acronymToMacromolecule3VO + * the macromolecules + * @param bufferNameToBuffer3VO + * the buffers + * @param plateIndexToSamplePlate3VO + * the plates + * @return HashMap where String is bufferName for the buffers and position for the Samples + */ + private HashMap getSpecimens(ArrayList> samples, + HashMap acronymToMacromolecule3VO, + HashMap bufferNameToBuffer3VO, HashMap plateIndexToSamplePlate3VO) { + + HashMap specimenIdToSpecimen3VO = new HashMap(); + + /** Looking for positions **/ + for (HashMap sample : samples) { + // String positionCode = sample.get("plate") + "_" + sample.get("row") + "_" + sample.get("well"); + String positionCode = this.getSamplePositionKey(sample); + Specimen3VO specimen = null; + + if (specimenIdToSpecimen3VO.get(positionCode) == null) { + /** Creating new Sample for the experiment **/ + specimen = new Specimen3VO(); + /** Buffer **/ + Buffer3VO buffer = bufferNameToBuffer3VO.get(sample.get("buffername").trim()); + if (buffer == null) { + log.error("Buffer not found with buffer name: " + sample.get("buffername")); + } + specimen.setBufferId(buffer.getBufferId()); + + /** Macromolecule **/ + if (sample.get("type").equals("Sample")) { + Macromolecule3VO macromolecule3VO = acronymToMacromolecule3VO.get(sample.get("macromolecule")); + specimen.setMacromolecule3VO(macromolecule3VO); + } + + /** Position **/ + if (plateIndexToSamplePlate3VO != null) { + Sampleplate3VO plate = plateIndexToSamplePlate3VO.get(sample.get("plate")); + if (plate != null) { + Sampleplateposition3VO position = new Sampleplateposition3VO(); + position.setSamplePlateId(plate.getSamplePlateId()); + position.setRowNumber(Integer.parseInt(sample.get("row"))); + position.setColumnNumber(Integer.parseInt(sample.get("well"))); + position = entityManager.merge(position); + specimen.setSampleplateposition3VO(position); + } + } + /** Specimen properties **/ + specimen.setConcentration(sample.get("concentration")); + specimen.setVolume(sample.get("volume")); +// specimen.setCode(sample.get("macromolecule")); + } else { + /** Updating volume to load **/ + specimen = specimenIdToSpecimen3VO.get(positionCode); + specimen.setVolume(String.valueOf(Integer.parseInt(specimen.getVolume()) + + Integer.parseInt(sample.get("volume")))); + } + + /** it is a buffer we keep the code of the buffername **/ + if (specimen.getMacromolecule3VO() == null) { + specimenIdToSpecimen3VO.put(sample.get("buffername"), specimen); + } else { + specimenIdToSpecimen3VO.put(positionCode, specimen); + } + } + return specimenIdToSpecimen3VO; + } + + /** + * Gets the plates. This should be changed depending on the bm configuration + * + * @param samples + * the samples + * @param storageTemperature + * the storage temperature + * @return the plates + */ +// private HashMap parsePlates(ArrayList> samples, String storageTemperature) { +// /** There is always three Plates **/ +// HashMap platePositionToSamplePlate3VO = new HashMap(); +// +// /** Plate 1 **/ +// Platetype3VO type = this.plateType3Service.findById(1); +// Sampleplate3VO samplePlate_1 = new Sampleplate3VO(); +// samplePlate_1.setStorageTemperature(storageTemperature); +// samplePlate_1.setName(type.getName()); +// samplePlate_1.setSlotPositionRow("1"); +// samplePlate_1.setSlotPositionColumn("1"); +// samplePlate_1.setPlatetype3VO(type); +// platePositionToSamplePlate3VO.put("1", samplePlate_1); +// +// /** Plate 2 **/ +// Platetype3VO type_2 = this.plateType3Service.findById(2); +// Sampleplate3VO samplePlate_2 = new Sampleplate3VO(); +// samplePlate_2.setStorageTemperature(storageTemperature); +// samplePlate_2.setName(type_2.getName()); +// samplePlate_2.setPlatetype3VO(type_2); +// samplePlate_2.setSlotPositionRow("1"); +// samplePlate_2.setSlotPositionColumn("2"); +// platePositionToSamplePlate3VO.put("2", samplePlate_2); +// +// /** Plate 3 **/ +// Platetype3VO type_3 = this.plateType3Service.findById(4); +// Sampleplate3VO samplePlate_3 = new Sampleplate3VO(); +// samplePlate_3.setStorageTemperature(storageTemperature); +// samplePlate_3.setName(type_3.getName()); +// samplePlate_3.setSlotPositionRow("1"); +// samplePlate_3.setSlotPositionColumn("3"); +// samplePlate_3.setPlatetype3VO(type_3); +// platePositionToSamplePlate3VO.put("3", samplePlate_3); +// +// return platePositionToSamplePlate3VO; +// } + + /** + * Parses the specimen. + * + * @param sample + * the sample + * @return the specimen3 vo + */ + private Measurement3VO parseSpecimen(HashMap sample) { + String waittime = sample.get("waittime"); + + /** + * Problem is that from BsxCube there is not any parameters for volume, volume in this case means volumeToLoad + * however from WUI there are both for indicating in buffers specially the volume and the volumeToLoad + * **/ + String volumeToLoad = sample.get("volume"); + if (sample.containsKey("volumeToLoad")) { + volumeToLoad = sample.get("volumeToLoad"); + } + String comments = sample.get("comments"); + String transmission = sample.get("transmission"); + String viscosity = sample.get("viscosity"); + String flow = sample.get("flow"); + String code = sample.get("code"); + + Measurement3VO measurement = new Measurement3VO(); +// measurement.setCode(code); + measurement.setWaitTime(waittime); + measurement.setVolumeToLoad(volumeToLoad); + measurement.setComment(comments); + measurement.setTransmission(transmission); + measurement.setViscosity(viscosity); + measurement.setFlow(Boolean.valueOf(flow)); + + return measurement; + } + + /** + * Creates the measurements. + * + * @param samples + * the samples + * @param sample3vOs + * the sample3v os + * @param experiment + * the experiment + * @param buffers + * the buffers + * @param mode + * the mode + * @param extraFlowTime + * the extra flow time + * @return the list + * @throws Exception + * the exception + */ + public List createMeasurements(ArrayList> samples, + HashMap sample3vOs, Experiment3VO experiment, HashMap buffers, + String mode, String extraFlowTime) throws Exception { + List dataCollections = new ArrayList(); + + /** Creating for measurements **/ + for (HashMap sample : samples) { + String positionCode = sample.get("plate") + "_" + sample.get("row") + "_" + sample.get("well"); + if (sample.get("type").equals("Sample")) { + + Specimen3VO specimen = sample3vOs.get(positionCode); + String exposureTemperature = sample.get("SEUtemperature"); + Measurement3VO measurement = this.parseSpecimen(sample); + measurement.setSpecimenId(specimen.getSpecimenId()); + measurement.setExtraFlowTime(extraFlowTime); + measurement.setExposureTemperature(exposureTemperature); + +// try{ + /** Code should be runNumber otherwise set as acronym + concentration **/ +// if (sample.get("code") != null){ +// measurement.setCode(sample.get("code")); +// } +// else{ +// measurement.setCode(specimen.getMacromolecule3VO().getAcronym() + "_" + specimen.getConcentration()); +// } +// } +// catch(Exception exp){ +// exp.printStackTrace(); +// System.out.println("It has not been possible to set the code to the measurement"); +// } + + /** Buffer **/ + Specimen3VO buffer = sample3vOs.get(sample.get("buffername")); + + if (buffer == null) { + throw new Exception("Buffer not found: " + sample.get("buffername")); + } + + /** MODE: Before and After **/ + if (mode.equals("BeforeAndAfter")) { + for (HashMap sampleBuffer : samples) { + if (sampleBuffer.get("type").equals("Buffer")) { + if (sampleBuffer.get("buffername").equals(sample.get("buffername"))) { + + /** Measurement Before **/ + Measurement3VO measurementBufferBefore = this.parseSpecimen(sampleBuffer); + measurementBufferBefore.setSpecimenId(buffer.getSpecimenId()); + measurementBufferBefore.setExtraFlowTime(extraFlowTime); + measurementBufferBefore.setExposureTemperature(exposureTemperature); + + /** Copying parameters of the sample measurement **/ + /** Update, buffer's parameter may be different **/ + measurementBufferBefore.setVolumeToLoad(measurement.getVolumeToLoad()); + measurementBufferBefore.setTransmission(measurement.getTransmission()); + measurementBufferBefore.setFlow(measurement.getFlow()); + measurementBufferBefore.setViscosity(measurement.getViscosity()); + measurementBufferBefore.setWaitTime(measurement.getWaitTime()); + + /** But not exposure temperature **/ + measurementBufferBefore.setExposureTemperature(measurement.getExposureTemperature()); + + measurementBufferBefore = this.measurement3ServiceLocal.merge(measurementBufferBefore); + buffer.getMeasurements().add(measurementBufferBefore); + + /** Sample **/ + measurement = this.measurement3ServiceLocal.merge(measurement); + specimen.getMeasurements().add(measurement); + + /** After **/ + Measurement3VO specimenBufferAfter = this.parseSpecimen(sampleBuffer); + specimenBufferAfter.setSpecimenId(buffer.getSpecimenId()); + specimenBufferAfter.setExtraFlowTime(extraFlowTime); + specimenBufferAfter.setExposureTemperature(exposureTemperature); + /** Specimen After Code **/ + // specimenBufferAfter.setCode(experiment.getCodeSpecimen(buffer, specimenBufferAfter)); + + /** Copying parameters of the sample measurement **/ + specimenBufferAfter.setVolumeToLoad(measurement.getVolumeToLoad()); + specimenBufferAfter.setTransmission(measurement.getTransmission()); + specimenBufferAfter.setFlow(measurement.getFlow()); + specimenBufferAfter.setViscosity(measurement.getViscosity()); + specimenBufferAfter.setWaitTime(measurement.getWaitTime()); + + specimenBufferAfter.setExposureTemperature(measurement.getExposureTemperature()); + + specimenBufferAfter = this.measurement3ServiceLocal.merge(specimenBufferAfter); + buffer.getMeasurements().add(specimenBufferAfter); + + /** Creating Data Collections **/ + List dataCollectionSpecimens = new ArrayList(); + dataCollectionSpecimens.add(measurementBufferBefore); + dataCollectionSpecimens.add(measurement); + dataCollectionSpecimens.add(specimenBufferAfter); + dataCollections.add(this.createDataCollection(dataCollectionSpecimens, experiment)); + } + } + } + } + + if (mode.equals("After")) { + for (HashMap sampleBuffer : samples) { + if (sampleBuffer.get("type").equals("Buffer")) { + if (sampleBuffer.get("buffername").equals(sample.get("buffername"))) { + + /** Sample **/ + measurement = this.measurement3ServiceLocal.merge(measurement); + + /** After **/ + Measurement3VO measurementBufferAfter = this.parseSpecimen(sampleBuffer); + measurementBufferAfter.setSpecimenId(buffer.getSpecimenId()); + measurementBufferAfter.setExtraFlowTime(extraFlowTime); + measurementBufferAfter.setExposureTemperature(exposureTemperature); + + /** Specimen code **/ + // measurementBufferAfter.setCode(experiment.getCodeSpecimen(buffer, + // measurementBufferAfter)); + measurementBufferAfter = this.measurement3ServiceLocal.merge(measurementBufferAfter); + + /** Creating Data Collections **/ + List dataCollectionSpecimens = new ArrayList(); + dataCollectionSpecimens.add(measurement); + dataCollections.add(this.createDataCollection(dataCollectionSpecimens, experiment)); + } + } + } + } + + if (mode.equals("Before")) { + for (HashMap sampleBuffer : samples) { + if (sampleBuffer.get("type").equals("Buffer")) { + if (sampleBuffer.get("buffername").equals(sample.get("buffername"))) { + Measurement3VO specimenBufferBefore = this.parseSpecimen(sampleBuffer); + specimenBufferBefore.setSpecimenId(buffer.getSpecimenId()); + specimenBufferBefore.setExtraFlowTime(extraFlowTime); + specimenBufferBefore.setExposureTemperature(exposureTemperature); + + /** Specimen Before **/ + // specimenBufferBefore.setCode(experiment.getCodeSpecimen(buffer, + // specimenBufferBefore)); + specimenBufferBefore = this.measurement3ServiceLocal.merge(specimenBufferBefore); + buffer.getMeasurements().add(specimenBufferBefore); + + /** Sample **/ + measurement = this.measurement3ServiceLocal.merge(measurement); + specimen.getMeasurements().add(measurement); + + /** Creating Data Collections **/ + List dataCollectionSpecimens = new ArrayList(); + dataCollectionSpecimens.add(specimenBufferBefore); + dataCollectionSpecimens.add(measurement); + dataCollections.add(this.createDataCollection(dataCollectionSpecimens, experiment)); + } + } + } + } + + if (mode.equals("None")) { + /** Single sample with no data collection **/ + measurement = this.measurement3ServiceLocal.merge(measurement); + specimen.getMeasurements().add(measurement); + + } + } + } + return dataCollections; + } + + /** + * Creates the data collection. + * + * @param specimens + * the specimens + * @param experiment + * the experiment + * @return the saxs data collection3 vo + */ + private SaxsDataCollection3VO createDataCollection(List specimens, Experiment3VO experiment) { + SaxsDataCollection3VO dc = new SaxsDataCollection3VO(); + dc.setExperimentId(experiment.getExperimentId()); + dc = this.entityManager.merge(dc); + + int order = 1; + for (Measurement3VO specimen3vo : specimens) { + MeasurementTodataCollection3VO mc = new MeasurementTodataCollection3VO(); + mc.setDataCollectionId(dc.getDataCollectionId()); + mc.setMeasurementId(specimen3vo.getMeasurementId()); + mc.setDataCollectionOrder(order); + dc.getMeasurementtodatacollection3VOs().add(this.measurementToDataCollection3Service.merge(mc)); + order++; + + } + return dc; + } + + private Specimen3VO isAlreadyCreatedByPosition(Specimen3VO specimen, List specimens) { + if (specimen.getSampleplateposition3VO() != null) { + /** Checking for position of the specimen **/ + for (Specimen3VO existingSpecimen : specimens) { + if (existingSpecimen.getSampleplateposition3VO() != null) { + if (existingSpecimen.getSampleplateposition3VO().getSamplePlateId() + .equals(specimen.getSampleplateposition3VO().getSamplePlateId())) { + if (existingSpecimen.getSampleplateposition3VO().getColumnNumber() == specimen + .getSampleplateposition3VO().getColumnNumber()) { + if (existingSpecimen.getSampleplateposition3VO().getRowNumber() == specimen + .getSampleplateposition3VO().getRowNumber()) { + return existingSpecimen; + } + } + } + } + } + } + return null; + } + + private Experiment3VO createExperimentFromRobotParams(Experiment3VO experiment, + ArrayList> samples, int proposalId, String mode, String storageTemperature, + String extraFlowTime, Boolean optimize) throws Exception { + + try { + ArrayList bufferList = new ArrayList(); + HashMap bufferNameToBuffer3VO = parseBuffers(samples, proposalId); + HashMap bufferIdToBuffer3VO = new HashMap(); + for (String key : bufferNameToBuffer3VO.keySet()) { + Buffer3VO buffer3VO = bufferNameToBuffer3VO.get(key); + buffer3VO.setProposalId(proposalId); + buffer3VO = this.proposal3Service.merge(buffer3VO); + bufferNameToBuffer3VO.put(key, buffer3VO); + bufferIdToBuffer3VO.put(buffer3VO.getBufferId(), buffer3VO); + bufferList.add(buffer3VO); + } + + HashMap acronymToMacromolecule3VO = parseMacromolecules(samples, proposalId); + log.debug("----------------- MACROMOLECULES ------------"); + log.debug(acronymToMacromolecule3VO.toString()); + for (String key : acronymToMacromolecule3VO.keySet()) { + Macromolecule3VO macromolecule3VO = acronymToMacromolecule3VO.get(key); + macromolecule3VO.setProposalId(proposalId); + macromolecule3VO = this.proposal3Service.merge(macromolecule3VO); + acronymToMacromolecule3VO.put(key, macromolecule3VO); + } + + /** There is only one plate group **/ + PlateGroup3VO plateGroup = new PlateGroup3VO(); + plateGroup.setName("BsxCube Group 1"); + plateGroup.setStorageTemperature(storageTemperature); + plateGroup = this.samplePlate3Service.merge(plateGroup); + + /** Plate index is the position in the plate in the sample changer: 1,2,3 for bm29 **/ +// HashMap plateIndexToSamplePlate3VO = parsePlates(samples, storageTemperature); + HashMap plateIndexToSamplePlate3VO = getDefaultSampleChangerPlateconfiguration(storageTemperature); + System.out.println(new GsonBuilder().excludeFieldsWithModifiers(Modifier.PRIVATE).create().toJson(plateIndexToSamplePlate3VO)); + + /** Creating plates **/ + for (String key : plateIndexToSamplePlate3VO.keySet()) { + Sampleplate3VO plate = plateIndexToSamplePlate3VO.get(key); + plate.setExperimentId(experiment.getExperimentId()); + plate.setStorageTemperature(storageTemperature); + plate.setPlategroup3VO(plateGroup); + plate = this.samplePlate3Service.merge(plate); + plateIndexToSamplePlate3VO.put(key, plate); + experiment.getSamplePlate3VOs().add(plate); + } + + log.debug("----------------- buffers ------------"); + log.debug(bufferNameToBuffer3VO.toString()); + + log.debug("----------------- samples ------------"); + log.debug(samples.toString()); + + HashMap specimens = getSpecimens(samples, acronymToMacromolecule3VO, + bufferNameToBuffer3VO, plateIndexToSamplePlate3VO); + log.debug("----------------- specimens ------------"); + log.debug(specimens.toString()); + + HashMap sampleBuffersVOs = new HashMap(); + + List specimensCreated = new ArrayList(); + /** + * Key: For buffers is the buffer name For samples is the position + */ + for (String key : specimens.keySet()) { + Specimen3VO specimen = specimens.get(key); + if (specimen.getSampleplateposition3VO() != null) { + specimen.setSampleplateposition3VO(entityManager.merge(specimen.getSampleplateposition3VO())); + } + specimen.setExperimentId(experiment.getExperimentId()); + + /** + * Feature when samples contains macromolecule and buffername is the backgroundId Before doing the merge + * we have to check that this is not the case: BUFFERNAME PLATE ROW WELL BUFFER 1 1 1 1 BBSA 2 1 1 1 + * BBSA + * **/ + Specimen3VO existingSpecimenAtSamePosition = isAlreadyCreatedByPosition(specimen, specimensCreated); + if (existingSpecimenAtSamePosition == null) { + specimen = specimen3Service.merge(specimen); + specimensCreated.add(specimen); + experiment.getSamples().add(specimen); + } else { + specimen = existingSpecimenAtSamePosition; + } + + specimens.put(key, specimen); + if (specimen.getMacromolecule3VO() == null) { + sampleBuffersVOs.put(bufferIdToBuffer3VO.get(specimen.getBufferId()).getAcronym(), specimen); + } + } + + List dataCollectionList = createMeasurements(samples, specimens, experiment, + bufferNameToBuffer3VO, mode, extraFlowTime); + + /** Set the priorities but also optimize the buffers **/ + if (optimize) { + // measurement3ServiceLocal.optimizeAndPrioritize(experiment, dataCollections, 1); + measurement3ServiceLocal.resetAllPriorities(experiment, dataCollectionList); + measurement3ServiceLocal.optimizeDatacollectionByRemovingDuplicatedBuffers(experiment, + dataCollectionList, 1); + } + return experiment; + } catch (Exception e) { + throw e; + } + + } + + /** + * createExperimentFromRobotParams + * + * Create the macromolecule, buffer, plates, samples, measurement and executes the prioritizer in order to optimize + * the buffers duplicated When a data collection is: buffer1 sample1 buffer1 buffer1 sample2 buffer1 + * + * Because there are two buffer1 together the prioritizer will remove one like: buffer1 sample1 buffer1 sample2 + * buffer1 + * + * @param samples + * ArrayList> containing the hashmaps with all the values required to generate an + * experiment samples = "[{'code': 'Buff1', 'plate': '2', 'enable': True, 'title': 'P2-1:9', + * 'transmission': 20.0, 'viscosity': 'Low', 'recuperate': False, 'SEUtemperature': 4.0, 'flow': False, + * 'comments': 'test', 'volume': 20, 'buffername': 'testot', 'typen': 0, 'waittime': 0.0, + * 'concentration': 0.0, 'type': 'Buffer', 'well': '9', 'row': 1}] + * + * Coming form WUI we add a new parameter called volumeToLoad in order to distinguish both specially for + * buffer measurements + * + * @param proposalId + * the proposal id + * @param mode + * the mode: "BeforeAndAfter", "None", "Before" and "After" + * @param storageTemperature + * the storage temperature + * @param extraFlowTime + * the extra flow time + * @return the experiment3VO + * + * TODO: Mode should replaced by enumeration + */ + @Override + public Experiment3VO createExperimentFromRobotParams(ArrayList> samples, Integer sessionId, + int proposalId, String mode, String storageTemperature, String extraFlowTime, String type, + String sourceFilePath, String name, String comments) throws Exception { + return this.createExperimentFromRobotParams(samples, sessionId, proposalId, mode, storageTemperature, + extraFlowTime, type, sourceFilePath, name, true, comments); + } + + @Override + public Experiment3VO createExperimentFromRobotParams(ArrayList> samples, Integer sessionId, + int proposalId, String mode, String storageTemperature, String extraFlowTime, String type, + String sourceFilePath, String name, Boolean optimize, String comments) throws Exception { + + Experiment3VO experiment = new Experiment3VO(); + experiment = this.experiment3ServiceLocal.initPlates(experiment); + experiment.setType(type); + experiment.setSourceFilePath(sourceFilePath); + experiment.setCreationDate(getNow()); + experiment.setName(name); + experiment.setProposalId(proposalId); + experiment.setSessionId(sessionId); + experiment.setComments(comments); + experiment = this.experiment3ServiceLocal.merge(experiment); + + if (sourceFilePath != null) { + /** Modifying the path if contains __ID__ then we replace __ID__ by the real id of the experiment **/ + experiment.setSourceFilePath(experiment.getSourceFilePath().replace("__ID__", + String.valueOf(experiment.getExperimentId()))); + experiment = this.experiment3ServiceLocal.merge(experiment); + } + + /** Triming all values **/ + for (HashMap hashMap : samples) { + for (String key : hashMap.keySet()) { + /** It may be null when creating template and plate = null **/ + if (hashMap.get(key) != null) { + hashMap.put(key, hashMap.get(key).trim()); + } + } + } + + /** Dealing with macromolecule **/ + for (HashMap hashMap : samples) { + if (!hashMap.containsKey("macromolecule")) { + hashMap.put("macromolecule", hashMap.get("code")); + } + } + + experiment = this.createExperimentFromRobotParams(experiment, samples, proposalId, mode, storageTemperature, + extraFlowTime, optimize); + return experiment; + } + + @Override + public Experiment3VO addMeasurementsToExperiment(int experimentId, int proposalId, + ArrayList> samples) throws Exception { + System.out.println("addMeasurementsToExperiment"); + this.log.info("addMeasurementsToExperiment"); +// Experiment3VO experiment = this.experiment3ServiceLocal.findById(experimentId, ExperimentScope.PREPARE_EXPERIMENT, proposalId); + Experiment3VO experiment = this.experiment3ServiceLocal.findById(experimentId, ExperimentScope.MINIMAL, proposalId); + System.out.println("PREPARE_EXPERIMENT"); + this.log.info("PREPARE_EXPERIMENT"); + /** Triming all values **/ + for (HashMap hashMap : samples) { + for (String key : hashMap.keySet()) { + /** It may be null when creating template and plate = null **/ + if (hashMap.get(key) != null) { + hashMap.put(key, hashMap.get(key).trim()); + } + } + } + + HashMap buffers = this.parseBuffers(samples, proposalId); + for (String key : buffers.keySet()) { + System.out.println(buffers.get(key)); + } + + HashMap macromolecules = this.parseMacromolecules(samples, proposalId); + for (String key : macromolecules.keySet()) { + System.out.println(macromolecules.get(key)); + } + + HashMap specimens = this.getSpecimens(samples, macromolecules, buffers); + for (String key : specimens.keySet()) { + System.out.println(specimens.get(key)); + } + + for (String key : specimens.keySet()) { + Specimen3VO sample = specimens.get(key); + sample.setExperimentId(experiment.getExperimentId()); + sample.setSampleplateposition3VO(null); + sample = specimen3Service.merge(sample); + experiment.getSamples().add(sample); + specimens.put(key, sample); + } + this.createMeasurements(samples, specimens, experiment, buffers, "BeforeAndAfter", "0"); + experiment = this.experiment3ServiceLocal.findById(experimentId, ExperimentScope.MEDIUM, proposalId); + return experiment; + } + + private HashMap getSpecimens(ArrayList> samples, + HashMap macromolecules, HashMap buffers) { + + HashMap sample3VOs = new HashMap(); + + /** Looking for positions **/ + for (HashMap sample : samples) { + String positionCode = sample.get("plate") + "_" + sample.get("row") + "_" + sample.get("well"); + Specimen3VO sample3VO = null; + + if (sample3VOs.get(positionCode) == null) { + /** Creating new Sample for the experiment **/ + sample3VO = new Specimen3VO(); + + /** Buffer **/ + Buffer3VO buffer = buffers.get(sample.get("buffername").trim()); + if (buffer == null) { + log.error("Buffer not found with buffer name: " + sample.get("buffername")); + } + sample3VO.setBufferId(buffer.getBufferId()); + + /** Macromolecule **/ + if (sample.get("type").equals("Sample")) { + Macromolecule3VO macromolecule3VO = macromolecules.get(sample.get("macromolecule")); + sample3VO.setMacromolecule3VO(macromolecule3VO); + } + + /** Sample properties **/ + sample3VO.setConcentration(sample.get("concentration")); + sample3VO.setVolume(sample.get("volume")); +// sample3VO.setCode(sample.get("macromolecule")); + + } else { + /** Updating volume to load **/ + sample3VO = sample3VOs.get(positionCode); + sample3VO.setVolume(String.valueOf(Integer.parseInt(sample3VO.getVolume()) + + Integer.parseInt(sample.get("volume")))); + } + + /** it is a buffer we keep the code of the buffername **/ + if (sample3VO.getMacromolecule3VO() == null) { + sample3VOs.put(sample.get("buffername"), sample3VO); + } else { + sample3VOs.put(positionCode, sample3VO); + } + + } + return sample3VOs; + } + + + /** + * Gets the plates. This should be changed depending on the bm configuration + * + * @param samples + * the samples + * @param storageTemperature + * the storage temperature + * @return the plates + */ + @Override + public HashMap getDefaultSampleChangerPlateconfiguration( String storageTemperature) { + /** There is always three Plates **/ + HashMap platePositionToSamplePlate3VO = new HashMap(); + + /** Plate 1 **/ + Platetype3VO type = this.plateType3Service.findById(1); + Sampleplate3VO samplePlate_1 = new Sampleplate3VO(); + samplePlate_1.setStorageTemperature(storageTemperature); + samplePlate_1.setName(type.getName()); + samplePlate_1.setSlotPositionRow("1"); + samplePlate_1.setSlotPositionColumn("1"); + samplePlate_1.setPlatetype3VO(type); + platePositionToSamplePlate3VO.put("1", samplePlate_1); + + /** Plate 2 **/ + Platetype3VO type_2 = this.plateType3Service.findById(2); + Sampleplate3VO samplePlate_2 = new Sampleplate3VO(); + samplePlate_2.setStorageTemperature(storageTemperature); + samplePlate_2.setName(type_2.getName()); + samplePlate_2.setPlatetype3VO(type_2); + samplePlate_2.setSlotPositionRow("1"); + samplePlate_2.setSlotPositionColumn("2"); + platePositionToSamplePlate3VO.put("2", samplePlate_2); + + /** Plate 3 **/ + Platetype3VO type_3 = this.plateType3Service.findById(4); + Sampleplate3VO samplePlate_3 = new Sampleplate3VO(); + samplePlate_3.setStorageTemperature(storageTemperature); + samplePlate_3.setName(type_3.getName()); + samplePlate_3.setSlotPositionRow("1"); + samplePlate_3.setSlotPositionColumn("3"); + samplePlate_3.setPlatetype3VO(type_3); + platePositionToSamplePlate3VO.put("3", samplePlate_3); + + /** At EMBL Layout is vertical instead of horizontal **/ + if (Constants.SITE_IS_EMBL()){ + samplePlate_1.setSlotPositionRow("1"); + samplePlate_1.setSlotPositionColumn("1"); + samplePlate_2.setSlotPositionRow("2"); + samplePlate_2.setSlotPositionColumn("1"); + samplePlate_3.setSlotPositionRow("3"); + samplePlate_3.setSlotPositionColumn("1"); + } + return platePositionToSamplePlate3VO; + } + + public ArrayList createOrUpdateMacromolecule(ArrayList> samples, int proposalId) throws Exception { + Robot3ServiceBean.log.info("createOrUpdateMacromolecule"); + + ArrayList macromoleculeList = new ArrayList(); + + /** Triming all values **/ + for (HashMap hashMap : samples) { + for (String key : hashMap.keySet()) { + /** It may be null when creating template and plate = null **/ + if (hashMap.get(key) != null) { + hashMap.put(key, hashMap.get(key).trim()); + } + } + } + + HashMap macromolecules = this.parseMacromolecules(samples, proposalId); + for (String key : macromolecules.keySet()) { + Robot3ServiceBean.log.info("createOrUpdateMacromolecule: " + macromolecules.get(key)); + } + + /** Macromolecule **/ + for (HashMap sample : samples) { + if (sample.get("type").equals("Sample")) { + Macromolecule3VO macromolecule3VO = macromolecules.get(sample.get("macromolecule")); + Robot3ServiceBean.log.info("createOrUpdateMacromolecule macromolecule.acronym = " + macromolecule3VO.getAcronym()); + if (macromolecule3VO != null) { + macromolecule3VO.setProposalId(proposalId); + macromolecule3VO = this.proposal3Service.merge(macromolecule3VO); + macromoleculeList.add(macromolecule3VO); + } + } + } + Robot3ServiceBean.log.info("createOrUpdateMacromolecule size = " + macromoleculeList.size()); + + return macromoleculeList; + } + + public ArrayList createOrUpdateBuffer(ArrayList> samples, int proposalId) throws Exception { + Robot3ServiceBean.log.info("createOrUpdateBuffer"); + + ArrayList bufferList = new ArrayList(); + + /** Triming all values **/ + for (HashMap hashMap : samples) { + for (String key : hashMap.keySet()) { + /** It may be null when creating template and plate = null **/ + if (hashMap.get(key) != null) { + hashMap.put(key, hashMap.get(key).trim()); + } + } + } + + HashMap buffers = this.parseBuffers(samples, proposalId); + for (String key : buffers.keySet()) { + Robot3ServiceBean.log.info("createOrUpdateBuffer: key= " + key + " | value= " + buffers.get(key)); + } + + /** Buffer **/ + for (HashMap sample : samples) { + Buffer3VO buffer3VO = buffers.get(sample.get("buffername").trim()); + if (buffer3VO == null) { + log.error("Buffer not found with buffer name: " + sample.get("buffername")); + } + + if (buffer3VO != null) { + buffer3VO.setProposalId(proposalId); + buffer3VO = this.proposal3Service.merge(buffer3VO); + bufferList.add(buffer3VO); + } + } + Robot3ServiceBean.log.info("createOrUpdateBuffer size = " + bufferList.size()); + + return bufferList; + } +} diff --git a/ispyb-ejb/src/main/java/ispyb/server/biosaxs/services/webservice/ATSASPipeline3ServiceBean.java b/ispyb-ejb/src/main/java/ispyb/server/biosaxs/services/webservice/ATSASPipeline3ServiceBean.java index 702368ff6..63ce622a9 100644 --- a/ispyb-ejb/src/main/java/ispyb/server/biosaxs/services/webservice/ATSASPipeline3ServiceBean.java +++ b/ispyb-ejb/src/main/java/ispyb/server/biosaxs/services/webservice/ATSASPipeline3ServiceBean.java @@ -1,1049 +1,1050 @@ -/******************************************************************************* - * This file is part of ISPyB. - * - * ISPyB is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * ISPyB is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with ISPyB. If not, see . - * - * Contributors : S. Delageniere, R. Leal, L. Launer, K. Levik, S. Veyrier, P. Brenchereau, M. Bodin, A. De Maria Antolinos - ******************************************************************************************************************************/ - -package ispyb.server.biosaxs.services.webservice; - -import ispyb.common.util.Constants; -import ispyb.server.biosaxs.services.core.ExperimentScope; -import ispyb.server.biosaxs.services.core.analysis.abInitioModelling.AbInitioModelling3Service; -import ispyb.server.biosaxs.services.core.analysis.advanced.AdvancedAnalysis3Service; -import ispyb.server.biosaxs.services.core.analysis.primaryDataProcessing.PrimaryDataProcessing3Service; -import ispyb.server.biosaxs.services.core.experiment.Experiment3Service; -import ispyb.server.biosaxs.services.core.measurement.Measurement3Service; -import ispyb.server.biosaxs.services.core.measurementToDataCollection.MeasurementToDataCollection3Service; -import ispyb.server.biosaxs.services.core.proposal.SaxsProposal3Service; -import ispyb.server.biosaxs.services.core.robot.Robot3Service; -import ispyb.server.biosaxs.services.core.samplePlate.Sampleplate3Service; -import ispyb.server.biosaxs.services.core.specimen.Specimen3Service; -import ispyb.server.biosaxs.vos.advanced.FitStructureToExperimentalData3VO; -import ispyb.server.biosaxs.vos.advanced.MixtureToStructure; -import ispyb.server.biosaxs.vos.advanced.RigidBodyModeling3VO; -import ispyb.server.biosaxs.vos.advanced.Superposition3VO; -import ispyb.server.biosaxs.vos.assembly.Macromolecule3VO; -import ispyb.server.biosaxs.vos.assembly.Structure3VO; -import ispyb.server.biosaxs.vos.dataAcquisition.Buffer3VO; -import ispyb.server.biosaxs.vos.dataAcquisition.Experiment3VO; -import ispyb.server.biosaxs.vos.dataAcquisition.Measurement3VO; -import ispyb.server.biosaxs.vos.dataAcquisition.Specimen3VO; -import ispyb.server.biosaxs.vos.dataAcquisition.plate.PlateGroup3VO; -import ispyb.server.biosaxs.vos.dataAcquisition.plate.Sampleplate3VO; -import ispyb.server.biosaxs.vos.dataAcquisition.plate.Sampleplateposition3VO; -import ispyb.server.biosaxs.vos.datacollection.Framelist3VO; -import ispyb.server.biosaxs.vos.datacollection.MeasurementTodataCollection3VO; -import ispyb.server.biosaxs.vos.datacollection.Merge3VO; -import ispyb.server.biosaxs.vos.datacollection.Model3VO; -import ispyb.server.biosaxs.vos.datacollection.SaxsDataCollection3VO; -import ispyb.server.biosaxs.vos.datacollection.Subtraction3VO; -import ispyb.server.common.services.proposals.Proposal3Service; -import ispyb.server.common.services.sessions.Session3Service; -import ispyb.server.common.util.LoggerFormatter; -import ispyb.server.common.vos.proposals.Proposal3VO; -import ispyb.server.common.vos.proposals.ProposalWS3VO; -import ispyb.server.mx.vos.collections.Session3VO; -import ispyb.server.mx.vos.collections.SessionWS3VO; - -import java.io.File; -import java.lang.reflect.Type; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Calendar; -import java.util.Date; -import java.util.GregorianCalendar; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Set; - -import javax.ejb.EJB; -import javax.ejb.Stateless; -import javax.persistence.EntityManager; -import javax.persistence.PersistenceContext; - -import org.apache.log4j.Logger; - -import com.google.gson.Gson; -import com.google.gson.JsonElement; -import com.google.gson.reflect.TypeToken; - -@Stateless -public class ATSASPipeline3ServiceBean implements ATSASPipeline3Service, DesySampleChangerConnectorServiceLocal { - private final static Logger LOG = Logger.getLogger("ATSASPipeline3ServiceBean"); - - /** The entity manager. */ - @PersistenceContext(unitName = "ispyb_db") - private EntityManager entityManager; - - @EJB - private Proposal3Service proposalService; - - @EJB - private SaxsProposal3Service saxsProposal3Service; - - @EJB - private AbInitioModelling3Service AbInitioModelling3Service; - - @EJB - private Experiment3Service experiment3Service; - - @EJB - private Measurement3Service measurement3Service; - - @EJB - private Specimen3Service specimen3Service; - - @EJB - private Sampleplate3Service sampleplate3Service; - - @EJB - private Robot3Service robot3Service; - - @EJB - private MeasurementToDataCollection3Service measurementToDataCollection3Service; - - @EJB - private PrimaryDataProcessing3Service primaryDataProcessing3Service; - - @EJB - private AdvancedAnalysis3Service advancedAnalysis3Service; - - private long now; - - @EJB - private Session3Service sessionService; - - /** The now. */ - protected static Calendar NOW; - - private Date getNow(){ - NOW = GregorianCalendar.getInstance(); - return NOW.getTime(); - } - - private Integer getSessionIdForExperiment(ProposalWS3VO proposal) throws Exception { - Calendar cal = Calendar.getInstance(); - Date today = cal.getTime(); - cal.add(Calendar.HOUR_OF_DAY, 24); - // by default the session created contains 3 shifts of 8hours each. - Date endTime = cal.getTime(); - - Integer sessionId = null; - - SessionWS3VO[] sessionTab = sessionService.findForWSByProposalCodeAndNumber(proposal.getCode(), - proposal.getNumber(), Constants.getSAXSBeamline()); - - if (sessionTab == null || sessionTab.length == 0) { - // No session found we have to create one to attach the experiment data to it - Session3VO ses = new Session3VO(); - ses.setBeamlineName(Constants.getSAXSBeamline()); - ses.setProposalVO(proposalService.findByPk(proposal.getProposalId())); - ses.setStartDate(today); - ses.setEndDate(endTime); - ses.setComments("session created by ISPyBB"); - ses.setScheduled((byte) 0); - ses.setNbShifts(3); - ses.setUsedFlag((byte) 1); - ses.setTimeStamp(today); - ses.setLastUpdate(endTime); - ses = sessionService.create(ses); - sessionId = ses.getSessionId(); - } else { - sessionId = sessionTab[0].getSessionId(); - } - - return sessionId; - } - - @Override - public Experiment3VO createEmptyExperiment(String proposalCode, String proposalNumber, String name) throws Exception { - ProposalWS3VO proposal = proposalService.findForWSByCodeAndNumber(proposalCode, proposalNumber); - - Experiment3VO experiment = new Experiment3VO(); - experiment.setName(name); - experiment.setType("STATIC"); - try{ - experiment.setSessionId(getSessionIdForExperiment(proposal)); - } - catch(Exception exp){ - /** Non-blocking exception **/ - exp.printStackTrace(); - } - experiment.setCreationDate(GregorianCalendar.getInstance().getTime()); - - - experiment.setProposalId(proposal.getProposalId()); - experiment = this.experiment3Service.merge(experiment); - HashMap plates = robot3Service.getDefaultSampleChangerPlateconfiguration("20"); - - PlateGroup3VO plateGroup = new PlateGroup3VO(); - plateGroup.setStorageTemperature("20"); - plateGroup = sampleplate3Service.merge(plateGroup); - - for (String key : plates.keySet()) { - Sampleplate3VO plate = plates.get(key); - plate.setExperimentId(experiment.getExperimentId()); - plate.setPlategroup3VO(plateGroup); - plate = sampleplate3Service.merge(plate); - sampleplate3Service.merge(plate); - } - - /** Creating a session **/ - Integer sessionId = null; - - - return experiment; - } - - @Override - public Measurement3VO appendMeasurementToExperiment(String experimentId, - String runNumber, String type, String plate, String row, String well, String name, String bufferName, - String concentration, String sEUtemperature, String viscosity, String volume, String volumeToLoad, String waitTime, String transmission, String comments) { - - // return experiment3Service.appendMeasurementToExperiment(experimentId, - // runNumber, type, plate, row, well, name, bufferName, concentration, - // sEUtemperature, viscosity, volume, volumeToLoad, waitTime, - // transmission, comments); - Experiment3VO experiment = experiment3Service.findById(Integer.parseInt(experimentId), ExperimentScope.MEDIUM); - System.out.println("Getting Experiment:" + experiment.getExperimentId()); - System.out.println("NAME:" + name); - System.out.println("proposal:" + experiment.getProposalId()); - - List macromolecules = new ArrayList(); - try { - List macromoleculeList = saxsProposal3Service.findMacromoleculesBy(name, experiment.getProposalId()); - System.out.println(macromoleculeList); - if (macromoleculeList != null) { - for (Macromolecule3VO macromolecule3vo : macromoleculeList) { - if (macromolecule3vo.getAcronym() != null) { - if (macromolecule3vo.getAcronym().toLowerCase().trim().equals(name.toLowerCase().trim())) { - macromolecules.add(macromolecule3vo); - System.out.println("Found"); - } - } - } - } - } catch (Exception e) { - e.printStackTrace(); - macromolecules = new ArrayList(); - } - - Specimen3VO specimen = new Specimen3VO(); - - /** - * - * We don't check location of the specimen for ATSAS - * http://forge.epn-campus.eu/issues/2095 - * - */ - Set plates = experiment.getSamplePlate3VOs(); - - - - - specimen.setConcentration(concentration); - if (type.toUpperCase().equals("SAMPLE")) { - Macromolecule3VO macromolecule = null; - if (macromolecules.size() == 0) { - if (name == null) { - name = ""; - } - Macromolecule3VO m = new Macromolecule3VO(experiment.getProposalId(), name, name, ""); - m.setCreationDate(GregorianCalendar.getInstance().getTime()); - macromolecule = entityManager.merge(m); - } else { - macromolecule = macromolecules.get(0); - } - specimen.setMacromolecule3VO(macromolecule); - } - - List buffers = saxsProposal3Service.findBuffersByProposalId(experiment.getProposalId()); - for (Buffer3VO buffer3vo : buffers) { - if (buffer3vo.getAcronym() != null) { - if (buffer3vo.getAcronym().equals(bufferName)) { - specimen.setBufferId(buffer3vo.getBufferId()); - } - } - } - /** If buffer not found **/ - if (specimen.getBufferId() == null) { - Buffer3VO buffer3VO = new Buffer3VO(); - if (bufferName == null) { - bufferName = ""; - } - buffer3VO.setAcronym(bufferName); - buffer3VO.setName(bufferName); - buffer3VO.setProposalId(experiment.getProposalId()); - buffer3VO = entityManager.merge(buffer3VO); - specimen.setBufferId(buffer3VO.getBufferId()); - } - - /** Sample plate position **/ - LOG.info("getSampleplateposition3VO: " + specimen.getSampleplateposition3VO()); - LOG.info("plates: " + plates.size()); - if (specimen.getSampleplateposition3VO() == null) { - /** this specimen is new because has no position **/ - for (Sampleplate3VO samplePlate : plates) { - if (samplePlate.getSlotPositionColumn().equals(plate)) { - Sampleplateposition3VO position = new Sampleplateposition3VO(); - position.setColumnNumber(Integer.parseInt(well)); - position.setRowNumber(Integer.parseInt(row)); - position.setSamplePlateId(samplePlate.getSamplePlateId()); - position = entityManager.merge(position); - specimen.setSampleplateposition3VO(position); - } - } - } - - specimen.setExperimentId(experiment.getExperimentId()); - specimen.setVolume(volume); - - specimen = entityManager.merge(specimen); - - Measurement3VO measurement = new Measurement3VO(); - measurement.setCode(runNumber); - measurement.setSpecimenId(specimen.getSpecimenId()); - measurement.setExposureTemperature(sEUtemperature); - measurement.setTransmission(transmission); - measurement.setComment(comments); - measurement.setViscosity(viscosity); - measurement.setVolumeToLoad(volumeToLoad); - measurement.setWaitTime(waitTime); - measurement.setPriority(experiment.getMeasurements().size() + 1); - measurement = entityManager.merge(measurement); - return measurement; - } - - public void updateAverage(int measurementId, int averagedCount, int framesCount, String oneDimensionalDataFilePathArray, String discardedCurves, String averageFilePath) { - List curveList = Arrays.asList(oneDimensionalDataFilePathArray.split(",")); - List mergeList = this.primaryDataProcessing3Service.findByMeasurementId(measurementId); - if (mergeList != null){ - if (mergeList.size() > 0){ - Merge3VO merge = mergeList.get(0); - if (Integer.parseInt(merge.getFramesCount()) >= framesCount){ - LOG.info("Same number of frames count. No update"); - return; - } - else{ - this.primaryDataProcessing3Service.addMerge(measurementId, averagedCount, framesCount, oneDimensionalDataFilePathArray, null, averageFilePath); - } - } - } - } - - @Override - public void addAveraged(String measurementId, String averaged, String discarded, String averageFile) { - - Type listType = new TypeToken>>() { - }.getType(); - List> averageFrames = new Gson().fromJson(averaged, listType); - List> discardeFrames = new Gson().fromJson(discarded, listType); - - - ArrayList oneDimensionalFiles = new ArrayList(); - LOG.info("########### Averaged Frames ############# "); - for (HashMap frame : averageFrames) { - if (frame.containsKey("filePath")) { - oneDimensionalFiles.add(frame.get("filePath")); - LOG.info("\t" + frame.get("filePath")); - } - } - - LOG.info("########### Discarded Frames ############# "); - for (HashMap frame : discardeFrames) { - if (frame.containsKey("filePath")) { - oneDimensionalFiles.add(frame.get("filePath")); - LOG.info("\t" + frame.get("filePath")); - } - } - - /** Does it contains already a average **/ - List merges = primaryDataProcessing3Service.findByMeasurementId(Integer.parseInt(measurementId)); - LOG.info("------ Number of Averages by measurementId --------"); - LOG.info(merges.size()); - - if (merges.size() == 0) { - LOG.info("\t Creating a new average"); - /** Not any average found so we create a new one **/ - this.primaryDataProcessing3Service.addMerge(Integer.parseInt(measurementId), averageFrames.size(), oneDimensionalFiles.size(), - oneDimensionalFiles.toString().replace("[", "").replace("]", ""), null, averageFile); - } else { - LOG.info("\t Updating a new average"); - /** It is an update of the average **/ - - this.updateAverage(Integer.parseInt(measurementId), averageFrames.size(), oneDimensionalFiles.size(), - oneDimensionalFiles.toString().replace("[", "").replace("]", ""), null, averageFile); - - } - } - -// private void removeFrameListById(int frameListId) { -// Framelist3VO frameList = this.entityManager.find(Framelist3VO.class, frameListId); -// /** Looking for FrameToListObjects **/ -// -// String query = "select f from Frametolist3VO f where f.frameListId=:frameListId"; -// Query EJBQuery = this.entityManager.createQuery(query).setParameter("frameListId", frameListId); -// List frameToListList = (List)EJBQuery.getResultList(); -// for (Frametolist3VO frametolist3vo : frameToListList) { -// System.out.println("Found " + frametolist3vo.getFrameToListId()); -// } -// -// } - - /** - * True if a datacollection contains all the measurementIds otherwise false - * - * @param dataCollection - * @param measurementIds - * @return - */ - private Boolean containsSameMeasurements(SaxsDataCollection3VO dataCollection, List measurementIds) { - Iterator mIterator = dataCollection.getMeasurementtodatacollection3VOs().iterator(); - HashSet measurementDataCollectionSet = new HashSet(); - while (mIterator.hasNext()) { - MeasurementTodataCollection3VO measurementToDataCollection = mIterator.next(); - measurementDataCollectionSet.add(measurementToDataCollection.getMeasurementId()); - } - for (Integer measurementId : measurementIds) { - if (!measurementDataCollectionSet.contains(measurementId)) { - return false; - } - } - return true; - } - - private SaxsDataCollection3VO getDataCollectionByMeasurementsId(Experiment3VO experiment, List measurementIds) { - Iterator iterator = experiment.getDataCollections().iterator(); - while (iterator.hasNext()) { - SaxsDataCollection3VO dataCollection = iterator.next(); - if (this.containsSameMeasurements(dataCollection, measurementIds)) { - /** - * Returning the existing one - */ - return dataCollection; - } - } - return null; - } - /** - * Returns the measurement id corresponding to a sample or null if all measurementIds are buffers - * @param experiment - * @param measurementIds - * @return - */ - private Integer getSampleFromMeasurementList(Experiment3VO experiment, List measurementIds) { - for (Integer measurementId : measurementIds) { - Specimen3VO specimen = experiment.getSampleById(experiment.getMeasurementById(measurementId).getSpecimenId()); - if (specimen.getMacromolecule3VO() != null){ - return measurementId; - } - } - return null; - } - - /** - * return a existing datacollection if found containing the same - * measurements or create a new one - * - * @param experiment - * @param measurementIds - * @return - */ - private SaxsDataCollection3VO getSaxsDataCollectionByMeasurementIds(Experiment3VO experiment, List measurementIds) { -// SaxsDataCollection3VO datacollection = this.getDataCollectionByMeasurementsId(experiment, measurementIds); - SaxsDataCollection3VO datacollection = null; - LOG.info("Getting data collection"); - LOG.info("Getting data collection: " + measurementIds.toString()); - Integer sampleMeasurementId = this.getSampleFromMeasurementList(experiment, measurementIds); - LOG.info("sampleMeasurementId: " + sampleMeasurementId); - if (sampleMeasurementId == null){ - datacollection = this.getDataCollectionByMeasurementsId(experiment, measurementIds); - } - else{ - /** Look for a subtraction already containing this sample **/ - datacollection = experiment.getDataCollectionByMeasurementId(sampleMeasurementId); - - if (datacollection != null){ - /** Adding extra measurements **/ - Set measurementToDatacollectionList = datacollection.getMeasurementtodatacollection3VOs(); - Set measurementKeys = new HashSet(); - for (MeasurementTodataCollection3VO measurementToDatacollection : measurementToDatacollectionList) { - measurementKeys.add(measurementToDatacollection.getMeasurementId()); - } - LOG.info("measurementKeys:" + measurementKeys.toString()); - for (Integer measurementId : measurementIds) { - LOG.info("measurementKeys:" + measurementId + " " + measurementKeys.toString()); - if(!measurementKeys.contains(measurementId)){ - /** This measurement does not belong to the data collection yet **/ - MeasurementTodataCollection3VO measurementToDataCollection = new MeasurementTodataCollection3VO(); - measurementToDataCollection.setDataCollectionId(datacollection.getDataCollectionId()); - measurementToDataCollection.setMeasurementId(measurementId); - measurementToDataCollection.setDataCollectionOrder(datacollection.getMeasurementtodatacollection3VOs().size() + 1); - LOG.info("setDataCollectionOrder:" + datacollection.getMeasurementtodatacollection3VOs().size() + 1); - this.entityManager.merge(measurementToDataCollection); - } - } - } - } - - if (datacollection != null) { - return datacollection; - } else { - /** We create a new one **/ - SaxsDataCollection3VO dataCollection = new SaxsDataCollection3VO(); - dataCollection.setExperimentId(experiment.getExperimentId()); - dataCollection = entityManager.merge(dataCollection); - int dataCollectionOrder = 1; - for (Integer measurementId : measurementIds) { - MeasurementTodataCollection3VO measurementToDataCollection = new MeasurementTodataCollection3VO(); - measurementToDataCollection.setDataCollectionId(dataCollection.getDataCollectionId()); - measurementToDataCollection.setMeasurementId(measurementId); - measurementToDataCollection.setDataCollectionOrder(dataCollectionOrder); - this.entityManager.merge(measurementToDataCollection); - dataCollectionOrder++; - } - return dataCollection; - } - } - - private Framelist3VO createFrames(List sampleOneDimensionalFilesList){ - if (sampleOneDimensionalFilesList.size() > 0){ - return this.primaryDataProcessing3Service.addFrameList(sampleOneDimensionalFilesList); - } - return null; - } - - private void addSubtraction(Integer dataCollectionId, String rgStdev, String i0, String i0Stdev, String firstPointUsed, - String lastPointUsed, String quality, String isagregated, String rgGuinier, String rgGnom, String dmax, String total, - String volume, String sampleOneDimensionalFiles, String bufferOneDimensionalFiles, String sampleAverageFilePath, - String bestBufferFilePath, String subtractedFilePath, String experimentalDataPlotFilePath, String densityPlotFilePath, - String guinierPlotFilePath, String kratkyPlotFilePath, String gnomOutputFilePath, Subtraction3VO subtraction) { - - HashMap params = new HashMap(); - - long start = this.logInit("addSubtraction_CP_3.1", new Gson().toJson(params)); - - /** Getting the frames **/ - Type typeOfHash = new TypeToken>>() {}.getType(); - List> sampleOneDimensionalFilesListObject = new Gson().fromJson(sampleOneDimensionalFiles, typeOfHash); - List> bufferOneDimensionalFilesListObject = new Gson().fromJson(bufferOneDimensionalFiles, typeOfHash); - - List sampleOneDimensionalFilesList = new ArrayList(); - List bufferOneDimensionalFilesList = new ArrayList(); - - for (HashMap fileObject : sampleOneDimensionalFilesListObject) { - if (fileObject.get("filePath")!= null){ - sampleOneDimensionalFilesList.add(fileObject.get("filePath")); - } - } - - for (HashMap fileObject : bufferOneDimensionalFilesListObject) { - if (fileObject.get("filePath")!= null){ - bufferOneDimensionalFilesList.add(fileObject.get("filePath")); - } - } - - LOG.info(sampleOneDimensionalFilesList); - Framelist3VO sampleFiles = this.createFrames(sampleOneDimensionalFilesList); - this.logFinish("addSubtraction_CP_3.1", start); - - start = this.logInit("addSubtraction_CP_3.2", new Gson().toJson(params)); - Framelist3VO bufferFiles = this.createFrames(bufferOneDimensionalFilesList); - this.logFinish("addSubtraction_CP_3.2", start); - - - start = this.logInit("addSubtraction_CP_3.3", new Gson().toJson(params)); - subtraction.setDataCollectionId(dataCollectionId); - subtraction.setRg(rgGuinier); - subtraction.setRgStdev(rgStdev); - subtraction.setI0(i0); - subtraction.setI0stdev(i0Stdev); - subtraction.setFirstPointUsed(firstPointUsed); - subtraction.setLastPointUsed(lastPointUsed); - subtraction.setQuality(quality); - subtraction.setIsagregated(isagregated); - subtraction.setGnomFilePath(densityPlotFilePath); - subtraction.setRgGuinier(rgGuinier); - subtraction.setRgGnom(rgGnom); - subtraction.setDmax(dmax); - subtraction.setTotal(total); - subtraction.setVolume(volume); - subtraction.setKratkyFilePath(kratkyPlotFilePath); - subtraction.setScatteringFilePath(experimentalDataPlotFilePath); - subtraction.setGuinierFilePath(guinierPlotFilePath); - subtraction.setSubstractedFilePath(subtractedFilePath); - subtraction.setGnomFilePathOutput(gnomOutputFilePath); - subtraction.setCreationTime(getNow()); - - if (sampleFiles != null){ - subtraction.setSampleOneDimensionalFiles(sampleFiles); - } - if (bufferFiles != null){ - subtraction.setBufferOneDimensionalFiles(bufferFiles); - } - subtraction.setSampleAverageFilePath(sampleAverageFilePath); - subtraction.setBufferAverageFilePath(bestBufferFilePath); - - this.entityManager.merge(subtraction); - this.logFinish("addSubtraction_CP_3.3", start); - - } - - private void addSubtraction(int dataCollectionId, String rgStdev, String i0, String i0Stdev, String firstPointUsed, - String lastPointUsed, String quality, String isagregated, String rgGuinier, String rgGnom, String dmax, String total, - String volume, String sampleOneDimensionalFiles, String bufferOneDimensionalFiles, String sampleAverageFilePath, - String bestBufferFilePath, String subtractedFilePath, String experimentalDataPlotFilePath, String densityPlotFilePath, - String guinierPlotFilePath, String kratkyPlotFilePath, String gnomOutputFilePath) { - - Subtraction3VO subtraction = new Subtraction3VO(); - this.addSubtraction(dataCollectionId, rgStdev, i0, i0Stdev, firstPointUsed, lastPointUsed, quality, isagregated, rgGuinier, rgGnom, dmax, total, volume, sampleOneDimensionalFiles, bufferOneDimensionalFiles, sampleAverageFilePath, bestBufferFilePath, subtractedFilePath, experimentalDataPlotFilePath, densityPlotFilePath, guinierPlotFilePath, kratkyPlotFilePath, gnomOutputFilePath, subtraction); - - } - - - @Override - public void addSubtraction(String sampleMeasurementId, String rgStdev, String i0, String i0Stdev, String firstPointUsed, - String lastPointUsed, String quality, String isagregated, String rgGuinier, String rgGnom, String dmax, String total, - String volume, String sampleOneDimensionalFiles, String bufferOneDimensionalFiles, String sampleAverageFilePath, - String bestBufferFilePath, String subtractedFilePath, String experimentalDataPlotFilePath, String densityPlotFilePath, - String guinierPlotFilePath, String kratkyPlotFilePath, String gnomOutputFilePath) { - - - - HashMap params = new HashMap(); - long start = this.logInit("addSubtraction_CP_1", new Gson().toJson(params)); - if (sampleMeasurementId != null){ - // TODO: To be changed, performace problems!!! - Experiment3VO experiment = experiment3Service.findByMeasurementId(Integer.parseInt(sampleMeasurementId)); - this.logFinish("addSubtraction_CP_1", start); - - - if (experiment != null) { - start = this.logInit("addSubtraction_CP_2", new Gson().toJson(params)); - int dataCollectionId = experiment.getDataCollectionByMeasurementId(Integer.parseInt(sampleMeasurementId)).getDataCollectionId(); - this.logFinish("addSubtraction_CP_2", start); - - - start = this.logInit("addSubtraction_CP_3", new Gson().toJson(params)); - this.addSubtraction(dataCollectionId, rgStdev, i0, i0Stdev, firstPointUsed, lastPointUsed, quality, isagregated, rgGuinier, rgGnom, dmax, total, volume, sampleOneDimensionalFiles, bufferOneDimensionalFiles, sampleAverageFilePath, bestBufferFilePath, subtractedFilePath, experimentalDataPlotFilePath, densityPlotFilePath, guinierPlotFilePath, kratkyPlotFilePath, gnomOutputFilePath); - this.logFinish("addSubtraction_CP_3", start); - - } - } - } - - - /** - * Adds a new subtraction to several measurements To note: there are not any data collection yet - */ - public void addSubtraction( - String experimentId, - String runNumberList, - String rgStdev, - String i0, - String i0Stdev, - String firstPointUsed, - String lastPointUsed, - String quality, - String isagregated, - String rgGuinier, - String rgGnom, - String dmax, - String total, - String volume, - String sampleOneDimensionalFiles, - String bufferOneDimensionalFiles, - String sampleAverageFilePath, - String bestBufferFilePath, - String subtractedFilePath, - String experimentalDataPlotFilePath, - String densityPlotFilePath, - String guinierPlotFilePath, - String kratkyPlotFilePath, - String gnomOutputFilePath) throws Exception { - - Type typeOfList = new TypeToken>() {}.getType(); - List ids = new Gson().fromJson(runNumberList, typeOfList); - - /** Finding measurement by experimentId and run number **/ - List measurementIds = new ArrayList(); - for (String runNumber : ids) { - Measurement3VO measurement = measurement3Service.findMeasurementByCode(Integer.parseInt(experimentId), runNumber.toString()); - if (measurement != null) { - measurementIds.add(measurement.getMeasurementId()); - } else { - throw new Exception("No measurement found for runNumber: " + runNumber + " and experimentId: " + experimentId); - } - } - - System.out.println("Adding data collection and subtraction"); - - /** Adding data collection and subtraction **/ - if (measurementIds != null) { - if (measurementIds.size() > 0) { - Experiment3VO experiment = experiment3Service.findById(Integer.parseInt(experimentId), ExperimentScope.MEDIUM); - if (experiment != null) { - /** This method creates the data collection if it does not exist yet **/ - SaxsDataCollection3VO dataCollection = getSaxsDataCollectionByMeasurementIds(experiment, measurementIds); - - if (dataCollection.getSubstraction3VOs().size() == 0){ - this.addSubtraction(dataCollection.getDataCollectionId(), rgStdev, i0, i0Stdev, firstPointUsed, lastPointUsed, quality, isagregated, rgGuinier, rgGnom, dmax, total, volume, sampleOneDimensionalFiles, bufferOneDimensionalFiles, sampleAverageFilePath, bestBufferFilePath, subtractedFilePath, experimentalDataPlotFilePath, densityPlotFilePath, guinierPlotFilePath, kratkyPlotFilePath, gnomOutputFilePath); - } - else{ - /** - * Upgrading subtraction - */ - Subtraction3VO subtraction = (Subtraction3VO) Arrays.asList(dataCollection.getSubstraction3VOs().toArray()).get(0); - this.addSubtraction(dataCollection.getDataCollectionId(), rgStdev, i0, i0Stdev, firstPointUsed, lastPointUsed, quality, isagregated, rgGuinier, rgGnom, dmax, total, volume, sampleOneDimensionalFiles, bufferOneDimensionalFiles, sampleAverageFilePath, bestBufferFilePath, subtractedFilePath, experimentalDataPlotFilePath, densityPlotFilePath, guinierPlotFilePath, kratkyPlotFilePath, gnomOutputFilePath, subtraction); - } - } - } - } - } - - - - @Override - public void addAbinitioModelling(String experimentId, String runNumber, - ArrayList models3vo, Model3VO referenceModel, - Model3VO refinedModel) throws Exception { - - Measurement3VO measurement = measurement3Service.findMeasurementByCode(Integer.parseInt(experimentId), (runNumber)); - if (measurement != null){ - ArrayList measurementIds = new ArrayList(); - measurementIds.add(measurement.getMeasurementId()); - AbInitioModelling3Service.addAbinitioModeling(measurementIds, - models3vo, - referenceModel, - refinedModel); - - } - else{ - throw new Exception("No measurement found for " + experimentId + " and " + runNumber); - } - } - - @Override - public Model3VO getModel(String experimentId, String runNumber, String pdbfilepath) throws Exception { - Measurement3VO measurement = measurement3Service.findMeasurementByCode(Integer.parseInt(experimentId), (runNumber)); - if (measurement != null){ - return AbInitioModelling3Service.getModel(measurement.getMeasurementId(), pdbfilepath); - } - else{ - throw new Exception("Not measurement found for " + experimentId + " and " + runNumber); - } - } - - @Override - public Model3VO merge(Model3VO model) { - return this.entityManager.merge(model); - } - - @Override - public List getMacromoleculeByAcronym(String proposal, String acronym) { - List proposal3VOs = this.proposalService.findProposalByLoginName(proposal); - if (proposal3VOs != null){ - if (proposal3VOs.size() > 0){ - Proposal3VO proposal3VO = proposal3VOs.get(0); - List macromolecules = this.saxsProposal3Service.findMacromoleculesBy(acronym, proposal3VO.getProposalId()); - return macromolecules; - } - } - return new ArrayList(); - } - - public List getSubtractionByCode(String experimentId, String runNumber) { - Measurement3VO measurement = measurement3Service.findMeasurementByCode(Integer.valueOf(experimentId), runNumber); - System.out.println("measurement " + measurement); - if (measurement != null){ - return primaryDataProcessing3Service.getSubstractionsByMeasurementId(measurement.getMeasurementId()); - } - System.out.println("measurement not found "); - return null; - } - - public Macromolecule3VO getMacromoleculeByCode(String experimentId, String runNumber) { - Measurement3VO measurement = measurement3Service.findMeasurementByCode(Integer.valueOf(experimentId), runNumber); - if (measurement != null){ - Specimen3VO specimen = entityManager.find(Specimen3VO.class, measurement.getSpecimenId()); - if (specimen != null){ - return saxsProposal3Service.findMacromoleculesById(specimen.getMacromolecule3VO().getMacromoleculeId()); -// return experiment3Service.findMacromoleculeById(specimen.getMacromolecule3VO().getMacromoleculeId()); - } - } - return null; - } - - /** - * - * @param experimentId - * @param runNumber - * @param filePath {filePath:"/filepath.dat"} - * @return - */ - public Structure3VO getStructuresByCode(String experimentId, String runNumber, String filePath) { - Macromolecule3VO macromolecule = this.getMacromoleculeByCode(experimentId, runNumber); - for (Structure3VO structure : macromolecule.getStructure3VOs()) { - if (structure.getFilePath().trim().equals(filePath.trim())){ - return structure; - } - } - - return null; - } - - @Override - public void addMixture(String experimentId, String runNumber, String fitFilePath, String pdb) throws Exception { - List subtractions = this.getSubtractionByCode(experimentId, runNumber); - System.out.println("+++addMixture " + experimentId + " " + runNumber); - if (subtractions != null){ - if (subtractions.size() > 0){ - /** Getting last one are they should come by order **/ - Subtraction3VO subtraction = subtractions.get(subtractions.size() - 1); - - System.out.println("+++Subtraction Found"); - /** Getting Structure **/ - - Type ListType = new TypeToken>>() {}.getType(); - - /** Reading pdbs **/ - List> pdbFilePath = new Gson().fromJson(pdb, ListType); - - List mixtures = new ArrayList(); - if (pdbFilePath.size() > 0){ - for (HashMap pdbHash : pdbFilePath) { - MixtureToStructure mixture = new MixtureToStructure(); - - if (pdbHash.containsKey("filePath")){ - /** Finding the structue linked to such filePath */ - Structure3VO structure = this.getStructuresByCode(experimentId, runNumber, pdbHash.get("filePath")); - /** If structure does not exist we create a new one **/ - if (structure == null){ - structure = new Structure3VO(); - Macromolecule3VO macromolecule = this.getMacromoleculeByCode(experimentId, runNumber); - System.out.println("Macromolecule found"); - if (macromolecule != null){ - structure.setMacromoleculeId(macromolecule.getMacromoleculeId()); - structure.setFilePath(pdbHash.get("filePath")); - if (pdbHash.get("filePath") != null){ - String name = new File(pdbHash.get("filePath")).getName(); - structure.setName(name); - } - structure.setType("PDB"); - structure.setCreationDate(getNow()); - structure = this.entityManager.merge(structure); - System.out.println("Structure created"); - } - else{ - throw new Exception("Macromolecule not found for experimentId " + experimentId + " and runNumber " + runNumber ); - } - } - - /** Structure exists here **/ - mixture.setStructureId(structure.getStructureId()); - mixture.setCreationDate(getNow()); - - } - - if (pdbHash.containsKey("volumeFraction")){ - System.out.println("+++" +"Volume: " + pdbHash.get("volumeFraction")); - mixture.setVolumeFraction(pdbHash.get("volumeFraction")); - } - - mixtures.add(mixture); - } - - System.out.println( "+++Mixtures --- " + mixtures.size()); - System.out.println("+++" +fitFilePath + " --- " + fitFilePath); - - - /** Creating FitStructureToExperimentalData **/ - FitStructureToExperimentalData3VO fit = new FitStructureToExperimentalData3VO(); - fit.setSubtractionId(subtraction.getSubtractionId()); - fit.setCreationDate(getNow()); - fit.setFitFilePath(fitFilePath); - fit = entityManager.merge(fit); - System.out.println("+++Merging fit " + fit.getFitFilePath() + " " + fit.getFitStructureToExperimentalDataId()); - - /** Creating MixtureToStructures **/ - for (MixtureToStructure mixtureToStructure : mixtures) { - mixtureToStructure.setMixtureId(fit.getFitStructureToExperimentalDataId()); - mixtureToStructure = this.entityManager.merge(mixtureToStructure); - System.out.println("+++Merging mixturetoStructure " + mixtureToStructure.getVolumeFraction() + " " + mixtureToStructure.getStructureId() + " " + mixtureToStructure.getFitToStructureId()); - } - } - - /** Reading fitFilePath **/ -// Type HashType = new TypeToken>() {}.getType(); -// HashMap fitFilePathHash = new Gson().fromJson(fitFilePath, HashType); -// if (fitFilePathHash.containsKey("filePath")){ -// fitFilePath = fitFilePathHash.get("filePath"); -// } - - - } - else{ - throw new Exception("0 subtractions has been found for experimentId= " + experimentId + " and runNumber= " + runNumber); - } - - } - else{ - throw new Exception("No subtraction has been found for experimentId= " + experimentId + " and runNumber= " + runNumber); - } -// if (fitStructure == null){ -// fitStructure = new FitStructureToExperimentalData(); -// } -// -// fitStructure.setFitFilePath(fitFilePath); -//// fitStructure.setLogFilePath(logFile); -// fitStructure.setOutputFilePath(summaryFile); -//// fitStructure.setStructureId(Integer.parseInt(structureId)); -// fitStructure.setSubtractionId(Integer.parseInt(subtractionId)); -// fitStructure.setWorkflowId(Integer.parseInt(workflowId)); -// fitStructure.setComments("Finished at " + getNow()); -// fitStructure = advancedAnalysis3Service.merge(fitStructure); - - } - - @Override - public void addRigidBodyModeling(String experimentId, String runNumber, String fitFilePath, String rigidBodyModelFilePath, - String logFilePath, String curveConfigFilePath, String subunitConfigFilePath, String crosscorrConfigFilePath, - String contactConditionsFilePath, String masterSymmetry) throws Exception { - - List subtractions = this.getSubtractionByCode(experimentId, runNumber); - if (subtractions != null){ - if (subtractions.size() > 0){ - RigidBodyModeling3VO rigid = new RigidBodyModeling3VO(); - rigid.setSubtractionId(subtractions.get(subtractions.size() - 1).getSubtractionId()); - rigid.setFitFilePath(fitFilePath); - rigid.setRigidBodyModelFilePath(rigidBodyModelFilePath); - rigid.setLogFilePath(logFilePath); - rigid.setCurveConfigFilePath(curveConfigFilePath); - rigid.setSubUnitConfigFilePath(subunitConfigFilePath); - rigid.setCrossCorrConfigFilePath(crosscorrConfigFilePath); - rigid.setContactDescriptionFilePath(contactConditionsFilePath); - rigid.setSymmetry(masterSymmetry); - rigid.setCreationDate(getNow()); - this.entityManager.merge(rigid); - } - else{ - throw new Exception("No subtraction found for " + experimentId + " " + runNumber); - } - } - else{ - throw new Exception("No subtraction found for " + experimentId + " " + runNumber + "(null)"); - } - } - - @Override - public void addSuperposition(String experimentId, String runNumber, String abinitioModelPdbFilePath, String aprioriPdbFilePath, - String alignedPdbFilePath) throws Exception { - - List subtractions = this.getSubtractionByCode(experimentId, runNumber); - if (subtractions != null){ - Superposition3VO superposition = new Superposition3VO(); - superposition.setSubtractionId(subtractions.get(subtractions.size() - 1).getSubtractionId()); - superposition.setAbinitioModelPdbFilePath(abinitioModelPdbFilePath); - superposition.setAprioriPdbFilePath(aprioriPdbFilePath); - superposition.setAlignedPdbFilePath(alignedPdbFilePath); - superposition.setCreationDate(getNow()); - this.entityManager.merge(superposition); - } - else{ - throw new Exception("No subtraction found for " + experimentId + " " + runNumber + "(null)"); - } - - - } - - @Override - public HashMap getAprioriInformationByRunNumber(String proposal, String acronym) throws Exception { - HashMap info = new HashMap(); - List macromolecules = this.getMacromoleculeByAcronym(proposal, acronym); - if (macromolecules.size() == 1){ - Macromolecule3VO macromolecule = macromolecules.get(0); - if (macromolecule != null){ - info.put("ACRONYM", macromolecule.getAcronym()); - List> pdb = new ArrayList>(); - List> fasta = new ArrayList>(); - List> rbms = new ArrayList>(); - if (macromolecule.getStructure3VOs() != null){ - for (Structure3VO structure : macromolecule.getStructure3VOs()) { - if (structure.getType().equals("PDB")){ - HashMap pdbEntry = new HashMap(); - pdbEntry.put("FILEPATH", structure.getFilePath()); - pdbEntry.put("SYMMETRY", structure.getSymmetry()); - pdbEntry.put("MULTIPLICITY", structure.getMultiplicity()); - pdb.add(pdbEntry); - } - if (structure.getType().equals("SEQUENCE")){ - HashMap fastaEntry = new HashMap(); - fastaEntry.put("FILEPATH", structure.getFilePath()); - fasta.add(fastaEntry); - } - } - } - - HashMap rbm = new HashMap(); - rbm.put("FILEPATH", macromolecule.getContactsDescriptionFilePath()); - rbm.put("SYMMETRY", macromolecule.getSymmetry()); - rbms.add(rbm); - - info.put("PDB", pdb); - info.put("SEQUENCE", fasta); - info.put("RIGIDBODY", rbms); - return info; - } - else{ - throw new Exception("macromolecule not found"); - } - } - else{ - throw new Exception(macromolecules.size() + " macromolecules found"); - } - } - - - private void logFinish(String methodName, long id) { - LOG.debug("### [" + methodName.toUpperCase() + "] Execution time was " + (System.currentTimeMillis() - this.now) + " ms."); - LoggerFormatter.log(LOG, LoggerFormatter.Package.BIOSAXS_WS, methodName, id, System.currentTimeMillis(), - System.currentTimeMillis() - this.now); - - } - - protected long logInit(String methodName, String params) { - LOG.info("-----------------------"); - this.now = System.currentTimeMillis(); - LOG.info(methodName.toUpperCase()); - LoggerFormatter.log(LOG, LoggerFormatter.Package.BIOSAXS_WS, methodName, System.currentTimeMillis(), - System.currentTimeMillis(), params); - return this.now; - } - - @Override - public List getMacromoleculesByProposal(String code, String number) throws Exception { - ProposalWS3VO proposal = proposalService.findForWSByCodeAndNumber(code, number); - return this.saxsProposal3Service.findMacromoleculesByProposalId(proposal.getProposalId()); - } - - @Override - public List getBuffersByProposal(String code, String number) throws Exception { - ProposalWS3VO proposal = proposalService.findForWSByCodeAndNumber(code, number); - return this.saxsProposal3Service.findBuffersByProposalId(proposal.getProposalId()); - } -} +/******************************************************************************* + * This file is part of ISPyB. + * + * ISPyB is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * ISPyB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with ISPyB. If not, see . + * + * Contributors : S. Delageniere, R. Leal, L. Launer, K. Levik, S. Veyrier, P. Brenchereau, M. Bodin, A. De Maria Antolinos + ******************************************************************************************************************************/ + +package ispyb.server.biosaxs.services.webservice; + +import ispyb.common.util.Constants; +import ispyb.server.biosaxs.services.core.ExperimentScope; +import ispyb.server.biosaxs.services.core.analysis.abInitioModelling.AbInitioModelling3Service; +import ispyb.server.biosaxs.services.core.analysis.advanced.AdvancedAnalysis3Service; +import ispyb.server.biosaxs.services.core.analysis.primaryDataProcessing.PrimaryDataProcessing3Service; +import ispyb.server.biosaxs.services.core.experiment.Experiment3Service; +import ispyb.server.biosaxs.services.core.measurement.Measurement3Service; +import ispyb.server.biosaxs.services.core.measurementToDataCollection.MeasurementToDataCollection3Service; +import ispyb.server.biosaxs.services.core.proposal.SaxsProposal3Service; +import ispyb.server.biosaxs.services.core.robot.Robot3Service; +import ispyb.server.biosaxs.services.core.samplePlate.Sampleplate3Service; +import ispyb.server.biosaxs.services.core.specimen.Specimen3Service; +import ispyb.server.biosaxs.vos.advanced.FitStructureToExperimentalData3VO; +import ispyb.server.biosaxs.vos.advanced.MixtureToStructure; +import ispyb.server.biosaxs.vos.advanced.RigidBodyModeling3VO; +import ispyb.server.biosaxs.vos.advanced.Superposition3VO; +import ispyb.server.biosaxs.vos.assembly.Macromolecule3VO; +import ispyb.server.biosaxs.vos.assembly.Structure3VO; +import ispyb.server.biosaxs.vos.dataAcquisition.Buffer3VO; +import ispyb.server.biosaxs.vos.dataAcquisition.Experiment3VO; +import ispyb.server.biosaxs.vos.dataAcquisition.Measurement3VO; +import ispyb.server.biosaxs.vos.dataAcquisition.Specimen3VO; +import ispyb.server.biosaxs.vos.dataAcquisition.plate.PlateGroup3VO; +import ispyb.server.biosaxs.vos.dataAcquisition.plate.Sampleplate3VO; +import ispyb.server.biosaxs.vos.dataAcquisition.plate.Sampleplateposition3VO; +import ispyb.server.biosaxs.vos.datacollection.Framelist3VO; +import ispyb.server.biosaxs.vos.datacollection.MeasurementTodataCollection3VO; +import ispyb.server.biosaxs.vos.datacollection.Merge3VO; +import ispyb.server.biosaxs.vos.datacollection.Model3VO; +import ispyb.server.biosaxs.vos.datacollection.SaxsDataCollection3VO; +import ispyb.server.biosaxs.vos.datacollection.Subtraction3VO; +import ispyb.server.common.services.proposals.Proposal3Service; +import ispyb.server.common.services.sessions.Session3Service; +import ispyb.server.common.util.LoggerFormatter; +import ispyb.server.common.vos.proposals.Proposal3VO; +import ispyb.server.common.vos.proposals.ProposalWS3VO; +import ispyb.server.mx.vos.collections.Session3VO; +import ispyb.server.mx.vos.collections.SessionWS3VO; + +import java.io.File; +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Calendar; +import java.util.Date; +import java.util.GregorianCalendar; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Set; + +import javax.ejb.EJB; +import javax.ejb.Stateless; +import javax.persistence.EntityManager; +import javax.persistence.PersistenceContext; + +import org.apache.log4j.Logger; + +import com.google.gson.Gson; +import com.google.gson.JsonElement; +import com.google.gson.reflect.TypeToken; + +@Stateless +public class ATSASPipeline3ServiceBean implements ATSASPipeline3Service, DesySampleChangerConnectorServiceLocal { + private final static Logger LOG = Logger.getLogger("ATSASPipeline3ServiceBean"); + + /** The entity manager. */ + @PersistenceContext(unitName = "ispyb_db") + private EntityManager entityManager; + + @EJB + private Proposal3Service proposalService; + + @EJB + private SaxsProposal3Service saxsProposal3Service; + + @EJB + private AbInitioModelling3Service AbInitioModelling3Service; + + @EJB + private Experiment3Service experiment3Service; + + @EJB + private Measurement3Service measurement3Service; + + @EJB + private Specimen3Service specimen3Service; + + @EJB + private Sampleplate3Service sampleplate3Service; + + @EJB + private Robot3Service robot3Service; + + @EJB + private MeasurementToDataCollection3Service measurementToDataCollection3Service; + + @EJB + private PrimaryDataProcessing3Service primaryDataProcessing3Service; + + @EJB + private AdvancedAnalysis3Service advancedAnalysis3Service; + + private long now; + + @EJB + private Session3Service sessionService; + + /** The now. */ + protected static Calendar NOW; + + private Date getNow(){ + NOW = GregorianCalendar.getInstance(); + return NOW.getTime(); + } + + private Integer getSessionIdForExperiment(ProposalWS3VO proposal) throws Exception { + Calendar cal = Calendar.getInstance(); + Date today = cal.getTime(); + cal.add(Calendar.HOUR_OF_DAY, 24); + // by default the session created contains 3 shifts of 8hours each. + Date endTime = cal.getTime(); + + Integer sessionId = null; + + SessionWS3VO[] sessionTab = sessionService.findForWSByProposalCodeAndNumber(proposal.getCode(), + proposal.getNumber(), Constants.getSAXSBeamline()); + + if (sessionTab == null || sessionTab.length == 0) { + // No session found we have to create one to attach the experiment data to it + Session3VO ses = new Session3VO(); + ses.setBeamlineName(Constants.getSAXSBeamline()); + ses.setProposalVO(proposalService.findByPk(proposal.getProposalId())); + ses.setStartDate(today); + ses.setEndDate(endTime); + ses.setComments("session created by ISPyBB"); + ses.setScheduled((byte) 0); + ses.setNbShifts(3); + ses.setUsedFlag((byte) 1); + ses.setTimeStamp(today); + ses.setLastUpdate(endTime); + ses = sessionService.create(ses); + sessionId = ses.getSessionId(); + } else { + sessionId = sessionTab[0].getSessionId(); + } + + return sessionId; + } + + @Override + public Experiment3VO createEmptyExperiment(String proposalCode, String proposalNumber, String name) throws Exception { + ProposalWS3VO proposal = proposalService.findForWSByCodeAndNumber(proposalCode, proposalNumber); + + Experiment3VO experiment = new Experiment3VO(); + experiment = this.experiment3Service.initPlates(experiment); + experiment.setName(name); + experiment.setType("STATIC"); + try{ + experiment.setSessionId(getSessionIdForExperiment(proposal)); + } + catch(Exception exp){ + /** Non-blocking exception **/ + exp.printStackTrace(); + } + experiment.setCreationDate(GregorianCalendar.getInstance().getTime()); + + + experiment.setProposalId(proposal.getProposalId()); + experiment = this.experiment3Service.merge(experiment); + HashMap plates = robot3Service.getDefaultSampleChangerPlateconfiguration("20"); + + PlateGroup3VO plateGroup = new PlateGroup3VO(); + plateGroup.setStorageTemperature("20"); + plateGroup = sampleplate3Service.merge(plateGroup); + + for (String key : plates.keySet()) { + Sampleplate3VO plate = plates.get(key); + plate.setExperimentId(experiment.getExperimentId()); + plate.setPlategroup3VO(plateGroup); + plate = sampleplate3Service.merge(plate); + sampleplate3Service.merge(plate); + } + + /** Creating a session **/ + Integer sessionId = null; + + + return experiment; + } + + @Override + public Measurement3VO appendMeasurementToExperiment(String experimentId, + String runNumber, String type, String plate, String row, String well, String name, String bufferName, + String concentration, String sEUtemperature, String viscosity, String volume, String volumeToLoad, String waitTime, String transmission, String comments) { + + // return experiment3Service.appendMeasurementToExperiment(experimentId, + // runNumber, type, plate, row, well, name, bufferName, concentration, + // sEUtemperature, viscosity, volume, volumeToLoad, waitTime, + // transmission, comments); + Experiment3VO experiment = experiment3Service.findById(Integer.parseInt(experimentId), ExperimentScope.MEDIUM); + System.out.println("Getting Experiment:" + experiment.getExperimentId()); + System.out.println("NAME:" + name); + System.out.println("proposal:" + experiment.getProposalId()); + + List macromolecules = new ArrayList(); + try { + List macromoleculeList = saxsProposal3Service.findMacromoleculesBy(name, experiment.getProposalId()); + System.out.println(macromoleculeList); + if (macromoleculeList != null) { + for (Macromolecule3VO macromolecule3vo : macromoleculeList) { + if (macromolecule3vo.getAcronym() != null) { + if (macromolecule3vo.getAcronym().toLowerCase().trim().equals(name.toLowerCase().trim())) { + macromolecules.add(macromolecule3vo); + System.out.println("Found"); + } + } + } + } + } catch (Exception e) { + e.printStackTrace(); + macromolecules = new ArrayList(); + } + + Specimen3VO specimen = new Specimen3VO(); + + /** + * + * We don't check location of the specimen for ATSAS + * http://forge.epn-campus.eu/issues/2095 + * + */ + Set plates = experiment.getSamplePlate3VOs(); + + + + + specimen.setConcentration(concentration); + if (type.toUpperCase().equals("SAMPLE")) { + Macromolecule3VO macromolecule = null; + if (macromolecules.size() == 0) { + if (name == null) { + name = ""; + } + Macromolecule3VO m = new Macromolecule3VO(experiment.getProposalId(), name, name, ""); + m.setCreationDate(GregorianCalendar.getInstance().getTime()); + macromolecule = entityManager.merge(m); + } else { + macromolecule = macromolecules.get(0); + } + specimen.setMacromolecule3VO(macromolecule); + } + + List buffers = saxsProposal3Service.findBuffersByProposalId(experiment.getProposalId()); + for (Buffer3VO buffer3vo : buffers) { + if (buffer3vo.getAcronym() != null) { + if (buffer3vo.getAcronym().equals(bufferName)) { + specimen.setBufferId(buffer3vo.getBufferId()); + } + } + } + /** If buffer not found **/ + if (specimen.getBufferId() == null) { + Buffer3VO buffer3VO = new Buffer3VO(); + if (bufferName == null) { + bufferName = ""; + } + buffer3VO.setAcronym(bufferName); + buffer3VO.setName(bufferName); + buffer3VO.setProposalId(experiment.getProposalId()); + buffer3VO = entityManager.merge(buffer3VO); + specimen.setBufferId(buffer3VO.getBufferId()); + } + + /** Sample plate position **/ + LOG.info("getSampleplateposition3VO: " + specimen.getSampleplateposition3VO()); + LOG.info("plates: " + plates.size()); + if (specimen.getSampleplateposition3VO() == null) { + /** this specimen is new because has no position **/ + for (Sampleplate3VO samplePlate : plates) { + if (samplePlate.getSlotPositionColumn().equals(plate)) { + Sampleplateposition3VO position = new Sampleplateposition3VO(); + position.setColumnNumber(Integer.parseInt(well)); + position.setRowNumber(Integer.parseInt(row)); + position.setSamplePlateId(samplePlate.getSamplePlateId()); + position = entityManager.merge(position); + specimen.setSampleplateposition3VO(position); + } + } + } + + specimen.setExperimentId(experiment.getExperimentId()); + specimen.setVolume(volume); + + specimen = entityManager.merge(specimen); + + Measurement3VO measurement = new Measurement3VO(); + measurement.setCode(runNumber); + measurement.setSpecimenId(specimen.getSpecimenId()); + measurement.setExposureTemperature(sEUtemperature); + measurement.setTransmission(transmission); + measurement.setComment(comments); + measurement.setViscosity(viscosity); + measurement.setVolumeToLoad(volumeToLoad); + measurement.setWaitTime(waitTime); + measurement.setPriority(experiment.getMeasurements().size() + 1); + measurement = entityManager.merge(measurement); + return measurement; + } + + public void updateAverage(int measurementId, int averagedCount, int framesCount, String oneDimensionalDataFilePathArray, String discardedCurves, String averageFilePath) { + List curveList = Arrays.asList(oneDimensionalDataFilePathArray.split(",")); + List mergeList = this.primaryDataProcessing3Service.findByMeasurementId(measurementId); + if (mergeList != null){ + if (mergeList.size() > 0){ + Merge3VO merge = mergeList.get(0); + if (Integer.parseInt(merge.getFramesCount()) >= framesCount){ + LOG.info("Same number of frames count. No update"); + return; + } + else{ + this.primaryDataProcessing3Service.addMerge(measurementId, averagedCount, framesCount, oneDimensionalDataFilePathArray, null, averageFilePath); + } + } + } + } + + @Override + public void addAveraged(String measurementId, String averaged, String discarded, String averageFile) { + + Type listType = new TypeToken>>() { + }.getType(); + List> averageFrames = new Gson().fromJson(averaged, listType); + List> discardeFrames = new Gson().fromJson(discarded, listType); + + + ArrayList oneDimensionalFiles = new ArrayList(); + LOG.info("########### Averaged Frames ############# "); + for (HashMap frame : averageFrames) { + if (frame.containsKey("filePath")) { + oneDimensionalFiles.add(frame.get("filePath")); + LOG.info("\t" + frame.get("filePath")); + } + } + + LOG.info("########### Discarded Frames ############# "); + for (HashMap frame : discardeFrames) { + if (frame.containsKey("filePath")) { + oneDimensionalFiles.add(frame.get("filePath")); + LOG.info("\t" + frame.get("filePath")); + } + } + + /** Does it contains already a average **/ + List merges = primaryDataProcessing3Service.findByMeasurementId(Integer.parseInt(measurementId)); + LOG.info("------ Number of Averages by measurementId --------"); + LOG.info(merges.size()); + + if (merges.size() == 0) { + LOG.info("\t Creating a new average"); + /** Not any average found so we create a new one **/ + this.primaryDataProcessing3Service.addMerge(Integer.parseInt(measurementId), averageFrames.size(), oneDimensionalFiles.size(), + oneDimensionalFiles.toString().replace("[", "").replace("]", ""), null, averageFile); + } else { + LOG.info("\t Updating a new average"); + /** It is an update of the average **/ + + this.updateAverage(Integer.parseInt(measurementId), averageFrames.size(), oneDimensionalFiles.size(), + oneDimensionalFiles.toString().replace("[", "").replace("]", ""), null, averageFile); + + } + } + +// private void removeFrameListById(int frameListId) { +// Framelist3VO frameList = this.entityManager.find(Framelist3VO.class, frameListId); +// /** Looking for FrameToListObjects **/ +// +// String query = "select f from Frametolist3VO f where f.frameListId=:frameListId"; +// Query EJBQuery = this.entityManager.createQuery(query).setParameter("frameListId", frameListId); +// List frameToListList = (List)EJBQuery.getResultList(); +// for (Frametolist3VO frametolist3vo : frameToListList) { +// System.out.println("Found " + frametolist3vo.getFrameToListId()); +// } +// +// } + + /** + * True if a datacollection contains all the measurementIds otherwise false + * + * @param dataCollection + * @param measurementIds + * @return + */ + private Boolean containsSameMeasurements(SaxsDataCollection3VO dataCollection, List measurementIds) { + Iterator mIterator = dataCollection.getMeasurementtodatacollection3VOs().iterator(); + HashSet measurementDataCollectionSet = new HashSet(); + while (mIterator.hasNext()) { + MeasurementTodataCollection3VO measurementToDataCollection = mIterator.next(); + measurementDataCollectionSet.add(measurementToDataCollection.getMeasurementId()); + } + for (Integer measurementId : measurementIds) { + if (!measurementDataCollectionSet.contains(measurementId)) { + return false; + } + } + return true; + } + + private SaxsDataCollection3VO getDataCollectionByMeasurementsId(Experiment3VO experiment, List measurementIds) { + Iterator iterator = experiment.getDataCollections().iterator(); + while (iterator.hasNext()) { + SaxsDataCollection3VO dataCollection = iterator.next(); + if (this.containsSameMeasurements(dataCollection, measurementIds)) { + /** + * Returning the existing one + */ + return dataCollection; + } + } + return null; + } + /** + * Returns the measurement id corresponding to a sample or null if all measurementIds are buffers + * @param experiment + * @param measurementIds + * @return + */ + private Integer getSampleFromMeasurementList(Experiment3VO experiment, List measurementIds) { + for (Integer measurementId : measurementIds) { + Specimen3VO specimen = experiment.getSampleById(experiment.getMeasurementById(measurementId).getSpecimenId()); + if (specimen.getMacromolecule3VO() != null){ + return measurementId; + } + } + return null; + } + + /** + * return a existing datacollection if found containing the same + * measurements or create a new one + * + * @param experiment + * @param measurementIds + * @return + */ + private SaxsDataCollection3VO getSaxsDataCollectionByMeasurementIds(Experiment3VO experiment, List measurementIds) { +// SaxsDataCollection3VO datacollection = this.getDataCollectionByMeasurementsId(experiment, measurementIds); + SaxsDataCollection3VO datacollection = null; + LOG.info("Getting data collection"); + LOG.info("Getting data collection: " + measurementIds.toString()); + Integer sampleMeasurementId = this.getSampleFromMeasurementList(experiment, measurementIds); + LOG.info("sampleMeasurementId: " + sampleMeasurementId); + if (sampleMeasurementId == null){ + datacollection = this.getDataCollectionByMeasurementsId(experiment, measurementIds); + } + else{ + /** Look for a subtraction already containing this sample **/ + datacollection = experiment.getDataCollectionByMeasurementId(sampleMeasurementId); + + if (datacollection != null){ + /** Adding extra measurements **/ + Set measurementToDatacollectionList = datacollection.getMeasurementtodatacollection3VOs(); + Set measurementKeys = new HashSet(); + for (MeasurementTodataCollection3VO measurementToDatacollection : measurementToDatacollectionList) { + measurementKeys.add(measurementToDatacollection.getMeasurementId()); + } + LOG.info("measurementKeys:" + measurementKeys.toString()); + for (Integer measurementId : measurementIds) { + LOG.info("measurementKeys:" + measurementId + " " + measurementKeys.toString()); + if(!measurementKeys.contains(measurementId)){ + /** This measurement does not belong to the data collection yet **/ + MeasurementTodataCollection3VO measurementToDataCollection = new MeasurementTodataCollection3VO(); + measurementToDataCollection.setDataCollectionId(datacollection.getDataCollectionId()); + measurementToDataCollection.setMeasurementId(measurementId); + measurementToDataCollection.setDataCollectionOrder(datacollection.getMeasurementtodatacollection3VOs().size() + 1); + LOG.info("setDataCollectionOrder:" + datacollection.getMeasurementtodatacollection3VOs().size() + 1); + this.entityManager.merge(measurementToDataCollection); + } + } + } + } + + if (datacollection != null) { + return datacollection; + } else { + /** We create a new one **/ + SaxsDataCollection3VO dataCollection = new SaxsDataCollection3VO(); + dataCollection.setExperimentId(experiment.getExperimentId()); + dataCollection = entityManager.merge(dataCollection); + int dataCollectionOrder = 1; + for (Integer measurementId : measurementIds) { + MeasurementTodataCollection3VO measurementToDataCollection = new MeasurementTodataCollection3VO(); + measurementToDataCollection.setDataCollectionId(dataCollection.getDataCollectionId()); + measurementToDataCollection.setMeasurementId(measurementId); + measurementToDataCollection.setDataCollectionOrder(dataCollectionOrder); + this.entityManager.merge(measurementToDataCollection); + dataCollectionOrder++; + } + return dataCollection; + } + } + + private Framelist3VO createFrames(List sampleOneDimensionalFilesList){ + if (sampleOneDimensionalFilesList.size() > 0){ + return this.primaryDataProcessing3Service.addFrameList(sampleOneDimensionalFilesList); + } + return null; + } + + private void addSubtraction(Integer dataCollectionId, String rgStdev, String i0, String i0Stdev, String firstPointUsed, + String lastPointUsed, String quality, String isagregated, String rgGuinier, String rgGnom, String dmax, String total, + String volume, String sampleOneDimensionalFiles, String bufferOneDimensionalFiles, String sampleAverageFilePath, + String bestBufferFilePath, String subtractedFilePath, String experimentalDataPlotFilePath, String densityPlotFilePath, + String guinierPlotFilePath, String kratkyPlotFilePath, String gnomOutputFilePath, Subtraction3VO subtraction) { + + HashMap params = new HashMap(); + + long start = this.logInit("addSubtraction_CP_3.1", new Gson().toJson(params)); + + /** Getting the frames **/ + Type typeOfHash = new TypeToken>>() {}.getType(); + List> sampleOneDimensionalFilesListObject = new Gson().fromJson(sampleOneDimensionalFiles, typeOfHash); + List> bufferOneDimensionalFilesListObject = new Gson().fromJson(bufferOneDimensionalFiles, typeOfHash); + + List sampleOneDimensionalFilesList = new ArrayList(); + List bufferOneDimensionalFilesList = new ArrayList(); + + for (HashMap fileObject : sampleOneDimensionalFilesListObject) { + if (fileObject.get("filePath")!= null){ + sampleOneDimensionalFilesList.add(fileObject.get("filePath")); + } + } + + for (HashMap fileObject : bufferOneDimensionalFilesListObject) { + if (fileObject.get("filePath")!= null){ + bufferOneDimensionalFilesList.add(fileObject.get("filePath")); + } + } + + LOG.info(sampleOneDimensionalFilesList); + Framelist3VO sampleFiles = this.createFrames(sampleOneDimensionalFilesList); + this.logFinish("addSubtraction_CP_3.1", start); + + start = this.logInit("addSubtraction_CP_3.2", new Gson().toJson(params)); + Framelist3VO bufferFiles = this.createFrames(bufferOneDimensionalFilesList); + this.logFinish("addSubtraction_CP_3.2", start); + + + start = this.logInit("addSubtraction_CP_3.3", new Gson().toJson(params)); + subtraction.setDataCollectionId(dataCollectionId); + subtraction.setRg(rgGuinier); + subtraction.setRgStdev(rgStdev); + subtraction.setI0(i0); + subtraction.setI0stdev(i0Stdev); + subtraction.setFirstPointUsed(firstPointUsed); + subtraction.setLastPointUsed(lastPointUsed); + subtraction.setQuality(quality); + subtraction.setIsagregated(isagregated); + subtraction.setGnomFilePath(densityPlotFilePath); + subtraction.setRgGuinier(rgGuinier); + subtraction.setRgGnom(rgGnom); + subtraction.setDmax(dmax); + subtraction.setTotal(total); + subtraction.setVolume(volume); + subtraction.setKratkyFilePath(kratkyPlotFilePath); + subtraction.setScatteringFilePath(experimentalDataPlotFilePath); + subtraction.setGuinierFilePath(guinierPlotFilePath); + subtraction.setSubstractedFilePath(subtractedFilePath); + subtraction.setGnomFilePathOutput(gnomOutputFilePath); + subtraction.setCreationTime(getNow()); + + if (sampleFiles != null){ + subtraction.setSampleOneDimensionalFiles(sampleFiles); + } + if (bufferFiles != null){ + subtraction.setBufferOneDimensionalFiles(bufferFiles); + } + subtraction.setSampleAverageFilePath(sampleAverageFilePath); + subtraction.setBufferAverageFilePath(bestBufferFilePath); + + this.entityManager.merge(subtraction); + this.logFinish("addSubtraction_CP_3.3", start); + + } + + private void addSubtraction(int dataCollectionId, String rgStdev, String i0, String i0Stdev, String firstPointUsed, + String lastPointUsed, String quality, String isagregated, String rgGuinier, String rgGnom, String dmax, String total, + String volume, String sampleOneDimensionalFiles, String bufferOneDimensionalFiles, String sampleAverageFilePath, + String bestBufferFilePath, String subtractedFilePath, String experimentalDataPlotFilePath, String densityPlotFilePath, + String guinierPlotFilePath, String kratkyPlotFilePath, String gnomOutputFilePath) { + + Subtraction3VO subtraction = new Subtraction3VO(); + this.addSubtraction(dataCollectionId, rgStdev, i0, i0Stdev, firstPointUsed, lastPointUsed, quality, isagregated, rgGuinier, rgGnom, dmax, total, volume, sampleOneDimensionalFiles, bufferOneDimensionalFiles, sampleAverageFilePath, bestBufferFilePath, subtractedFilePath, experimentalDataPlotFilePath, densityPlotFilePath, guinierPlotFilePath, kratkyPlotFilePath, gnomOutputFilePath, subtraction); + + } + + + @Override + public void addSubtraction(String sampleMeasurementId, String rgStdev, String i0, String i0Stdev, String firstPointUsed, + String lastPointUsed, String quality, String isagregated, String rgGuinier, String rgGnom, String dmax, String total, + String volume, String sampleOneDimensionalFiles, String bufferOneDimensionalFiles, String sampleAverageFilePath, + String bestBufferFilePath, String subtractedFilePath, String experimentalDataPlotFilePath, String densityPlotFilePath, + String guinierPlotFilePath, String kratkyPlotFilePath, String gnomOutputFilePath) { + + + + HashMap params = new HashMap(); + long start = this.logInit("addSubtraction_CP_1", new Gson().toJson(params)); + if (sampleMeasurementId != null){ + // TODO: To be changed, performace problems!!! + Experiment3VO experiment = experiment3Service.findByMeasurementId(Integer.parseInt(sampleMeasurementId)); + this.logFinish("addSubtraction_CP_1", start); + + + if (experiment != null) { + start = this.logInit("addSubtraction_CP_2", new Gson().toJson(params)); + int dataCollectionId = experiment.getDataCollectionByMeasurementId(Integer.parseInt(sampleMeasurementId)).getDataCollectionId(); + this.logFinish("addSubtraction_CP_2", start); + + + start = this.logInit("addSubtraction_CP_3", new Gson().toJson(params)); + this.addSubtraction(dataCollectionId, rgStdev, i0, i0Stdev, firstPointUsed, lastPointUsed, quality, isagregated, rgGuinier, rgGnom, dmax, total, volume, sampleOneDimensionalFiles, bufferOneDimensionalFiles, sampleAverageFilePath, bestBufferFilePath, subtractedFilePath, experimentalDataPlotFilePath, densityPlotFilePath, guinierPlotFilePath, kratkyPlotFilePath, gnomOutputFilePath); + this.logFinish("addSubtraction_CP_3", start); + + } + } + } + + + /** + * Adds a new subtraction to several measurements To note: there are not any data collection yet + */ + public void addSubtraction( + String experimentId, + String runNumberList, + String rgStdev, + String i0, + String i0Stdev, + String firstPointUsed, + String lastPointUsed, + String quality, + String isagregated, + String rgGuinier, + String rgGnom, + String dmax, + String total, + String volume, + String sampleOneDimensionalFiles, + String bufferOneDimensionalFiles, + String sampleAverageFilePath, + String bestBufferFilePath, + String subtractedFilePath, + String experimentalDataPlotFilePath, + String densityPlotFilePath, + String guinierPlotFilePath, + String kratkyPlotFilePath, + String gnomOutputFilePath) throws Exception { + + Type typeOfList = new TypeToken>() {}.getType(); + List ids = new Gson().fromJson(runNumberList, typeOfList); + + /** Finding measurement by experimentId and run number **/ + List measurementIds = new ArrayList(); + for (String runNumber : ids) { + Measurement3VO measurement = measurement3Service.findMeasurementByCode(Integer.parseInt(experimentId), runNumber.toString()); + if (measurement != null) { + measurementIds.add(measurement.getMeasurementId()); + } else { + throw new Exception("No measurement found for runNumber: " + runNumber + " and experimentId: " + experimentId); + } + } + + System.out.println("Adding data collection and subtraction"); + + /** Adding data collection and subtraction **/ + if (measurementIds != null) { + if (measurementIds.size() > 0) { + Experiment3VO experiment = experiment3Service.findById(Integer.parseInt(experimentId), ExperimentScope.MEDIUM); + if (experiment != null) { + /** This method creates the data collection if it does not exist yet **/ + SaxsDataCollection3VO dataCollection = getSaxsDataCollectionByMeasurementIds(experiment, measurementIds); + + if (dataCollection.getSubstraction3VOs().size() == 0){ + this.addSubtraction(dataCollection.getDataCollectionId(), rgStdev, i0, i0Stdev, firstPointUsed, lastPointUsed, quality, isagregated, rgGuinier, rgGnom, dmax, total, volume, sampleOneDimensionalFiles, bufferOneDimensionalFiles, sampleAverageFilePath, bestBufferFilePath, subtractedFilePath, experimentalDataPlotFilePath, densityPlotFilePath, guinierPlotFilePath, kratkyPlotFilePath, gnomOutputFilePath); + } + else{ + /** + * Upgrading subtraction + */ + Subtraction3VO subtraction = (Subtraction3VO) Arrays.asList(dataCollection.getSubstraction3VOs().toArray()).get(0); + this.addSubtraction(dataCollection.getDataCollectionId(), rgStdev, i0, i0Stdev, firstPointUsed, lastPointUsed, quality, isagregated, rgGuinier, rgGnom, dmax, total, volume, sampleOneDimensionalFiles, bufferOneDimensionalFiles, sampleAverageFilePath, bestBufferFilePath, subtractedFilePath, experimentalDataPlotFilePath, densityPlotFilePath, guinierPlotFilePath, kratkyPlotFilePath, gnomOutputFilePath, subtraction); + } + } + } + } + } + + + + @Override + public void addAbinitioModelling(String experimentId, String runNumber, + ArrayList models3vo, Model3VO referenceModel, + Model3VO refinedModel) throws Exception { + + Measurement3VO measurement = measurement3Service.findMeasurementByCode(Integer.parseInt(experimentId), (runNumber)); + if (measurement != null){ + ArrayList measurementIds = new ArrayList(); + measurementIds.add(measurement.getMeasurementId()); + AbInitioModelling3Service.addAbinitioModeling(measurementIds, + models3vo, + referenceModel, + refinedModel); + + } + else{ + throw new Exception("No measurement found for " + experimentId + " and " + runNumber); + } + } + + @Override + public Model3VO getModel(String experimentId, String runNumber, String pdbfilepath) throws Exception { + Measurement3VO measurement = measurement3Service.findMeasurementByCode(Integer.parseInt(experimentId), (runNumber)); + if (measurement != null){ + return AbInitioModelling3Service.getModel(measurement.getMeasurementId(), pdbfilepath); + } + else{ + throw new Exception("Not measurement found for " + experimentId + " and " + runNumber); + } + } + + @Override + public Model3VO merge(Model3VO model) { + return this.entityManager.merge(model); + } + + @Override + public List getMacromoleculeByAcronym(String proposal, String acronym) { + List proposal3VOs = this.proposalService.findProposalByLoginName(proposal); + if (proposal3VOs != null){ + if (proposal3VOs.size() > 0){ + Proposal3VO proposal3VO = proposal3VOs.get(0); + List macromolecules = this.saxsProposal3Service.findMacromoleculesBy(acronym, proposal3VO.getProposalId()); + return macromolecules; + } + } + return new ArrayList(); + } + + public List getSubtractionByCode(String experimentId, String runNumber) { + Measurement3VO measurement = measurement3Service.findMeasurementByCode(Integer.valueOf(experimentId), runNumber); + System.out.println("measurement " + measurement); + if (measurement != null){ + return primaryDataProcessing3Service.getSubstractionsByMeasurementId(measurement.getMeasurementId()); + } + System.out.println("measurement not found "); + return null; + } + + public Macromolecule3VO getMacromoleculeByCode(String experimentId, String runNumber) { + Measurement3VO measurement = measurement3Service.findMeasurementByCode(Integer.valueOf(experimentId), runNumber); + if (measurement != null){ + Specimen3VO specimen = entityManager.find(Specimen3VO.class, measurement.getSpecimenId()); + if (specimen != null){ + return saxsProposal3Service.findMacromoleculesById(specimen.getMacromolecule3VO().getMacromoleculeId()); +// return experiment3Service.findMacromoleculeById(specimen.getMacromolecule3VO().getMacromoleculeId()); + } + } + return null; + } + + /** + * + * @param experimentId + * @param runNumber + * @param filePath {filePath:"/filepath.dat"} + * @return + */ + public Structure3VO getStructuresByCode(String experimentId, String runNumber, String filePath) { + Macromolecule3VO macromolecule = this.getMacromoleculeByCode(experimentId, runNumber); + for (Structure3VO structure : macromolecule.getStructure3VOs()) { + if (structure.getFilePath().trim().equals(filePath.trim())){ + return structure; + } + } + + return null; + } + + @Override + public void addMixture(String experimentId, String runNumber, String fitFilePath, String pdb) throws Exception { + List subtractions = this.getSubtractionByCode(experimentId, runNumber); + System.out.println("+++addMixture " + experimentId + " " + runNumber); + if (subtractions != null){ + if (subtractions.size() > 0){ + /** Getting last one are they should come by order **/ + Subtraction3VO subtraction = subtractions.get(subtractions.size() - 1); + + System.out.println("+++Subtraction Found"); + /** Getting Structure **/ + + Type ListType = new TypeToken>>() {}.getType(); + + /** Reading pdbs **/ + List> pdbFilePath = new Gson().fromJson(pdb, ListType); + + List mixtures = new ArrayList(); + if (pdbFilePath.size() > 0){ + for (HashMap pdbHash : pdbFilePath) { + MixtureToStructure mixture = new MixtureToStructure(); + + if (pdbHash.containsKey("filePath")){ + /** Finding the structue linked to such filePath */ + Structure3VO structure = this.getStructuresByCode(experimentId, runNumber, pdbHash.get("filePath")); + /** If structure does not exist we create a new one **/ + if (structure == null){ + structure = new Structure3VO(); + Macromolecule3VO macromolecule = this.getMacromoleculeByCode(experimentId, runNumber); + System.out.println("Macromolecule found"); + if (macromolecule != null){ + structure.setMacromoleculeId(macromolecule.getMacromoleculeId()); + structure.setFilePath(pdbHash.get("filePath")); + if (pdbHash.get("filePath") != null){ + String name = new File(pdbHash.get("filePath")).getName(); + structure.setName(name); + } + structure.setType("PDB"); + structure.setCreationDate(getNow()); + structure = this.entityManager.merge(structure); + System.out.println("Structure created"); + } + else{ + throw new Exception("Macromolecule not found for experimentId " + experimentId + " and runNumber " + runNumber ); + } + } + + /** Structure exists here **/ + mixture.setStructureId(structure.getStructureId()); + mixture.setCreationDate(getNow()); + + } + + if (pdbHash.containsKey("volumeFraction")){ + System.out.println("+++" +"Volume: " + pdbHash.get("volumeFraction")); + mixture.setVolumeFraction(pdbHash.get("volumeFraction")); + } + + mixtures.add(mixture); + } + + System.out.println( "+++Mixtures --- " + mixtures.size()); + System.out.println("+++" +fitFilePath + " --- " + fitFilePath); + + + /** Creating FitStructureToExperimentalData **/ + FitStructureToExperimentalData3VO fit = new FitStructureToExperimentalData3VO(); + fit.setSubtractionId(subtraction.getSubtractionId()); + fit.setCreationDate(getNow()); + fit.setFitFilePath(fitFilePath); + fit = entityManager.merge(fit); + System.out.println("+++Merging fit " + fit.getFitFilePath() + " " + fit.getFitStructureToExperimentalDataId()); + + /** Creating MixtureToStructures **/ + for (MixtureToStructure mixtureToStructure : mixtures) { + mixtureToStructure.setMixtureId(fit.getFitStructureToExperimentalDataId()); + mixtureToStructure = this.entityManager.merge(mixtureToStructure); + System.out.println("+++Merging mixturetoStructure " + mixtureToStructure.getVolumeFraction() + " " + mixtureToStructure.getStructureId() + " " + mixtureToStructure.getFitToStructureId()); + } + } + + /** Reading fitFilePath **/ +// Type HashType = new TypeToken>() {}.getType(); +// HashMap fitFilePathHash = new Gson().fromJson(fitFilePath, HashType); +// if (fitFilePathHash.containsKey("filePath")){ +// fitFilePath = fitFilePathHash.get("filePath"); +// } + + + } + else{ + throw new Exception("0 subtractions has been found for experimentId= " + experimentId + " and runNumber= " + runNumber); + } + + } + else{ + throw new Exception("No subtraction has been found for experimentId= " + experimentId + " and runNumber= " + runNumber); + } +// if (fitStructure == null){ +// fitStructure = new FitStructureToExperimentalData(); +// } +// +// fitStructure.setFitFilePath(fitFilePath); +//// fitStructure.setLogFilePath(logFile); +// fitStructure.setOutputFilePath(summaryFile); +//// fitStructure.setStructureId(Integer.parseInt(structureId)); +// fitStructure.setSubtractionId(Integer.parseInt(subtractionId)); +// fitStructure.setWorkflowId(Integer.parseInt(workflowId)); +// fitStructure.setComments("Finished at " + getNow()); +// fitStructure = advancedAnalysis3Service.merge(fitStructure); + + } + + @Override + public void addRigidBodyModeling(String experimentId, String runNumber, String fitFilePath, String rigidBodyModelFilePath, + String logFilePath, String curveConfigFilePath, String subunitConfigFilePath, String crosscorrConfigFilePath, + String contactConditionsFilePath, String masterSymmetry) throws Exception { + + List subtractions = this.getSubtractionByCode(experimentId, runNumber); + if (subtractions != null){ + if (subtractions.size() > 0){ + RigidBodyModeling3VO rigid = new RigidBodyModeling3VO(); + rigid.setSubtractionId(subtractions.get(subtractions.size() - 1).getSubtractionId()); + rigid.setFitFilePath(fitFilePath); + rigid.setRigidBodyModelFilePath(rigidBodyModelFilePath); + rigid.setLogFilePath(logFilePath); + rigid.setCurveConfigFilePath(curveConfigFilePath); + rigid.setSubUnitConfigFilePath(subunitConfigFilePath); + rigid.setCrossCorrConfigFilePath(crosscorrConfigFilePath); + rigid.setContactDescriptionFilePath(contactConditionsFilePath); + rigid.setSymmetry(masterSymmetry); + rigid.setCreationDate(getNow()); + this.entityManager.merge(rigid); + } + else{ + throw new Exception("No subtraction found for " + experimentId + " " + runNumber); + } + } + else{ + throw new Exception("No subtraction found for " + experimentId + " " + runNumber + "(null)"); + } + } + + @Override + public void addSuperposition(String experimentId, String runNumber, String abinitioModelPdbFilePath, String aprioriPdbFilePath, + String alignedPdbFilePath) throws Exception { + + List subtractions = this.getSubtractionByCode(experimentId, runNumber); + if (subtractions != null){ + Superposition3VO superposition = new Superposition3VO(); + superposition.setSubtractionId(subtractions.get(subtractions.size() - 1).getSubtractionId()); + superposition.setAbinitioModelPdbFilePath(abinitioModelPdbFilePath); + superposition.setAprioriPdbFilePath(aprioriPdbFilePath); + superposition.setAlignedPdbFilePath(alignedPdbFilePath); + superposition.setCreationDate(getNow()); + this.entityManager.merge(superposition); + } + else{ + throw new Exception("No subtraction found for " + experimentId + " " + runNumber + "(null)"); + } + + + } + + @Override + public HashMap getAprioriInformationByRunNumber(String proposal, String acronym) throws Exception { + HashMap info = new HashMap(); + List macromolecules = this.getMacromoleculeByAcronym(proposal, acronym); + if (macromolecules.size() == 1){ + Macromolecule3VO macromolecule = macromolecules.get(0); + if (macromolecule != null){ + info.put("ACRONYM", macromolecule.getAcronym()); + List> pdb = new ArrayList>(); + List> fasta = new ArrayList>(); + List> rbms = new ArrayList>(); + if (macromolecule.getStructure3VOs() != null){ + for (Structure3VO structure : macromolecule.getStructure3VOs()) { + if (structure.getType().equals("PDB")){ + HashMap pdbEntry = new HashMap(); + pdbEntry.put("FILEPATH", structure.getFilePath()); + pdbEntry.put("SYMMETRY", structure.getSymmetry()); + pdbEntry.put("MULTIPLICITY", structure.getMultiplicity()); + pdb.add(pdbEntry); + } + if (structure.getType().equals("SEQUENCE")){ + HashMap fastaEntry = new HashMap(); + fastaEntry.put("FILEPATH", structure.getFilePath()); + fasta.add(fastaEntry); + } + } + } + + HashMap rbm = new HashMap(); + rbm.put("FILEPATH", macromolecule.getContactsDescriptionFilePath()); + rbm.put("SYMMETRY", macromolecule.getSymmetry()); + rbms.add(rbm); + + info.put("PDB", pdb); + info.put("SEQUENCE", fasta); + info.put("RIGIDBODY", rbms); + return info; + } + else{ + throw new Exception("macromolecule not found"); + } + } + else{ + throw new Exception(macromolecules.size() + " macromolecules found"); + } + } + + + private void logFinish(String methodName, long id) { + LOG.debug("### [" + methodName.toUpperCase() + "] Execution time was " + (System.currentTimeMillis() - this.now) + " ms."); + LoggerFormatter.log(LOG, LoggerFormatter.Package.BIOSAXS_WS, methodName, id, System.currentTimeMillis(), + System.currentTimeMillis() - this.now); + + } + + protected long logInit(String methodName, String params) { + LOG.info("-----------------------"); + this.now = System.currentTimeMillis(); + LOG.info(methodName.toUpperCase()); + LoggerFormatter.log(LOG, LoggerFormatter.Package.BIOSAXS_WS, methodName, System.currentTimeMillis(), + System.currentTimeMillis(), params); + return this.now; + } + + @Override + public List getMacromoleculesByProposal(String code, String number) throws Exception { + ProposalWS3VO proposal = proposalService.findForWSByCodeAndNumber(code, number); + return this.saxsProposal3Service.findMacromoleculesByProposalId(proposal.getProposalId()); + } + + @Override + public List getBuffersByProposal(String code, String number) throws Exception { + ProposalWS3VO proposal = proposalService.findForWSByCodeAndNumber(code, number); + return this.saxsProposal3Service.findBuffersByProposalId(proposal.getProposalId()); + } +} diff --git a/ispyb-ejb/src/main/java/ispyb/server/biosaxs/vos/dataAcquisition/Experiment3VO.java b/ispyb-ejb/src/main/java/ispyb/server/biosaxs/vos/dataAcquisition/Experiment3VO.java index c68884135..4610826b9 100644 --- a/ispyb-ejb/src/main/java/ispyb/server/biosaxs/vos/dataAcquisition/Experiment3VO.java +++ b/ispyb-ejb/src/main/java/ispyb/server/biosaxs/vos/dataAcquisition/Experiment3VO.java @@ -1,505 +1,501 @@ -/******************************************************************************* - * This file is part of ISPyB. - * - * ISPyB is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * ISPyB is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with ISPyB. If not, see . - * - * Contributors : S. Delageniere, R. Leal, L. Launer, K. Levik, S. Veyrier, P. Brenchereau, M. Bodin, A. De Maria Antolinos - ******************************************************************************************************************************/ - -package ispyb.server.biosaxs.vos.dataAcquisition; - -import static javax.persistence.GenerationType.IDENTITY; -import ispyb.server.biosaxs.services.core.plateType.PlateType3Service; -import ispyb.server.biosaxs.vos.assembly.Macromolecule3VO; -import ispyb.server.biosaxs.vos.dataAcquisition.plate.Platetype3VO; -import ispyb.server.biosaxs.vos.dataAcquisition.plate.Sampleplate3VO; -import ispyb.server.biosaxs.vos.dataAcquisition.plate.Sampleplateposition3VO; -import ispyb.server.biosaxs.vos.datacollection.MeasurementTodataCollection3VO; -import ispyb.server.biosaxs.vos.datacollection.SaxsDataCollection3VO; -import ispyb.server.common.util.ejb.Ejb3ServiceLocator; - -import java.util.ArrayList; -import java.util.Date; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Set; - -import javax.naming.NamingException; -import javax.persistence.CascadeType; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.FetchType; -import javax.persistence.GeneratedValue; -import javax.persistence.Id; -import javax.persistence.JoinColumn; -import javax.persistence.OneToMany; -import javax.persistence.Table; -import javax.persistence.Temporal; -import javax.persistence.TemporalType; -import javax.persistence.Transient; - -/** - * Experiment3VO generated by hbm2java - */ -@Entity -@Table(name = "Experiment") -public class Experiment3VO implements java.io.Serializable { - - private static final long serialVersionUID = 1L; - - protected Integer experimentId; - - protected Integer sessionId; - - protected String name; - - protected Date creationDate; - - protected String sourceFilePath; - - protected String type; - - protected String comments; - - protected String dataAcquisitionFilePath; - - protected String status; - - protected int proposalId; - - protected Set samplePlate3VOs = new HashSet(0); - - protected List platetype3VOs = new ArrayList(); - - protected Set samples3VOs = new HashSet(0); - - protected Set dataCollections = new HashSet(0); - - /** Services for filling up plate types **/ - private static final Ejb3ServiceLocator ejb3ServiceLocator = Ejb3ServiceLocator.getInstance(); - - private PlateType3Service plateType3Service; - - public Experiment3VO() { - try { - this.plateType3Service = (PlateType3Service) ejb3ServiceLocator.getLocalService(PlateType3Service.class); - this.platetype3VOs = this.plateType3Service.findAll(); - } catch (NamingException e) { - // //e.printStackTrace(); - } - } - - @Id - @GeneratedValue(strategy = IDENTITY) - @Column(name = "experimentId", unique = true, nullable = false) - public Integer getExperimentId() { - return this.experimentId; - } - - public void setExperimentId(Integer experimentId) { - this.experimentId = experimentId; - } - - @Column(name = "name") - public String getName() { - return this.name; - } - - public void setName(String name) { - this.name = name; - } - - @Column(name = "sessionId") - public Integer getSessionId() { - return this.sessionId; - } - - public void setSessionId(Integer sessionId) { - this.sessionId = sessionId; - } - - @Temporal(TemporalType.TIMESTAMP) - @Column(name = "creationDate", length = 0) - public Date getCreationDate() { - return this.creationDate; - } - - public void setCreationDate(Date creationDate) { - this.creationDate = creationDate; - } - - @Column(name = "comments", length = 512) - public String getComments() { - return this.comments; - } - - public void setComments(String comments) { - this.comments = comments; - } - - @Column(name = "proposalId") - public int getProposalId() { - return this.proposalId; - } - - public void setProposalId(int proposalId) { - this.proposalId = proposalId; - } - - /** - * TRANSIENT METHODS *. - * - * @param macromolecules - * List of all the macromolecules of the proposal which this experiment belongs to - * @return the macromolecules - */ - @Transient - public Set getMacromolecules() { - Set macromolecules = new HashSet(); - for (Specimen3VO sample : this.getSamples()) { - if (sample.getMacromolecule3VO() != null) { - macromolecules.add(sample.getMacromolecule3VO()); - } - } - return macromolecules; - } - - @Transient - public List getMeasurements() { - List specimen3VOs = new ArrayList(); - for (Specimen3VO sample : this.getSamples()) { - specimen3VOs.addAll(sample.getMeasurements()); - } - - return specimen3VOs; - } - - @Transient - public Specimen3VO getSampleWithThisBufferId(int bufferId) { - for (Specimen3VO sample : this.getSamples()) { - if (sample.getBufferId().equals(bufferId) && (sample.getMacromolecule3VO() == null)) { - return sample; - } - } - return null; - } - - @OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.REMOVE) - @JoinColumn(name = "experimentId") - public Set getSamplePlate3VOs() { - return samplePlate3VOs; - } - - public void setSamplePlate3VOs(Set samplePlate3VOs) { - this.samplePlate3VOs = samplePlate3VOs; - } - - @Transient - public List getPlatetype3VOs() { - if (this.platetype3VOs == null) { - try { - this.plateType3Service = (PlateType3Service) ejb3ServiceLocator - .getLocalService(PlateType3Service.class); - this.platetype3VOs = this.plateType3Service.findAll(); - } catch (NamingException e) { - e.printStackTrace(); - } - } - return this.platetype3VOs; - } - - @Transient - public Measurement3VO getMeasurementById(Integer measurementId) { - List measurements = this.getMeasurements(); - for (Measurement3VO measurement : measurements) { - // System.out.println("Experiment3VO.getSpecimenById: " + measurement); - if (measurement.getMeasurementId() != null) { - if (measurement.getMeasurementId().equals(measurementId)) { - return measurement; - } - } - } - return null; - } - - @Transient - public Sampleplate3VO getSamplePlateById(Integer samplePlateId) { - Set samplePlates = this.getSamplePlate3VOs(); - for (Sampleplate3VO samplePlate : samplePlates) { - if (samplePlate.getSamplePlateId().equals(samplePlateId)) { - return samplePlate; - } - } - return null; - } - - @Transient - public Sampleplateposition3VO getPosition(Integer samplePlateId, int row, int well) { - Sampleplate3VO samplePlate = this.getSamplePlateById(samplePlateId); - if (samplePlate != null) { - for (Sampleplateposition3VO position : samplePlate.getSampleplateposition3VOs()) { - if ((position.getRowNumber() == row) && (well == position.getColumnNumber())) { - return position; - } - } - } - - /** If not found **/ - return null; - - } - - - @Transient - public String getCodeSample(Specimen3VO sample) { - int count = 1; - String name = new String(); - do { - if (sample.getMacromolecule3VO() != null) { - name = sample.getMacromolecule3VO().getAcronym() + "_" + "Buffer_Not-Know";// this.getBufferById(sample.getBufferId()).getAcronym(); - } else { - name = "Buffer_Not-Know"; // this.getBufferById(sample.getBufferId()).getAcronym(); - } - if (count > 0) { - name = name + "-" + count; - } - count++; - } while (this.isSpecimenCodeUsed(name, sample)); - return name; - - } - - @Transient - public boolean isSpecimenCodeUsed(String code, Specimen3VO specimen) { - Set samples = this.getSamples(); - for (Specimen3VO sample3vo : samples) { - if (sample3vo.getCode().equals(code)) { - if (specimen.getSpecimenId() == null) { - return true; - } else { - if (specimen.getSpecimenId().equals(sample3vo.getSpecimenId())) { - return false; - } else { - return true; - } - } - } - } - return false; - } - - @OneToMany(fetch = FetchType.EAGER) - @JoinColumn(name = "experimentId") - public Set getDataCollections() { - return dataCollections; - } - - public void setDataCollections(Set dataCollections) { - this.dataCollections = dataCollections; - } - - @Transient - public List getDataCollectionList() { - List list = new ArrayList(); - Iterator iterator = this.getDataCollections().iterator(); - while (iterator.hasNext()) { - list.add(iterator.next()); - } - return list; - } - - @OneToMany(fetch = FetchType.LAZY) - @JoinColumn(name = "experimentId") - public Set getSamples() { - return samples3VOs; - } - - public void setSamples(Set samples) { - this.samples3VOs = samples; - } - - @Transient - public Specimen3VO getSampleById(int sampleId) { - for (Specimen3VO sample : this.getSamples()) { - if (sample.getSpecimenId().equals(sampleId)) { - return sample; - } - } - return null; - - } - - public String getCodeSpecimen(Measurement3VO specimen) { - Specimen3VO sample = this.getSampleById(specimen.getSpecimenId()); - return this.getCodeSpecimen(sample, specimen); - } - - public String getCodeSpecimen(Specimen3VO sample3vo, Measurement3VO specimen) { - String code = sample3vo.getCode();// this.getCodeSample(sample3vo); - if (sample3vo.getMacromolecule3VO() == null) { - return code + "_" + specimen.getExposureTemperature(); - } else { - return code + "_" + specimen.getExposureTemperature() + "_" + sample3vo.getConcentration(); - } - } - - @Transient - public Specimen3VO getSampleByPosition(Sampleplateposition3VO position) { - Set specimens = this.getSamples(); - for (Specimen3VO sample : specimens) { - if (sample.getSampleplateposition3VO() != null) { - Sampleplateposition3VO specimenPosition = sample.getSampleplateposition3VO(); - if (specimenPosition.getSamplePlateId().equals(position.getSamplePlateId())) { - if (specimenPosition.getRowNumber() == position.getRowNumber() - && specimenPosition.getColumnNumber() == position.getColumnNumber()) { - return sample; - } - } - } - } - return null; - } - - @Transient - public SaxsDataCollection3VO getDataCollectionByMeasurementId(int measurementId) { - Measurement3VO measurement = this.getMeasurementById(measurementId); - Set dataCollections = this.getDataCollections(); - for (SaxsDataCollection3VO saxsDataCollection3VO : dataCollections) { - for (MeasurementTodataCollection3VO measurementToDataCollection : saxsDataCollection3VO - .getMeasurementtodatacollection3VOs()) { - if (measurement.getMeasurementId().equals(measurementToDataCollection.getMeasurementId())) { - return saxsDataCollection3VO; - } - } - } - return null; - } - - @Transient - public List getDataCollectionListByMeasurementId(int measurementId) { - List list = new ArrayList(); - Measurement3VO measurement = this.getMeasurementById(measurementId); - Set dataCollections = this.getDataCollections(); - for (SaxsDataCollection3VO saxsDataCollection3VO : dataCollections) { - for (MeasurementTodataCollection3VO measurementToDataCollection : saxsDataCollection3VO - .getMeasurementtodatacollection3VOs()) { - if (measurement.getMeasurementId().equals(measurementToDataCollection.getMeasurementId())) { - list.add(saxsDataCollection3VO); - } - } - } - return list; - } - - @Transient - public List getMeasurementsOfDataCollectionByMeasurementId(int measurementId) { - SaxsDataCollection3VO dc = getDataCollectionByMeasurementId(measurementId); - List list = new ArrayList(); - for (MeasurementTodataCollection3VO iterable_element : dc.getMeasurementtodatacollection3VOs()) { - list.add(iterable_element); - } - return list; - } - - @Transient - public SaxsDataCollection3VO getDataCollectionById(int dataCollectionId) { - Set dataCollections = this.getDataCollections(); - for (SaxsDataCollection3VO saxsDataCollection3VO : dataCollections) { - if (saxsDataCollection3VO.equals(dataCollectionId)) { - return saxsDataCollection3VO; - } - } - return null; - } - - @Transient - public Measurement3VO getMeasurementBefore(int measurementId) { - Integer measurementBeforeId = this.getMeasurementIdBefore(measurementId); - if (measurementBeforeId != null) - return this.getMeasurementById(measurementBeforeId); - return null; - } - - @Transient - public Integer getMeasurementIdBefore(int measurementId) { - SaxsDataCollection3VO dataCollection = this.getDataCollectionByMeasurementId(measurementId); - if (dataCollection != null) { - for (MeasurementTodataCollection3VO measurement : dataCollection.getMeasurementtodatacollection3VOs()) { - if (measurement.getDataCollectionOrder() == 1) { - return measurement.getMeasurementId(); - } - } - } - return null; - } - - @Transient - public Measurement3VO getMeasurementAfter(int measurementId) { - Integer measurementAfterId = this.getMeasurementIdAfter(measurementId); - if (measurementAfterId != null) - return this.getMeasurementById(measurementAfterId); - return null; - } - - @Transient - public Integer getMeasurementIdAfter(int measurementId) { - SaxsDataCollection3VO dataCollection = this.getDataCollectionByMeasurementId(measurementId); - if (dataCollection != null) { - for (MeasurementTodataCollection3VO measurement : dataCollection.getMeasurementtodatacollection3VOs()) { - if (measurement.getDataCollectionOrder() == 3) { - return measurement.getMeasurementId(); - } - } - } - return null; - } - - @Column(name = "sourceFilePath") - public String getSourceFilePath() { - return sourceFilePath; - } - - public void setSourceFilePath(String sourceFilePath) { - this.sourceFilePath = sourceFilePath; - } - - @Column(name = "experimentType") - public String getType() { - return type; - } - - public void setType(String type) { - this.type = type; - } - - @Column(name = "dataAcquisitionFilePath") - public String getDataAcquisitionFilePath() { - return dataAcquisitionFilePath; - } - - public void setDataAcquisitionFilePath(String dataAcquisitionFilePath) { - this.dataAcquisitionFilePath = dataAcquisitionFilePath; - } - - @Column(name = "status") - public String getStatus() { - return status; - } - - public void setStatus(String status) { - this.status = status; - } - -} +/******************************************************************************* + * This file is part of ISPyB. + * + * ISPyB is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * ISPyB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with ISPyB. If not, see . + * + * Contributors : S. Delageniere, R. Leal, L. Launer, K. Levik, S. Veyrier, P. Brenchereau, M. Bodin, A. De Maria Antolinos + ******************************************************************************************************************************/ + +package ispyb.server.biosaxs.vos.dataAcquisition; + +import static javax.persistence.GenerationType.IDENTITY; +//import ispyb.server.biosaxs.services.core.plateType.PlateType3Service; +import ispyb.server.biosaxs.vos.assembly.Macromolecule3VO; +import ispyb.server.biosaxs.vos.dataAcquisition.plate.Platetype3VO; +import ispyb.server.biosaxs.vos.dataAcquisition.plate.Sampleplate3VO; +import ispyb.server.biosaxs.vos.dataAcquisition.plate.Sampleplateposition3VO; +import ispyb.server.biosaxs.vos.datacollection.MeasurementTodataCollection3VO; +import ispyb.server.biosaxs.vos.datacollection.SaxsDataCollection3VO; +import ispyb.server.common.util.ejb.Ejb3ServiceLocator; + +import java.util.ArrayList; +import java.util.Date; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Set; + +import javax.naming.NamingException; +import javax.persistence.CascadeType; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.OneToMany; +import javax.persistence.Table; +import javax.persistence.Temporal; +import javax.persistence.TemporalType; +import javax.persistence.Transient; + +/** + * Experiment3VO generated by hbm2java + */ +@Entity +@Table(name = "Experiment") +public class Experiment3VO implements java.io.Serializable { + + private static final long serialVersionUID = 1L; + + protected Integer experimentId; + + protected Integer sessionId; + + protected String name; + + protected Date creationDate; + + protected String sourceFilePath; + + protected String type; + + protected String comments; + + protected String dataAcquisitionFilePath; + + protected String status; + + protected int proposalId; + + protected Set samplePlate3VOs = new HashSet(0); + + protected List platetype3VOs = new ArrayList(); + + protected Set samples3VOs = new HashSet(0); + + protected Set dataCollections = new HashSet(0); + + /** Services for filling up plate types **/ + private static final Ejb3ServiceLocator ejb3ServiceLocator = Ejb3ServiceLocator.getInstance(); + + //private PlateType3Service plateType3Service; + + public Experiment3VO() { + // TODO remove this : not correct design no service should be called from a VO to be replaced by the init method of ExperimentService +// try { +// this.plateType3Service = (PlateType3Service) ejb3ServiceLocator.getLocalService(PlateType3Service.class); +// this.platetype3VOs = this.plateType3Service.findAll(); +// } catch (NamingException e) { +// e.printStackTrace(); +// } + } + + @Id + @GeneratedValue(strategy = IDENTITY) + @Column(name = "experimentId", unique = true, nullable = false) + public Integer getExperimentId() { + return this.experimentId; + } + + public void setExperimentId(Integer experimentId) { + this.experimentId = experimentId; + } + + @Column(name = "name") + public String getName() { + return this.name; + } + + public void setName(String name) { + this.name = name; + } + + @Column(name = "sessionId") + public Integer getSessionId() { + return this.sessionId; + } + + public void setSessionId(Integer sessionId) { + this.sessionId = sessionId; + } + + @Temporal(TemporalType.TIMESTAMP) + @Column(name = "creationDate", length = 0) + public Date getCreationDate() { + return this.creationDate; + } + + public void setCreationDate(Date creationDate) { + this.creationDate = creationDate; + } + + @Column(name = "comments", length = 512) + public String getComments() { + return this.comments; + } + + public void setComments(String comments) { + this.comments = comments; + } + + @Column(name = "proposalId") + public int getProposalId() { + return this.proposalId; + } + + public void setProposalId(int proposalId) { + this.proposalId = proposalId; + } + + /** + * TRANSIENT METHODS *. + * + * @param macromolecules + * List of all the macromolecules of the proposal which this experiment belongs to + * @return the macromolecules + */ + @Transient + public Set getMacromolecules() { + Set macromolecules = new HashSet(); + for (Specimen3VO sample : this.getSamples()) { + if (sample.getMacromolecule3VO() != null) { + macromolecules.add(sample.getMacromolecule3VO()); + } + } + return macromolecules; + } + + @Transient + public List getMeasurements() { + List specimen3VOs = new ArrayList(); + for (Specimen3VO sample : this.getSamples()) { + specimen3VOs.addAll(sample.getMeasurements()); + } + + return specimen3VOs; + } + + @Transient + public Specimen3VO getSampleWithThisBufferId(int bufferId) { + for (Specimen3VO sample : this.getSamples()) { + if (sample.getBufferId().equals(bufferId) && (sample.getMacromolecule3VO() == null)) { + return sample; + } + } + return null; + } + + @OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.REMOVE) + @JoinColumn(name = "experimentId") + public Set getSamplePlate3VOs() { + return samplePlate3VOs; + } + + public void setSamplePlate3VOs(Set samplePlate3VOs) { + this.samplePlate3VOs = samplePlate3VOs; + } + + @Transient + public List getPlatetype3VOs() { + return this.platetype3VOs; + } + + public void setPlatetype3VOs(List platetype3vOs) { + platetype3VOs = platetype3vOs; + } + + @Transient + public Measurement3VO getMeasurementById(Integer measurementId) { + List measurements = this.getMeasurements(); + for (Measurement3VO measurement : measurements) { + // System.out.println("Experiment3VO.getSpecimenById: " + measurement); + if (measurement.getMeasurementId() != null) { + if (measurement.getMeasurementId().equals(measurementId)) { + return measurement; + } + } + } + return null; + } + + @Transient + public Sampleplate3VO getSamplePlateById(Integer samplePlateId) { + Set samplePlates = this.getSamplePlate3VOs(); + for (Sampleplate3VO samplePlate : samplePlates) { + if (samplePlate.getSamplePlateId().equals(samplePlateId)) { + return samplePlate; + } + } + return null; + } + + @Transient + public Sampleplateposition3VO getPosition(Integer samplePlateId, int row, int well) { + Sampleplate3VO samplePlate = this.getSamplePlateById(samplePlateId); + if (samplePlate != null) { + for (Sampleplateposition3VO position : samplePlate.getSampleplateposition3VOs()) { + if ((position.getRowNumber() == row) && (well == position.getColumnNumber())) { + return position; + } + } + } + + /** If not found **/ + return null; + + } + + + @Transient + public String getCodeSample(Specimen3VO sample) { + int count = 1; + String name = new String(); + do { + if (sample.getMacromolecule3VO() != null) { + name = sample.getMacromolecule3VO().getAcronym() + "_" + "Buffer_Not-Know";// this.getBufferById(sample.getBufferId()).getAcronym(); + } else { + name = "Buffer_Not-Know"; // this.getBufferById(sample.getBufferId()).getAcronym(); + } + if (count > 0) { + name = name + "-" + count; + } + count++; + } while (this.isSpecimenCodeUsed(name, sample)); + return name; + + } + + @Transient + public boolean isSpecimenCodeUsed(String code, Specimen3VO specimen) { + Set samples = this.getSamples(); + for (Specimen3VO sample3vo : samples) { + if (sample3vo.getCode().equals(code)) { + if (specimen.getSpecimenId() == null) { + return true; + } else { + if (specimen.getSpecimenId().equals(sample3vo.getSpecimenId())) { + return false; + } else { + return true; + } + } + } + } + return false; + } + + @OneToMany(fetch = FetchType.EAGER) + @JoinColumn(name = "experimentId") + public Set getDataCollections() { + return dataCollections; + } + + public void setDataCollections(Set dataCollections) { + this.dataCollections = dataCollections; + } + + @Transient + public List getDataCollectionList() { + List list = new ArrayList(); + Iterator iterator = this.getDataCollections().iterator(); + while (iterator.hasNext()) { + list.add(iterator.next()); + } + return list; + } + + @OneToMany(fetch = FetchType.LAZY) + @JoinColumn(name = "experimentId") + public Set getSamples() { + return samples3VOs; + } + + public void setSamples(Set samples) { + this.samples3VOs = samples; + } + + @Transient + public Specimen3VO getSampleById(int sampleId) { + for (Specimen3VO sample : this.getSamples()) { + if (sample.getSpecimenId().equals(sampleId)) { + return sample; + } + } + return null; + + } + + public String getCodeSpecimen(Measurement3VO specimen) { + Specimen3VO sample = this.getSampleById(specimen.getSpecimenId()); + return this.getCodeSpecimen(sample, specimen); + } + + public String getCodeSpecimen(Specimen3VO sample3vo, Measurement3VO specimen) { + String code = sample3vo.getCode();// this.getCodeSample(sample3vo); + if (sample3vo.getMacromolecule3VO() == null) { + return code + "_" + specimen.getExposureTemperature(); + } else { + return code + "_" + specimen.getExposureTemperature() + "_" + sample3vo.getConcentration(); + } + } + + @Transient + public Specimen3VO getSampleByPosition(Sampleplateposition3VO position) { + Set specimens = this.getSamples(); + for (Specimen3VO sample : specimens) { + if (sample.getSampleplateposition3VO() != null) { + Sampleplateposition3VO specimenPosition = sample.getSampleplateposition3VO(); + if (specimenPosition.getSamplePlateId().equals(position.getSamplePlateId())) { + if (specimenPosition.getRowNumber() == position.getRowNumber() + && specimenPosition.getColumnNumber() == position.getColumnNumber()) { + return sample; + } + } + } + } + return null; + } + + @Transient + public SaxsDataCollection3VO getDataCollectionByMeasurementId(int measurementId) { + Measurement3VO measurement = this.getMeasurementById(measurementId); + Set dataCollections = this.getDataCollections(); + for (SaxsDataCollection3VO saxsDataCollection3VO : dataCollections) { + for (MeasurementTodataCollection3VO measurementToDataCollection : saxsDataCollection3VO + .getMeasurementtodatacollection3VOs()) { + if (measurement.getMeasurementId().equals(measurementToDataCollection.getMeasurementId())) { + return saxsDataCollection3VO; + } + } + } + return null; + } + + @Transient + public List getDataCollectionListByMeasurementId(int measurementId) { + List list = new ArrayList(); + Measurement3VO measurement = this.getMeasurementById(measurementId); + Set dataCollections = this.getDataCollections(); + for (SaxsDataCollection3VO saxsDataCollection3VO : dataCollections) { + for (MeasurementTodataCollection3VO measurementToDataCollection : saxsDataCollection3VO + .getMeasurementtodatacollection3VOs()) { + if (measurement.getMeasurementId().equals(measurementToDataCollection.getMeasurementId())) { + list.add(saxsDataCollection3VO); + } + } + } + return list; + } + + @Transient + public List getMeasurementsOfDataCollectionByMeasurementId(int measurementId) { + SaxsDataCollection3VO dc = getDataCollectionByMeasurementId(measurementId); + List list = new ArrayList(); + for (MeasurementTodataCollection3VO iterable_element : dc.getMeasurementtodatacollection3VOs()) { + list.add(iterable_element); + } + return list; + } + + @Transient + public SaxsDataCollection3VO getDataCollectionById(int dataCollectionId) { + Set dataCollections = this.getDataCollections(); + for (SaxsDataCollection3VO saxsDataCollection3VO : dataCollections) { + if (saxsDataCollection3VO.equals(dataCollectionId)) { + return saxsDataCollection3VO; + } + } + return null; + } + + @Transient + public Measurement3VO getMeasurementBefore(int measurementId) { + Integer measurementBeforeId = this.getMeasurementIdBefore(measurementId); + if (measurementBeforeId != null) + return this.getMeasurementById(measurementBeforeId); + return null; + } + + @Transient + public Integer getMeasurementIdBefore(int measurementId) { + SaxsDataCollection3VO dataCollection = this.getDataCollectionByMeasurementId(measurementId); + if (dataCollection != null) { + for (MeasurementTodataCollection3VO measurement : dataCollection.getMeasurementtodatacollection3VOs()) { + if (measurement.getDataCollectionOrder() == 1) { + return measurement.getMeasurementId(); + } + } + } + return null; + } + + @Transient + public Measurement3VO getMeasurementAfter(int measurementId) { + Integer measurementAfterId = this.getMeasurementIdAfter(measurementId); + if (measurementAfterId != null) + return this.getMeasurementById(measurementAfterId); + return null; + } + + @Transient + public Integer getMeasurementIdAfter(int measurementId) { + SaxsDataCollection3VO dataCollection = this.getDataCollectionByMeasurementId(measurementId); + if (dataCollection != null) { + for (MeasurementTodataCollection3VO measurement : dataCollection.getMeasurementtodatacollection3VOs()) { + if (measurement.getDataCollectionOrder() == 3) { + return measurement.getMeasurementId(); + } + } + } + return null; + } + + @Column(name = "sourceFilePath") + public String getSourceFilePath() { + return sourceFilePath; + } + + public void setSourceFilePath(String sourceFilePath) { + this.sourceFilePath = sourceFilePath; + } + + @Column(name = "experimentType") + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + @Column(name = "dataAcquisitionFilePath") + public String getDataAcquisitionFilePath() { + return dataAcquisitionFilePath; + } + + public void setDataAcquisitionFilePath(String dataAcquisitionFilePath) { + this.dataAcquisitionFilePath = dataAcquisitionFilePath; + } + + @Column(name = "status") + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + +} diff --git a/ispyb-ui/pom.xml b/ispyb-ui/pom.xml index 7d2baa67a..8710e2a4b 100644 --- a/ispyb-ui/pom.xml +++ b/ispyb-ui/pom.xml @@ -179,12 +179,17 @@ org.hibernate hibernate-core - 4.0.0.Final + 5.0.10.Final + provided antlr antlr + + dom4j + dom4j + @@ -470,4 +475,4 @@ - + \ No newline at end of file diff --git a/ispyb-ui/src/main/java/ispyb/client/biosaxs/dataAdapter/BiosaxsActions.java b/ispyb-ui/src/main/java/ispyb/client/biosaxs/dataAdapter/BiosaxsActions.java index 5f865c402..3e1b36c2e 100644 --- a/ispyb-ui/src/main/java/ispyb/client/biosaxs/dataAdapter/BiosaxsActions.java +++ b/ispyb-ui/src/main/java/ispyb/client/biosaxs/dataAdapter/BiosaxsActions.java @@ -179,6 +179,7 @@ public Experiment3VO getExperimentById(Integer experimentId, ExperimentScope exp */ public Experiment3VO createExperiment(Integer proposalId, Integer sessionId, String name, String comments, Date createTime) { Experiment3VO experiment = new Experiment3VO(); + experiment = experiment3Service.initPlates(experiment); experiment.setCreationDate(createTime); experiment.setComments(comments); experiment.setName(name); diff --git a/ispyb-ui/src/main/webapp/js/ispyb/min/ispyb-bx.js b/ispyb-ui/src/main/webapp/js/ispyb/min/ispyb-bx.js index a98a06520..23e07209a 100644 --- a/ispyb-ui/src/main/webapp/js/ispyb/min/ispyb-bx.js +++ b/ispyb-ui/src/main/webapp/js/ispyb/min/ispyb-bx.js @@ -38,321 +38,321 @@ Event.prototype = { } }; -function GenericGraph(args) { - this.width = 600; - this.height = 400; - - this.targetId = null; - /** Free spaces in the borders * */ - this.top = 10; - this.left = 10; - this.bottom = 50; - this.right = 40; - - /** Ruler * */ - this.rulerHeight = 50; - this.rulerWidth = 50; - this.rulerStroke = 2; - this.rulerVerticalMarksNumber = 5; - - this.rulerMaxValue = null; - this.rulerMinValue = null; - - /** plot options * */ - this.plotPoints = true; - this.pointRadius = 2; - this.fillOpacityPoint = 0.2; - this.strokeOpacityPoint = 0.2; - - /** Cluster titles * */ - this.clusterTitleHeight = 20; - this.interClassesSpace = 2; - this.interClustersSpace = 4; - this.fontSize = 7; - - /** - * If true classes and title will be rendender in the rule otherwise will be - * integer values - */ - this.plotHorizontalByCluster = true; - - if (args != null) { - if (args.targetId != null) { - this.targetId = args.targetId; - } - if (args.plotHorizontalByCluster != null) { - this.plotHorizontalByCluster = args.plotHorizontalByCluster; - } - if (args.height != null) { - this.height = args.height; - } - if (args.width != null) { - this.width = args.width; - } - if (args.rulerMinValue != null) { - this.rulerMinValue = args.rulerMinValue; - } - if (args.rulerMaxValue != null) { - this.rulerMaxValue = args.rulerMaxValue; - } - if (args.rulerHeight != null) { - this.rulerHeight = args.rulerHeight; - } - - } -} - -GenericGraph.prototype.calculate = function(data) { - var result = {}; - - var checked = this.cleanArray(data); - - /** sorting array * */ - checked.sort(function(a, b) { - return a - b; - }); - - var median = this.getMedian(checked); - - result.median = median; - result.Q1 = Number(this.getQ1(checked)); - result.Q2 = Number(this.getQ2(checked)); - result.Q3 = Number(this.getQ3(checked)); - result.population = checked; - - result.IQR = Number(result.Q3 - result.Q1); - result.outlier_below_limit = result.Q1 - (1.5 * result.IQR); - result.outlier_above_limit = result.Q3 + (1.5 * result.IQR); - result.below_outliers = this.getBelowOutliers(result.outlier_below_limit, checked); - result.above_outliers = this.getAboveOutliers(result.outlier_above_limit, checked); - - result.min_whisker = this.minValueWhisker(result.outlier_below_limit, result.Q1, checked); - result.max_whisker = this.maxValueWhisker(result.outlier_above_limit, result.Q3, checked); - - return result; -}; - -GenericGraph.prototype.drawSVGVerticalText = function(x, y, text, properties) { - var transform = [ "transform", "translate(" + x + ", " + y + "), rotate(-90) " ]; - properties.push(transform); - SVG.drawText(0, 0, text, this.svg, properties); -}; - -/** Plot the numbers on the axis * */ -GenericGraph.prototype.plotRuler = function(rulerProperties) { - - SVG.drawRectangle(rulerProperties.vertical.x, rulerProperties.vertical.y, rulerProperties.vertical.width, rulerProperties.vertical.height, - this.svg, [ [ "fill", "black" ] ]); - var distance = rulerProperties.vertical.height / (this.rulerVerticalMarksNumber + 1); - for ( var i = 0; i <= this.rulerVerticalMarksNumber + 1; i++) { - var deltaHeight = distance * i; - var aux = rulerProperties.vertical.height - deltaHeight; - - var text = Number((aux / rulerProperties.vertical.height) * (rulerProperties.maxPoint - rulerProperties.minPoint) + rulerProperties.minPoint) - .toFixed(3); - - SVG.drawLine(rulerProperties.horizontal.x, this.top + deltaHeight, rulerProperties.horizontal.x - rulerProperties.markWidth, - this.top + deltaHeight, this.svg, [ [ "stroke", "black" ] ]); - SVG.drawText(this.left, this.top + distance * i + 5, text, this.svg, [ [ "style", "font-size:xx-small" ] ]); - /** Drawing the mark up to the end * */ - SVG.drawLine(rulerProperties.horizontal.x, this.top + deltaHeight, this.width - this.right, this.top + deltaHeight, this.svg, [ - [ "stroke", rulerProperties.markColor ], [ "stroke-width", "0.3" ] ]); - - if (i == this.rulerVerticalMarksNumber + 1) { - SVG.drawLine(rulerProperties.horizontal.x, this.top + deltaHeight, this.width - this.right, this.top + deltaHeight, this.svg, [ [ - "stroke", rulerProperties.markColor ] ]); - } - } - - /** Drawing horizontal rulers * */ - if (!this.plotHorizontalByCluster) { - var width = rulerProperties.horizontal.width; - var ratio = width / (rulerProperties.horizontal.xValues.range); - for ( i = 0; i < rulerProperties.horizontal.xValues.values.length; i++) { - var coorX = rulerProperties.horizontal.xValues.values[i] - rulerProperties.horizontal.xValues.min; - SVG.drawText(rulerProperties.horizontal.x + coorX * ratio, this.height - (this.bottom + (this.clusterTitleHeight)), - rulerProperties.horizontal.xValues.values[i], this.svg, [ [ "style", "font-size:small" ] ]); - } - } -}; - -GenericGraph.prototype.plotAxes = function(properties) { - /** - * Drawing canvas plot-free spaces SVG.drawRectangle(0, this.top, this.left, - * this.height, this.svg, [["fill", "pink"]]); SVG.drawRectangle(this.width - - * this.right, this.top, plot.width, plot.height, this.svg, [["fill", - * "pink"]]); SVG.drawRectangle(0, 0, this.width, this.top, this.svg, - * [["fill", "red"]]); SVG.drawRectangle(0, this.height - this.bottom, - * this.width, this.bottom, this.svg, [["fill", "red"]]); - */ - - /** Drawing ruler Space * */ - this.plotRuler({ - minPoint : Number(properties.minPoint), - maxPoint : Number(properties.maxPoint), - markColor : "black", - markWidth : 20, - vertical : { - x : this.left + this.rulerWidth - this.rulerStroke, - y : this.top, - width : this.rulerStroke, - height : this.height - (this.top + this.bottom + this.clusterTitleHeight) - this.rulerHeight - }, - horizontal : { - x : this.left + this.rulerWidth - this.rulerStroke, - y : this.height - (this.bottom + this.clusterTitleHeight + this.rulerHeight), - width : properties.width, - height : this.rulerStroke, - xValues : properties.xValues - } - }); - -}; - -/** Remove nulls and NaN elements in the array * */ -GenericGraph.prototype.cleanArray = function(data) { - var checked = []; - - /** checking data are numbers * */ - for ( var i = 0; i < data.length; i++) { - if (data[i] != null) { - if (!isNaN(data[i])) { - checked.push(data[i]); - } - } - } - return checked; -}; - -GenericGraph.prototype.refresh = function(data) { - document.getElementById(this.targetId).innerHTML = ""; - this.draw(this.targetId, data); -}; - -GenericGraph.prototype.getClassColor = function(className) { - for ( var i = 0; i < this.data.clusters.length; i++) { - var cluster = this.data.clusters[i]; - for ( var j = 0; j < cluster.classes.length; j++) { - var classes = cluster.classes[j]; - if (classes.name == className) { - if (classes.color != null) { - return classes.color; - } - } - } - } - return "black"; -}; - -GenericGraph.prototype.getDimensions = function(data) { - var results = {}; - var points = []; - - this.data = data; - var classesNumber = 0; - var xValues = []; - - for ( var i = 0; i < data.clusters.length; i++) { - var cluster = data.clusters[i]; - if (!this.plotHorizontalByCluster) { - xValues.push(data.clusters[i].x); - } - for ( var j = 0; j < cluster.classes.length; j++) { - var classes = cluster.classes[j]; - points = points.concat(classes.values); - classesNumber = classesNumber + 1; - } - } - - var checked = this.cleanArray(points); - - checked.sort(function(a, b) { - return a - b; - }); - - results.minPoint = checked[0]; - if (this.rulerMinValue != null) { - results.minPoint = this.rulerMinValue; - } - results.maxPoint = checked[checked.length - 1]; - if (this.rulerMaxValue != null) { - results.maxPoint = this.rulerMaxValue; - } - - results.classesNumber = classesNumber; - results.clusterNumber = data.clusters.length; - - var totalInterClassesFreeSpace = (classesNumber - data.clusters.length) * this.interClassesSpace; - var totalInterClusterFreeSpace = (data.clusters.length) * this.interClustersSpace; - results.classWidth = (this.width - (this.rulerWidth + this.left + this.right + totalInterClassesFreeSpace + totalInterClusterFreeSpace))/ classesNumber; - results.width = this.width - (this.right + this.left) - this.rulerWidth + this.rulerStroke; - - results.xValues = {}; - - xValues.sort(function(a, b) { - return a - b; - }); - results.xValues.values = xValues; - if (xValues.length > 0) { - results.xValues.min = xValues[0]; - results.xValues.max = xValues[xValues.length - 1]; - results.xValues.range = results.xValues.max - results.xValues.min; - } - return results; - -}; - -GenericGraph.prototype.draw = function(targetId, data) { - this.targetId = targetId; - var properties = (this.getDimensions(data)); - this.svg = SVG.createSVGCanvas(document.getElementById(this.targetId), [ [ "width", this.width ], [ "height", this.height ] ]); - this.plotAxes(properties); -}; - -GenericGraph.prototype.getMedian = function(checked) { - /** Calculating median * */ - if (checked.length % 2 == 1) { - return checked[Math.floor(checked.length / 2)]; - } else { - return (Number(checked[(checked.length / 2) - 1]) + Number(checked[checked.length / 2])) / 2; - } -}; - -GenericGraph.prototype.pointToPixel = function(value, boxProperties) { - var ratio = (value - boxProperties.minPoint) / (boxProperties.maxPoint - boxProperties.minPoint); - var pixelLength = boxProperties.height - boxProperties.y + this.top; - return (-1 * ratio) * (pixelLength) + (boxProperties.y + boxProperties.height); -}; - -GenericGraph.prototype.maxValueWhisker = function(aboveLimit, q3, array) { - var points = []; - for ( var i = 0; i < array.length; i++) { - if ((array[i] > q3) && (array[i]) <= aboveLimit) { - points.push(array[i]); - } - } - if (points.length > 0) { - points.sort(function(a, b) { - return a - b; - }); - return points[points.length - 1]; - } - return null; -}; - -GenericGraph.prototype.input = function() { - return DATADOC.getBoxWhikerData(); -}; - -GenericGraph.prototype.test = function(targetId) { - var plot = new GenericGraph({ - targetId : targetId, - height : 350, - width : 450, - maxBoxWidth : 25 - }); - plot.refresh(this.input()); -}; +function GenericGraph(args) { + this.width = 600; + this.height = 400; + + this.targetId = null; + /** Free spaces in the borders * */ + this.top = 10; + this.left = 10; + this.bottom = 50; + this.right = 40; + + /** Ruler * */ + this.rulerHeight = 50; + this.rulerWidth = 50; + this.rulerStroke = 2; + this.rulerVerticalMarksNumber = 5; + + this.rulerMaxValue = null; + this.rulerMinValue = null; + + /** plot options * */ + this.plotPoints = true; + this.pointRadius = 2; + this.fillOpacityPoint = 0.2; + this.strokeOpacityPoint = 0.2; + + /** Cluster titles * */ + this.clusterTitleHeight = 20; + this.interClassesSpace = 2; + this.interClustersSpace = 4; + this.fontSize = 7; + + /** + * If true classes and title will be rendender in the rule otherwise will be + * integer values + */ + this.plotHorizontalByCluster = true; + + if (args != null) { + if (args.targetId != null) { + this.targetId = args.targetId; + } + if (args.plotHorizontalByCluster != null) { + this.plotHorizontalByCluster = args.plotHorizontalByCluster; + } + if (args.height != null) { + this.height = args.height; + } + if (args.width != null) { + this.width = args.width; + } + if (args.rulerMinValue != null) { + this.rulerMinValue = args.rulerMinValue; + } + if (args.rulerMaxValue != null) { + this.rulerMaxValue = args.rulerMaxValue; + } + if (args.rulerHeight != null) { + this.rulerHeight = args.rulerHeight; + } + + } +} + +GenericGraph.prototype.calculate = function(data) { + var result = {}; + + var checked = this.cleanArray(data); + + /** sorting array * */ + checked.sort(function(a, b) { + return a - b; + }); + + var median = this.getMedian(checked); + + result.median = median; + result.Q1 = Number(this.getQ1(checked)); + result.Q2 = Number(this.getQ2(checked)); + result.Q3 = Number(this.getQ3(checked)); + result.population = checked; + + result.IQR = Number(result.Q3 - result.Q1); + result.outlier_below_limit = result.Q1 - (1.5 * result.IQR); + result.outlier_above_limit = result.Q3 + (1.5 * result.IQR); + result.below_outliers = this.getBelowOutliers(result.outlier_below_limit, checked); + result.above_outliers = this.getAboveOutliers(result.outlier_above_limit, checked); + + result.min_whisker = this.minValueWhisker(result.outlier_below_limit, result.Q1, checked); + result.max_whisker = this.maxValueWhisker(result.outlier_above_limit, result.Q3, checked); + + return result; +}; + +GenericGraph.prototype.drawSVGVerticalText = function(x, y, text, properties) { + var transform = [ "transform", "translate(" + x + ", " + y + "), rotate(-90) " ]; + properties.push(transform); + SVG.drawText(0, 0, text, this.svg, properties); +}; + +/** Plot the numbers on the axis * */ +GenericGraph.prototype.plotRuler = function(rulerProperties) { + + SVG.drawRectangle(rulerProperties.vertical.x, rulerProperties.vertical.y, rulerProperties.vertical.width, rulerProperties.vertical.height, + this.svg, [ [ "fill", "black" ] ]); + var distance = rulerProperties.vertical.height / (this.rulerVerticalMarksNumber + 1); + for ( var i = 0; i <= this.rulerVerticalMarksNumber + 1; i++) { + var deltaHeight = distance * i; + var aux = rulerProperties.vertical.height - deltaHeight; + + var text = Number((aux / rulerProperties.vertical.height) * (rulerProperties.maxPoint - rulerProperties.minPoint) + rulerProperties.minPoint) + .toFixed(3); + + SVG.drawLine(rulerProperties.horizontal.x, this.top + deltaHeight, rulerProperties.horizontal.x - rulerProperties.markWidth, + this.top + deltaHeight, this.svg, [ [ "stroke", "black" ] ]); + SVG.drawText(this.left, this.top + distance * i + 5, text, this.svg, [ [ "style", "font-size:xx-small" ] ]); + /** Drawing the mark up to the end * */ + SVG.drawLine(rulerProperties.horizontal.x, this.top + deltaHeight, this.width - this.right, this.top + deltaHeight, this.svg, [ + [ "stroke", rulerProperties.markColor ], [ "stroke-width", "0.3" ] ]); + + if (i == this.rulerVerticalMarksNumber + 1) { + SVG.drawLine(rulerProperties.horizontal.x, this.top + deltaHeight, this.width - this.right, this.top + deltaHeight, this.svg, [ [ + "stroke", rulerProperties.markColor ] ]); + } + } + + /** Drawing horizontal rulers * */ + if (!this.plotHorizontalByCluster) { + var width = rulerProperties.horizontal.width; + var ratio = width / (rulerProperties.horizontal.xValues.range); + for ( i = 0; i < rulerProperties.horizontal.xValues.values.length; i++) { + var coorX = rulerProperties.horizontal.xValues.values[i] - rulerProperties.horizontal.xValues.min; + SVG.drawText(rulerProperties.horizontal.x + coorX * ratio, this.height - (this.bottom + (this.clusterTitleHeight)), + rulerProperties.horizontal.xValues.values[i], this.svg, [ [ "style", "font-size:small" ] ]); + } + } +}; + +GenericGraph.prototype.plotAxes = function(properties) { + /** + * Drawing canvas plot-free spaces SVG.drawRectangle(0, this.top, this.left, + * this.height, this.svg, [["fill", "pink"]]); SVG.drawRectangle(this.width - + * this.right, this.top, plot.width, plot.height, this.svg, [["fill", + * "pink"]]); SVG.drawRectangle(0, 0, this.width, this.top, this.svg, + * [["fill", "red"]]); SVG.drawRectangle(0, this.height - this.bottom, + * this.width, this.bottom, this.svg, [["fill", "red"]]); + */ + + /** Drawing ruler Space * */ + this.plotRuler({ + minPoint : Number(properties.minPoint), + maxPoint : Number(properties.maxPoint), + markColor : "black", + markWidth : 20, + vertical : { + x : this.left + this.rulerWidth - this.rulerStroke, + y : this.top, + width : this.rulerStroke, + height : this.height - (this.top + this.bottom + this.clusterTitleHeight) - this.rulerHeight + }, + horizontal : { + x : this.left + this.rulerWidth - this.rulerStroke, + y : this.height - (this.bottom + this.clusterTitleHeight + this.rulerHeight), + width : properties.width, + height : this.rulerStroke, + xValues : properties.xValues + } + }); + +}; + +/** Remove nulls and NaN elements in the array * */ +GenericGraph.prototype.cleanArray = function(data) { + var checked = []; + + /** checking data are numbers * */ + for ( var i = 0; i < data.length; i++) { + if (data[i] != null) { + if (!isNaN(data[i])) { + checked.push(data[i]); + } + } + } + return checked; +}; + +GenericGraph.prototype.refresh = function(data) { + document.getElementById(this.targetId).innerHTML = ""; + this.draw(this.targetId, data); +}; + +GenericGraph.prototype.getClassColor = function(className) { + for ( var i = 0; i < this.data.clusters.length; i++) { + var cluster = this.data.clusters[i]; + for ( var j = 0; j < cluster.classes.length; j++) { + var classes = cluster.classes[j]; + if (classes.name == className) { + if (classes.color != null) { + return classes.color; + } + } + } + } + return "black"; +}; + +GenericGraph.prototype.getDimensions = function(data) { + var results = {}; + var points = []; + + this.data = data; + var classesNumber = 0; + var xValues = []; + + for ( var i = 0; i < data.clusters.length; i++) { + var cluster = data.clusters[i]; + if (!this.plotHorizontalByCluster) { + xValues.push(data.clusters[i].x); + } + for ( var j = 0; j < cluster.classes.length; j++) { + var classes = cluster.classes[j]; + points = points.concat(classes.values); + classesNumber = classesNumber + 1; + } + } + + var checked = this.cleanArray(points); + + checked.sort(function(a, b) { + return a - b; + }); + + results.minPoint = checked[0]; + if (this.rulerMinValue != null) { + results.minPoint = this.rulerMinValue; + } + results.maxPoint = checked[checked.length - 1]; + if (this.rulerMaxValue != null) { + results.maxPoint = this.rulerMaxValue; + } + + results.classesNumber = classesNumber; + results.clusterNumber = data.clusters.length; + + var totalInterClassesFreeSpace = (classesNumber - data.clusters.length) * this.interClassesSpace; + var totalInterClusterFreeSpace = (data.clusters.length) * this.interClustersSpace; + results.classWidth = (this.width - (this.rulerWidth + this.left + this.right + totalInterClassesFreeSpace + totalInterClusterFreeSpace))/ classesNumber; + results.width = this.width - (this.right + this.left) - this.rulerWidth + this.rulerStroke; + + results.xValues = {}; + + xValues.sort(function(a, b) { + return a - b; + }); + results.xValues.values = xValues; + if (xValues.length > 0) { + results.xValues.min = xValues[0]; + results.xValues.max = xValues[xValues.length - 1]; + results.xValues.range = results.xValues.max - results.xValues.min; + } + return results; + +}; + +GenericGraph.prototype.draw = function(targetId, data) { + this.targetId = targetId; + var properties = (this.getDimensions(data)); + this.svg = SVG.createSVGCanvas(document.getElementById(this.targetId), [ [ "width", this.width ], [ "height", this.height ] ]); + this.plotAxes(properties); +}; + +GenericGraph.prototype.getMedian = function(checked) { + /** Calculating median * */ + if (checked.length % 2 == 1) { + return checked[Math.floor(checked.length / 2)]; + } else { + return (Number(checked[(checked.length / 2) - 1]) + Number(checked[checked.length / 2])) / 2; + } +}; + +GenericGraph.prototype.pointToPixel = function(value, boxProperties) { + var ratio = (value - boxProperties.minPoint) / (boxProperties.maxPoint - boxProperties.minPoint); + var pixelLength = boxProperties.height - boxProperties.y + this.top; + return (-1 * ratio) * (pixelLength) + (boxProperties.y + boxProperties.height); +}; + +GenericGraph.prototype.maxValueWhisker = function(aboveLimit, q3, array) { + var points = []; + for ( var i = 0; i < array.length; i++) { + if ((array[i] > q3) && (array[i]) <= aboveLimit) { + points.push(array[i]); + } + } + if (points.length > 0) { + points.sort(function(a, b) { + return a - b; + }); + return points[points.length - 1]; + } + return null; +}; + +GenericGraph.prototype.input = function() { + return DATADOC.getBoxWhikerData(); +}; + +GenericGraph.prototype.test = function(targetId) { + var plot = new GenericGraph({ + targetId : targetId, + height : 350, + width : 450, + maxBoxWidth : 25 + }); + plot.refresh(this.input()); +}; /** * Using dygraph it plots a chart. Params: targetId, labelsContainerId, args @@ -2087,338 +2087,338 @@ MacromoleculeConditionGrid.prototype.test = function(targetId) { panel.render(targetId); }; - -function ItemGraphFormatter(id, defaultFormat, selectedFormat, overFormat, draggingFormat){ - this.id = id; - this.args = new Object(); - - this.defaultFormat = new ItemFormat(defaultFormat); - - if(selectedFormat != null){ - this.selected = new ItemFormat(selectedFormat); - } - else{ - this.selected = new ItemFormat(defaultFormat); - } - - if(overFormat != null){ - this.over = new ItemFormat(overFormat); - } - else{ - this.over = new ItemFormat(defaultFormat); - } - - if(draggingFormat != null){ - this.dragging = new ItemFormat(draggingFormat); - } - else{ - this.dragging = new ItemFormat(defaultFormat); - } - - //Events - this.stateChanged = new Event(this); - - - //Attaching events - var _this = this; - this._setEvents(); -}; - -ItemGraphFormatter.prototype.getType = function(){ - return this.args.type; -}; - - -ItemGraphFormatter.prototype.toJSON = function(){ - var json = this.args; - json.defaultFormat = this.getDefault().toJSON(); - json.over = this.getOver().toJSON(); - json.selected = this.getSelected().toJSON(); - json.dragging = this.getDragging().toJSON(); - json.id = this.id; - return json; -}; - -ItemGraphFormatter.prototype.loadFromJSON = function(json){ - this.args = json; - this.defaultFormat = new ItemFormat(json.defaultFormat); - this.over = new ItemFormat(json.over); - this.selected = new ItemFormat(json.selected); - this.dragging = new ItemFormat(json.dragging); - this._setEvents(); -}; - -ItemGraphFormatter.prototype._setEvents = function(){ - //Attaching events - var _this = this; - - this.defaultFormat.changed.attach(function (sender, item){ - _this.over.setSize(_this.defaultFormat.getSize()); - _this.selected.setSize(_this.defaultFormat.getSize()); - _this.dragging.setSize(_this.defaultFormat.getSize()); - _this.stateChanged.notify(_this); - }); - - this.selected.changed.attach(function (sender, item){ - _this.stateChanged.notify(_this); - }); - - this.over.changed.attach(function (sender, item){ - _this.stateChanged.notify(_this); - }); - - this.dragging.changed.attach(function (sender, item){ - _this.stateChanged.notify(_this); - }); -}; - -/** Getters **/ -ItemGraphFormatter.prototype.getId = function(){return this.id;}; -ItemGraphFormatter.prototype.getDefault = function(){return this.defaultFormat;}; -ItemGraphFormatter.prototype.getSelected = function(){return this.selected;}; -ItemGraphFormatter.prototype.getOver = function(){return this.over;}; -ItemGraphFormatter.prototype.getDragging = function(){return this.dragging;}; - -function ItemFormat(args){ - this.defaultFormat = new Object(); - this.args = new Object(); - this.args.title = new Object(); - //Defult properties - this.args.visible = true; - this.args.hidden = false; - this.args.stroke = "#000000"; - this.args.strokeOpacity = 0.8; - this.args["stroke-width"] = 1; - this.args.fill = "#000000"; - this.args["fill-opacity"] = 1; - this.args.size = 1; - this.args.opacity = 1; - this.args.fontSize = "8"; - this.args.fontColor = "#000000"; - - /** For directed edge with arrow **/ - //this.args.arrowSize = 1; - - - if (args != null){ - if (args.visible != null){ - this.args.visible = args.visible; - } - if (args.opacity != null){ - this.args.opacity = args.opacity; - } - if (args.size != null){ - this.args.size = args.size; - } - if (args.hidden != null){ - this.args.hidden = args.hidden; - } - if (args.stroke != null){ - this.args.stroke = this._fixColor(args.stroke); - } - if (args.strokeOpacity != null){ - this.args.strokeOpacity = args.strokeOpacity; - } - if (args["stroke-width"]!=null){ - this.args["stroke-width"] = args["stroke-width"]; - } - if (args["fill-opacity"]!=null){ - this.args["fill-opacity"] = args["fill-opacity"]; - } - if (args.shape!=null){ - this.args.shape = args.shape; - } - if (args.fill!=null){ - this.args.fill = this._fixColor(args.fill); - } - - - if (args.title!=null){ - if (args.title.fontSize!=null){ - this.args.title.fontSize = args.title.fontSize; - } - if (args.title.fill!=null){ - this.args.title.fill = this._fixColor(args.title.fill); - } - } - - /** For directed edge with arrow **/ - /*if (args.arrowSize!=null){ - this.args.arrowSize = args.arrowSize; - }*/ - - } - - this.changed = new Event(); -}; - -ItemFormat.prototype._fixColor = function(color){ - var fixed = color; - if (color.indexOf("green") != -1){ - fixed = '#04B431'; - } - - if (color.indexOf("blue") != -1){ - fixed = '#045FB4'; - } - - if (color.indexOf("red") != -1){ - fixed = '#DF0101'; - } - - if (color.indexOf("black") != -1){ - fixed = '#000000'; - } - - if (color.indexOf("white") != -1){ - fixed = '#FFFFFF'; - } - - if (color.indexOf("#") == -1){ - fixed = "#" + color; - } - return fixed; -}; - -ItemFormat.prototype.toJSON = function(){ - if(this.args.strokeOpacity != null){ - this.args["stroke-opacity"] = this.args.strokeOpacity; - delete this.args.strokeOpacity; - } - -// if(this.args.strokeWidth != null){ -// this.args["stroke-width"] = this.args.strokeWidth; -// delete this.args["stroke-width"]; -// } - - if(this.args.title.fontColor != null){ - this.args.title["font-color"] = this.args.title.fontColor; - } - else{ - this.args.title["font-color"] = this.args.fontColor;//;this.args.title.fontColor; - } - - if(this.args.title.fontSize != null){ - this.args.title["font-size"] = this.args.title.fontSize;//;this.args.title.fontColor; - } - else{ - this.args.title["font-size"] = this.args.fontSize;//;this.args.title.fontColor; - } - //return this.args; - return this.args; -}; - -ItemFormat.prototype.getAttribute = function(name){return this.args[name];}; - -//Getters and Setters -ItemFormat.prototype.setVisible = function(visible){ - if (this.args.visible != visible){ - this.args.visible = visible; - this.changed.notify(this); - } -}; - -ItemFormat.prototype.getVisible = function(){return this.args.visible;}; - -ItemFormat.prototype.setHidden = function(hidden){ - if (this.args.hidden != hidden){ - this.args.hidden = hidden; - this.changed.notify(this); - } -}; - -ItemFormat.prototype.getHidden = function(){return this.args.hidden;}; - - -ItemFormat.prototype.setStroke = function(stroke){ - if (this.args.stroke != stroke){ - this.args.stroke = stroke; - this.changed.notify(this); - } -}; - -ItemFormat.prototype.getStroke = function(){return this.args.stroke;}; - -ItemFormat.prototype.setStrokeOpacity = function(strokeOpacity){ - if (this.args.strokeOpacity != strokeOpacity){ - this.args.strokeOpacity = strokeOpacity; - this.changed.notify(this); - } -}; - -ItemFormat.prototype.getStrokeOpacity = function(){return this.args["stroke-opacity"];}; - -ItemFormat.prototype.setStrokeWidth = function(strokeWidth){ - if (this.args["stroke-width"] != strokeWidth){ - this.args["stroke-width"] = strokeWidth; - this.changed.notify(this); - } -}; - - -ItemFormat.prototype.getFillOpacity = function(){return this.args["fill-opacity"];}; - -ItemFormat.prototype.setfillOpacity = function(strokeWidth){ - if (this.args["fill-opacity"] != strokeWidth){ - this.args["fill-opacity"] = strokeWidth; - this.changed.notify(this); - } -}; - - -ItemFormat.prototype.getStrokeWidth = function(){ - return this.args["stroke-width"]; -}; - -ItemFormat.prototype.setFill = function(fill){ - if (this.args.fill != fill){ - this.args.fill = fill; - this.changed.notify(this); - } -}; - -ItemFormat.prototype.getFill = function(){return this.args.fill;}; - -ItemFormat.prototype.setSize = function(size){ - if (this.args.size != size){ - this.args.size = size; - this.changed.notify(this); - } -}; - -ItemFormat.prototype.getSize = function(){return this.args.size;}; - -ItemFormat.prototype.setOpacity = function(opacity){ - if (this.args.opacity != opacity){ - this.args.opacity = opacity; - this.changed.notify(this); - } -}; - -ItemFormat.prototype.getOpacity = function(){return this.args.opacity;}; - -ItemFormat.prototype.getArrowSize = function(){return this.args.arrowSize;}; - -ItemFormat.prototype.setArrowSize = function(arrowSize){ - if (this.args.arrowSize != arrowSize){ - this.args.arrowSize = arrowSize; - this.changed.notify(this); - } -}; - -ItemFormat.prototype.getFontSize = function(){return this.args.title.fontSize;}; - -ItemFormat.prototype.setFontSize = function(fontSize){ - - if (this.args.title.fontSize != fontSize){ - this.args.title.fontSize = fontSize; - this.changed.notify(this); - } -}; - - - - + +function ItemGraphFormatter(id, defaultFormat, selectedFormat, overFormat, draggingFormat){ + this.id = id; + this.args = new Object(); + + this.defaultFormat = new ItemFormat(defaultFormat); + + if(selectedFormat != null){ + this.selected = new ItemFormat(selectedFormat); + } + else{ + this.selected = new ItemFormat(defaultFormat); + } + + if(overFormat != null){ + this.over = new ItemFormat(overFormat); + } + else{ + this.over = new ItemFormat(defaultFormat); + } + + if(draggingFormat != null){ + this.dragging = new ItemFormat(draggingFormat); + } + else{ + this.dragging = new ItemFormat(defaultFormat); + } + + //Events + this.stateChanged = new Event(this); + + + //Attaching events + var _this = this; + this._setEvents(); +}; + +ItemGraphFormatter.prototype.getType = function(){ + return this.args.type; +}; + + +ItemGraphFormatter.prototype.toJSON = function(){ + var json = this.args; + json.defaultFormat = this.getDefault().toJSON(); + json.over = this.getOver().toJSON(); + json.selected = this.getSelected().toJSON(); + json.dragging = this.getDragging().toJSON(); + json.id = this.id; + return json; +}; + +ItemGraphFormatter.prototype.loadFromJSON = function(json){ + this.args = json; + this.defaultFormat = new ItemFormat(json.defaultFormat); + this.over = new ItemFormat(json.over); + this.selected = new ItemFormat(json.selected); + this.dragging = new ItemFormat(json.dragging); + this._setEvents(); +}; + +ItemGraphFormatter.prototype._setEvents = function(){ + //Attaching events + var _this = this; + + this.defaultFormat.changed.attach(function (sender, item){ + _this.over.setSize(_this.defaultFormat.getSize()); + _this.selected.setSize(_this.defaultFormat.getSize()); + _this.dragging.setSize(_this.defaultFormat.getSize()); + _this.stateChanged.notify(_this); + }); + + this.selected.changed.attach(function (sender, item){ + _this.stateChanged.notify(_this); + }); + + this.over.changed.attach(function (sender, item){ + _this.stateChanged.notify(_this); + }); + + this.dragging.changed.attach(function (sender, item){ + _this.stateChanged.notify(_this); + }); +}; + +/** Getters **/ +ItemGraphFormatter.prototype.getId = function(){return this.id;}; +ItemGraphFormatter.prototype.getDefault = function(){return this.defaultFormat;}; +ItemGraphFormatter.prototype.getSelected = function(){return this.selected;}; +ItemGraphFormatter.prototype.getOver = function(){return this.over;}; +ItemGraphFormatter.prototype.getDragging = function(){return this.dragging;}; + +function ItemFormat(args){ + this.defaultFormat = new Object(); + this.args = new Object(); + this.args.title = new Object(); + //Defult properties + this.args.visible = true; + this.args.hidden = false; + this.args.stroke = "#000000"; + this.args.strokeOpacity = 0.8; + this.args["stroke-width"] = 1; + this.args.fill = "#000000"; + this.args["fill-opacity"] = 1; + this.args.size = 1; + this.args.opacity = 1; + this.args.fontSize = "8"; + this.args.fontColor = "#000000"; + + /** For directed edge with arrow **/ + //this.args.arrowSize = 1; + + + if (args != null){ + if (args.visible != null){ + this.args.visible = args.visible; + } + if (args.opacity != null){ + this.args.opacity = args.opacity; + } + if (args.size != null){ + this.args.size = args.size; + } + if (args.hidden != null){ + this.args.hidden = args.hidden; + } + if (args.stroke != null){ + this.args.stroke = this._fixColor(args.stroke); + } + if (args.strokeOpacity != null){ + this.args.strokeOpacity = args.strokeOpacity; + } + if (args["stroke-width"]!=null){ + this.args["stroke-width"] = args["stroke-width"]; + } + if (args["fill-opacity"]!=null){ + this.args["fill-opacity"] = args["fill-opacity"]; + } + if (args.shape!=null){ + this.args.shape = args.shape; + } + if (args.fill!=null){ + this.args.fill = this._fixColor(args.fill); + } + + + if (args.title!=null){ + if (args.title.fontSize!=null){ + this.args.title.fontSize = args.title.fontSize; + } + if (args.title.fill!=null){ + this.args.title.fill = this._fixColor(args.title.fill); + } + } + + /** For directed edge with arrow **/ + /*if (args.arrowSize!=null){ + this.args.arrowSize = args.arrowSize; + }*/ + + } + + this.changed = new Event(); +}; + +ItemFormat.prototype._fixColor = function(color){ + var fixed = color; + if (color.indexOf("green") != -1){ + fixed = '#04B431'; + } + + if (color.indexOf("blue") != -1){ + fixed = '#045FB4'; + } + + if (color.indexOf("red") != -1){ + fixed = '#DF0101'; + } + + if (color.indexOf("black") != -1){ + fixed = '#000000'; + } + + if (color.indexOf("white") != -1){ + fixed = '#FFFFFF'; + } + + if (color.indexOf("#") == -1){ + fixed = "#" + color; + } + return fixed; +}; + +ItemFormat.prototype.toJSON = function(){ + if(this.args.strokeOpacity != null){ + this.args["stroke-opacity"] = this.args.strokeOpacity; + delete this.args.strokeOpacity; + } + +// if(this.args.strokeWidth != null){ +// this.args["stroke-width"] = this.args.strokeWidth; +// delete this.args["stroke-width"]; +// } + + if(this.args.title.fontColor != null){ + this.args.title["font-color"] = this.args.title.fontColor; + } + else{ + this.args.title["font-color"] = this.args.fontColor;//;this.args.title.fontColor; + } + + if(this.args.title.fontSize != null){ + this.args.title["font-size"] = this.args.title.fontSize;//;this.args.title.fontColor; + } + else{ + this.args.title["font-size"] = this.args.fontSize;//;this.args.title.fontColor; + } + //return this.args; + return this.args; +}; + +ItemFormat.prototype.getAttribute = function(name){return this.args[name];}; + +//Getters and Setters +ItemFormat.prototype.setVisible = function(visible){ + if (this.args.visible != visible){ + this.args.visible = visible; + this.changed.notify(this); + } +}; + +ItemFormat.prototype.getVisible = function(){return this.args.visible;}; + +ItemFormat.prototype.setHidden = function(hidden){ + if (this.args.hidden != hidden){ + this.args.hidden = hidden; + this.changed.notify(this); + } +}; + +ItemFormat.prototype.getHidden = function(){return this.args.hidden;}; + + +ItemFormat.prototype.setStroke = function(stroke){ + if (this.args.stroke != stroke){ + this.args.stroke = stroke; + this.changed.notify(this); + } +}; + +ItemFormat.prototype.getStroke = function(){return this.args.stroke;}; + +ItemFormat.prototype.setStrokeOpacity = function(strokeOpacity){ + if (this.args.strokeOpacity != strokeOpacity){ + this.args.strokeOpacity = strokeOpacity; + this.changed.notify(this); + } +}; + +ItemFormat.prototype.getStrokeOpacity = function(){return this.args["stroke-opacity"];}; + +ItemFormat.prototype.setStrokeWidth = function(strokeWidth){ + if (this.args["stroke-width"] != strokeWidth){ + this.args["stroke-width"] = strokeWidth; + this.changed.notify(this); + } +}; + + +ItemFormat.prototype.getFillOpacity = function(){return this.args["fill-opacity"];}; + +ItemFormat.prototype.setfillOpacity = function(strokeWidth){ + if (this.args["fill-opacity"] != strokeWidth){ + this.args["fill-opacity"] = strokeWidth; + this.changed.notify(this); + } +}; + + +ItemFormat.prototype.getStrokeWidth = function(){ + return this.args["stroke-width"]; +}; + +ItemFormat.prototype.setFill = function(fill){ + if (this.args.fill != fill){ + this.args.fill = fill; + this.changed.notify(this); + } +}; + +ItemFormat.prototype.getFill = function(){return this.args.fill;}; + +ItemFormat.prototype.setSize = function(size){ + if (this.args.size != size){ + this.args.size = size; + this.changed.notify(this); + } +}; + +ItemFormat.prototype.getSize = function(){return this.args.size;}; + +ItemFormat.prototype.setOpacity = function(opacity){ + if (this.args.opacity != opacity){ + this.args.opacity = opacity; + this.changed.notify(this); + } +}; + +ItemFormat.prototype.getOpacity = function(){return this.args.opacity;}; + +ItemFormat.prototype.getArrowSize = function(){return this.args.arrowSize;}; + +ItemFormat.prototype.setArrowSize = function(arrowSize){ + if (this.args.arrowSize != arrowSize){ + this.args.arrowSize = arrowSize; + this.changed.notify(this); + } +}; + +ItemFormat.prototype.getFontSize = function(){return this.args.title.fontSize;}; + +ItemFormat.prototype.setFontSize = function(fontSize){ + + if (this.args.title.fontSize != fontSize){ + this.args.title.fontSize = fontSize; + this.changed.notify(this); + } +}; + + + + /** * This class executes the actions @@ -4952,1101 +4952,2095 @@ function openExperiment(experimentId) { BIOSAXS.openExperiment(experimentId); } -var SITE_CONF = { - SAMPLE_CHANGER_CONFIGURATION : { - "3":{ - "platetype3VO": {"plateTypeId":4,"name":"96 Well plate","rowCount":8,"columnCount":12,"shape":"REC"}, - "name":"96 Well plate", - "slotPositionRow":"1", - "slotPositionColumn":"3", - "storageTemperature":"0", - "sampleplateposition3VOs":[], - "experimentId":0 - }, - "2":{ - "platetype3VO":{"plateTypeId":2,"name":" 4 x ( 8 + 3 ) Block","rowCount":4,"columnCount":11,"shape":"REC"}, - "name":" 4 x ( 8 + 3 ) Block", - "slotPositionRow":"1", - "slotPositionColumn":"2", - "storageTemperature":"0", - "sampleplateposition3VOs":[],"experimentId":0 - }, - "1":{ - "platetype3VO":{"plateTypeId":1,"name":"Deep Well","rowCount":8,"columnCount":12,"shape":"REC"}, - "name":"Deep Well", - "slotPositionRow":"1", - "slotPositionColumn":"1", - "storageTemperature":"0", - "sampleplateposition3VOs":[], - "experimentId":0 - } - } +var SITE_CONF = { + SAMPLE_CHANGER_CONFIGURATION : { + "3":{ + "platetype3VO": {"plateTypeId":4,"name":"96 Well plate","rowCount":8,"columnCount":12,"shape":"REC"}, + "name":"96 Well plate", + "slotPositionRow":"1", + "slotPositionColumn":"3", + "storageTemperature":"0", + "sampleplateposition3VOs":[], + "experimentId":0 + }, + "2":{ + "platetype3VO":{"plateTypeId":2,"name":" 4 x ( 8 + 3 ) Block","rowCount":4,"columnCount":11,"shape":"REC"}, + "name":" 4 x ( 8 + 3 ) Block", + "slotPositionRow":"1", + "slotPositionColumn":"2", + "storageTemperature":"0", + "sampleplateposition3VOs":[],"experimentId":0 + }, + "1":{ + "platetype3VO":{"plateTypeId":1,"name":"Deep Well","rowCount":8,"columnCount":12,"shape":"REC"}, + "name":"Deep Well", + "slotPositionRow":"1", + "slotPositionColumn":"1", + "storageTemperature":"0", + "sampleplateposition3VOs":[], + "experimentId":0 + } + } }; - -var ISPYB_CONF = { - load : function(config) { - for (var key in config) { - ISPYB_CONF[key] = config[key]; - } - }, - SAMPLE_CHANGER_CONFIGURATION : { - "1": { - "platetype3VO": {"plateTypeId": 1, "name": "Deep Well", "rowCount": 8, "columnCount": 12, "shape": "REC"}, - "name": "Deep Well", - "slotPositionRow": "1", - "slotPositionColumn": "1", - "storageTemperature": "0", - "sampleplateposition3VOs": [], - "experimentId": 0 - } - } + +var ISPYB_CONF = { + load : function(config) { + for (var key in config) { + ISPYB_CONF[key] = config[key]; + } + }, + SAMPLE_CHANGER_CONFIGURATION : { + "1": { + "platetype3VO": {"plateTypeId": 1, "name": "Deep Well", "rowCount": 8, "columnCount": 12, "shape": "REC"}, + "name": "Deep Well", + "slotPositionRow": "1", + "slotPositionColumn": "1", + "storageTemperature": "0", + "sampleplateposition3VOs": [], + "experimentId": 0 + } + } }; -var SITE_CONF = { - -}; +var SITE_CONF = { + +}; -/** - * Example form - * - * @witdh - * @height - */ -function AbinitioForm(args) { - this.id = BUI.id(); - this.width = null; - this.height = null; - - if (args != null) { - if (args.width != null) { - this.width = args.width; - } - if (args.height != null) { - this.height = args.height; - } - } - - var _this = this; - /** Widgets **/ - this.abinitioGrid = new AbinitioGrid({ - width : null, - height : 200 - }); - - this.abinitioGrid.onSelected.attach(function(sender, models) { - var modelsIdList = []; - for ( var i in models) { - modelsIdList.push(models[i].modelId); - } - _this._renderPlot(_this.subtractions[0].subtractionId, modelsIdList); - _this._renderPDB(modelsIdList); - }); - - /** Dygraph Widget that plots fir files**/ - this.plotWidget = new PlotWidget({ - width : 745 / 2, - height : 300, - margin : "10 0 5 10" - }); - - /** PDB viewer **/ - this.viewer = new PDBViewer({ - width : 745 / 2, - height : 300 - }); - -} - -AbinitioForm.prototype._renderPlot = function(subtractionId, modelsIdList) { - /** Trying to plot tje subtraction and the models **/ - try { - var colors = [ "#669900", "#0000FF" ]; - this.plotWidget.refresh([], [ ], modelsIdList, [], [], colors); - } catch (e) { - console.log(e); - } -}; - -AbinitioForm.prototype._renderPDB = function(modelsIdList) { - /** Trying to plot the PDB file **/ - try { - var viz = []; - for (var i = 0; i < modelsIdList.length; i++) { - viz.push({ - modelId : modelsIdList[i], - color : "0xFF6600", - opacity : 0.8 - }); - } - this.viewer.refresh(viz); - } catch (e) { - console.log(e); - } -}; - -AbinitioForm.prototype._getItems = function() { - var _this = this; - return [ { - xtype : 'label', - forId : 'myFieldId', - text : 'INLINE HELP: To be updated', - margin : '15 0 20 10', - cls : "inline-help" - }, this.abinitioGrid.getPanel(), { - xtype : 'container', - layout : 'hbox', - items : [ this.plotWidget.getPanel(), this.viewer.getPanel() ] - } ] -}; - -AbinitioForm.prototype._getButtons = function() { - return []; -}; - -AbinitioForm.prototype.getPanel = function() { - var _this = this; - this.panel = Ext.create('Ext.form.Panel', { - width : this.width, - height : this.height, - margin : 10, - border : 1, - defaultType : 'textfield', - items : this._getItems(), - buttons : this._getButtons(), - listeners : { - afterrender : function() { - _this._populate(); - - } - } - }); - return this.panel; -}; - -/** Populates could be call when the DOM is not filled yet **/ -AbinitioForm.prototype._populate = function() { -}; - -/** It populates the form * */ -AbinitioForm.prototype.refresh = function(subtractions) { - this.subtractions = subtractions; - this.abinitioGrid.refresh(subtractions); -}; - -AbinitioForm.prototype.input = function() { - return {}; -}; - -/** It populates the form **/ -AbinitioForm.prototype.test = function(targetId) { - var macromoleculeForm = new AbinitioForm(); - var panel = macromoleculeForm.getPanel(); - panel.render(targetId); -}; +/** + * Example form + * + * @witdh + * @height + */ +function AbinitioForm(args) { + this.id = BUI.id(); + this.width = null; + this.height = null; -/** - * Example form - * - * @witdh - * @height - */ -function DataReductionForm(args) { - this.id = BUI.id(); - this.width = null; - this.height = null; - - if (args != null) { - if (args.width != null) { - this.width = args.width; - } - if (args.height != null) { - this.height = args.height; - } - } - + if (args != null) { + if (args.width != null) { + this.width = args.width; + } + if (args.height != null) { + this.height = args.height; + } + } + + var _this = this; + /** Widgets **/ + this.abinitioGrid = new AbinitioGrid({ + width : null, + height : 200 + }); + + this.abinitioGrid.onSelected.attach(function(sender, models) { + var modelsIdList = []; + for ( var i in models) { + modelsIdList.push(models[i].modelId); + } + _this._renderPlot(_this.subtractions[0].subtractionId, modelsIdList); + _this._renderPDB(modelsIdList); + }); + + /** Dygraph Widget that plots fir files**/ + this.plotWidget = new PlotWidget({ + width : 745 / 2, + height : 300, + margin : "10 0 5 10" + }); + + /** PDB viewer **/ + this.viewer = new PDBViewer({ + width : 745 / 2, + height : 300 + }); + +} + +AbinitioForm.prototype._renderPlot = function(subtractionId, modelsIdList) { + /** Trying to plot tje subtraction and the models **/ + try { + var colors = [ "#669900", "#0000FF" ]; + this.plotWidget.refresh([], [ ], modelsIdList, [], [], colors); + } catch (e) { + console.log(e); + } +}; + +AbinitioForm.prototype._renderPDB = function(modelsIdList) { + /** Trying to plot the PDB file **/ + try { + var viz = []; + for (var i = 0; i < modelsIdList.length; i++) { + viz.push({ + modelId : modelsIdList[i], + color : "0xFF6600", + opacity : 0.8 + }); + } + this.viewer.refresh(viz); + } catch (e) { + console.log(e); + } +}; + +AbinitioForm.prototype._getItems = function() { + var _this = this; + return [ { + xtype : 'label', + forId : 'myFieldId', + text : 'INLINE HELP: To be updated', + margin : '15 0 20 10', + cls : "inline-help" + }, this.abinitioGrid.getPanel(), { + xtype : 'container', + layout : 'hbox', + items : [ this.plotWidget.getPanel(), this.viewer.getPanel() ] + } ] +}; + +AbinitioForm.prototype._getButtons = function() { + return []; +}; + +AbinitioForm.prototype.getPanel = function() { + var _this = this; + this.panel = Ext.create('Ext.form.Panel', { + width : this.width, + height : this.height, + margin : 10, + border : 1, + defaultType : 'textfield', + items : this._getItems(), + buttons : this._getButtons(), + listeners : { + afterrender : function() { + _this._populate(); + + } + } + }); + return this.panel; +}; + +/** Populates could be call when the DOM is not filled yet **/ +AbinitioForm.prototype._populate = function() { +}; + +/** It populates the form * */ +AbinitioForm.prototype.refresh = function(subtractions) { + this.subtractions = subtractions; + this.abinitioGrid.refresh(subtractions); +}; + +AbinitioForm.prototype.input = function() { + return {}; +}; + +/** It populates the form **/ +AbinitioForm.prototype.test = function(targetId) { + var macromoleculeForm = new AbinitioForm(); + var panel = macromoleculeForm.getPanel(); + panel.render(targetId); +}; + +/** + * Example form + * + * @witdh + * @height + */ +function DataReductionForm(args) { + this.id = BUI.id(); + this.width = null; + this.height = null; + + if (args != null) { + if (args.width != null) { + this.width = args.width; + } + if (args.height != null) { + this.height = args.height; + } + } + + var _this = this; + + /** Widgets **/ + this.plotWidget = new PlotWidget({ + width : 650, + height : 490 + }); + + /** Selected frames to be displayed **/ + this.selectedItems = { + frames : [], + averages : [], + subtractions : [] + }; + +} + +DataReductionForm.prototype._parseSelectedItemsToIds = function(selectedArray) { + var ids = []; + if (selectedArray != null) { + for (var i = 0; i < selectedArray.length; i++) { + ids.push(selectedArray[i].id); + } + } + return ids; +}; + +DataReductionForm.prototype.onSelectionChanged = function(columnName, selected) { + if (selected != null) { + if (columnName == "Frames") { + this.selectedItems.frames = selected; + this.selectedItems.subtractions = []; + } + if (columnName == "Averages") { + this.selectedItems.averages = selected; + } + if (columnName == "Subtractions") { + this.selectedItems.frames = []; + this.selectedItems.subtractions = selected; + } + } + this.plotWidget.refresh(this._parseSelectedItemsToIds(this.selectedItems.frames), this._parseSelectedItemsToIds(this.selectedItems.subtractions)); +}; + +DataReductionForm.prototype._getFramesExtPanel = function(store, columnName, height) { + var _this = this; + + var selModel = Ext.create('Ext.selection.RowModel', { + allowDeselect : true, + mode : 'MULTI', + listeners : { + selectionchange : function(sm, selections) { + var selected = []; + for (var i = 0; i < selections.length; i++) { + selected.push(selections[i].raw); + } + _this.onSelectionChanged(columnName, selected); + } + } + }); + + return Ext.create('Ext.grid.Panel', { + store : store, + margin : 10, + height : height, + width : 200, + selModel : selModel, + columns : [ { + text : columnName, + dataIndex : 'fileName', + flex : 1 + } ], + viewConfig : { + } + }); +}; + +DataReductionForm.prototype._getFramesPanel = function() { + var fields = [ 'fileName', 'type', 'id' ]; + + this.framesStore = Ext.create('Ext.data.Store', { + fields : fields, + sorters : 'fileName' + }); + + this.averagesStore = Ext.create('Ext.data.Store', { + fields : fields + }); + + this.subtractionStore = Ext.create('Ext.data.Store', { + fields : fields + }); + + var gridFrames = this._getFramesExtPanel(this.framesStore, "Frames", 375); + var gridAvgs = this._getFramesExtPanel(this.averagesStore, "Averages", 125); + var subtractionAvgs = this._getFramesExtPanel(this.subtractionStore, "Subtractions", 75); + + return { + xtype : 'container', + layout : 'vbox', + items : [ gridFrames, subtractionAvgs ] + }; +}; + +DataReductionForm.prototype._getImageContainer = function(name, help) { + var html = "
" + name + "
" + return { + xtype : 'container', + layout : 'vbox', + items : [ { + html : html, + margin : "5 0 0 0", + height : 95, + width : 100 + }, { + xtype : 'label', + forId : 'myFieldId', + text : help, + margin : '5 0 0 0', + cls : "inline-help" + } ] + } +}; + +DataReductionForm.prototype._getItems = function() { + var _this = this; + return [ { + xtype : 'container', + layout : 'hbox', + items : [ + this._getFramesPanel(), + this.plotWidget.getPanel(), + { + xtype : 'panel', + width : 110, + frame : true, + margin : "10 5 5 5", + border : 0, + layout : 'vbox', + items : [ this._getImageContainer("scattering", "Scattering"), this._getImageContainer("guinier", "Guinier Region"), + this._getImageContainer("kratky", "Kratky Plot"), this._getImageContainer("gnom", "P(r) distribution") ] + } ] + } ] +}; + +DataReductionForm.prototype._getButtons = function() { + return []; +}; + +DataReductionForm.prototype.getPanel = function() { + var _this = this; + this.panel = Ext.create('Ext.form.Panel', { + width : this.width, + height : this.height, + border : 0, + items : this._getItems(), + buttons : this._getButtons(), + listeners : { + afterrender : function() { + _this._populate(); + } + } + }); + return this.panel; +}; + +/** Populates could be call when the DOM is not filled yet **/ +DataReductionForm.prototype._populate = function() { +}; + +/** It populates the form * */ +DataReductionForm.prototype.refresh = function(subtractions) { + if (subtractions != null) { + for (var i = 0; i < subtractions.length; i++) { + /** Loading frame grids **/ + var subtraction = subtractions[i]; + var averages = [ { + fileName : BUI.getFileName(subtraction.bufferAverageFilePath), + type : 'bufferAvg', + id : subtraction.subtractionId + }, { + fileName : BUI.getFileName(subtraction.sampleAverageFilePath), + type : 'sampleAvg', + id : subtraction.subtractionId + } + + ]; + this.averagesStore.loadData(averages, true); + this.subtractionStore.loadData([ { + fileName : BUI.getFileName(subtraction.substractedFilePath), + type : 'SUBTRACTION', + id : subtraction.subtractionId + } ], true); + + var frames = []; + /** Buffers **/ + if (subtraction.bufferOneDimensionalFiles != null) { + if (subtraction.bufferOneDimensionalFiles.frametolist3VOs != null) { + for (var j = 0; j < subtraction.bufferOneDimensionalFiles.frametolist3VOs.length; j++) { + var frametolist3VO = subtraction.bufferOneDimensionalFiles.frametolist3VOs[j]; + if (frametolist3VO.frame3VO != null) { + frames.push({ + fileName : BUI.getFileName(frametolist3VO.frame3VO.filePath), + type : 'BUFFER', + id : frametolist3VO.frame3VO.frameId + }); + } + } + } + } + /** Samples **/ + if (subtraction.sampleOneDimensionalFiles != null) { + if (subtraction.sampleOneDimensionalFiles.frametolist3VOs != null) { + for (var j = 0; j < subtraction.sampleOneDimensionalFiles.frametolist3VOs.length; j++) { + var frametolist3VO = subtraction.sampleOneDimensionalFiles.frametolist3VOs[j]; + if (frametolist3VO.frame3VO != null) { + frames.push({ + fileName : BUI.getFileName(frametolist3VO.frame3VO.filePath), + type : 'SAMPLE', + id : frametolist3VO.frame3VO.frameId + }); + } + } + } + } + + this.framesStore.loadData(frames, true); + + /** Loading images **/ + this._displayImage("scattering", subtraction.subtractionId); + this._displayImage("kratky", subtraction.subtractionId); + this._displayImage("guinier", subtraction.subtractionId); + this._displayImage("gnom", subtraction.subtractionId); + } + } +}; + +DataReductionForm.prototype._displayImage = function(name, subtractionId) { + var url = BUI.getURL() + '&type=' + name + '&subtractionId=' + subtractionId; + var event = "OnClick= window.open('" + url + "')"; + document.getElementById(this.id + "_" + name).innerHTML = ''; +}; + +DataReductionForm.prototype.input = function() { + return {}; +}; + +/** It populates the form **/ +DataReductionForm.prototype.test = function(targetId) { + var macromoleculeForm = new DataReductionForm(); + var panel = macromoleculeForm.getPanel(); + panel.render(targetId); +}; + +function PlotWidget(args) { + this.width = 600; + this.height = 600; + this.id = BUI.id(); + + this.linear = false; + + this.margin = "10 0 0 0"; + /** for each stat the max and minimum value when it is scaled in order to show correctly in the legend **/ + this.ranges = {}; + if (args != null) { + if (args.width != null) { + this.width = args.width; + } + if (args.height != null) { + this.height = args.height; + } + if (args.margin != null) { + this.margin = args.margin; + } + } + +} + +PlotWidget.prototype.getMenu = function() { + var _this = this; + /** Actions buttons **/ + var actions = []; + actions.push("->"); + actions.push({ + text : "Export as Image", + scope : this, + icon : '../images/save.gif', + handler : function(item, pressed) { + var largeImage = document.createElement("img"); + largeImage.style.display = 'block'; + largeImage.style.width = 200 + "px"; + largeImage.style.height = 200 + "px"; + largeImage.setAttribute('src', Dygraph.Export.asCanvas(this.dygraphObject.dygraph).toDataURL()); + window.open(Dygraph.Export.asCanvas(this.dygraphObject.dygraph).toDataURL(), 'Image', ''); + } + }); + + return actions; +}; + +/** Looks for the maximum value and then divide everything but that value **/ +PlotWidget.prototype.scaledData = function(data) { + for (var i = 0; i < data.length; i++) { + var values = this.getMaxAndMinValue(data[i]); + data[i] = this.divideValuesByMax(data[i], values.max); + this.ranges[data[i].label] = values; + } + return data; +}; + +/** Given a stat float[] and a max number it will divide each value by max **/ +PlotWidget.prototype.divideValuesByMax = function(stat, max) { + for (var j = 0; j < stat.data.length; j++) { + if (max != 0) { + stat.data[j] = Number(stat.data[j]) / max; + stat.std[j] = Number(stat.std[j]) / max; + } + } + return stat; +}; + +/** returns max value of a stat **/ +PlotWidget.prototype.getMaxAndMinValue = function(stat) { + var max = 0; + var min = stat.data[0]; + for (var j = 0; j < stat.data.length; j++) { + if (Number(stat.data[j]) > max) { + max = Number(stat.data[j]); + } + if (Number(stat.std[j]) > max) { + max = Number(stat.std[j]); + } + if (Number(stat.data[j]) < min) { + min = Number(stat.data[j]); + } + } + return { + max : Number(max), + min : Number(min) + }; +}; + +PlotWidget.prototype._renderDygraph = function(parsed, colors, labels) { + this.dygraphObject = new StdDevDyGraph(this.id, { + width : this.width - 20, + height : this.height - 40, + xlabel : "", + }); + + this.dygraphObject.draw(parsed, colors, labels); + +}; + +PlotWidget.prototype.getPanel = function() { + this.panel = Ext.create('Ext.panel.Panel', { + width : this.width, + height : this.height, + margin : this.margin, + tbar : this.getMenu(), + items : [ { + html : "", + id : this.id, + width : this.width, + height : this.height, + padding : 10, + margin : "0 0 0 -30", + border : 0 + } ] + }); + + return this.panel; +}; + +PlotWidget.prototype.getPoint = function(y, error) { + var minus = y - error; + var max = y + error; + + if (this.linear) { + return [ Math.abs(minus), y, Math.abs(max) ]; + } + if ((minus != 0) && (max != 0)) { + return [ Math.log(Math.abs(minus)), Math.log(y), Math.log(Math.abs(max)) ]; + } else { + return [ Math.log(y), Math.log(y), Math.log(y) ]; + } + +}; + +PlotWidget.prototype.getDataPlot = function(frames, subtractions, models, fits, rbms) { + var files = []; + var labels = [ "Intensity" ]; + if (frames != null) { + for (var i = 0; i < frames.length; i++) { + files.push(frames[i].data); + labels.push(frames[i].fileName); + } + } + function splitData(data, column, errorColumn, name){ + var result = [] + for (var j = 0; j < data.length; j++) { + console.log(data[j][column]); + result.push([j,data[j][column],data[j][errorColumn]]);//[0, data[i][column],0]]); + } + files.push(result); + labels.push(name); + } + + if (subtractions != null) { + for (var i = 0; i < subtractions.length; i++) { + /** For subtraction **/ + files.push(subtractions[i].subtraction.data); + labels.push(subtractions[i].subtraction.fileName); + /** For sample average **/ +// files.push(subtractions[i].sampleAvg.data); +// labels.push(subtractions[i].sampleAvg.fileName); + /** For buffer average **/ +// files.push(subtractions[i].bufferAvg.data); +// labels.push(subtractions[i].bufferAvg.fileName); + } + } + + if (models != null) { + for (var i = 0; i < models.length; i++) { + for ( var key in models[i]) { + splitData(models[i][key].fir.data, 1, 2, "Intensity"); + splitData(models[i][key].fir.data, 3, 3, "Fit"); + } + } + } + + if (fits != null) { + for (var i = 0; i < fits.length; i++) { + for ( var key in fits[i]) { + + /** adding fit file to be plotted **/ + if (fits[i][key].fit.data[0].length == 3){ + splitData(fits[i][key].fit.data, 1, 1, "Intensity"); + splitData(fits[i][key].fit.data, 2, 2, "Fit"); + } + + /** s, Iexp(s), err, Ifit(s). **/ + if (fits[i][key].fit.data[0].length == 4){ + splitData(fits[i][key].fit.data, 1, 2, "Intensity"); + splitData(fits[i][key].fit.data, 3, 3, "Fit"); + } + + if (fits[i][key].fit.data[0].length == 5){ + /** X Intensity Fit Error Residues **/ + + splitData(fits[i][key].fit.data, 1, 3, "Intensity"); + splitData(fits[i][key].fit.data, 2, 2, "Fit"); + } + } + } + } + + var dataPoints = []; + if (files.length > 0) { + for (var i = 0; i < files[0].length; i++) { + dataPoints.push([ files[0][i][0], this.getPoint(files[0][i][1], files[0][i][2]) ]); + } + if (files.length > 1) { + for (var i = 1; i < files.length; i++) { + for (var j = 0; j < dataPoints.length; j++) { + if (files[i][j] != null){ + dataPoints[j].push(this.getPoint(files[i][j][1], files[i][j][2])); + } + else{ + dataPoints[j].push([0,0,0]); + } + } + } + } + } + + return { + dataPoints : dataPoints, + labels : labels + } +}; + +PlotWidget.prototype.refresh = function(frames, subtractions, models, fits, rbms, colors) { + + var _this = this; + this.panel.setLoading("Reading Files"); + + var dataAdapter = new BiosaxsDataAdapter(); + dataAdapter.onSuccess.attach(function(sender, data) { + _this.panel.setLoading("Rendering"); + var parsed = _this.getDataPlot(data.frames, data.subtractions, data.models, data.fits, rbms); + _this._renderDygraph(parsed.dataPoints, colors, parsed.labels); + _this.panel.setLoading(false); + }); + dataAdapter.onError.attach(function(sender, data) { + _this.panel.setLoading(false); + }); + dataAdapter.getDataPlot(frames, subtractions, models, fits, rbms); +}; + +PlotWidget.prototype.input = function() { + return DATADOC.getHPLCData(); +}; + + +/** + * Fit form + * + * @witdh + * @height + */ +function FitForm(args) { + this.id = BUI.id(); + this.width = null; + this.height = null; + + if (args != null) { + if (args.width != null) { + this.width = args.width; + } + if (args.height != null) { + this.height = args.height; + } + } + + var _this = this; + + /** Widgets **/ + this.fitGrid = new FitStructureToExperimentDataGrid({ + width : null, + height : 200 + }); + + this.fitGrid.onSelected.attach(function(sender, fits) { + var modelsIdList = []; + for ( var i in fits) { + modelsIdList.push(fits[i].fitStructureToExperimentalDataId); + } + _this._renderPlot(_this.subtractions[0].subtractionId, modelsIdList); +// var adapter = new BiosaxsDataAdapter(); +// adapter.onSuccess.attach(function(sender, data) { +// +// }); +// adapter.getScatteringCurveByFrameIdsList([], [], [], ids); + }); + + + /** Dygraph Widget that plots fir files**/ + this.plotWidget = new PlotWidget({ + width : 745, + height : 300, + margin : "10 0 10 10" + }); +} + +FitForm.prototype._renderPlot = function(subtractionId, modelsIdList) { + /** Trying to plot tje subtraction and the models **/ + try { + var colors = [ "#669900", "#0000FF" ]; + + this.plotWidget.refresh([], [ ], [], modelsIdList, [], colors); + } catch (e) { + console.log(e); + } +}; + +FitForm.prototype._getItems = function() { + var _this = this; + return [ { + xtype : 'label', + forId : 'myFieldId', + text : 'INLINE HELP: To be updated', + margin : '15 0 20 10', + cls : "inline-help" + }, this.fitGrid.getPanel(), this.plotWidget.getPanel() ] + +}; + +FitForm.prototype._getButtons = function() { + return []; +}; + +FitForm.prototype.getPanel = function() { + var _this = this; + this.panel = Ext.create('Ext.form.Panel', { + width : this.width, + height : this.height, + margin : 10, + border : 1, + defaultType : 'textfield', + items : this._getItems(), + buttons : this._getButtons(), + listeners : { + afterrender : function() { + _this._populate(); + + } + } + }); + return this.panel; +}; + +/** Populates could be call when the DOM is not filled yet **/ +FitForm.prototype._populate = function() { +}; + +/** It populates the form * */ +FitForm.prototype.refresh = function(subtractions) { + this.subtractions = subtractions; + this.fitGrid.refresh(subtractions); +}; + +FitForm.prototype.input = function() { + return {}; +}; + +/** It populates the form **/ +FitForm.prototype.test = function(targetId) { + var macromoleculeForm = new FitForm(); + var panel = macromoleculeForm.getPanel(); + panel.render(targetId); +}; + +/** + * Example form + * + * @witdh + * @height + */ +function RigidBodyModelingResultForm(args) { + this.id = BUI.id(); + this.width = null; + this.height = null; + + if (args != null) { + if (args.width != null) { + this.width = args.width; + } + if (args.height != null) { + this.height = args.height; + } + } + + var _this = this; + /** Widgets **/ + this.rigidModelGrid = new RigidModelGrid({ + width : null, + height : 200 + }); + + this.rigidModelGrid.onSelected.attach(function(sender, fits){ + var ids = []; + for ( var i in fits) { + ids.push(fits[i].fitStructureToExperimentalDataId); + } + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, data){ +// debugger + + }); + + adapter.getScatteringCurveByFrameIdsList([], [], [], ids); + }); + +} + +RigidBodyModelingResultForm.prototype._getItems = function() { + var _this = this; + return [ { + xtype : 'label', + forId : 'myFieldId', + text : 'INLINE HELP: To be updated', + margin : '15 0 20 10', + cls : "inline-help" + }, this.rigidModelGrid.getPanel() ] + +}; + +RigidBodyModelingResultForm.prototype._getButtons = function() { + return []; +}; + +RigidBodyModelingResultForm.prototype.getPanel = function() { + var _this = this; + this.panel = Ext.create('Ext.form.Panel', { + width : this.width, + height : this.height, + margin : 10, + border : 1, + defaultType : 'textfield', + items : this._getItems(), + buttons : this._getButtons(), + listeners : { + afterrender : function() { + _this._populate(); + + } + } + }); + return this.panel; +}; + +/** Populates could be call when the DOM is not filled yet **/ +RigidBodyModelingResultForm.prototype._populate = function() { +}; + +/** It populates the form * */ +RigidBodyModelingResultForm.prototype.refresh = function(subtractions) { + this.rigidModelGrid.refresh(subtractions); +}; + +RigidBodyModelingResultForm.prototype.input = function() { + return {}; +}; + +/** It populates the form **/ +RigidBodyModelingResultForm.prototype.test = function(targetId) { + var macromoleculeForm = new RigidBodyModelingResultForm(); + var panel = macromoleculeForm.getPanel(); + panel.render(targetId); +}; + +/** + * Example form + * + * @witdh + * @height + */ +function SuperpositionForm(args) { + this.id = BUI.id(); + this.width = null; + this.height = null; + + if (args != null) { + if (args.width != null) { + this.width = args.width; + } + if (args.height != null) { + this.height = args.height; + } + } + + var _this = this; + + /** Widgets **/ + this.superpositionGrid = new SuperpositionGrid({ + width : null, + height : 200 + }); + + this.superpositionGrid.onSelected.attach(function(sender, superpositions) { + var ids = []; + for ( var i in superpositions) { + ids.push(superpositions[i].superpositionId); + } +// _this._renderAbinitio(ids); +// _this.aprioriPDBViewer.refresh(); + _this._renderAligned(ids); + // getAlignedPDBContentBySuperpositionList + }); + + /** PDB viewer **/ +// this.abinitioPDBViewer = new PDBViewer({ +// width : 860 / 2, +// height : 300 / 2, +// margin : 0 +// }); +// +// /** PDB viewer **/ +// this.aprioriPDBViewer = new StructurePDBViewer({ +// width : 860 / 2, +// height : 300 / 2, +// margin : 0 +// }); + + this.alignedPDBViewer = new AlignedSuperpositionPDBViewer({ + width : 860 , + height : 300 + }); + +} + +SuperpositionForm.prototype._renderAligned = function(modelsIdList) { + /** Trying to plot the PDB file **/ + try { + this.alignedPDBViewer.refresh(modelsIdList); + } catch (e) { + console.log(e); + } +}; + +SuperpositionForm.prototype._renderAbinitio = function(modelsIdList) { + /** Trying to plot the PDB file **/ + try { + var viz = []; + for (var i = 0; i < modelsIdList.length; i++) { + viz.push({ + modelId : modelsIdList[i], + color : "0xFF6600", + opacity : 0.8 + }); + } + this.abinitioPDBViewer.refresh(viz); + } catch (e) { + console.log(e); + } +}; + +SuperpositionForm.prototype._getItems = function() { + var _this = this; + return [ { + xtype : 'label', + forId : 'myFieldId', + text : 'INLINE HELP: To be updated', + margin : '15 0 20 10', + cls : "inline-help" + }, this.superpositionGrid.getPanel(), { + xtype : 'container', + layout : 'hbox', + items : [ +// { +// xtype : 'container', +// layout : 'vbox', +// items : [ this.abinitioPDBViewer.getPanel(), this.aprioriPDBViewer.getPanel() +// +// ] +// }, + + this.alignedPDBViewer.getPanel() ] + } ] + +}; + +SuperpositionForm.prototype._getButtons = function() { + return []; +}; + +SuperpositionForm.prototype.getPanel = function() { + var _this = this; + this.panel = Ext.create('Ext.form.Panel', { + width : this.width, + height : this.height, + margin : 10, + border : 1, + defaultType : 'textfield', + items : this._getItems(), + buttons : this._getButtons(), + listeners : { + afterrender : function() { + _this._populate(); + + } + } + }); + return this.panel; +}; + +/** Populates could be call when the DOM is not filled yet **/ +SuperpositionForm.prototype._populate = function() { +}; + +/** It populates the form * */ +SuperpositionForm.prototype.refresh = function(subtractions) { + this.subtractions = subtractions; + this.superpositionGrid.refresh(subtractions); +}; + +SuperpositionForm.prototype.input = function() { + return {}; +}; + +/** It populates the form **/ +SuperpositionForm.prototype.test = function(targetId) { + var macromoleculeForm = new SuperpositionForm(); + var panel = macromoleculeForm.getPanel(); + panel.render(targetId); +}; + +/** + * Example form + * + * @witdh + * @height + */ +function AssemblyForm(args) { + this.id = BUI.id(); + this.width = 700; + this.height = 500; + + if (args != null) { + if (args.width != null) { + this.width = args.width; + } + if (args.height != null) { + this.height = args.height; + } + } + + this.molarityGrid = new MolarityGrid({height : this.height - 50}); +} + +AssemblyForm.prototype._getItems = function() { + return [ { + xtype : 'label', + forId : 'myFieldId', + text : 'List of previously defined macromolecules present in the assembly. This information will be used for additional cross-checks where possible', + margin : '15 0 20 10', + cls : "inline-help" + }, this.molarityGrid.getPanel() ]; +}; + +AssemblyForm.prototype._getButtons = function() { + return []; +}; + +AssemblyForm.prototype.getPanel = function() { + this.panel = Ext.create('Ext.form.Panel', { + width : this.width, + height : this.height, + margin : 10, + border : 0, + defaultType : 'textfield', + items : this._getItems(), + buttons : this._getButtons() + }); + return this.panel; +}; + + +/** It populates the form **/ +AssemblyForm.prototype.refresh = function(macromolecule) { + this.macromolecule = macromolecule; + this.molarityGrid.refresh(macromolecule); +}; + +AssemblyForm.prototype.input = function() { + return {}; +}; + + +AssemblyForm.prototype.test = function(targetId) { + var assemblyForm = new AssemblyForm(); + + var panel = assemblyForm.getPanel(); + panel.render(targetId); +}; +/** + * Edit the information of a buffer + * + * #onSaved + * #onRemoveAdditive + */ +function BufferForm() { var _this = this; - /** Widgets **/ - this.plotWidget = new PlotWidget({ - width : 650, - height : 490 + this.additiveGrid = new AdditiveGrid(); + this.additiveGrid.onRemoveButtonClicked.attach(function(sender, args) { + _this.onRemoveAdditive.notify(args); }); - /** Selected frames to be displayed **/ - this.selectedItems = { - frames : [], - averages : [], - subtractions : [] - }; - + this.onSaved = new Event(this); + this.onRemoveAdditive = new Event(this); } -DataReductionForm.prototype._parseSelectedItemsToIds = function(selectedArray) { - var ids = []; - if (selectedArray != null) { - for (var i = 0; i < selectedArray.length; i++) { - ids.push(selectedArray[i].id); - } - } - return ids; -}; - -DataReductionForm.prototype.onSelectionChanged = function(columnName, selected) { - if (selected != null) { - if (columnName == "Frames") { - this.selectedItems.frames = selected; - this.selectedItems.subtractions = []; - } - if (columnName == "Averages") { - this.selectedItems.averages = selected; - } - if (columnName == "Subtractions") { - this.selectedItems.frames = []; - this.selectedItems.subtractions = selected; - } - } - this.plotWidget.refresh(this._parseSelectedItemsToIds(this.selectedItems.frames), this._parseSelectedItemsToIds(this.selectedItems.subtractions)); -}; - -DataReductionForm.prototype._getFramesExtPanel = function(store, columnName, height) { - var _this = this; - - var selModel = Ext.create('Ext.selection.RowModel', { - allowDeselect : true, - mode : 'MULTI', - listeners : { - selectionchange : function(sm, selections) { - var selected = []; - for (var i = 0; i < selections.length; i++) { - selected.push(selections[i].raw); - } - _this.onSelectionChanged(columnName, selected); - } - } - }); - - return Ext.create('Ext.grid.Panel', { - store : store, - margin : 10, - height : height, - width : 200, - selModel : selModel, - columns : [ { - text : columnName, - dataIndex : 'fileName', - flex : 1 - } ], - viewConfig : { - } - }); +BufferForm.prototype.getBuffer = function() { + this.buffer.name = Ext.getCmp("buffer_name").getValue(); + this.buffer.acronym = Ext.getCmp("buffer_acronym").getValue(); + this.buffer.comments = Ext.getCmp("buffer_comments").getValue(); + this.buffer.ph = Ext.getCmp("buffer_ph").getValue(); + this.buffer.composition = Ext.getCmp("buffer_composition").getValue(); + this.buffer.bufferhasadditive3VOs = this.additiveGrid.getAdditives(); + return this.buffer; }; -DataReductionForm.prototype._getFramesPanel = function() { - var fields = [ 'fileName', 'type', 'id' ]; - - this.framesStore = Ext.create('Ext.data.Store', { - fields : fields, - sorters : 'fileName' - }); - - this.averagesStore = Ext.create('Ext.data.Store', { - fields : fields - }); - - this.subtractionStore = Ext.create('Ext.data.Store', { - fields : fields - }); - - var gridFrames = this._getFramesExtPanel(this.framesStore, "Frames", 375); - var gridAvgs = this._getFramesExtPanel(this.averagesStore, "Averages", 125); - var subtractionAvgs = this._getFramesExtPanel(this.subtractionStore, "Subtractions", 75); - - return { - xtype : 'container', - layout : 'vbox', - items : [ gridFrames, subtractionAvgs ] - }; -}; - -DataReductionForm.prototype._getImageContainer = function(name, help) { - var html = "
" + name + "
" +BufferForm.prototype._getTopPanel = function() { return { xtype : 'container', - layout : 'vbox', + layout : 'hbox', + border : 0, + frame : true, items : [ { - html : html, - margin : "5 0 0 0", - height : 95, - width : 100 + xtype : 'container', + layout : 'hbox', + items : [ { + xtype : 'container', + flex : 1, + border : false, + layout : 'anchor', + defaultType : 'textfield', + items : [ { + xtype : 'requiredtext', + id : 'buffer_name', + fieldLabel : 'Name', + name : 'name', + width : '200px', + value : this.buffer.name + }, { + xtype : 'requiredtext', + id : 'buffer_acronym', + fieldLabel : 'Acronym', + name : 'acronym', + width : '200px', + value : this.buffer.acronym + } ] + } ] }, { - xtype : 'label', - forId : 'myFieldId', - text : help, - margin : '5 0 0 0', - cls : "inline-help" + xtype : 'container', + flex : 1, + layout : 'anchor', + defaultType : 'textfield', + margin : '0 0 0 10', + items : [ { + id : 'buffer_ph', + fieldLabel : 'pH', + name : 'ph', + value : this.buffer.ph, + xtype : 'numberfield', + width : 200, + minValue : 0, + maxValue : 15 + }, { + xtype : 'requiredtext', + id : 'buffer_composition', + fieldLabel : 'Composition', + name : 'composition', + width : 200, + value : this.buffer.composition + } ] } ] - } + }; }; -DataReductionForm.prototype._getItems = function() { - var _this = this; - return [ { +BufferForm.prototype.getPanel = function(buffer) { + this.buffer = buffer; + this.panel = Ext.createWidget({ xtype : 'container', - layout : 'hbox', - items : [ - this._getFramesPanel(), - this.plotWidget.getPanel(), - { - xtype : 'panel', - width : 110, - frame : true, - margin : "10 5 5 5", - border : 0, - layout : 'vbox', - items : [ this._getImageContainer("scattering", "Scattering"), this._getImageContainer("guinier", "Guinier Region"), - this._getImageContainer("kratky", "Kratky Plot"), this._getImageContainer("gnom", "P(r) distribution") ] - } ] - } ] -}; - -DataReductionForm.prototype._getButtons = function() { - return []; -}; + layout : 'vbox', + style : { + padding : '10px' + }, + fieldDefaults : { + labelAlign : 'left', + labelWidth : 50 -DataReductionForm.prototype.getPanel = function() { - var _this = this; - this.panel = Ext.create('Ext.form.Panel', { - width : this.width, - height : this.height, - border : 0, - items : this._getItems(), - buttons : this._getButtons(), - listeners : { - afterrender : function() { - _this._populate(); - } - } + }, + items : [ this._getTopPanel(), { + id : 'buffer_comments', + xtype : 'textareafield', + name : 'comments', + fieldLabel : 'Comments', + width : '100%', + value : buffer.comments + }, this.additiveGrid.getPanel(buffer) ] }); return this.panel; }; -/** Populates could be call when the DOM is not filled yet **/ -DataReductionForm.prototype._populate = function() { -}; - -/** It populates the form * */ -DataReductionForm.prototype.refresh = function(subtractions) { - if (subtractions != null) { - for (var i = 0; i < subtractions.length; i++) { - /** Loading frame grids **/ - var subtraction = subtractions[i]; - var averages = [ { - fileName : BUI.getFileName(subtraction.bufferAverageFilePath), - type : 'bufferAvg', - id : subtraction.subtractionId - }, { - fileName : BUI.getFileName(subtraction.sampleAverageFilePath), - type : 'sampleAvg', - id : subtraction.subtractionId - } - - ]; - this.averagesStore.loadData(averages, true); - this.subtractionStore.loadData([ { - fileName : BUI.getFileName(subtraction.substractedFilePath), - type : 'SUBTRACTION', - id : subtraction.subtractionId - } ], true); - - var frames = []; - /** Buffers **/ - if (subtraction.bufferOneDimensionalFiles != null) { - if (subtraction.bufferOneDimensionalFiles.frametolist3VOs != null) { - for (var j = 0; j < subtraction.bufferOneDimensionalFiles.frametolist3VOs.length; j++) { - var frametolist3VO = subtraction.bufferOneDimensionalFiles.frametolist3VOs[j]; - if (frametolist3VO.frame3VO != null) { - frames.push({ - fileName : BUI.getFileName(frametolist3VO.frame3VO.filePath), - type : 'BUFFER', - id : frametolist3VO.frame3VO.frameId - }); - } - } - } - } - /** Samples **/ - if (subtraction.sampleOneDimensionalFiles != null) { - if (subtraction.sampleOneDimensionalFiles.frametolist3VOs != null) { - for (var j = 0; j < subtraction.sampleOneDimensionalFiles.frametolist3VOs.length; j++) { - var frametolist3VO = subtraction.sampleOneDimensionalFiles.frametolist3VOs[j]; - if (frametolist3VO.frame3VO != null) { - frames.push({ - fileName : BUI.getFileName(frametolist3VO.frame3VO.filePath), - type : 'SAMPLE', - id : frametolist3VO.frame3VO.frameId - }); - } - } - } - } - - this.framesStore.loadData(frames, true); - - /** Loading images **/ - this._displayImage("scattering", subtraction.subtractionId); - this._displayImage("kratky", subtraction.subtractionId); - this._displayImage("guinier", subtraction.subtractionId); - this._displayImage("gnom", subtraction.subtractionId); +BufferForm.prototype.input = function() { + return { + buffer : { + "bufferId" : 422, + "proposalId" : 10, + "safetyLevelId" : null, + "name" : "B1", + "acronym" : "B1", + "ph" : null, + "composition" : null, + "bufferhasadditive3VOs" : [], + "comments" : null } - } -}; - -DataReductionForm.prototype._displayImage = function(name, subtractionId) { - var url = BUI.getURL() + '&type=' + name + '&subtractionId=' + subtractionId; - var event = "OnClick= window.open('" + url + "')"; - document.getElementById(this.id + "_" + name).innerHTML = ''; -}; - -DataReductionForm.prototype.input = function() { - return {}; + }; }; -/** It populates the form **/ -DataReductionForm.prototype.test = function(targetId) { - var macromoleculeForm = new DataReductionForm(); - var panel = macromoleculeForm.getPanel(); +BufferForm.prototype.test = function(targetId) { + var bufferForm = new BufferForm(); + var panel = bufferForm.getPanel(bufferForm.input().buffer); panel.render(targetId); }; - -function PlotWidget(args) { - this.width = 600; - this.height = 600; - this.id = BUI.id(); - - this.linear = false; - - this.margin = "10 0 0 0"; - /** for each stat the max and minimum value when it is scaled in order to show correctly in the legend **/ - this.ranges = {}; + +/** + * @showTitle + * + * #onSaved + * #onAddPlates + * #onRemovePlates + **/ +function CaseForm(args) { + this.width = 700; + this.showTitle = true; if (args != null) { - if (args.width != null) { - this.width = args.width; - } - if (args.height != null) { - this.height = args.height; - } - if (args.margin != null) { - this.margin = args.margin; + if (args.showTitle != null) { + this.showTitle = args.showTitle; } } -} - -PlotWidget.prototype.getMenu = function() { var _this = this; - /** Actions buttons **/ - var actions = []; - actions.push("->"); - actions.push({ - text : "Export as Image", - scope : this, - icon : '../images/save.gif', - handler : function(item, pressed) { - var largeImage = document.createElement("img"); - largeImage.style.display = 'block'; - largeImage.style.width = 200 + "px"; - largeImage.style.height = 200 + "px"; - largeImage.setAttribute('src', Dygraph.Export.asCanvas(this.dygraphObject.dygraph).toDataURL()); - window.open(Dygraph.Export.asCanvas(this.dygraphObject.dygraph).toDataURL(), 'Image', ''); + this.stockSolutionGrid = new StockSolutionGrid({ + width : this.width - 10, + minHeight : 300, + height : 300, + tbar : true, + showTitle : true, + isPackedVisible : false, + btnAddExisting : true, + btnRemoveVisible : false, + btnUnpackVisible : true + }); + + /** When selecting existing stock solutions **/ + this.stockSolutionGrid.onStockSolutionSelected.attach(function(sender, stockSolutions) { + if (stockSolutions != null) { + for ( var i = 0; i < stockSolutions.length; i++) { + _this.saveStockSolution(stockSolutions[i]); + } } }); - return actions; -}; + /** it can be because it has been added a new one or removed **/ + this.stockSolutionGrid.onProposalChanged.attach(function(sender, stockSolution) { + if (stockSolution != null) { + _this.saveStockSolution(stockSolution); + } else { + BIOSAXS.proposal.onInitialized.attach(function() { + _this.stockSolutionGrid.refresh(BIOSAXS.proposal.getStockSolutionsByDewarId(_this.dewar.dewarId)); -/** Looks for the maximum value and then divide everything but that value **/ -PlotWidget.prototype.scaledData = function(data) { - for (var i = 0; i < data.length; i++) { - var values = this.getMaxAndMinValue(data[i]); - data[i] = this.divideValuesByMax(data[i], values.max); - this.ranges[data[i].label] = values; - } - return data; -}; + _this.stockSolutionGrid.grid.setLoading(false); + }); + BIOSAXS.proposal.init(); -/** Given a stat float[] and a max number it will divide each value by max **/ -PlotWidget.prototype.divideValuesByMax = function(stat, max) { - for (var j = 0; j < stat.data.length; j++) { - if (max != 0) { - stat.data[j] = Number(stat.data[j]) / max; - stat.std[j] = Number(stat.std[j]) / max; } - } - return stat; -}; -/** returns max value of a stat **/ -PlotWidget.prototype.getMaxAndMinValue = function(stat) { - var max = 0; - var min = stat.data[0]; - for (var j = 0; j < stat.data.length; j++) { - if (Number(stat.data[j]) > max) { - max = Number(stat.data[j]); - } - if (Number(stat.std[j]) > max) { - max = Number(stat.std[j]); - } - if (Number(stat.data[j]) < min) { - min = Number(stat.data[j]); - } - } - return { - max : Number(max), - min : Number(min) - }; -}; - -PlotWidget.prototype._renderDygraph = function(parsed, colors, labels) { - this.dygraphObject = new StdDevDyGraph(this.id, { - width : this.width - 20, - height : this.height - 40, - xlabel : "", }); - this.dygraphObject.draw(parsed, colors, labels); + this.onSaved = new Event(this); + this.onAddPlates = new Event(this); + this.onRemovePlates = new Event(this); +} +CaseForm.prototype.saveStockSolution = function(stockSolution) { + var _this = this; + var adapter = new BiosaxsDataAdapter(); + this.stockSolutionGrid.grid.setLoading("ISPyB: setting case to Stock solution"); + adapter.onSuccess.attach(function(sender, stockSolution) { + BIOSAXS.proposal.onInitialized.attach(function() { + _this.stockSolutionGrid.refresh(BIOSAXS.proposal.getStockSolutionsByDewarId(_this.dewar.dewarId)); + _this.stockSolutionGrid.grid.setLoading(false); + }); + BIOSAXS.proposal.init(); + }); + adapter.onError.attach(function(sender, data) { + _this.stockSolutionGrid.grid.setLoading(false); + BUI.showError(data); + }); + stockSolution.boxId = _this.dewar.dewarId; + adapter.saveStockSolution(stockSolution); }; -PlotWidget.prototype.getPanel = function() { - this.panel = Ext.create('Ext.panel.Panel', { - width : this.width, - height : this.height, - margin : this.margin, - tbar : this.getMenu(), - items : [ { - html : "", - id : this.id, - width : this.width, - height : this.height, - padding : 10, - margin : "0 0 0 -30", - border : 0 - } ] +CaseForm.prototype.fillStores = function() { + var _this = this; + this.panel.setLoading("Loading Labcontacts from database"); + + var proposal = BUI.getProposal(); + proposal.onDataRetrieved.attach(function(sender, data) { + _this.labContactForSendingStore.loadData(data, false); + _this.labContactForReturnStore.loadData(data, false); + _this.panel.setLoading(false); }); + proposal.getLabContactsByProposalId(); - return this.panel; }; -PlotWidget.prototype.getPoint = function(y, error) { - var minus = y - error; - var max = y + error; - - if (this.linear) { - return [ Math.abs(minus), y, Math.abs(max) ]; - } - if ((minus != 0) && (max != 0)) { - return [ Math.log(Math.abs(minus)), Math.log(y), Math.log(Math.abs(max)) ]; - } else { - return [ Math.log(y), Math.log(y), Math.log(y) ]; - } - +CaseForm.prototype.refresh = function(dewar) { + this.setDewar(dewar); + this.stockSolutionGrid.refresh(BIOSAXS.proposal.getStockSolutionsByDewarId(dewar.dewarId)); }; -PlotWidget.prototype.getDataPlot = function(frames, subtractions, models, fits, rbms) { - var files = []; - var labels = [ "Intensity" ]; - if (frames != null) { - for (var i = 0; i < frames.length; i++) { - files.push(frames[i].data); - labels.push(frames[i].fileName); - } - } - function splitData(data, column, errorColumn, name){ - var result = [] - for (var j = 0; j < data.length; j++) { - console.log(data[j][column]); - result.push([j,data[j][column],data[j][errorColumn]]);//[0, data[i][column],0]]); - } - files.push(result); - labels.push(name); - } - - if (subtractions != null) { - for (var i = 0; i < subtractions.length; i++) { - /** For subtraction **/ - files.push(subtractions[i].subtraction.data); - labels.push(subtractions[i].subtraction.fileName); - /** For sample average **/ -// files.push(subtractions[i].sampleAvg.data); -// labels.push(subtractions[i].sampleAvg.fileName); - /** For buffer average **/ -// files.push(subtractions[i].bufferAvg.data); -// labels.push(subtractions[i].bufferAvg.fileName); - } - } - - if (models != null) { - for (var i = 0; i < models.length; i++) { - for ( var key in models[i]) { - splitData(models[i][key].fir.data, 1, 2, "Intensity"); - splitData(models[i][key].fir.data, 3, 3, "Fit"); - } - } - } - - if (fits != null) { - for (var i = 0; i < fits.length; i++) { - for ( var key in fits[i]) { - - /** adding fit file to be plotted **/ - if (fits[i][key].fit.data[0].length == 3){ - splitData(fits[i][key].fit.data, 1, 1, "Intensity"); - splitData(fits[i][key].fit.data, 2, 2, "Fit"); - } - - /** s, Iexp(s), err, Ifit(s). **/ - if (fits[i][key].fit.data[0].length == 4){ - splitData(fits[i][key].fit.data, 1, 2, "Intensity"); - splitData(fits[i][key].fit.data, 3, 3, "Fit"); - } - - if (fits[i][key].fit.data[0].length == 5){ - /** X Intensity Fit Error Residues **/ - - splitData(fits[i][key].fit.data, 1, 3, "Intensity"); - splitData(fits[i][key].fit.data, 2, 2, "Fit"); - } - } - } - } - - var dataPoints = []; - if (files.length > 0) { - for (var i = 0; i < files[0].length; i++) { - dataPoints.push([ files[0][i][0], this.getPoint(files[0][i][1], files[0][i][2]) ]); - } - if (files.length > 1) { - for (var i = 1; i < files.length; i++) { - for (var j = 0; j < dataPoints.length; j++) { - if (files[i][j] != null){ - dataPoints[j].push(this.getPoint(files[i][j][1], files[i][j][2])); - } - else{ - dataPoints[j].push([0,0,0]); - } - } - } - } - } +CaseForm.prototype.getDewar = function() { + this.dewar.code = Ext.getCmp("dewar_code").getValue(); + this.dewar.comments = Ext.getCmp("dewar_comments").getValue(); + this.dewar.trackingNumberFromSynchrotron = Ext.getCmp("dewar_trackingNumberFromSynchrotron").getValue(); + this.dewar.trackingNumberToSynchrotron = Ext.getCmp("dewar_trackingNumberToSynchrotron").getValue(); + this.dewar.transportValue = Ext.getCmp("dewar_transportValue").getValue(); + this.dewar.storageLocation = Ext.getCmp("dewar_storageLocation").getValue(); + this.dewar.firstExperimentId = this.macromoleculeCombo.getValue(); + return this.dewar; +}; - return { - dataPoints : dataPoints, - labels : labels +CaseForm.prototype.setDewar = function(dewar) { + this.dewar = dewar; + Ext.getCmp("dewar_code").setValue(this.dewar.code); + Ext.getCmp("dewar_dewarStatus").setText(new String(this.dewar.dewarStatus).toUpperCase()); + Ext.getCmp("dewar_comments").setValue(this.dewar.comments); + Ext.getCmp("dewar_trackingNumberFromSynchrotron").setValue(this.dewar.trackingNumberFromSynchrotron); + Ext.getCmp("dewar_trackingNumberToSynchrotron").setValue(this.dewar.trackingNumberToSynchrotron); + Ext.getCmp("dewar_transportValue").setValue(this.dewar.transportValue); + Ext.getCmp("dewar_storageLocation").setValue(this.dewar.storageLocation); + if (dewar.sessionVO != null) { + this.macromoleculeCombo.setValue(dewar.sessionVO.sessionId); } }; -PlotWidget.prototype.refresh = function(frames, subtractions, models, fits, rbms, colors) { - - var _this = this; - this.panel.setLoading("Reading Files"); - - var dataAdapter = new BiosaxsDataAdapter(); - dataAdapter.onSuccess.attach(function(sender, data) { - _this.panel.setLoading("Rendering"); - var parsed = _this.getDataPlot(data.frames, data.subtractions, data.models, data.fits, rbms); - _this._renderDygraph(parsed.dataPoints, colors, parsed.labels); - _this.panel.setLoading(false); - }); - dataAdapter.onError.attach(function(sender, data) { - _this.panel.setLoading(false); +CaseForm.prototype.getSessionCombo = function() { + this.macromoleculeCombo = BIOSAXS_COMBOMANAGER.getComboSessions(BIOSAXS.proposal.getSessions(), { + labelWidth : 100, + margin : '5 0 00 0', + width : 250 }); - dataAdapter.getDataPlot(frames, subtractions, models, fits, rbms); -}; - -PlotWidget.prototype.input = function() { - return DATADOC.getHPLCData(); + return this.macromoleculeCombo; }; - -/** - * Fit form - * - * @witdh - * @height - */ -function FitForm(args) { - this.id = BUI.id(); - this.width = null; - this.height = null; - - if (args != null) { - if (args.width != null) { - this.width = args.width; - } - if (args.height != null) { - this.height = args.height; - } - } +CaseForm.prototype.getInformationPanel = function() { + if (this.panel == null) { + this.informationPanel = Ext.create('Ext.form.Panel', { + width : this.width - 10, + border : 0, + items : [ { + xtype : 'container', + margin : "2 2 2 2", + collapsible : false, + defaultType : 'textfield', + layout : 'anchor', + items : [ { + xtype : 'container', + layout : 'hbox', + items : [ { + xtype : 'requiredtext', + fieldLabel : 'Code', + allowBlank : false, + name : 'code', + id : 'dewar_code', + anchor : '50%' + }, { + xtype : 'label', + margin : '0 0 0 20', + readOnly : true, + id : 'dewar_dewarStatus', + anchor : '50%' + } ] + }, this.getSessionCombo(), { + margin : '5 0 0 0', + xtype : 'textareafield', + name : 'comments', + id : 'dewar_comments', + width : this.width - 50, + fieldLabel : 'Comments' + } ] + }, { + xtype : 'fieldset', + title : 'Courier Accounts Details for Return', + collapsible : false, + layout : 'anchor', + defaults : { + anchor : '100%' + }, + items : [ { + xtype : 'container', + layout : 'hbox', + items : [ { + xtype : 'textfield', + labelWidth : 200, + width : 280, + fieldLabel : 'Track Number From Synchrotron', + id : 'dewar_trackingNumberFromSynchrotron' + }, { + xtype : 'numberfield', + width : 190, + labelWidth : 110, + margin : '0 0 0 30', + fieldLabel : 'Transport Value', + id : 'dewar_transportValue' + } - var _this = this; - - /** Widgets **/ - this.fitGrid = new FitStructureToExperimentDataGrid({ - width : null, - height : 200 - }); + ] + }, { + xtype : 'container', + layout : 'hbox', + margin : '10 0 0 0', + items : [ - this.fitGrid.onSelected.attach(function(sender, fits) { - var modelsIdList = []; - for ( var i in fits) { - modelsIdList.push(fits[i].fitStructureToExperimentalDataId); - } - _this._renderPlot(_this.subtractions[0].subtractionId, modelsIdList); -// var adapter = new BiosaxsDataAdapter(); -// adapter.onSuccess.attach(function(sender, data) { -// -// }); -// adapter.getScatteringCurveByFrameIdsList([], [], [], ids); - }); - - - /** Dygraph Widget that plots fir files**/ - this.plotWidget = new PlotWidget({ - width : 745, - height : 300, - margin : "10 0 10 10" - }); -} + { + xtype : 'textfield', + labelWidth : 200, + width : 280, + fieldLabel : 'Track Number To Synchrotron', + id : 'dewar_trackingNumberToSynchrotron' + }, { + xtype : 'textfield', + margin : '0 0 0 30', + width : 190, + labelWidth : 110, + fieldLabel : 'Storage Location', + id : 'dewar_storageLocation' + } -FitForm.prototype._renderPlot = function(subtractionId, modelsIdList) { - /** Trying to plot tje subtraction and the models **/ - try { - var colors = [ "#669900", "#0000FF" ]; - - this.plotWidget.refresh([], [ ], [], modelsIdList, [], colors); - } catch (e) { - console.log(e); + ] + } ] + } ] + }); } -}; - -FitForm.prototype._getItems = function() { - var _this = this; - return [ { - xtype : 'label', - forId : 'myFieldId', - text : 'INLINE HELP: To be updated', - margin : '15 0 20 10', - cls : "inline-help" - }, this.fitGrid.getPanel(), this.plotWidget.getPanel() ] - -}; -FitForm.prototype._getButtons = function() { - return []; + return this.informationPanel; }; -FitForm.prototype.getPanel = function() { - var _this = this; - this.panel = Ext.create('Ext.form.Panel', { +CaseForm.prototype.getPanel = function(dewar) { + this.dewar = dewar; + this.panel = Ext.createWidget({ + xtype : 'container', + layout : 'vbox', width : this.width, - height : this.height, - margin : 10, - border : 1, - defaultType : 'textfield', - items : this._getItems(), - buttons : this._getButtons(), - listeners : { - afterrender : function() { - _this._populate(); + border : 0, + items : [ { + items : { + xtype : "container", + layout : "vbox", + margin : "5 5 5 5", + items : [ this.getInformationPanel(dewar), this.stockSolutionGrid.getPanel() ] } - } + } ] }); + + this.refresh(dewar); return this.panel; -}; -/** Populates could be call when the DOM is not filled yet **/ -FitForm.prototype._populate = function() { }; -/** It populates the form * */ -FitForm.prototype.refresh = function(subtractions) { - this.subtractions = subtractions; - this.fitGrid.refresh(subtractions); -}; +CaseForm.prototype.input = function() { + return { + dewar : { + "dewarId" : 305861, + "code" : "ESRF-TEST", + "comments" : "comments", + "storageLocation" : "FRIDGE", + "dewarStatus" : "opened", + "timeStamp" : null, + "isStorageDewar" : null, + "barCode" : "ESRF305861", + "customsValue" : null, + "transportValue" : null, + "trackingNumberToSynchrotron" : "3333", + "trackingNumberFromSynchrotron" : "224466", + "type" : "Dewar", + "sessionVO" : { + "sessionId" : 31697, + "expSessionPk" : null, + "projectCode" : null, + "startDate" : "2012 07 21", + "endDate" : "2012 07 23", + "beamlineName" : "BM29", + "scheduled" : 1, + "nbShifts" : 2, + "comments" : null, + "beamlineOperator" : "PERNOT P", + "usedFlag" : null, + "sessionTitle" : null, + "structureDeterminations" : null, + "dewarTransport" : null, + "databackupFrance" : null, + "databackupEurope" : null, + "visit_number" : null, + "operatorSiteNumber" : "14061", + "timeStamp" : "2012 04 25" + } + }, + proposal : { + "assemblies" : [], + "sessions" : [ { + "sessionId" : 31697, + "expSessionPk" : null, + "projectCode" : null, + "startDate" : "2012 07 21", + "endDate" : "2012 07 23", + "beamlineName" : "BM29", + "scheduled" : 1, + "nbShifts" : 2, + "comments" : null, + "beamlineOperator" : "PERNOT P", + "usedFlag" : null, + "sessionTitle" : null, + "structureDeterminations" : null, + "dewarTransport" : null, + "databackupFrance" : null, + "databackupEurope" : null, + "visit_number" : null, + "operatorSiteNumber" : "14061", + "timeStamp" : "2012 04 25" + } ], + "labcontacts" : [ { + "labContactId" : 787, + "personVO" : { + "personId" : 304252, + "personUUID" : null, + "familyName" : "KIM", + "givenName" : "Henry", + "title" : null, + "emailAddress" : "henry-sung.kim@ibs.fr", + "phoneNumber" : "", + "login" : "", + "passwd" : "", + "faxNumber" : "" + }, + "cardName" : "KIM-Institut de Bio", + "defaultCourrierCompany" : "22", + "courierAccount" : "", + "billingReference" : "", + "dewarAvgCustomsValue" : 0, + "dewarAvgTransportValue" : 0 + } ], + "buffers" : [ { + "bufferId" : 811, + "proposalId" : 3124, + "safetyLevelId" : null, + "name" : "EDTA", + "acronym" : "EDTA", + "ph" : null, + "composition" : "", + "bufferhasadditive3VOs" : [], + "comments" : "" + }, { + "bufferId" : 810, + "proposalId" : 3124, + "safetyLevelId" : null, + "name" : "HEPES", + "acronym" : "HEPES", + "ph" : null, + "composition" : "", + "bufferhasadditive3VOs" : [], + "comments" : "" + } ], + "shippings" : [ { + "shippingId" : 304107, + "shippingName" : "TEST", + "deliveryAgentAgentName" : null, + "deliveryAgentShippingDate" : null, + "deliveryAgentDeliveryDate" : null, + "deliveryAgentAgentCode" : null, + "deliveryAgentFlightCode" : null, + "shippingStatus" : "opened", + "timeStamp" : "2013 09 25", + "laboratoryId" : null, + "isStorageShipping" : null, + "creationDate" : "2013 09 25", + "comments" : "test", + "sendingLabContactVO" : { + "labContactId" : 787, + "personVO" : { + "personId" : 304252, + "personUUID" : null, + "familyName" : "KIM", + "givenName" : "Henry", + "title" : null, + "emailAddress" : "henry-sung.kim@ibs.fr", + "phoneNumber" : "", + "login" : "", + "passwd" : "", + "faxNumber" : "" + }, + "cardName" : "KIM-Institut de Bio", + "defaultCourrierCompany" : "22", + "courierAccount" : "", + "billingReference" : "", + "dewarAvgCustomsValue" : 0, + "dewarAvgTransportValue" : 0 + }, + "returnLabContactVO" : { + "labContactId" : 787, + "personVO" : { + "personId" : 304252, + "personUUID" : null, + "familyName" : "KIM", + "givenName" : "Henry", + "title" : null, + "emailAddress" : "henry-sung.kim@ibs.fr", + "phoneNumber" : "", + "login" : "", + "passwd" : "", + "faxNumber" : "" + }, + "cardName" : "KIM-Institut de Bio", + "defaultCourrierCompany" : "22", + "courierAccount" : "", + "billingReference" : "", + "dewarAvgCustomsValue" : 0, + "dewarAvgTransportValue" : 0 + }, + "returnCourier" : null, + "dateOfShippingToUser" : null, + "shippingType" : "DewarTracking", + "dewarVOs" : [ { + "dewarId" : 305861, + "code" : "ESRF-TEST", + "comments" : "comments", + "storageLocation" : "FRIDGE", + "dewarStatus" : "opened", + "timeStamp" : null, + "isStorageDewar" : null, + "barCode" : "ESRF305861", + "customsValue" : null, + "transportValue" : null, + "trackingNumberToSynchrotron" : "3333", + "trackingNumberFromSynchrotron" : "224466", + "type" : "Dewar", + "sessionVO" : { + "sessionId" : 31697, + "expSessionPk" : null, + "projectCode" : null, + "startDate" : "2012 07 21", + "endDate" : "2012 07 23", + "beamlineName" : "BM29", + "scheduled" : 1, + "nbShifts" : 2, + "comments" : null, + "beamlineOperator" : "PERNOT P", + "usedFlag" : null, + "sessionTitle" : null, + "structureDeterminations" : null, + "dewarTransport" : null, + "databackupFrance" : null, + "databackupEurope" : null, + "visit_number" : null, + "operatorSiteNumber" : "14061", + "timeStamp" : "2012 04 25" + } + } ] + } ], + "macromolecules" : [ { + "macromoleculeId" : 5933, + "safetylevelId" : null, + "proposalId" : 3124, + "name" : "A", + "acronym" : "A", + "molecularMass" : "", + "extintionCoefficient" : "", + "sequence" : null, + "creationDate" : null, + "comments" : "", + "macromoleculeregion3VOs" : [], + "stoichiometry3VOsForHostMacromoleculeId" : [], + "structure3VOs" : [] + } ], + "stockSolutions" : [ { + "stockSolutionId" : 6, + "proposalId" : 3124, + "macromoleculeId" : 5933, + "bufferId" : 811, + "instructionSet3VO" : null, + "boxId" : 305861, + "storageTemperature" : "20", + "volume" : "300", + "concentration" : "1.2", + "comments" : "Buffer EDTA with A", + "name" : "A_EDTA_1.2", + "samples" : [] + } ] + } -FitForm.prototype.input = function() { - return {}; + }; }; -/** It populates the form **/ -FitForm.prototype.test = function(targetId) { - var macromoleculeForm = new FitForm(); - var panel = macromoleculeForm.getPanel(); - panel.render(targetId); -}; - -/** - * Example form - * - * @witdh - * @height - */ -function RigidBodyModelingResultForm(args) { +CaseForm.prototype.test = function(targetId) { + var caseForm = new CaseForm(); + BIOSAXS.proposal = new Proposal(caseForm.input().proposal); + var panel = caseForm.getPanel(caseForm.input().dewar); + panel.render(targetId); +}; + +/** + * Example form + * + * @witdh + * @height + */ +function ExampleForm(args) { + this.id = BUI.id(); + this.width = 700; + this.height = 500; + + if (args != null) { + if (args.width != null) { + this.width = args.width; + } + if (args.height != null) { + this.height = args.height; + } + } +} + +ExampleForm.prototype._getItems = function() { + return [{ + fieldLabel : 'First Name', + name : 'first', + allowBlank : false + }, { + fieldLabel : 'Last Name', + name : 'last', + allowBlank : false + } ]; +}; + +ExampleForm.prototype._getItems = function() { + return [ ]; +}; + +ExampleForm.prototype._getButtons = function() { + return [ ]; +}; + +ExampleForm.prototype.getPanel = function() { + this.panel = Ext.create('Ext.form.Panel', { + width : this.width, + height : this.height, + margin : 10, + border : 1, + defaultType : 'textfield', + items : this._getItems(), + buttons : this._getButtons() + }); + return this.panel; +}; + +/** It populates the form **/ +ExampleForm.prototype.refresh = function(macromolecule) { +}; + +function ExperimentForm(args) { this.id = BUI.id(); - this.width = null; - this.height = null; if (args != null) { - if (args.width != null) { - this.width = args.width; - } - if (args.height != null) { - this.height = args.height; - } } - var _this = this; - /** Widgets **/ - this.rigidModelGrid = new RigidModelGrid({ - width : null, - height : 200 + this.onSaved = new Event(this); +} + +ExperimentForm.prototype._getItems = function(experiment) { + this.experiment = experiment; + var typeCombo = Ext.create('Ext.form.ComboBox', { + id : this.id + 'type', + fieldLabel : 'Type', + store : [ "STATIC", "CALIBRATION", "HPLC" ], + queryMode : 'local', + labelWidth : 120, + displayField : 'name', + valueField : 'name', + value : experiment.json.type, + disabled : (experiment.json.type == 'TEMPLATE') }); - this.rigidModelGrid.onSelected.attach(function(sender, fits){ - var ids = []; - for ( var i in fits) { - ids.push(fits[i].fitStructureToExperimentalDataId); + var items = []; + + + if (experiment.json.type == "HPLC" ){ + var typeMacromolecule = BIOSAXS_COMBOMANAGER.getComboMacromoleculeByMacromolecules(BIOSAXS.proposal.getMacromolecules(), {labelWidth : 120, width: "120px"}); + if (experiment.getHPLCMacromolecule() != null){ + typeMacromolecule.setValue(experiment.getHPLCMacromolecule().macromoleculeId); + items.push(typeMacromolecule); } - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, data){ -// debugger - - }); - - adapter.getScatteringCurveByFrameIdsList([], [], [], ids); + } + + + items.push(typeCombo, { + id : this.id + 'name', + xtype : 'textfield', + fieldLabel : 'Name', + labelWidth : 120, + width : '100%', + value : experiment.json.name + }, { + id : this.id + 'comments', + xtype : 'textareafield', + name : 'comments', + fieldLabel : 'Comments', + labelWidth : 120, + height : 120, + width : '100%', + value : experiment.json.comments }); - -} - -RigidBodyModelingResultForm.prototype._getItems = function() { - var _this = this; - return [ { - xtype : 'label', - forId : 'myFieldId', - text : 'INLINE HELP: To be updated', - margin : '15 0 20 10', - cls : "inline-help" - }, this.rigidModelGrid.getPanel() ] - -}; - -RigidBodyModelingResultForm.prototype._getButtons = function() { - return []; + return items; }; - -RigidBodyModelingResultForm.prototype.getPanel = function() { +ExperimentForm.prototype.getPanel = function(experiment) { var _this = this; - this.panel = Ext.create('Ext.form.Panel', { - width : this.width, - height : this.height, - margin : 10, - border : 1, - defaultType : 'textfield', - items : this._getItems(), - buttons : this._getButtons(), - listeners : { - afterrender : function() { - _this._populate(); - - } - } + + this.panel = Ext.createWidget({ + xtype : 'container', + layout : 'vbox', + border : 0, + style : { + padding : '10px' + }, + fieldDefaults : { + labelAlign : 'left', + labelWidth : 50 + }, + items : this._getItems(experiment) }); return this.panel; }; -/** Populates could be call when the DOM is not filled yet **/ -RigidBodyModelingResultForm.prototype._populate = function() { -}; - -/** It populates the form * */ -RigidBodyModelingResultForm.prototype.refresh = function(subtractions) { - this.rigidModelGrid.refresh(subtractions); -}; - -RigidBodyModelingResultForm.prototype.input = function() { - return {}; +ExperimentForm.prototype.input = function() { + return new ExperimentHeaderForm().input(); }; -/** It populates the form **/ -RigidBodyModelingResultForm.prototype.test = function(targetId) { - var macromoleculeForm = new RigidBodyModelingResultForm(); - var panel = macromoleculeForm.getPanel(); +ExperimentForm.prototype.test = function(targetId) { + var experimentForm = new ExperimentForm(); + var panel = experimentForm.getPanel(new Experiment(experimentForm.input().experiment)); panel.render(targetId); }; /** - * Example form + * Shows the header for the experiments changing the color and parameters depending on experiment type * - * @witdh - * @height */ -function SuperpositionForm(args) { +function ExperimentHeaderForm(args) { this.id = BUI.id(); - this.width = null; - this.height = null; + this.backgroundColor = '#FFFFFF'; +} - if (args != null) { - if (args.width != null) { - this.width = args.width; - } - if (args.height != null) { - this.height = args.height; +ExperimentHeaderForm.prototype.getHTMLSource = function(experiment) { + var html = BUI.createFormLabel("Name :", experiment.json.name, 75, 400, this.backgroundColor); + if (experiment.json.type == "HPLC") { + if (experiment.getHPLCMacromolecule() != null){ + html = html + BUI.createFormLabel("Molecule :", experiment.getHPLCMacromolecule().acronym, 75, 400, this.backgroundColor); } } + else{ + html = html + BUI.createFormLabel("Type :", experiment.json.type, 75, 400, this.backgroundColor); + } + + html = html + BUI.createFormLabel("Date :", experiment.json.creationDate, 75, 400, this.backgroundColor); + return html; +}; - var _this = this; - - /** Widgets **/ - this.superpositionGrid = new SuperpositionGrid({ - width : null, - height : 200 - }); - - this.superpositionGrid.onSelected.attach(function(sender, superpositions) { - var ids = []; - for ( var i in superpositions) { - ids.push(superpositions[i].superpositionId); - } -// _this._renderAbinitio(ids); -// _this.aprioriPDBViewer.refresh(); - _this._renderAligned(ids); - // getAlignedPDBContentBySuperpositionList - }); - - /** PDB viewer **/ -// this.abinitioPDBViewer = new PDBViewer({ -// width : 860 / 2, -// height : 300 / 2, -// margin : 0 -// }); -// -// /** PDB viewer **/ -// this.aprioriPDBViewer = new StructurePDBViewer({ -// width : 860 / 2, -// height : 300 / 2, -// margin : 0 -// }); +ExperimentHeaderForm.prototype.getHTMLDownload = function(experiment) { + var bgcolor = "background-color:" + this.backgroundColor + ";"; + var html = "
"; + if ((experiment.json.type == "CALIBATION") || (experiment.json.type == "STATIC")) { + html = html + " Download Source File
"; + html = html + + ""; + } + if (experiment.json.type == "TEMPLATE") { + html = html + + " Download Source File"; + } - this.alignedPDBViewer = new AlignedSuperpositionPDBViewer({ - width : 860 , - height : 300 - }); + if (experiment.json.type == "HPLC") { + html = html + " Download h5 File"; + } -} + return html; +}; -SuperpositionForm.prototype._renderAligned = function(modelsIdList) { - /** Trying to plot the PDB file **/ - try { - this.alignedPDBViewer.refresh(modelsIdList); - } catch (e) { - console.log(e); - } +ExperimentHeaderForm.prototype.getTopPanel = function(experiment) { + return { + xtype : 'container', + layout : 'hbox', + items : [ { + margin : "0 0 0 0", + width : 475, + border : 0, + html : this.getHTMLSource(experiment) + }, { + margin : "0 0 0 0", + width : 475, + border : 0, + html : this.getHTMLDownload(experiment) + } ] + }; }; -SuperpositionForm.prototype._renderAbinitio = function(modelsIdList) { - /** Trying to plot the PDB file **/ - try { - var viz = []; - for (var i = 0; i < modelsIdList.length; i++) { - viz.push({ - modelId : modelsIdList[i], - color : "0xFF6600", - opacity : 0.8 +ExperimentHeaderForm.prototype.getButton = function(experiment) { + var _this = this; + return Ext.create('Ext.Button', { + text : 'EDIT', + minWidth : '100', + margin : '10 0 0 30', + handler : function() { + var experimentWindow = new ExperimentWindow(); + experimentWindow.onSaved.attach(function(sender, data) { + _this.experiment.json.name = data.name; + _this.experiment.json.type = data.type; + _this.experiment.json.comments = data.comments; + _this.panel.remove(_this.panel.items.items[0]); + _this.panel.remove(_this.panel.items.items[0]); + _this.panel.insert(_this.getTopPanel(_this.experiment)); + _this.panel.insert(_this.getBottomPanel(_this.experiment)); }); + experimentWindow.show(experiment); } - this.abinitioPDBViewer.refresh(viz); - } catch (e) { - console.log(e); - } + }); }; -SuperpositionForm.prototype._getItems = function() { - var _this = this; - return [ { - xtype : 'label', - forId : 'myFieldId', - text : 'INLINE HELP: To be updated', - margin : '15 0 20 10', - cls : "inline-help" - }, this.superpositionGrid.getPanel(), { +ExperimentHeaderForm.prototype.getBottomPanel = function(experiment) { + return { xtype : 'container', layout : 'hbox', - items : [ -// { -// xtype : 'container', -// layout : 'vbox', -// items : [ this.abinitioPDBViewer.getPanel(), this.aprioriPDBViewer.getPanel() -// -// ] -// }, - - this.alignedPDBViewer.getPanel() ] - } ] - -}; - -SuperpositionForm.prototype._getButtons = function() { - return []; + margin : '10 0 0 0', + items : [ this.getComments(experiment), this.getButton(experiment) ] + }; }; -SuperpositionForm.prototype.getPanel = function() { - var _this = this; - this.panel = Ext.create('Ext.form.Panel', { - width : this.width, - height : this.height, - margin : 10, - border : 1, - defaultType : 'textfield', - items : this._getItems(), - buttons : this._getButtons(), - listeners : { - afterrender : function() { - _this._populate(); - - } - } - }); - return this.panel; +ExperimentHeaderForm.prototype.getComments = function(experiment) { + return { + xtype : 'textareafield', + labelStyle : 'font-weight: bold;', + name : 'comments', + fieldLabel : 'Comments', + labelWidth : 70, + height : 40, + minWidth : '450', + readOnly : true, + value : experiment.json.comments + }; }; -/** Populates could be call when the DOM is not filled yet **/ -SuperpositionForm.prototype._populate = function() { -}; +ExperimentHeaderForm.prototype.getPanel = function(experiment) { + this.experiment = experiment; + + if (experiment.json.type == 'CALIBRATION') { + this.backgroundColor = '#EFFBFB'; + } + if (experiment.json.type == 'TEMPLATE') { + this.backgroundColor = '#E0F8E6'; + } -/** It populates the form * */ -SuperpositionForm.prototype.refresh = function(subtractions) { - this.subtractions = subtractions; - this.superpositionGrid.refresh(subtractions); + this.panel = Ext.create('Ext.container.Container', { + frame : false, + layout : 'vbox', + padding : 5, + bodyPadding : '5 5 0 0', + width : 890, + margin : '0 0 10 0', + height : 120, + style : { + borderColor : '#99bce8', + borderStyle : 'solid', + borderWidth : '1px', + 'background-color' : this.backgroundColor + }, + fieldDefaults : { + msgTarget : 'side', + labelWidth : 100 + }, + items : [ this.getTopPanel(experiment), this.getBottomPanel(experiment) ] + }); + return this.panel; }; -SuperpositionForm.prototype.input = function() { - return {}; +ExperimentHeaderForm.prototype.input = function() { + return { + experiment : DATADOC.getExperiment_10() + }; }; -/** It populates the form **/ -SuperpositionForm.prototype.test = function(targetId) { - var macromoleculeForm = new SuperpositionForm(); - var panel = macromoleculeForm.getPanel(); +ExperimentHeaderForm.prototype.test = function(targetId) { + var experimentHeaderForm = new ExperimentHeaderForm(); + var panel = experimentHeaderForm.getPanel(new Experiment(experimentHeaderForm.input().experiment)); panel.render(targetId); + }; /** - * Example form + * Macromolecule form with the general parameters of a macromolecule * * @witdh * @height + * + * #onSave button save has been clicked + * #onClose button close has been clicked */ -function AssemblyForm(args) { +function MacromoleculeForm(args) { this.id = BUI.id(); this.width = 700; this.height = 500; @@ -6059,15944 +7053,9793 @@ function AssemblyForm(args) { this.height = args.height; } } - - this.molarityGrid = new MolarityGrid({height : this.height - 50}); + + /** Events **/ + this.onSave = new Event(this); + this.onClose = new Event(this); } -AssemblyForm.prototype._getItems = function() { - return [ { - xtype : 'label', - forId : 'myFieldId', - text : 'List of previously defined macromolecules present in the assembly. This information will be used for additional cross-checks where possible', - margin : '15 0 20 10', - cls : "inline-help" - }, this.molarityGrid.getPanel() ]; -}; - -AssemblyForm.prototype._getButtons = function() { - return []; +/** Type : is the Ext type then requiredtext or textfield * */ +MacromoleculeForm.prototype._getFieldTextWithHelp = function(type, fieldLabel, fieldName, help) { + return Ext.create('Ext.container.Container', { + margin : "10 0 0 10", + items : [ { + xtype : type, + fieldLabel : fieldLabel, + name : fieldName, + id : this.id + fieldName + }, { + xtype : 'label', + forId : 'myFieldId', + text : help, + margin : "5 0 0 105", + cls : "inline-help" + } ] + }); }; -AssemblyForm.prototype.getPanel = function() { - this.panel = Ext.create('Ext.form.Panel', { - width : this.width, - height : this.height, - margin : 10, - border : 0, - defaultType : 'textfield', - items : this._getItems(), - buttons : this._getButtons() +MacromoleculeForm.prototype._getNumericWithHelp = function(type, fieldLabel, fieldName, help) { + return Ext.create('Ext.container.Container', { + margin : "0 0 0 10", + items : [ { + xtype : type, + fieldLabel : fieldLabel, + name : fieldName, + id : this.id + fieldName, + decimalPrecision : 6, + width : 220 + }, { + xtype : 'label', + forId : 'myFieldId', + text : help, + margin : "5 0 0 10", + cls : "inline-help" + } ] }); - return this.panel; }; - -/** It populates the form **/ -AssemblyForm.prototype.refresh = function(macromolecule) { - this.macromolecule = macromolecule; - this.molarityGrid.refresh(macromolecule); +MacromoleculeForm.prototype._getButtons = function() { + var _this = this; + return [ { + text : 'Save', + handler : function() { + _this._save(); + } + },{ + text : 'Close', + handler : function() { + _this.onClose.notify(); + + } + } ]; }; -AssemblyForm.prototype.input = function() { - return {}; +/** It persits the macromolecule in the database **/ +MacromoleculeForm.prototype._persist = function(macromoleculeId, acronym, name, molecularMass, extintionCoefficient, comments, refractiveIndex, solventViscosity) { + + /** Checking not duplicated acronym **/ + if (((macromoleculeId == null) && (BIOSAXS.proposal.getMacromoleculeByAcronym(acronym) != null))== true){ + BUI.showError("Duplicated acronym"); + return; + } + + + if (macromoleculeId == null){ + /** new macromolecule **/ + this.macromolecule = {}; + this.macromolecule.macromoleculeId = null; + } + else{ + this.macromolecule.macromoleculeId = macromoleculeId; + } + + this.macromolecule["acronym"] = acronym; + this.macromolecule["name"] = name; + this.macromolecule["molecularMass"] = molecularMass; + this.macromolecule["extintionCoefficient"] = extintionCoefficient; + this.macromolecule["comments"] = comments; + this.macromolecule["symmetry"] = Ext.getCmp(this.id + 'comboSym').getValue(); + this.macromolecule["refractiveIndex"] = refractiveIndex; + this.macromolecule["solventViscosity"] = solventViscosity; + + var _this = this; + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, proposal) { + /** Updating the proposal global object **/ + BIOSAXS.proposal.setItems(proposal); + _this.panel.setLoading(false); + + var saved = BIOSAXS.proposal.getMacromoleculeByAcronym(acronym); + /** Refreshing to check that everything is ok **/ + _this.refresh(saved); + _this.onSave.notify(saved); + }); + + this.panel.setLoading("Saving Macromolecule") + adapter.saveMacromolecule(this.macromolecule); }; - -AssemblyForm.prototype.test = function(targetId) { - var assemblyForm = new AssemblyForm(); +/** Save the macromolecule in the DB **/ +MacromoleculeForm.prototype._save = function() { - var panel = assemblyForm.getPanel(); - panel.render(targetId); -}; -/** - * Edit the information of a buffer - * - * #onSaved - * #onRemoveAdditive - */ -function BufferForm() { var _this = this; + + var acronym = this._getField("acronym"); + var name = this._getField("name"); + var molecularMass = this._getField("molecularMass"); + var extintionCoefficient = this._getField("extintionCoefficient"); + var comments = this._getField("comments"); + + var refractiveIndex = this._getField("refractiveIndex"); + var solventViscosity = this._getField("solventViscosity"); + + /** Checking required fields **/ + if (name == "") { + BUI.showError("Name field is mandatory"); + return; + } + if (acronym == "") { + BUI.showError("Acroynm field is mandatory"); + return; + } - this.additiveGrid = new AdditiveGrid(); - this.additiveGrid.onRemoveButtonClicked.attach(function(sender, args) { - _this.onRemoveAdditive.notify(args); - }); + if (this.macromolecule != null){ + /** Checking if it is a new macromolecule **/ + if (this.macromolecule.macromoleculeId == null){ + /** Check if the acronym exists already **/ + this._persist(null, acronym, name, molecularMass, extintionCoefficient, comments, refractiveIndex, solventViscosity); + } + else{ + /** It is an update **/ + this._persist(this.macromolecule.macromoleculeId, acronym, name, molecularMass, extintionCoefficient, comments, refractiveIndex, solventViscosity); + } + } + else{ + /** It is a new macromolecule **/ + this._persist(null, acronym, name, molecularMass, extintionCoefficient, comments, refractiveIndex, solventViscosity); + } +}; - this.onSaved = new Event(this); - this.onRemoveAdditive = new Event(this); -} -BufferForm.prototype.getBuffer = function() { - this.buffer.name = Ext.getCmp("buffer_name").getValue(); - this.buffer.acronym = Ext.getCmp("buffer_acronym").getValue(); - this.buffer.comments = Ext.getCmp("buffer_comments").getValue(); - this.buffer.ph = Ext.getCmp("buffer_ph").getValue(); - this.buffer.composition = Ext.getCmp("buffer_composition").getValue(); - this.buffer.bufferhasadditive3VOs = this.additiveGrid.getAdditives(); - return this.buffer; -}; -BufferForm.prototype._getTopPanel = function() { - return { - xtype : 'container', - layout : 'hbox', - border : 0, - frame : true, - items : [ { - xtype : 'container', - layout : 'hbox', - items : [ { +MacromoleculeForm.prototype._getItems = function() { + var _this = this; + /** Symmetry combo box **/ + var symmetry = Ext.create('Ext.data.Store', { + fields : [ 's' ], + data : this._getSymmetries() + }); + + this.symmetryComboBox = Ext.create('Ext.form.ComboBox', { + fieldLabel : 'Symmetry', + store : symmetry, + id : this.id + 'comboSym', + queryMode : 'local', + displayField : 's', + valueField : 's', + value : "P1", + margin : "0 0 0 30", + width : 220 + }); + + return [this._getFieldTextWithHelp("requiredtext", "Name", "name", "Long name. i.e: Bovine serum albumin"), + this._getFieldTextWithHelp("requiredtext", "Acronym", "acronym", "Acronym will be used in the files and analisys. i.e: BSA"), + this._getFieldTextWithHelp("textfield", "Mol. Mass (Da)", "molecularMass", "Atomic mass estimation measured in Da"), + { xtype : 'container', - flex : 1, - border : false, - layout : 'anchor', - defaultType : 'textfield', - items : [ { - xtype : 'requiredtext', - id : 'buffer_name', - fieldLabel : 'Name', - name : 'name', - width : '200px', - value : this.buffer.name - }, { - xtype : 'requiredtext', - id : 'buffer_acronym', - fieldLabel : 'Acronym', - name : 'acronym', - width : '200px', - value : this.buffer.acronym - } ] - } ] - }, { - xtype : 'container', - flex : 1, - layout : 'anchor', - defaultType : 'textfield', - margin : '0 0 0 10', - items : [ { - id : 'buffer_ph', - fieldLabel : 'pH', - name : 'ph', - value : this.buffer.ph, - xtype : 'numberfield', - width : 200, - minValue : 0, - maxValue : 15 - }, { - xtype : 'requiredtext', - id : 'buffer_composition', - fieldLabel : 'Composition', - name : 'composition', - width : 200, - value : this.buffer.composition - } ] - } ] - }; + layout : 'hbox', + margin : "10 0 0 0", + items :[ + this._getNumericWithHelp("numberfield", "Extinction coef.", "extintionCoefficient", ""), + this.symmetryComboBox + ] + }, + { + xtype : 'container', + layout : 'hbox', + margin : "5 0 0 0", + items :[ + this._getNumericWithHelp("numberfield", "Refractive Index", "refractiveIndex", "How radiation propagates through the medium"), + this._getNumericWithHelp("numberfield", "Solvent Viscosity", "solventViscosity", "") + ] + }, + { + id : this.id + "comments", + xtype : 'textareafield', + name : 'comments', + margin : '35 0 0 10', + fieldLabel : 'Comments', + width : this.width - 100 + } ]; }; -BufferForm.prototype.getPanel = function(buffer) { - this.buffer = buffer; - this.panel = Ext.createWidget({ - xtype : 'container', - layout : 'vbox', - style : { - padding : '10px' - }, - fieldDefaults : { - labelAlign : 'left', - labelWidth : 50 +MacromoleculeForm.prototype._getSymmetries = function() { + return [ { + "s" : "P1" + }, { + "s" : "P2" + }, { + "s" : "P3" + }, { + "s" : "P4" + }, { + "s" : "P5" + }, { + "s" : "P6" + }, { + "s" : "P32" + }, { + "s" : "P42" + }, { + "s" : "P52" + }, { + "s" : "P62" + }, { + "s" : "P222" + } ] +}; - }, - items : [ this._getTopPanel(), { - id : 'buffer_comments', - xtype : 'textareafield', - name : 'comments', - fieldLabel : 'Comments', - width : '100%', - value : buffer.comments - }, this.additiveGrid.getPanel(buffer) ] +MacromoleculeForm.prototype.getPanel = function() { + this.panel = Ext.create('Ext.form.Panel', { + width : this.width, + height : this.height, + margin : 10, + border : 1, + defaultType : 'textfield', + items : this._getItems(), + buttons : this._getButtons() }); return this.panel; }; -BufferForm.prototype.input = function() { - return { - buffer : { - "bufferId" : 422, - "proposalId" : 10, - "safetyLevelId" : null, - "name" : "B1", - "acronym" : "B1", - "ph" : null, - "composition" : null, - "bufferhasadditive3VOs" : [], - "comments" : null - } - }; + +/** Populates each text field by field name and value **/ +MacromoleculeForm.prototype._populateField = function(fieldName, value) { + if (value != null){ + Ext.getCmp(this.id + fieldName).setValue(value); + } }; -BufferForm.prototype.test = function(targetId) { - var bufferForm = new BufferForm(); - var panel = bufferForm.getPanel(bufferForm.input().buffer); - panel.render(targetId); +/** Gets the value of a textfield **/ +MacromoleculeForm.prototype._getField = function(fieldName) { + return Ext.getCmp(this.id + fieldName).getValue(); }; - -/** - * @showTitle - * - * #onSaved - * #onAddPlates - * #onRemovePlates - **/ -function CaseForm(args) { - this.width = 700; - this.showTitle = true; - if (args != null) { - if (args.showTitle != null) { - this.showTitle = args.showTitle; - } - } - var _this = this; - this.stockSolutionGrid = new StockSolutionGrid({ - width : this.width - 10, - minHeight : 300, - height : 300, - tbar : true, - showTitle : true, - isPackedVisible : false, - btnAddExisting : true, - btnRemoveVisible : false, - btnUnpackVisible : true - }); - /** When selecting existing stock solutions **/ - this.stockSolutionGrid.onStockSolutionSelected.attach(function(sender, stockSolutions) { - if (stockSolutions != null) { - for ( var i = 0; i < stockSolutions.length; i++) { - _this.saveStockSolution(stockSolutions[i]); - } +/** It populates the form **/ +MacromoleculeForm.prototype.refresh = function(macromolecule) { + this.macromolecule = macromolecule; + if (macromolecule != null){ + this._populateField("name", macromolecule.name); + this._populateField("acronym", macromolecule.acronym); + this._populateField("extintionCoefficient", macromolecule.extintionCoefficient); + this._populateField("molecularMass", macromolecule.molecularMass); + this._populateField("comments", macromolecule.comments); + this._populateField("refractiveIndex", macromolecule.refractiveIndex); + this._populateField("solventViscosity", macromolecule.solventViscosity); + if (macromolecule.symmetry != null){ + Ext.getCmp(this.id + 'comboSym').setValue(macromolecule.symmetry); } - }); + } +}; - /** it can be because it has been added a new one or removed **/ - this.stockSolutionGrid.onProposalChanged.attach(function(sender, stockSolution) { - if (stockSolution != null) { - _this.saveStockSolution(stockSolution); - } else { - BIOSAXS.proposal.onInitialized.attach(function() { - _this.stockSolutionGrid.refresh(BIOSAXS.proposal.getStockSolutionsByDewarId(_this.dewar.dewarId)); - _this.stockSolutionGrid.grid.setLoading(false); - }); - BIOSAXS.proposal.init(); +MacromoleculeForm.prototype.input = function() { + return {}; +}; - } +/** It populates the form **/ +MacromoleculeForm.prototype.test = function(targetId) { + var macromoleculeForm = new MacromoleculeForm(); + macromoleculeForm.onClose.attach(function(sender){ + alert("Click on close"); }); + var panel = macromoleculeForm.getPanel(); + panel.render(targetId); +}; - this.onSaved = new Event(this); - this.onAddPlates = new Event(this); - this.onRemovePlates = new Event(this); -} -CaseForm.prototype.saveStockSolution = function(stockSolution) { - var _this = this; - var adapter = new BiosaxsDataAdapter(); - this.stockSolutionGrid.grid.setLoading("ISPyB: setting case to Stock solution"); - adapter.onSuccess.attach(function(sender, stockSolution) { - BIOSAXS.proposal.onInitialized.attach(function() { - _this.stockSolutionGrid.refresh(BIOSAXS.proposal.getStockSolutionsByDewarId(_this.dewar.dewarId)); - _this.stockSolutionGrid.grid.setLoading(false); - }); - BIOSAXS.proposal.init(); - }); - adapter.onError.attach(function(sender, data) { - _this.stockSolutionGrid.grid.setLoading(false); - BUI.showError(data); - }); - stockSolution.boxId = _this.dewar.dewarId; - adapter.saveStockSolution(stockSolution); -}; - -CaseForm.prototype.fillStores = function() { - var _this = this; - this.panel.setLoading("Loading Labcontacts from database"); + - var proposal = BUI.getProposal(); - proposal.onDataRetrieved.attach(function(sender, data) { - _this.labContactForSendingStore.loadData(data, false); - _this.labContactForReturnStore.loadData(data, false); - _this.panel.setLoading(false); - }); - proposal.getLabContactsByProposalId(); +function ModelVisualizerForm(args){ + this.id =BUI.id(); + this.width = 600; + this.height = 400; + if (args!= null){ + if (args.width != null){ + this.width = args.width; + } + if (args.height != null){ + this.height = args.height; + } + } }; -CaseForm.prototype.refresh = function(dewar) { - this.setDewar(dewar); - this.stockSolutionGrid.refresh(BIOSAXS.proposal.getStockSolutionsByDewarId(dewar.dewarId)); +ModelVisualizerForm.prototype._getFirHTML = function(modelId, width, height, type, desc) { + var html = ""; + html = html + ''; + html = html + ''; + html = html + ''; + html = html + '
dammin.' + type + '
' + desc + '
'; + return html; }; -CaseForm.prototype.getDewar = function() { - this.dewar.code = Ext.getCmp("dewar_code").getValue(); - this.dewar.comments = Ext.getCmp("dewar_comments").getValue(); - this.dewar.trackingNumberFromSynchrotron = Ext.getCmp("dewar_trackingNumberFromSynchrotron").getValue(); - this.dewar.trackingNumberToSynchrotron = Ext.getCmp("dewar_trackingNumberToSynchrotron").getValue(); - this.dewar.transportValue = Ext.getCmp("dewar_transportValue").getValue(); - this.dewar.storageLocation = Ext.getCmp("dewar_storageLocation").getValue(); - this.dewar.firstExperimentId = this.macromoleculeCombo.getValue(); - return this.dewar; +ModelVisualizerForm.prototype.getItems = function(modelPanel){ + _this = this; + var height = _this.height/2 - 60; + var width = _this.width/2 - 10; + + return Ext.create('Ext.container.Container', { + layout: { + type: 'vbox', // Arrange child items vertically + }, + items: [ + modelPanel, + { + xtype : 'container', + layout: { + type: 'hbox', // Arrange child items vertically + }, + items : [{ + html : this._getFirHTML("id", width, height, "fir", "Fit of the simulated scattering curve versus a smoothed experimental data (spline interpolation)"), + height :height, + width : width, + padding: 2 + }, + { + html : this._getFirHTML("id", width, height, "fit", "Fit of the simulated scattering curve versus the experimental data."), + height : height, + width : width, + padding: 2 + }] + } + + ] + }); }; -CaseForm.prototype.setDewar = function(dewar) { - this.dewar = dewar; - Ext.getCmp("dewar_code").setValue(this.dewar.code); - Ext.getCmp("dewar_dewarStatus").setText(new String(this.dewar.dewarStatus).toUpperCase()); - Ext.getCmp("dewar_comments").setValue(this.dewar.comments); - Ext.getCmp("dewar_trackingNumberFromSynchrotron").setValue(this.dewar.trackingNumberFromSynchrotron); - Ext.getCmp("dewar_trackingNumberToSynchrotron").setValue(this.dewar.trackingNumberToSynchrotron); - Ext.getCmp("dewar_transportValue").setValue(this.dewar.transportValue); - Ext.getCmp("dewar_storageLocation").setValue(this.dewar.storageLocation); - if (dewar.sessionVO != null) { - this.macromoleculeCombo.setValue(dewar.sessionVO.sessionId); +ModelVisualizerForm.prototype.refresh = function(models){ + var input = []; +// var colors = ["008000", "F0A804", "0000FF", "800080", "C0C0C0"]; + for (var i = 0; i < models.length; i++) { + console.log(BUI.rainbow(models.length, i).replace("#", "0x")); + input.push({ + color: BUI.rainbow(models.length, i).replace("#", "0x"), + modelId: models[i].modelId, + opacity: 0.8, + radius: 3, + title: BUI.getFileNameByPath(models[i].pdbFile), + type: "SHAPEDETERMINATIONMODEL" + + }); } + + this.panel.removeAll(); + this.panel.add( + _this.getItems( + new PDBViewer({ + width : this.width - 10, + height : (this.height/2) - 10, + title : "" + }).draw(input) + ) + ); + + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, data) { + var splitted = data.toString().split("\n"); + var array = []; + for ( var i = 0; i < splitted.length; i++) { + var line = splitted[i].trim(); + var line_splited = line.split(/\s*[\s,]\s*/); + if (line_splited.length == 4) { + array.push([ Number(line_splited[0]), BUI.getStvArray(line_splited[1], line_splited[2]), BUI.getStvArray(line_splited[3], 0) ]); + } + } + + var id = (_this.id + "firid"); + var dygraphWidget = new StdDevDyGraph(id, { + width : (_this.width/2) - 10, + height :(_this.height/2) -110, + xlabel : 'q(nm-1)' + }); + dygraphWidget.draw(array, [ "#FF0000", "#0000FF", "#FF00FF" ], [ 's', 'I(exp)', 'I(sim)' ]); + }); + + adapter.getModelFile("FIR", models[0].modelId); + + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, data) { + var splitted = data.toString().split("\n"); + var array = []; + for ( var i = 0; i < splitted.length; i++) { + var line = splitted[i].trim(); + var line_splited = line.split(/\s*[\s,]\s*/); + if (line_splited.length == 3) { + array.push([ Number(line_splited[0]), BUI.getStvArray(line_splited[1], 0), BUI.getStvArray(line_splited[2], 0) ]); + } + } + + var id = (_this.id + "fitid"); + + var dygraphWidget = new StdDevDyGraph(id, { + width : (_this.width/2) - 10, + height : (_this.height/2) - 110, + xlabel : 'q(nm-1)' + }); + dygraphWidget.draw(array, [ "#FF0000", "#0000FF" ], [ "s", "I(exp)", "I(sim)" ]); + }); + adapter.getModelFile("FIT", models[0].modelId); }; -CaseForm.prototype.getSessionCombo = function() { - this.macromoleculeCombo = BIOSAXS_COMBOMANAGER.getComboSessions(BIOSAXS.proposal.getSessions(), { - labelWidth : 100, - margin : '5 0 00 0', - width : 250 +ModelVisualizerForm.prototype.getPanel = function(modelList){ + _this = this; + this.modelList = modelList; + this.panel = Ext.create('Ext.Panel', { + title: 'Results', + width: this.width, + height: this.height, + layout: { + type: 'vbox', // Arrange child items vertically +// align: 'stretch' // Each takes up full width + }, + items: [ + + ], + listeners : { + afterrender : function(grid, eOpts) { +// alert(_this.modelList) + } + } }); - return this.macromoleculeCombo; + + return this.panel; + }; -CaseForm.prototype.getInformationPanel = function() { - if (this.panel == null) { - this.informationPanel = Ext.create('Ext.form.Panel', { - width : this.width - 10, - border : 0, - items : [ { - xtype : 'container', - margin : "2 2 2 2", - collapsible : false, - defaultType : 'textfield', - layout : 'anchor', - items : [ { - xtype : 'container', - layout : 'hbox', - items : [ { - xtype : 'requiredtext', - fieldLabel : 'Code', - allowBlank : false, - name : 'code', - id : 'dewar_code', - anchor : '50%' - }, { - xtype : 'label', - margin : '0 0 0 20', - readOnly : true, - id : 'dewar_dewarStatus', - anchor : '50%' - } ] - }, this.getSessionCombo(), { - margin : '5 0 0 0', - xtype : 'textareafield', - name : 'comments', - id : 'dewar_comments', - width : this.width - 50, - fieldLabel : 'Comments' - } ] - }, { - xtype : 'fieldset', - title : 'Courier Accounts Details for Return', - collapsible : false, - layout : 'anchor', - defaults : { - anchor : '100%' - }, - items : [ { - xtype : 'container', - layout : 'hbox', - items : [ { - xtype : 'textfield', - labelWidth : 200, - width : 280, - fieldLabel : 'Track Number From Synchrotron', - id : 'dewar_trackingNumberFromSynchrotron' - }, { - xtype : 'numberfield', - width : 190, - labelWidth : 110, - margin : '0 0 0 30', - fieldLabel : 'Transport Value', - id : 'dewar_transportValue' - } - - ] - }, { - xtype : 'container', - layout : 'hbox', - margin : '10 0 0 0', - items : [ - - { - xtype : 'textfield', - labelWidth : 200, - width : 280, - fieldLabel : 'Track Number To Synchrotron', - id : 'dewar_trackingNumberToSynchrotron' - }, { - xtype : 'textfield', - margin : '0 0 0 30', - width : 190, - labelWidth : 110, - fieldLabel : 'Storage Location', - id : 'dewar_storageLocation' - } - - ] - } ] - } ] - }); - } - - return this.informationPanel; -}; - -CaseForm.prototype.getPanel = function(dewar) { - this.dewar = dewar; - this.panel = Ext.createWidget({ - xtype : 'container', - layout : 'vbox', - width : this.width, - border : 0, - items : [ { - items : { - xtype : "container", - layout : "vbox", - margin : "5 5 5 5", - items : [ this.getInformationPanel(dewar), this.stockSolutionGrid.getPanel() ] - - } - } ] - }); - - this.refresh(dewar); - return this.panel; - -}; - -CaseForm.prototype.input = function() { - return { - dewar : { - "dewarId" : 305861, - "code" : "ESRF-TEST", - "comments" : "comments", - "storageLocation" : "FRIDGE", - "dewarStatus" : "opened", - "timeStamp" : null, - "isStorageDewar" : null, - "barCode" : "ESRF305861", - "customsValue" : null, - "transportValue" : null, - "trackingNumberToSynchrotron" : "3333", - "trackingNumberFromSynchrotron" : "224466", - "type" : "Dewar", - "sessionVO" : { - "sessionId" : 31697, - "expSessionPk" : null, - "projectCode" : null, - "startDate" : "2012 07 21", - "endDate" : "2012 07 23", - "beamlineName" : "BM29", - "scheduled" : 1, - "nbShifts" : 2, - "comments" : null, - "beamlineOperator" : "PERNOT P", - "usedFlag" : null, - "sessionTitle" : null, - "structureDeterminations" : null, - "dewarTransport" : null, - "databackupFrance" : null, - "databackupEurope" : null, - "visit_number" : null, - "operatorSiteNumber" : "14061", - "timeStamp" : "2012 04 25" - } - }, - proposal : { - "assemblies" : [], - "sessions" : [ { - "sessionId" : 31697, - "expSessionPk" : null, - "projectCode" : null, - "startDate" : "2012 07 21", - "endDate" : "2012 07 23", - "beamlineName" : "BM29", - "scheduled" : 1, - "nbShifts" : 2, - "comments" : null, - "beamlineOperator" : "PERNOT P", - "usedFlag" : null, - "sessionTitle" : null, - "structureDeterminations" : null, - "dewarTransport" : null, - "databackupFrance" : null, - "databackupEurope" : null, - "visit_number" : null, - "operatorSiteNumber" : "14061", - "timeStamp" : "2012 04 25" - } ], - "labcontacts" : [ { - "labContactId" : 787, - "personVO" : { - "personId" : 304252, - "personUUID" : null, - "familyName" : "KIM", - "givenName" : "Henry", - "title" : null, - "emailAddress" : "henry-sung.kim@ibs.fr", - "phoneNumber" : "", - "login" : "", - "passwd" : "", - "faxNumber" : "" - }, - "cardName" : "KIM-Institut de Bio", - "defaultCourrierCompany" : "22", - "courierAccount" : "", - "billingReference" : "", - "dewarAvgCustomsValue" : 0, - "dewarAvgTransportValue" : 0 - } ], - "buffers" : [ { - "bufferId" : 811, - "proposalId" : 3124, - "safetyLevelId" : null, - "name" : "EDTA", - "acronym" : "EDTA", - "ph" : null, - "composition" : "", - "bufferhasadditive3VOs" : [], - "comments" : "" - }, { - "bufferId" : 810, - "proposalId" : 3124, - "safetyLevelId" : null, - "name" : "HEPES", - "acronym" : "HEPES", - "ph" : null, - "composition" : "", - "bufferhasadditive3VOs" : [], - "comments" : "" - } ], - "shippings" : [ { - "shippingId" : 304107, - "shippingName" : "TEST", - "deliveryAgentAgentName" : null, - "deliveryAgentShippingDate" : null, - "deliveryAgentDeliveryDate" : null, - "deliveryAgentAgentCode" : null, - "deliveryAgentFlightCode" : null, - "shippingStatus" : "opened", - "timeStamp" : "2013 09 25", - "laboratoryId" : null, - "isStorageShipping" : null, - "creationDate" : "2013 09 25", - "comments" : "test", - "sendingLabContactVO" : { - "labContactId" : 787, - "personVO" : { - "personId" : 304252, - "personUUID" : null, - "familyName" : "KIM", - "givenName" : "Henry", - "title" : null, - "emailAddress" : "henry-sung.kim@ibs.fr", - "phoneNumber" : "", - "login" : "", - "passwd" : "", - "faxNumber" : "" - }, - "cardName" : "KIM-Institut de Bio", - "defaultCourrierCompany" : "22", - "courierAccount" : "", - "billingReference" : "", - "dewarAvgCustomsValue" : 0, - "dewarAvgTransportValue" : 0 - }, - "returnLabContactVO" : { - "labContactId" : 787, - "personVO" : { - "personId" : 304252, - "personUUID" : null, - "familyName" : "KIM", - "givenName" : "Henry", - "title" : null, - "emailAddress" : "henry-sung.kim@ibs.fr", - "phoneNumber" : "", - "login" : "", - "passwd" : "", - "faxNumber" : "" - }, - "cardName" : "KIM-Institut de Bio", - "defaultCourrierCompany" : "22", - "courierAccount" : "", - "billingReference" : "", - "dewarAvgCustomsValue" : 0, - "dewarAvgTransportValue" : 0 - }, - "returnCourier" : null, - "dateOfShippingToUser" : null, - "shippingType" : "DewarTracking", - "dewarVOs" : [ { - "dewarId" : 305861, - "code" : "ESRF-TEST", - "comments" : "comments", - "storageLocation" : "FRIDGE", - "dewarStatus" : "opened", - "timeStamp" : null, - "isStorageDewar" : null, - "barCode" : "ESRF305861", - "customsValue" : null, - "transportValue" : null, - "trackingNumberToSynchrotron" : "3333", - "trackingNumberFromSynchrotron" : "224466", - "type" : "Dewar", - "sessionVO" : { - "sessionId" : 31697, - "expSessionPk" : null, - "projectCode" : null, - "startDate" : "2012 07 21", - "endDate" : "2012 07 23", - "beamlineName" : "BM29", - "scheduled" : 1, - "nbShifts" : 2, - "comments" : null, - "beamlineOperator" : "PERNOT P", - "usedFlag" : null, - "sessionTitle" : null, - "structureDeterminations" : null, - "dewarTransport" : null, - "databackupFrance" : null, - "databackupEurope" : null, - "visit_number" : null, - "operatorSiteNumber" : "14061", - "timeStamp" : "2012 04 25" - } - } ] - } ], - "macromolecules" : [ { - "macromoleculeId" : 5933, - "safetylevelId" : null, - "proposalId" : 3124, - "name" : "A", - "acronym" : "A", - "molecularMass" : "", - "extintionCoefficient" : "", - "sequence" : null, - "creationDate" : null, - "comments" : "", - "macromoleculeregion3VOs" : [], - "stoichiometry3VOsForHostMacromoleculeId" : [], - "structure3VOs" : [] - } ], - "stockSolutions" : [ { - "stockSolutionId" : 6, - "proposalId" : 3124, - "macromoleculeId" : 5933, - "bufferId" : 811, - "instructionSet3VO" : null, - "boxId" : 305861, - "storageTemperature" : "20", - "volume" : "300", - "concentration" : "1.2", - "comments" : "Buffer EDTA with A", - "name" : "A_EDTA_1.2", - "samples" : [] - } ] - } - - }; -}; - -CaseForm.prototype.test = function(targetId) { - var caseForm = new CaseForm(); - BIOSAXS.proposal = new Proposal(caseForm.input().proposal); - var panel = caseForm.getPanel(caseForm.input().dewar); - panel.render(targetId); -}; - -/** - * Example form - * - * @witdh - * @height - */ -function ExampleForm(args) { - this.id = BUI.id(); - this.width = 700; - this.height = 500; - - if (args != null) { - if (args.width != null) { - this.width = args.width; - } - if (args.height != null) { - this.height = args.height; - } - } -} - -ExampleForm.prototype._getItems = function() { - return [{ - fieldLabel : 'First Name', - name : 'first', - allowBlank : false - }, { - fieldLabel : 'Last Name', - name : 'last', - allowBlank : false - } ]; -}; - -ExampleForm.prototype._getItems = function() { - return [ ]; -}; - -ExampleForm.prototype._getButtons = function() { - return [ ]; -}; - -ExampleForm.prototype.getPanel = function() { - this.panel = Ext.create('Ext.form.Panel', { - width : this.width, - height : this.height, - margin : 10, - border : 1, - defaultType : 'textfield', - items : this._getItems(), - buttons : this._getButtons() - }); - return this.panel; -}; - -/** It populates the form **/ -ExampleForm.prototype.refresh = function(macromolecule) { -}; - -function ExperimentForm(args) { - this.id = BUI.id(); - - if (args != null) { - } - - this.onSaved = new Event(this); -} - -ExperimentForm.prototype._getItems = function(experiment) { - this.experiment = experiment; - var typeCombo = Ext.create('Ext.form.ComboBox', { - id : this.id + 'type', - fieldLabel : 'Type', - store : [ "STATIC", "CALIBRATION", "HPLC" ], - queryMode : 'local', - labelWidth : 120, - displayField : 'name', - valueField : 'name', - value : experiment.json.type, - disabled : (experiment.json.type == 'TEMPLATE') - }); - - var items = []; - - - if (experiment.json.type == "HPLC" ){ - var typeMacromolecule = BIOSAXS_COMBOMANAGER.getComboMacromoleculeByMacromolecules(BIOSAXS.proposal.getMacromolecules(), {labelWidth : 120, width: "120px"}); - if (experiment.getHPLCMacromolecule() != null){ - typeMacromolecule.setValue(experiment.getHPLCMacromolecule().macromoleculeId); - items.push(typeMacromolecule); - } - } - - - items.push(typeCombo, { - id : this.id + 'name', - xtype : 'textfield', - fieldLabel : 'Name', - labelWidth : 120, - width : '100%', - value : experiment.json.name - }, { - id : this.id + 'comments', - xtype : 'textareafield', - name : 'comments', - fieldLabel : 'Comments', - labelWidth : 120, - height : 120, - width : '100%', - value : experiment.json.comments - }); - return items; -}; -ExperimentForm.prototype.getPanel = function(experiment) { - var _this = this; - - this.panel = Ext.createWidget({ - xtype : 'container', - layout : 'vbox', - border : 0, - style : { - padding : '10px' - }, - fieldDefaults : { - labelAlign : 'left', - labelWidth : 50 - }, - items : this._getItems(experiment) - }); - return this.panel; -}; - -ExperimentForm.prototype.input = function() { - return new ExperimentHeaderForm().input(); -}; - -ExperimentForm.prototype.test = function(targetId) { - var experimentForm = new ExperimentForm(); - var panel = experimentForm.getPanel(new Experiment(experimentForm.input().experiment)); - panel.render(targetId); -}; - -/** - * Shows the header for the experiments changing the color and parameters depending on experiment type - * - */ -function ExperimentHeaderForm(args) { - this.id = BUI.id(); - this.backgroundColor = '#FFFFFF'; -} - -ExperimentHeaderForm.prototype.getHTMLSource = function(experiment) { - var html = BUI.createFormLabel("Name :", experiment.json.name, 75, 400, this.backgroundColor); - if (experiment.json.type == "HPLC") { - if (experiment.getHPLCMacromolecule() != null){ - html = html + BUI.createFormLabel("Molecule :", experiment.getHPLCMacromolecule().acronym, 75, 400, this.backgroundColor); - } - } - else{ - html = html + BUI.createFormLabel("Type :", experiment.json.type, 75, 400, this.backgroundColor); - } - - html = html + BUI.createFormLabel("Date :", experiment.json.creationDate, 75, 400, this.backgroundColor); - return html; -}; - -ExperimentHeaderForm.prototype.getHTMLDownload = function(experiment) { - var bgcolor = "background-color:" + this.backgroundColor + ";"; - var html = "
"; - if ((experiment.json.type == "CALIBATION") || (experiment.json.type == "STATIC")) { - html = html + " Download Source File
"; - html = html - + ""; - } - if (experiment.json.type == "TEMPLATE") { - html = html - + " Download Source File"; - } - - if (experiment.json.type == "HPLC") { - html = html + " Download h5 File"; - } - - return html; -}; - -ExperimentHeaderForm.prototype.getTopPanel = function(experiment) { - return { - xtype : 'container', - layout : 'hbox', - items : [ { - margin : "0 0 0 0", - width : 475, - border : 0, - html : this.getHTMLSource(experiment) - }, { - margin : "0 0 0 0", - width : 475, - border : 0, - html : this.getHTMLDownload(experiment) - } ] - }; -}; - -ExperimentHeaderForm.prototype.getButton = function(experiment) { - var _this = this; - return Ext.create('Ext.Button', { - text : 'EDIT', - minWidth : '100', - margin : '10 0 0 30', - handler : function() { - var experimentWindow = new ExperimentWindow(); - experimentWindow.onSaved.attach(function(sender, data) { - _this.experiment.json.name = data.name; - _this.experiment.json.type = data.type; - _this.experiment.json.comments = data.comments; - _this.panel.remove(_this.panel.items.items[0]); - _this.panel.remove(_this.panel.items.items[0]); - _this.panel.insert(_this.getTopPanel(_this.experiment)); - _this.panel.insert(_this.getBottomPanel(_this.experiment)); - }); - experimentWindow.show(experiment); - } - }); -}; - -ExperimentHeaderForm.prototype.getBottomPanel = function(experiment) { - return { - xtype : 'container', - layout : 'hbox', - margin : '10 0 0 0', - items : [ this.getComments(experiment), this.getButton(experiment) ] - }; -}; - -ExperimentHeaderForm.prototype.getComments = function(experiment) { - return { - xtype : 'textareafield', - labelStyle : 'font-weight: bold;', - name : 'comments', - fieldLabel : 'Comments', - labelWidth : 70, - height : 40, - minWidth : '450', - readOnly : true, - value : experiment.json.comments - }; -}; - -ExperimentHeaderForm.prototype.getPanel = function(experiment) { - this.experiment = experiment; - - if (experiment.json.type == 'CALIBRATION') { - this.backgroundColor = '#EFFBFB'; - } - if (experiment.json.type == 'TEMPLATE') { - this.backgroundColor = '#E0F8E6'; - } - - this.panel = Ext.create('Ext.container.Container', { - frame : false, - layout : 'vbox', - padding : 5, - bodyPadding : '5 5 0 0', - width : 890, - margin : '0 0 10 0', - height : 120, - style : { - borderColor : '#99bce8', - borderStyle : 'solid', - borderWidth : '1px', - 'background-color' : this.backgroundColor - }, - fieldDefaults : { - msgTarget : 'side', - labelWidth : 100 - }, - items : [ this.getTopPanel(experiment), this.getBottomPanel(experiment) ] - }); - return this.panel; -}; - -ExperimentHeaderForm.prototype.input = function() { - return { - experiment : DATADOC.getExperiment_10() - }; -}; - -ExperimentHeaderForm.prototype.test = function(targetId) { - var experimentHeaderForm = new ExperimentHeaderForm(); - var panel = experimentHeaderForm.getPanel(new Experiment(experimentHeaderForm.input().experiment)); - panel.render(targetId); - -}; - -/** - * Macromolecule form with the general parameters of a macromolecule - * - * @witdh - * @height - * - * #onSave button save has been clicked - * #onClose button close has been clicked - */ -function MacromoleculeForm(args) { - this.id = BUI.id(); - this.width = 700; - this.height = 500; - - if (args != null) { - if (args.width != null) { - this.width = args.width; - } - if (args.height != null) { - this.height = args.height; - } - } - - /** Events **/ - this.onSave = new Event(this); - this.onClose = new Event(this); -} - -/** Type : is the Ext type then requiredtext or textfield * */ -MacromoleculeForm.prototype._getFieldTextWithHelp = function(type, fieldLabel, fieldName, help) { - return Ext.create('Ext.container.Container', { - margin : "10 0 0 10", - items : [ { - xtype : type, - fieldLabel : fieldLabel, - name : fieldName, - id : this.id + fieldName - }, { - xtype : 'label', - forId : 'myFieldId', - text : help, - margin : "5 0 0 105", - cls : "inline-help" - } ] - }); -}; - -MacromoleculeForm.prototype._getNumericWithHelp = function(type, fieldLabel, fieldName, help) { - return Ext.create('Ext.container.Container', { - margin : "0 0 0 10", - items : [ { - xtype : type, - fieldLabel : fieldLabel, - name : fieldName, - id : this.id + fieldName, - decimalPrecision : 6, - width : 220 - }, { - xtype : 'label', - forId : 'myFieldId', - text : help, - margin : "5 0 0 10", - cls : "inline-help" - } ] - }); -}; - -MacromoleculeForm.prototype._getButtons = function() { - var _this = this; - return [ { - text : 'Save', - handler : function() { - _this._save(); - } - },{ - text : 'Close', - handler : function() { - _this.onClose.notify(); - - } - } ]; -}; - -/** It persits the macromolecule in the database **/ -MacromoleculeForm.prototype._persist = function(macromoleculeId, acronym, name, molecularMass, extintionCoefficient, comments, refractiveIndex, solventViscosity) { - - /** Checking not duplicated acronym **/ - if (((macromoleculeId == null) && (BIOSAXS.proposal.getMacromoleculeByAcronym(acronym) != null))== true){ - BUI.showError("Duplicated acronym"); - return; - } - - - if (macromoleculeId == null){ - /** new macromolecule **/ - this.macromolecule = {}; - this.macromolecule.macromoleculeId = null; - } - else{ - this.macromolecule.macromoleculeId = macromoleculeId; - } - - this.macromolecule["acronym"] = acronym; - this.macromolecule["name"] = name; - this.macromolecule["molecularMass"] = molecularMass; - this.macromolecule["extintionCoefficient"] = extintionCoefficient; - this.macromolecule["comments"] = comments; - this.macromolecule["symmetry"] = Ext.getCmp(this.id + 'comboSym').getValue(); - this.macromolecule["refractiveIndex"] = refractiveIndex; - this.macromolecule["solventViscosity"] = solventViscosity; - - var _this = this; - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, proposal) { - /** Updating the proposal global object **/ - BIOSAXS.proposal.setItems(proposal); - _this.panel.setLoading(false); - - var saved = BIOSAXS.proposal.getMacromoleculeByAcronym(acronym); - /** Refreshing to check that everything is ok **/ - _this.refresh(saved); - _this.onSave.notify(saved); - }); - - this.panel.setLoading("Saving Macromolecule") - adapter.saveMacromolecule(this.macromolecule); -}; - -/** Save the macromolecule in the DB **/ -MacromoleculeForm.prototype._save = function() { - - var _this = this; - - var acronym = this._getField("acronym"); - var name = this._getField("name"); - var molecularMass = this._getField("molecularMass"); - var extintionCoefficient = this._getField("extintionCoefficient"); - var comments = this._getField("comments"); - - var refractiveIndex = this._getField("refractiveIndex"); - var solventViscosity = this._getField("solventViscosity"); - - /** Checking required fields **/ - if (name == "") { - BUI.showError("Name field is mandatory"); - return; - } - if (acronym == "") { - BUI.showError("Acroynm field is mandatory"); - return; - } - - if (this.macromolecule != null){ - /** Checking if it is a new macromolecule **/ - if (this.macromolecule.macromoleculeId == null){ - /** Check if the acronym exists already **/ - this._persist(null, acronym, name, molecularMass, extintionCoefficient, comments, refractiveIndex, solventViscosity); - } - else{ - /** It is an update **/ - this._persist(this.macromolecule.macromoleculeId, acronym, name, molecularMass, extintionCoefficient, comments, refractiveIndex, solventViscosity); - } - } - else{ - /** It is a new macromolecule **/ - this._persist(null, acronym, name, molecularMass, extintionCoefficient, comments, refractiveIndex, solventViscosity); - } -}; - - - -MacromoleculeForm.prototype._getItems = function() { - var _this = this; - /** Symmetry combo box **/ - var symmetry = Ext.create('Ext.data.Store', { - fields : [ 's' ], - data : this._getSymmetries() - }); - - this.symmetryComboBox = Ext.create('Ext.form.ComboBox', { - fieldLabel : 'Symmetry', - store : symmetry, - id : this.id + 'comboSym', - queryMode : 'local', - displayField : 's', - valueField : 's', - value : "P1", - margin : "0 0 0 30", - width : 220 - }); - - return [this._getFieldTextWithHelp("requiredtext", "Name", "name", "Long name. i.e: Bovine serum albumin"), - this._getFieldTextWithHelp("requiredtext", "Acronym", "acronym", "Acronym will be used in the files and analisys. i.e: BSA"), - this._getFieldTextWithHelp("textfield", "Mol. Mass (Da)", "molecularMass", "Atomic mass estimation measured in Da"), - { - xtype : 'container', - layout : 'hbox', - margin : "10 0 0 0", - items :[ - this._getNumericWithHelp("numberfield", "Extinction coef.", "extintionCoefficient", ""), - this.symmetryComboBox - ] - }, - { - xtype : 'container', - layout : 'hbox', - margin : "5 0 0 0", - items :[ - this._getNumericWithHelp("numberfield", "Refractive Index", "refractiveIndex", "How radiation propagates through the medium"), - this._getNumericWithHelp("numberfield", "Solvent Viscosity", "solventViscosity", "") - ] - }, - { - id : this.id + "comments", - xtype : 'textareafield', - name : 'comments', - margin : '35 0 0 10', - fieldLabel : 'Comments', - width : this.width - 100 - } ]; -}; - -MacromoleculeForm.prototype._getSymmetries = function() { - return [ { - "s" : "P1" - }, { - "s" : "P2" - }, { - "s" : "P3" - }, { - "s" : "P4" - }, { - "s" : "P5" - }, { - "s" : "P6" - }, { - "s" : "P32" - }, { - "s" : "P42" - }, { - "s" : "P52" - }, { - "s" : "P62" - }, { - "s" : "P222" - } ] -}; - -MacromoleculeForm.prototype.getPanel = function() { - this.panel = Ext.create('Ext.form.Panel', { - width : this.width, - height : this.height, - margin : 10, - border : 1, - defaultType : 'textfield', - items : this._getItems(), - buttons : this._getButtons() - }); - return this.panel; -}; - - -/** Populates each text field by field name and value **/ -MacromoleculeForm.prototype._populateField = function(fieldName, value) { - if (value != null){ - Ext.getCmp(this.id + fieldName).setValue(value); - } -}; - -/** Gets the value of a textfield **/ -MacromoleculeForm.prototype._getField = function(fieldName) { - return Ext.getCmp(this.id + fieldName).getValue(); -}; - - -/** It populates the form **/ -MacromoleculeForm.prototype.refresh = function(macromolecule) { - this.macromolecule = macromolecule; - if (macromolecule != null){ - this._populateField("name", macromolecule.name); - this._populateField("acronym", macromolecule.acronym); - this._populateField("extintionCoefficient", macromolecule.extintionCoefficient); - this._populateField("molecularMass", macromolecule.molecularMass); - this._populateField("comments", macromolecule.comments); - this._populateField("refractiveIndex", macromolecule.refractiveIndex); - this._populateField("solventViscosity", macromolecule.solventViscosity); - if (macromolecule.symmetry != null){ - Ext.getCmp(this.id + 'comboSym').setValue(macromolecule.symmetry); - } - } -}; - - -MacromoleculeForm.prototype.input = function() { - return {}; -}; - - -/** It populates the form **/ -MacromoleculeForm.prototype.test = function(targetId) { - var macromoleculeForm = new MacromoleculeForm(); - macromoleculeForm.onClose.attach(function(sender){ - alert("Click on close"); - }); - var panel = macromoleculeForm.getPanel(); - panel.render(targetId); -}; - - - - - -function ModelVisualizerForm(args){ - this.id =BUI.id(); - this.width = 600; - this.height = 400; - if (args!= null){ - if (args.width != null){ - this.width = args.width; - } - if (args.height != null){ - this.height = args.height; - } - } -}; - -ModelVisualizerForm.prototype._getFirHTML = function(modelId, width, height, type, desc) { - var html = ""; - html = html + ''; - html = html + ''; - html = html + ''; - html = html + '
dammin.' + type + '
' + desc + '
'; - return html; -}; - -ModelVisualizerForm.prototype.getItems = function(modelPanel){ - _this = this; - var height = _this.height/2 - 60; - var width = _this.width/2 - 10; - - return Ext.create('Ext.container.Container', { - layout: { - type: 'vbox', // Arrange child items vertically - }, - items: [ - modelPanel, - { - xtype : 'container', - layout: { - type: 'hbox', // Arrange child items vertically - }, - items : [{ - html : this._getFirHTML("id", width, height, "fir", "Fit of the simulated scattering curve versus a smoothed experimental data (spline interpolation)"), - height :height, - width : width, - padding: 2 - }, - { - html : this._getFirHTML("id", width, height, "fit", "Fit of the simulated scattering curve versus the experimental data."), - height : height, - width : width, - padding: 2 - }] - } - - ] - }); -}; - -ModelVisualizerForm.prototype.refresh = function(models){ - var input = []; -// var colors = ["008000", "F0A804", "0000FF", "800080", "C0C0C0"]; - for (var i = 0; i < models.length; i++) { - console.log(BUI.rainbow(models.length, i).replace("#", "0x")); - input.push({ - color: BUI.rainbow(models.length, i).replace("#", "0x"), - modelId: models[i].modelId, - opacity: 0.8, - radius: 3, - title: BUI.getFileNameByPath(models[i].pdbFile), - type: "SHAPEDETERMINATIONMODEL" - - }); - } - - this.panel.removeAll(); - this.panel.add( - _this.getItems( - new PDBViewer({ - width : this.width - 10, - height : (this.height/2) - 10, - title : "" - }).draw(input) - ) - ); - - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, data) { - var splitted = data.toString().split("\n"); - var array = []; - for ( var i = 0; i < splitted.length; i++) { - var line = splitted[i].trim(); - var line_splited = line.split(/\s*[\s,]\s*/); - if (line_splited.length == 4) { - array.push([ Number(line_splited[0]), BUI.getStvArray(line_splited[1], line_splited[2]), BUI.getStvArray(line_splited[3], 0) ]); - } - } - - var id = (_this.id + "firid"); - var dygraphWidget = new StdDevDyGraph(id, { - width : (_this.width/2) - 10, - height :(_this.height/2) -110, - xlabel : 'q(nm-1)' - }); - dygraphWidget.draw(array, [ "#FF0000", "#0000FF", "#FF00FF" ], [ 's', 'I(exp)', 'I(sim)' ]); - }); - - adapter.getModelFile("FIR", models[0].modelId); - - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, data) { - var splitted = data.toString().split("\n"); - var array = []; - for ( var i = 0; i < splitted.length; i++) { - var line = splitted[i].trim(); - var line_splited = line.split(/\s*[\s,]\s*/); - if (line_splited.length == 3) { - array.push([ Number(line_splited[0]), BUI.getStvArray(line_splited[1], 0), BUI.getStvArray(line_splited[2], 0) ]); - } - } - - var id = (_this.id + "fitid"); - - var dygraphWidget = new StdDevDyGraph(id, { - width : (_this.width/2) - 10, - height : (_this.height/2) - 110, - xlabel : 'q(nm-1)' - }); - dygraphWidget.draw(array, [ "#FF0000", "#0000FF" ], [ "s", "I(exp)", "I(sim)" ]); - }); - adapter.getModelFile("FIT", models[0].modelId); -}; - -ModelVisualizerForm.prototype.getPanel = function(modelList){ - _this = this; - this.modelList = modelList; - this.panel = Ext.create('Ext.Panel', { - title: 'Results', - width: this.width, - height: this.height, - layout: { - type: 'vbox', // Arrange child items vertically -// align: 'stretch' // Each takes up full width - }, - items: [ - - ], - listeners : { - afterrender : function(grid, eOpts) { -// alert(_this.modelList) - } - } - }); - - return this.panel; - -}; - - -/** - * Example form - * - * @witdh - * @height - */ -function MolarityForm(args) { - this.id = BUI.id(); - this.width = 700; - this.height = 500; - - if (args != null) { - if (args.width != null) { - this.width = args.width; - } - if (args.height != null) { - this.height = args.height; - } - } - - this.onSave = new Event(this); - this.onClose = new Event(this); -} - - -MolarityForm.prototype._getNumericWithHelp = function(type, fieldLabel, fieldName, help) { - return Ext.create('Ext.container.Container', { - margin : "10 0 0 10", - items : [ { - xtype : type, - fieldLabel : fieldLabel, - name : fieldName, - id : this.id + fieldName, - decimalPrecision : 6 - }, { - xtype : 'label', - forId : 'myFieldId', - text : help, - margin : "5 0 0 105", - cls : "inline-help" - } ] - }); -}; - - -MolarityForm.prototype._getItems = function() { - this.macromoleculeCombo = BIOSAXS_COMBOMANAGER.getComboMacromoleculeByMacromolecules(this.getMacromuleculesCandidates(this.macromolecule), { - width : 250, - labelWidth : 100, - margin : 10 - }); - - return [ { - xtype : 'container', - flex : 1, - margin : '10 0 0 10', - border : 0, - layout : 'anchor', - defaultType : 'requiredtext', - items : [ this.macromoleculeCombo, - this._getNumericWithHelp("textfield", "Ratio", "ratio", "Number in assymmetric units") - ] - } ]; -}; - -MolarityForm.prototype._persist = function() { - var _this = this; - var macromoleculeId = this.macromoleculeCombo.getValue(); - var ratio = Ext.getCmp(this.id + "ratio").getValue(); - var comments = "Not used yet"; - var dataAdapter = new BiosaxsDataAdapter(); - this.panel.setLoading("Saving"); - dataAdapter.onSuccess.attach(function(sender, args) { - _this.onSave.notify(); - }); - dataAdapter.saveStoichiometry(this.macromolecule.macromoleculeId, macromoleculeId, ratio, comments); -}; - -MolarityForm.prototype._getButtons = function() { - var _this = this; - - function onClose() { - _this.onClose.notify(); - } - - return [ { - text : 'Save', - handler : function() { - _this._persist(); - } - }, { - text : 'Cancel', - handler : function() { - onClose(); - } - } ]; -}; - -MolarityForm.prototype.getPanel = function() { - this.panel = Ext.create('Ext.form.Panel', { -// width : null, - height : this.height, - margin : 2, - border : 1, - defaultType : 'requiredtext', - items : this._getItems(), - buttons : this._getButtons() - }); - return this.panel; -}; - -/** macromolecules contains all macromolecules except this one **/ -MolarityForm.prototype.getMacromuleculesCandidates = function(macromolecule) { - var macromolecules = []; - if ( BIOSAXS.proposal.macromolecules){ - for (var i = 0; i < BIOSAXS.proposal.macromolecules.length; i++) { - var m = BIOSAXS.proposal.macromolecules[i]; - if (this.macromolecule != null){ - if (m.macromoleculeId != this.macromolecule.macromoleculeId) { - macromolecules.push(m); - } - } - } - } - return macromolecules; -}; - - -/** It populates the form **/ -MolarityForm.prototype.refresh = function(macromolecule) { - this.macromolecule = macromolecule; - -}; - - -MolarityForm.prototype.input = function() { - return {}; -}; - - -/** It populates the form **/ -MolarityForm.prototype.test = function(targetId) { - var macromoleculeForm = new MolarityForm(); - var panel = macromoleculeForm.getPanel(); - panel.render(targetId); -}; - -///** -// * -// * @witdh -// * @height -// */ -//function MacromoleculeForm(args) { -// this.id = BUI.id(); -// this.width = 700; -// this.height = 500; -// -// if (args != null) { -// if (args.width != null) { -// this.width = args.width; -// } -// if (args.height != null) { -// this.height = args.height; -// } -// } -// -// this.onClose = new Event(this); -//} -// -//MacromoleculeForm.prototype.getMacromolecule = function() { -// this.macromolecule.name = Ext.getCmp(this.panel.getItemId()).getValues().name; -// this.macromolecule.acronym = Ext.getCmp(this.panel.getItemId()).getValues().acronym; -// this.macromolecule.comments = Ext.getCmp(this.panel.getItemId()).getValues().comments; -// this.macromolecule.extintionCoefficient = Ext.getCmp(this.panel.getItemId()).getValues().extintionCoefficient; -// this.macromolecule.molecularMass = Ext.getCmp(this.panel.getItemId()).getValues().molecularMass; -// return this.macromolecule; -//}; -// -//MacromoleculeForm.prototype.setMacromolecule = function(macromolecule) { -// this.pdbStore.loadData(macromolecule.structure3VOs); -// -//}; -// -//MacromoleculeForm.prototype.getForm = function(macromolecule) { -// this.panel = Ext.createWidget('form', { -// frame : false, -// border : 0, -// padding : 15, -// width : 550, -// height : 350, -// items : [ { -// xtype : 'container', -// layout : 'hbox', -// items : [ { -// xtype : 'container', -// flex : 1, -// border : false, -// layout : 'anchor', -// defaultType : 'requiredtext', -// items : [ { -// fieldLabel : 'Name', -// name : 'name', -// anchor : '95%', -// tooltip : "Name of the macromolecule", -// value : macromolecule.name -// }, { -// fieldLabel : 'Acronym', -// name : 'acronym', -// anchor : '95%', -// value : macromolecule.acronym -// } ] -// }, { -// xtype : 'container', -// flex : 1, -// layout : 'anchor', -// defaultType : 'textfield', -// items : [ { -// xtype : 'numberfield', -// fieldLabel : 'Mol. Mass (Da)', -// name : 'molecularMass', -// value : macromolecule.molecularMass, -// decimalPrecision : 6 -// }, { -// xtype : 'numberfield', -// fieldLabel : 'Extinction coef.', -// name : 'extintionCoefficient', -// value : macromolecule.extintionCoefficient, -// decimalPrecision : 6 -// } ] -// } ] -// }, { -// xtype : 'textareafield', -// name : 'comments', -// fieldLabel : 'Comments', -// value : macromolecule.comments, -// width : this.width - 10 -// }, -// -// { -// html : BUI.getWarningHTML("The macromolecule is unsaved. Save the macromolecule to get access to other tabs"), -// margin : "150 10 10 10", -// id : this.id + "unsavedWarning", -// hidden : !(!macromolecule.macromoleculeId) -// } -// -// ] -// }); -// return this.panel; -//}; -// -//MacromoleculeForm.prototype.save = function() { -// var _this = this; -// -// var adapter = new BiosaxsDataAdapter(); -// adapter.onSuccess.attach(function(sender, proposal) { -// BIOSAXS.proposal.setItems(proposal); -// _this.panel.setLoading(false); -// -// Ext.getCmp(_this.id + "assembly").enable() -// Ext.getCmp(_this.id + "advanced").enable(); -// Ext.getCmp(_this.id + "unsavedWarning").hide(); -// }); -// -// if (this.getMacromolecule().name == "") { -// BUI.showError("Name field is mandatory"); -// return; -// } -// if (this.getMacromolecule().acronym == "") { -// BUI.showError("Acroynm field is mandatory"); -// return; -// } -// -// /** Check if acronym is unique * */ -// if (this.getMacromolecule().macromoleculeId == null) { -// if (BIOSAXS.proposal.getMacromoleculeByAcronym(this.getMacromolecule().acronym) == null) { -// this.panel.setLoading("ISPyB: Saving Macromolecule") -// adapter.saveMacromolecule(this.getMacromolecule()); -// } else { -// alert("There is already an existing macromolecule with the same acronym"); -// -// } -// } else { -// this.panel.setLoading("ISPyB: Saving Macromolecule") -// adapter.saveMacromolecule(this.getMacromolecule()); -// } -// -//}; -// -//MacromoleculeForm.prototype.getPanel = function(macromolecule) { -// var _this = this; -// this.macromolecule = macromolecule; -// return Ext.createWidget('tabpanel', { -// height : this.height, -// margin : 5, -// plain : true, -// style : { -// padding : 5 -// }, -// items : [ { -// tabConfig : { -// title : "General", -// disabled : false -// }, -// items : [ this.getForm(macromolecule) ], -// bbar : [ "->", { -// text : 'Save', -// cls : 'btn-with-border', -// style : { -// -// border : 1 -// }, -// handler : function() { -// _this.save(); -// } -// }, { -// text : 'Close', -// cls : 'btn-with-border', -// handler : function() { -// _this.onClose.notify(); -// } -// } ] -// }, { -// tabConfig : { -// id : this.id + "assembly", -// title : "Assembly", -// tooltip : 'Description of subunits present in the macromolecule', -// // hidden : (!macromolecule.macromoleculeId), -// disabled : (!macromolecule.macromoleculeId) -// }, -// items : [ this.getMolarityGrid(macromolecule) ] -// }, { -// tabConfig : { -// id : this.id + "advanced", -// title : "Advanced Modeling", -// // hidden : (!macromolecule.macromoleculeId), -// tooltip : 'Definition of the description contacts and symetries', -// disabled : (!macromolecule.macromoleculeId) -// }, -// items : [ this.getRigidBodyForm(macromolecule) ] -// } ] -// }); -//}; -// -//MacromoleculeForm.prototype.getRigidBodyForm = function(macromolecule) { -// var _this = this; -// -// // [P1, P2, P3, P4 ,P5 ,P6 ,32, P42, P52, P62] + P222 -// var symmetry = Ext.create('Ext.data.Store', { -// fields : [ 's' ], -// data : [ { -// "s" : "P1" -// }, { -// "s" : "P2" -// }, { -// "s" : "P3" -// }, { -// "s" : "P4" -// }, { -// "s" : "P5" -// }, { -// "s" : "P6" -// }, { -// "s" : "P32" -// }, { -// "s" : "P42" -// }, { -// "s" : "P52" -// }, { -// "s" : "P62" -// }, { -// "s" : "P222" -// } ] -// }); -// -// if (macromolecule.symmetry == null) { -// macromolecule.symmetry = "P1"; -// } -// var comboBox = Ext.create('Ext.form.ComboBox', { -// fieldLabel : 'Symmetry', -// store : symmetry, -// id : 'comboSym', -// queryMode : 'local', -// displayField : 's', -// valueField : 's', -// value : macromolecule.symmetry, -// margin : "10 0 0 0", -// listeners : { -// change : function(combo, newValue, oldValue, eOpts) { -// macromolecule.symmetry = newValue; -// } -// } -// }); -// -// this.rigidBodyPanel = Ext.createWidget('form', { -// frame : false, -// border : 0, -// padding : 10, -// width : 550, -// height : 400, -// items : [ { -// xtype : 'container', -// layout : 'hbox', -// items : [ { -// xtype : 'container', -// border : false, -// layout : 'hbox', -// items : [ { -// xtype : 'label', -// forId : 'myFieldId', -// text : 'Contact Desc:', -// width : 105, -// margin : '0 0 0 0' -// }, { -// xtype : 'textfield', -// hideLabel : true, -// id : "contactsDescriptionFilePath", -// margin : '0 0 0 0', -// width : 300, -// value : macromolecule.contactsDescriptionFilePath -// }, { -// text : 'Upload', -// xtype : 'button', -// margin : "0 0 0 20", -// width : 100, -// handler : function() { -// _this._openUploadDialog(macromolecule.macromoleculeId, "CONTACTS", "Upload Contact Description File"); -// } -// } ] -// } ] -// }, -// -// comboBox, { -// xtype : 'checkbox', -// margin : '10 0 0 5', -// boxLabel : "I want rigid body modeling run on this stuff", -// checked : true, -// width : 300 -// }, _this.getPDBGrid(macromolecule) ] -// }); -// return this.rigidBodyPanel; -//}; -// -//MacromoleculeForm.prototype.update = function() { -// var _this = this; -// BIOSAXS.proposal.onInitialized.attach(function() { -// if (BIOSAXS.proposal != null) { -// var macromolecules = BIOSAXS.proposal.macromolecules; -// for (var i = 0; i < macromolecules.length; i++) { -// if (macromolecules[i].macromoleculeId == _this.macromolecule.macromoleculeId) { -// _this.macromolecule = macromolecules[i]; -// _this.setMacromolecule(_this.macromolecule); -// _this.molarityStore.loadData(_this.parseMolarityData(_this.macromolecule)); -// _this.pdbGrid.setLoading(false); -// Ext.getCmp("contactsDescriptionFilePath").setValue(_this.macromolecule.contactsDescriptionFilePath) -// _this.molarityGrid.setLoading(false); -// -// } -// } -// } -// }); -// this.molarityGrid.setLoading("Updating"); -// this.pdbGrid.setLoading("Updating"); -// BIOSAXS.proposal.init(); -//}; -// -///******************************************************************************* -// * MOLARITY GRID -// ******************************************************************************/ -// -//MacromoleculeForm.prototype.parseMolarityData = function(macromolecule) { -// var data = []; -// if (macromolecule.stoichiometry != null) { -// for (var i = 0; i < macromolecule.stoichiometry.length; i++) { -// data.push({ -// ratio : macromolecule.stoichiometry[i].ratio, -// acronym : macromolecule.stoichiometry[i].macromolecule3VO.acronym, -// comments : macromolecule.stoichiometry[i].macromolecule3VO.comments, -// stoichiometryId : macromolecule.stoichiometry[i].stoichiometryId, -// name : macromolecule.stoichiometry[i].macromolecule3VO.name -// }); -// } -// } -// return data; -//}; -// -//MacromoleculeForm.prototype.getMolarityGrid = function(macromolecule) { -// var _this = this; -// -// this.molarityStore = Ext.create('Ext.data.Store', { -// fields : [ 'acronym', 'ratio', 'comments', 'stoichiometryId', 'name' ], -// data : this.parseMolarityData(macromolecule), -// sorters : { -// property : 'ratio', -// direction : 'DESC' -// } -// }); -// -// this.molarityGrid = Ext.create('Ext.grid.Panel', { -// store : this.molarityStore, -// height : 350, -// padding : 5, -// columns : [ -// -// { -// text : 'Subunit', -// columns : [ { -// text : "Acronym", -// width : 100, -// hidden : false, -// dataIndex : 'acronym', -// sortable : true -// }, { -// text : "Name", -// width : 100, -// hidden : false, -// dataIndex : 'name', -// sortable : true -// }, { -// text : "Comments", -// width : 100, -// dataIndex : 'comments', -// sortable : true -// } ] -// }, { -// text : "Number
in assymmetric units", -// width : 150, -// dataIndex : 'ratio', -// tooltip : 'Number of times this subunit is present in the macromolecule', -// sortable : true -// }, { -// id : this.id + 'MOLARITY_REMOVE', -// flex : 0.1, -// sortable : false, -// renderer : function(value, metaData, record, rowIndex, colIndex, store) { -// return BUI.getRedButton('REMOVE'); -// } -// } ], -// listeners : { -// cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { -// if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'MOLARITY_REMOVE') { -// var dataAdapter = new BiosaxsDataAdapter(); -// dataAdapter.onSuccess.attach(function() { -// _this.molarityGrid.setLoading(false); -// _this.update(); -// }); -// dataAdapter.removeStoichiometry(record.data.stoichiometryId); -// _this.molarityGrid.setLoading("Removing Structure"); -// } -// } -// }, -// buttons : [ { -// text : 'Add molarity', -// handler : function() { -// -// function onClose() { -// w.destroy(); -// _this.update(); -// } -// var w = Ext.create('Ext.window.Window', { -// title : 'Molarity', -// height : 300, -// width : 500, -// modal : true, -// buttons : [ { -// text : 'Save', -// handler : function() { -// var macromoleculeId = (_this.macromoleculeCombo.getValue()); -// var ratio = Ext.getCmp(_this.id + "ratio").getValue(); -// var comments = ""; -// var dataAdapter = new BiosaxsDataAdapter(); -// dataAdapter.onSuccess.attach(function(sender, args) { -// _this.update(); -// w.destroy(); -// }); -// dataAdapter.saveStoichiometry(_this.macromolecule.macromoleculeId, macromoleculeId, ratio, comments); -// } -// }, { -// text : 'Cancel', -// handler : function() { -// onClose(); -// } -// } ], -// items : [ _this.getMolarityForm(macromolecule) ], -// listeners : { -// onEsc : function() { -// onClose(); -// }, -// close : function() { -// onClose(); -// } -// } -// }).show(); -// } -// } ] -// }); -// return this.molarityGrid; -//}; -// -///******************************************************************************* -// * PDB GRID -// ******************************************************************************/ -// -//MacromoleculeForm.prototype._openUploadDialog = function(macromoleculeId, type, title) { -// var _this = this; -// function onClose() { -// w.destroy(); -// _this.update(); -// } -// -// var w = Ext.create('Ext.window.Window', { -// title : title, -// height : 200, -// width : 400, -// modal : true, -// buttons : [ { -// text : 'Close', -// handler : function() { -// onClose(); -// } -// } ], -// layout : 'fit', -// items : { -// html : "" -// }, -// listeners : { -// onEsc : function() { -// onClose(); -// }, -// close : function() { -// onClose(); -// } -// } -// }).show(); -//}; -// -//MacromoleculeForm.prototype._getPlugins = function() { -// var _this = this; -// var plugins = []; -// // if (this.updateRowEnabled) { -// plugins.push(Ext.create('Ext.grid.plugin.RowEditing', { -// clicksToEdit : 1, -// listeners : { -// validateedit : function(grid, e) { -// /** Comments are always updatable* */ -// e.record.raw.symmetry = e.newValues.symmetry; -// e.record.raw.multiplicity = e.newValues.multiplicity; -// -// var adapter = new BiosaxsDataAdapter(); -// adapter.onSuccess.attach(function(sender, measurement) { -// // _this.grid.setLoading(false); -// }); -// adapter.onError.attach(function() { -// alert("Error"); -// // _this.grid.setLoading(false); -// }); -// -// // _this.grid.setLoading(); -// adapter.saveStructure(e.record.raw); -// } -// } -// })); -// // } -// return plugins; -//}; -// -//MacromoleculeForm.prototype.getPDBGrid = function(macromolecule) { -// var _this = this; -// -// var data = macromolecule.structure3VOs; -// -// // /** Getting PDB from subunits **/ -// // if (macromolecule != null){ -// // if (macromolecule.stoichiometry != null){ -// // for (var i =0; i < macromolecule.stoichiometry.length; i++){ -// // var stoichiometry = macromolecule.stoichiometry[i]; -// // if (stoichiometry.macromolecule3VO != null){ -// // if (stoichiometry.macromolecule3VO.structure3VOs != null){ -// // for (var j = 0; j < stoichiometry.macromolecule3VO.structure3VOs.length; -// // j++) { -// // var structure = stoichiometry.macromolecule3VO.structure3VOs[j]; -// // structure["isSubunit"] = stoichiometry.macromolecule3VO.acronym; -// // data.push(structure) -// // } -// // } -// // } -// // } -// // } -// // } -// -// this.pdbStore = Ext.create('Ext.data.Store', { -// fields : [ 'filePath', 'structureId', 'structureType', 'symmetry', 'structureId', 'name', 'multiplicity' ], -// data : macromolecule.structure3VOs, -// groupField : 'structureType', -// sorters : { -// property : 'structureId', -// direction : 'DESC' -// } -// }); -// -// var groupingFeature = Ext.create('Ext.grid.feature.Grouping', { -// groupHeaderTpl : Ext.create('Ext.XTemplate', -// "
{name:this.formatName}
", { -// formatName : function(name) { -// return name; -// } -// }), -// hideGroupedHeader : true, -// startCollapsed : false -// }); -// -// this.pdbGrid = Ext.create('Ext.grid.Panel', { -// margin : "15 0 0 5", -// height : 250, -// store : this.pdbStore, -// plugins : _this._getPlugins(), -// buttons : [ { -// // text : 'Add PDB file', -// text : 'Add Modeling Option', -// handler : function() { -// _this._openUploadDialog(macromolecule.macromoleculeId, "PDB", 'Upload PDB File'); -// } -// } -// -// ], -// columns : [ -// { -// text : "structureId", -// flex : 0.2, -// hidden : true, -// dataIndex : 'structureId', -// sortable : true -// }, -// { -// text : "File", -// flex : 0.5, -// dataIndex : 'filePath', -// sortable : true, -// hidden : true -// }, -// { -// text : "PDB", -// flex : 0.4, -// dataIndex : 'name', -// sortable : true -// }, -// { -// text : "Symmetry", -// flex : 0.4, -// dataIndex : 'symmetry', -// sortable : true, -// editor : { -// xtype : 'combobox', -// typeAhead : true, -// triggerAction : 'all', -// selectOnTab : true, -// store : [ [ "P1", "P1" ], [ "P2", "P2" ], [ "P3", "P3" ], [ "P4", "P4" ], [ "P5", "P5" ], [ "P6", "P6" ], [ "P32", "P32" ], [ "P42", "P42" ], -// [ "P52", "P52" ], [ "P62", "P62" ], [ "P222", "P222" ] ], -// } -// }, { -// text : "Multiplicity", -// flex : 0.4, -// dataIndex : 'multiplicity', -// sortable : true, -// editor : { -// xtype : 'textfield' -// } -// -// }, { -// text : "Subunit", -// flex : 0.2, -// dataIndex : 'isSubunit', -// sortable : true, -// hidden : true -// }, { -// text : "Type", -// flex : 0.2, -// dataIndex : 'structureType', -// sortable : true, -// hidden : true -// }, -// -// { -// id : this.id + 'REMOVE', -// flex : 0.2, -// hidden : true, -// sortable : false, -// renderer : function(value, metaData, record, rowIndex, colIndex, store) { -// return BUI.getRedButton('REMOVE'); -// } -// }, ], -// -// listeners : { -// itemdblclick : function(dataview, record, item, e) { -// _this._editExperiment(record.raw.experimentId); -// }, -// cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { -// -// if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'REMOVE') { -// var dataAdapter = new BiosaxsDataAdapter(); -// dataAdapter.onSuccess.attach(function() { -// _this.pdbGrid.setLoading(false); -// _this.update(); -// }); -// dataAdapter.removeStructure(record.data.structureId); -// _this.pdbGrid.setLoading("Removing PDB file"); -// } -// -// } -// }, -// viewConfig : { -// getRowClass : function(record, rowIdx, params, store) { -// if (record.raw.isSubunit != null) { -// return "blue-row"; -// } -// } -// } -// }); -// -// return this.pdbGrid; -//}; -// -//MacromoleculeForm.prototype.getMolarityForm = function(macromolecule) { -// var _this = this; -// var data = []; -// for (var i = 0; i < BIOSAXS.proposal.macromolecules.length; i++) { -// var m = BIOSAXS.proposal.macromolecules[i]; -// if (m.macromoleculeId != macromolecule.macromoleculeId) { -// data.push(m); -// } -// -// } -// this.macromoleculeCombo = BIOSAXS_COMBOMANAGER.getComboMacromoleculeByMacromolecules(data, { -// width : 250, -// labelWidth : 100, -// margin : 10 -// }); -// -// return Ext.createWidget('form', { -// -// frame : false, -// border : 0, -// // padding : 15, -// width : 550, -// height : 350, -// items : [ { -// xtype : 'container', -// flex : 1, -// border : false, -// layout : 'anchor', -// defaultType : 'requiredtext', -// items : [ this.macromoleculeCombo, { -// xtype : 'numberfield', -// name : 'Ratio', -// id : _this.id + "ratio", -// fieldLabel : 'Number in assymmetric units', -// value : 1, -// decimalPrecision : 6, -// margin : 10 -// }, { -// xtype : 'textareafield', -// name : 'comments', -// fieldLabel : 'Comments', -// margin : 10, -// width : 400, -// value : "" -// -// } ] -// } ] -// }); -// -//}; -// -///******************************************************************************* -// * JAVASCRIPT DOC -// ******************************************************************************/ -//MacromoleculeForm.prototype.input = function() { -// return { -// macromolecule : DATADOC.getMacromolecule_10() -// }; -//}; -// -//MacromoleculeForm.prototype.test = function(targetId) { -// var macromoleculeForm = new MacromoleculeForm(); -// var panel = macromoleculeForm.getPanel(macromoleculeForm.input().macromolecule); -// panel.render(targetId); -//}; - -function ResultSummaryForm() { - this.radiationDamageThreshold = BUI.getRadiationDamageThreshold(); - this.qualityThreshold = BUI.getQualityThreshold(); - - /** Data clusters with bufers **/ - this.clusterByBuffers = false; - - /** Visualization are groupeb by concenttrations. Ex: 4.5mg/ml and 4.7mg/ml will be clusterized together **/ - this.collapseConcentrations = true; - - this.summaryHeight = 350; - this.id = BUI.id(); - - var _this = this; - - this.qualityControlResultsWidget = new QualityControlResultsWidget({ - qualityThreshold : this.radiationDamageThreshold, - radiationDamageThreshold : this.qualityThreshold - - }); - this.qualityControlResultsWidget.onQualityChanged.attach(function(sender, value) { - _this.qualityThreshold = value; - _this.thresholdsChanged(); - }); - - this.qualityControlResultsWidget.onRadiationDatamageChanged.attach(function(sender, value) { - _this.radiationDamageThreshold = value; - _this.thresholdsChanged(); - - }); - this.concentrationHTMLTableWidget = new ConcentrationHTMLTableWidget({ - height : 250, - showBufferColumns : this.clusterByBuffers - }); - - this.plot = new BoxWhiskerGraph({ - targetId : _this.id + '_boxPlot', - height : 350, - width : 450, - maxBoxWidth : 25 - }); - - this.rangePlot = new RangeWhiskerGraph({ - targetId : _this.id + '_rangePlot', - height : 350, - width : 450, - maxBoxWidth : 25 - }); -} - -ResultSummaryForm.prototype.thresholdsChanged = function() { - var parsedData = this.prepareData(this.rawData); - - var filtered = JSON.parse(JSON.stringify(parsedData)); - filtered.concentration.concentrations = []; - for ( var conc in parsedData.concentration.concentrations) { - if (parsedData.concentration.concentrations[conc].calculation.rgGnom.validNumber > 0) { - filtered.concentration.concentrations.push(parsedData.concentration.concentrations[conc]); - } - } - - this.plotWhisker(filtered); - this.plotRange(filtered); - - this.concentrationHTMLTableWidget.refresh(parsedData); -}; - -/** Given a frame object (object returned by analyzeFrames()) and depending of the threshold indicates if a datacollection has radiation damage **/ -ResultSummaryForm.prototype.hasRadiationDamage = function(frameObject) { - var bb = frameObject.bufferBeforeFramesMerged / frameObject.framesCount; - var ba = frameObject.bufferAfterFramesMerged / frameObject.framesCount; - var mol = frameObject.framesMerge / frameObject.framesCount; - return !((bb >= this.radiationDamageThreshold) && (ba >= this.radiationDamageThreshold) && (mol >= this.radiationDamageThreshold)); -}; - -/** Return (frameObject) an object with the information about the frames for a data collection**/ -ResultSummaryForm.prototype.analyzeFrames = function(dataCollectionRow) { - return { - bufferAfterFramesMerged : dataCollectionRow.bufferAfterFramesMerged, - bufferBeforeFramesMerged : dataCollectionRow.bufferBeforeFramesMerged, - framesCount : dataCollectionRow.framesCount, - framesMerge : dataCollectionRow.framesMerge - }; -}; - -ResultSummaryForm.prototype.detectDataCollectionWarnings = function(dataCollectionRow) { - var frameObject = this.analyzeFrames(dataCollectionRow); - var warnings = { - count : 0, - type : [] - }; - - if (this.hasRadiationDamage(frameObject)) { - warnings.count = warnings.count + 1; - warnings.type.push("RADIATION DAMAGE"); - } - - if (Number(dataCollectionRow.quality) < this.qualityThreshold) { - warnings.count = 1;//warnings.count + 1; - warnings.type.push("Quality <" + this.qualityThreshold); - } - return warnings; -}; - -/** Return array composed by {concentration} objects **/ -ResultSummaryForm.prototype.getConcentrations = function(data) { - var concentrationsId = {}; - - for ( var i = 0; i < data.length; i++) { - var warning = this.detectDataCollectionWarnings(data[i]); - var id = data[i].conc;// + "_" + data[i].bufferId; - if (this.clusterByBuffers) { - id = data[i].conc + "_" + data[i].bufferId; - } - - if (concentrationsId[id] == null) { - concentrationsId[id] = { - id : id, - concentration : Number(data[i].conc).toFixed(2), - bufferId : data[i].bufferId, - bufferAcronym : data[i].bufferAcronym, - rgGuinier : [], - i0Guinier : [], - rgGnom : [], - dMax : [], - quality : [], - frames : [], - frames_warning : warning.count - }; - } else { - concentrationsId[id].frames_warning = concentrationsId[id].frames_warning + warning.count; - } - - concentrationsId[id].frames.push(data[i]); - - if (warning.count == 0) { - concentrationsId[id].rgGuinier.push(data[i].rgGuinier); - concentrationsId[id].i0Guinier.push(data[i].I0); - concentrationsId[id].quality.push(data[i].quality); - concentrationsId[id].rgGnom.push(data[i].rgGnom); - concentrationsId[id].dMax.push(data[i].dmax); - } - - } - var concentrations = []; - for ( var item in concentrationsId) { - if (concentrationsId.hasOwnProperty(item)) { - concentrations.push({ - concentration : concentrationsId[item].concentration, - id : item, - bufferId : Number(concentrationsId[item].bufferId).toFixed(2), - bufferAcronym : concentrationsId[item].bufferAcronym, - rgGuinier : concentrationsId[item].rgGuinier, - /** Frames **/ - frames : concentrationsId[item].frames, - frames_warning : concentrationsId[item].frames_warning, - /** Calculation **/ - calculation : { - rgGuinier : BUI.getStandardDeviation(concentrationsId[item].rgGuinier), - i0Guinier : BUI.getStandardDeviation(concentrationsId[item].i0Guinier), - quality : BUI.getStandardDeviation(concentrationsId[item].quality), - rgGnom : BUI.getStandardDeviation(concentrationsId[item].rgGnom), - dMax : BUI.getStandardDeviation(concentrationsId[item].dMax) - - } - }); - } - } - - return { - concentrations : concentrations - }; -}; - -ResultSummaryForm.prototype.prepareData = function(data) { - /** Return array composed by {acronym, bufferId} objects **/ - function getBuffers(data) { - var buffersId = {}; - for ( var i = 0; i < data.length; i++) { - buffersId[data[i].bufferId] = data[i].acronym; - } - var buffers = []; - for ( var id in buffersId) { - if (buffersId.hasOwnProperty(id)) { - buffers.push({ - acronym : buffersId[id], - bufferId : id - }); - } - } - return buffers; - } - - /** Get a string with all the concentrations **/ - function getConcentrationString(parseConcentrations) { - var concentrations = []; - for ( var i = 0; i < parseConcentrations.concentrations.length; i++) { - concentrations.push(parseConcentrations.concentrations[i].concentration + " mg/ml "); - } - return concentrations.toString(); - } - - var parseConcentrations = this.getConcentrations(data); - - return { - dataCollectionCount : data.length, - buffers : getBuffers(data), - concentration : parseConcentrations, - concentrationLabel : getConcentrationString(parseConcentrations) - }; -}; - -ResultSummaryForm.prototype.getDataForWhisker = function(data) { - var clusters = []; - - var concentrations = {}; - var i = 0; - var conc = 0; - if (this.collapseConcentrations) { - for (i = 0; i < data.concentration.concentrations.length; i++) { - conc = data.concentration.concentrations[i]; - var concentration = Number(conc.concentration).toFixed(0); - if (concentrations[concentration] == null) { - concentrations[concentration] = {}; - concentrations[concentration].concentration = concentration; - concentrations[concentration].calculation = {}; - concentrations[concentration].calculation.rgGuinier = {}; - concentrations[concentration].calculation.rgGuinier.values = []; - concentrations[concentration].calculation.rgGuinier.values = conc.calculation.rgGuinier.values; - concentrations[concentration].calculation.rgGnom = {}; - concentrations[concentration].calculation.rgGnom.values = []; - concentrations[concentration].calculation.rgGnom.values = conc.calculation.rgGnom.values; - } else { - concentrations[concentration].calculation.rgGuinier.values = concentrations[concentration].calculation.rgGuinier.values - .concat(conc.calculation.rgGuinier.values); - concentrations[concentration].calculation.rgGnom.values = concentrations[concentration].calculation.rgGnom.values - .concat(conc.calculation.rgGnom.values); - } - } - - /** From object to array **/ - var array = []; - for ( var key in concentrations) { - if (concentrations.hasOwnProperty(key)) { - array.push(concentrations[key]); - } - } - data.concentration.concentrations = array; - } - - for (i = 0; i < data.concentration.concentrations.length; i++) { - conc = data.concentration.concentrations[i]; - clusters.push({ - name : conc.concentration + "mg/ml", - concentration : Number(conc.concentration), - x : Number(conc.concentration), - classes : [] - }); - clusters[clusters.length - 1].classes.push({ - name : "Guinier", - color : '#9A2EFE', - values : conc.calculation.rgGuinier.values - - }); - clusters[clusters.length - 1].classes.push({ - name : "P(r)", - color : '#2E64FE', - values : conc.calculation.rgGnom.values - - }); - } - return { - clusters : clusters.sort(function(a, b) { - return a.concentration - b.concentration; - }) - }; -}; - -ResultSummaryForm.prototype.getDataForWhiskerQuality = function(data) { - var clusters = []; - - for ( var i = 0; i < data.concentration.concentrations.length; i++) { - var conc = data.concentration.concentrations[i]; - clusters.push({ - name : conc.concentration + "mg/ml", - classes : [] - }); - clusters[clusters.length - 1].classes.push({ - name : "Quality", - values : conc.calculation.quality.values - - }); - } - - return { - clusters : clusters - }; -}; - -ResultSummaryForm.prototype.plotQualityWhisker = function(parsedData) { - this.qualityPlot.refresh(this.getDataForWhiskerQuality(parsedData)); -}; - -ResultSummaryForm.prototype.plotWhisker = function(parsedData) { - this.plot.refresh(this.getDataForWhisker(parsedData)); -}; - -ResultSummaryForm.prototype.plotRange = function(parsedData) { - this.rangePlot.refresh(this.getDataForWhisker(parsedData)); -}; - -ResultSummaryForm.prototype.getPanel = function(data) { - var _this = this; - _this.rawData = data; - var parsedData = this.prepareData(data); - - this.panel = Ext - .createWidget( - 'form', - { - bodyPadding : 20, - frame : false, - border : 0, - width : this.width, - height : this.summaryHeight + 1000, - items : [ { - xtype : 'container', - layout : 'hbox', - items : [ { - xtype : 'container', - flex : 1, - border : false, - layout : 'anchor', - defaultType : 'textfield', - items : [ - _this.qualityControlResultsWidget.getPanel(), - { - xtype : 'fieldset', - width : 950, - margin : "20, 0 0 0", - height : this.summaryHeight + 500, - items : [ - { - html : "
Concentration Analysis
", - border : 0, - width : 900 - - }, - { - html : this.concentrationHTMLTableWidget.getPanel(parsedData), - border : 0, - width : 900, - margin : "10, 0 0 10" - - }, - { - xtype : 'container', - layout : 'vbox', - border : 5, - items : [ - { - html : "
Rg based on Guinier and Gnom
", - border : 8, - width : 900, - margin : "20 0 0 10" - - }, { - xtype : 'container', - layout : 'hbox', - items : [ { - xtype : 'container', - layout : 'vbox', - items : [ { - html : "
", - border : 0, - width : this.plot.width, - padding : "10 0 0 20", - listeners : { - afterrender : function() { - _this.plotWhisker(parsedData); - - } - } - }, { - html : "
Concentration (mg/ml)
", - width : 450, - border : 0, - margin : "-50 0 0 0" - - } ] - }, { - xtype : 'container', - layout : 'vbox', - items : [ { - html : "
", - border : 0, - width : this.rangePlot.width, - padding : "10 0 0 10", - listeners : { - afterrender : function() { - _this.plotRange(parsedData); - } - } - }, { - html : "
Concentration (mg/ml)
", - width : 450, - border : 0, - margin : "-50 0 0 0" - - } ] - } ] - } ] - } ] - } ] - } - - ] - } ] - }); - return this.panel; - -}; - -ResultSummaryForm.prototype.input = function() { - return { - data : DATADOC.getData_3367() - }; -}; - -ResultSummaryForm.prototype.test = function(targetId) { - var resultSummaryForm = new ResultSummaryForm(); - var panel = resultSummaryForm.getPanel(resultSummaryForm.input().data); - panel.render(targetId); - -}; - -/** - * Example form - * - * @witdh - * @height - */ -function RigibBodyModelingForm(args) { - this.id = BUI.id(); - this.width = 700; - this.height = 500; - - if (args != null) { - if (args.width != null) { - this.width = args.width; - } - if (args.height != null) { - this.height = args.height; - } - } - - var _this = this; - this.rigidBodyGrid = new AprioriRigidBodyGrid(); - - this.rigidBodyGrid.onUploadFile.attach(function(sender, type, title){ - _this._openUploadDialog(_this.macromolecule.macromoleculeId, type, title); - }); - - this.rigidBodyGrid.onRemove.attach(function(sender, type, title){ - _this._update(); - }); - - this.onSave = new Event(this); - -} - -RigibBodyModelingForm.prototype._getItems = function() { - var _this = this; - - - return [ { - xtype : 'label', - forId : 'myFieldId', - text : 'Information for model fit, mixture analysis and rigid body modeling', - margin : '15 0 20 10', - cls : "inline-help" - }, - this.rigidBodyGrid.getPanel(), - { - xtype : 'label', - forId : 'myFieldId', - text : 'Distance restraints may be imposed on the model using contacts conditions file (OPTIONAL)', - margin : '25 0 5 10', - cls : "inline-help" - }, - { - xtype : 'container', - layout : 'hbox', - items : [ { - xtype : 'container', - border : false, - layout : 'hbox', - items : [ { - xtype : 'label', - forId : 'myFieldId', - text : 'Contact Description File: (Optional)', - width : 150, - margin : '10 0 0 10' - }, { - id : this.id + "contactsDescriptionFilePath", - xtype : 'textfield', - hideLabel : true, - margin : '10 0 0 0', - width : 200, - disabled: true - }, { - text : 'Upload', - xtype : 'button', - margin : "10 0 0 10", - width : 80, - handler : function() { - - _this._openUploadDialog(_this.macromolecule.macromoleculeId, "CONTACTS", "Upload Contact Description File"); - } - }, { - text : 'Remove', - id : _this.id + "_remove", - xtype : 'button', - margin : "10 0 0 10", - width : 80, - handler : function() { - _this.macromolecule.contactsDescriptionFilePath = null; - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, proposal) { - /** Updating the proposal global object **/ - BIOSAXS.proposal.setItems(proposal); - _this.panel.setLoading(false); - - var saved = BIOSAXS.proposal.getMacromoleculeByAcronym(_this.macromolecule.acronym); - /** Refreshing to check that everything is ok **/ - _this.refresh(saved); - _this.onSave.notify(saved); - }); - - _this.panel.setLoading("Saving Macromolecule") - adapter.saveMacromolecule(_this.macromolecule); - } - } ] - } ] - }, { - xtype : 'panel', - html : "Go to SASREF manual for further information", - margin : "10 0 0 160", - border : 0 - - }, - { - xtype : 'checkbox', - margin : '10 0 0 5', - boxLabel : "I want rigid body modeling run on this stuff", - checked : true, - width : 300 - } ] - -}; - -/** Because update is a jsp page we don't know if the user has uploaded a file or not then we need to refresh **/ -RigibBodyModelingForm.prototype._update = function(macromoleculeId, type, title) { - var _this = this; - BIOSAXS.proposal.onInitialized.attach(function() { - if (BIOSAXS.proposal != null) { - _this.refresh(BIOSAXS.proposal.getMacromoleculeById(_this.macromolecule.macromoleculeId)); - _this.panel.setLoading(false); - } - }); - this.panel.setLoading(); - BIOSAXS.proposal.init(); -}; - -RigibBodyModelingForm.prototype._openUploadDialog = function(macromoleculeId, type, title) { - var _this = this; - function onClose() { - w.destroy(); - _this._update(); - - } - - var w = Ext.create('Ext.window.Window', { - title : title, - height : 200, - width : 400, - modal : true, - buttons : [ { - text : 'Close', - handler : function() { - onClose(); - } - } ], - layout : 'fit', - items : { - html : "" - }, - listeners : { - onEsc : function() { - onClose(); - }, - close : function() { - onClose(); - } - } - }).show(); -}; - -RigibBodyModelingForm.prototype._getButtons = function() { - return []; -}; - -RigibBodyModelingForm.prototype.getPanel = function() { - var _this = this; - this.panel = Ext.create('Ext.form.Panel', { - width : this.width, - height : this.height, - margin : 10, - border : 1, - defaultType : 'textfield', - items : this._getItems(), - buttons : this._getButtons(), - listeners : { - afterrender : function(){ - _this._populate(); - - } - } - }); - return this.panel; -}; - -/** Populates could be call when the DOM is not filled yet **/ -RigibBodyModelingForm.prototype._populate = function() { - if (this.macromolecule != null){ - if (Ext.getCmp(this.id + "contactsDescriptionFilePath") != null){ - if (this.macromolecule.contactsDescriptionFilePath != null){ - Ext.getCmp(this.id + "contactsDescriptionFilePath").setValue(this.macromolecule.contactsDescriptionFilePath); - Ext.getCmp(this.id + "_remove").enable(); - } - else{ - Ext.getCmp(this.id + "_remove").disable(); - Ext.getCmp(this.id + "contactsDescriptionFilePath").setValue(""); - - } - } - } -}; - -/** It populates the form * */ -RigibBodyModelingForm.prototype.refresh = function(macromolecule) { - this.macromolecule = macromolecule; - this.rigidBodyGrid.refresh(macromolecule); - this._populate(); -}; - -RigibBodyModelingForm.prototype.input = function() { - return {}; -}; - - -/** It populates the form **/ -RigibBodyModelingForm.prototype.test = function(targetId) { - var macromoleculeForm = new RigibBodyModelingForm(); - var panel = macromoleculeForm.getPanel(); - panel.render(targetId); -}; - - -/** - * Same form as MX part - * - * @creationMode if true a create button appears instead of save - * @showTitle true or false - */ -function ShipmentForm(args) { - this.id = BUI.id(); - - this.creationMode = false; - this.showTitle = true; - if (args != null) { - if (args.creationMode != null) { - this.creationMode = args.creationMode; - } - if (args.showTitle != null) { - this.showTitle = args.showTitle; - } - } - - this.onSaved = new Event(this); -} - -ShipmentForm.prototype.fillStores = function() { - this.panel.setLoading("Loading Labcontacts from database"); - this.labContactForSendingStore.loadData(BIOSAXS.proposal.getLabcontacts(), false); - this.labContactForReturnStore.loadData(BIOSAXS.proposal.getLabcontacts(), false); - this.panel.setLoading(false); - if (this.shipment != null) { - this.setShipment(this.shipment); - } -}; - -ShipmentForm.prototype.draw = function(targetId) { - this.getPanel().render(targetId); -}; - -ShipmentForm.prototype.setShipment = function(shipment) { - this.shipment = shipment; - var _this = this; - Ext.getCmp(_this.id + "shippingName").setValue(shipment.json.shippingName); - Ext.getCmp(_this.id + "shippingStatus").setValue(shipment.json.shippingStatus); - Ext.getCmp(_this.id + "comments").setValue(shipment.json.comments); - if (shipment.json.sendingLabContactVO != null) { - this.labContactsSendingCombo.setValue(shipment.json.sendingLabContactVO.labContactId); - } - if (shipment.json.returnLabContactVO != null) { - this.labContactsReturnCombo.setValue(shipment.json.returnLabContactVO.labContactId); - } - -}; - -ShipmentForm.prototype._saveShipment = function() { - var _this = this; - var shippingId = null; - if (this.shipment != null) { - shippingId = this.shipment.json.shippingId; - } - var json = { - shippingId : shippingId, - name : Ext.getCmp(_this.id + "shippingName").getValue(), - status : Ext.getCmp(_this.id + "shippingStatus").getValue(), - sendingLabContactId : Ext.getCmp(_this.id + "shipmentform_sendingLabContactId").getValue(), - returnLabContactId : Ext.getCmp(_this.id + "returnLabContactId").getValue(), - returnCourier : Ext.getCmp(_this.id + "returnCourier").getValue(), - courierAccount : Ext.getCmp(_this.id + "courierAccount").getValue(), - BillingReference : Ext.getCmp(_this.id + "BillingReference").getValue(), - dewarAvgCustomsValue : Ext.getCmp(_this.id + "dewarAvgCustomsValue").getValue(), - dewarAvgTransportValue : Ext.getCmp(_this.id + "dewarAvgTransportValue").getValue(), - comments : Ext.getCmp(_this.id + "comments").getValue() - }; - - var dataAdapter = new BiosaxsDataAdapter(); - dataAdapter.onSuccess.attach(function(sender, shipment) { - window.location = BUI.getShippingURL(shipment.shippingId); - }); - - dataAdapter.onError.attach(function(sender, error) { - _this.onError.notify(error); - }); - - /** Cheking params **/ - if (json.name == "") { - BUI.showError("Name field is mandatory"); - return; - } - - if (json.sendingLabContactId == null) { - BUI.showError("Lab contact for sending field is mandatory"); - return; - } - - if (json.returnLabContactId == null) { - BUI.showError("Lab contact for return field is mandatory"); - return; - } - - dataAdapter.createShipment(json.shippingId, json.name, json.status, json.comments, json.sendingLabContactId, json.returnLabContactId, - json.returnCourier, json.courierAccount, json.BillingReference, json.dewarAvgCustomsValue, json.dewarAvgTransportValue); - -}; - -ShipmentForm.prototype.getPanel = function(shipment) { - var _this = this; - this.shipment = shipment; - var required = '*'; - var buttons = []; - - if (_this.creationMode) { - buttons.push({ - text : 'Create', - scope : this, - handler : function() { - _this._saveShipment(); - } - }); - } else { - buttons.push({ - text : 'Save', - scope : this, - handler : function() { - _this._saveShipment(); - } - }); - - } - - this.labContactForSendingStore = Ext.create('Ext.data.Store', { - fields : [ 'cardName', 'labContactId' ] - }); - - this.labContactForReturnStore = Ext.create('Ext.data.Store', { - fields : [ 'cardName', 'labContactId' ] - }); - - // Create the combo box, attached to the states data store - this.labContactsSendingCombo = Ext.create('Ext.form.ComboBox', { - id : _this.id + "shipmentform_sendingLabContactId", - fieldLabel : 'Lab contact for sending', - afterLabelTextTpl : required, - store : this.labContactForSendingStore, - queryMode : 'local', - labelWidth : 200, - displayField : 'cardName', - valueField : 'labContactId' - }); - - this.labContactsReturnCombo = Ext.create('Ext.form.ComboBox', { - id : _this.id + "returnLabContactId", - fieldLabel : 'If No, Lab-Contact for Return', - afterLabelTextTpl : required, - store : this.labContactForReturnStore, - queryMode : 'local', - labelWidth : 200, - displayField : 'cardName', - valueField : 'labContactId', - listeners : { - change : function(x, newValue) { - for ( var i = 0; i < x.getStore().data.items.length; i++) { - if (x.getStore().data.items[i].raw.labContactId == newValue) { - Ext.getCmp(_this.id + "returnCourier").setValue(x.getStore().data.items[i].raw.defaultCourrierCompany); - Ext.getCmp(_this.id + "courierAccount").setValue(x.getStore().data.items[i].raw.courierAccount); - Ext.getCmp(_this.id + "BillingReference").setValue(x.getStore().data.items[i].raw.billingReference); - Ext.getCmp(_this.id + "dewarAvgCustomsValue").setValue(x.getStore().data.items[i].raw.dewarAvgCustomsValue); - Ext.getCmp(_this.id + "dewarAvgTransportValue").setValue(x.getStore().data.items[i].raw.dewarAvgTransportValue); - } - } - } - } - }); - - if (this.panel == null) { - this.panel = Ext.create('Ext.form.Panel', { - bodyPadding : 5, - width : 600, - border : 1, - items : [ { - xtype : 'fieldset', - title : 'Details', - collapsible : false, - defaultType : 'textfield', - layout : 'anchor', - defaults : { - anchor : '100%' - }, - items : [ { - xtype : 'requiredtext', - fieldLabel : 'Shipment Label', - allowBlank : false, - name : 'shippingName', - id : _this.id + 'shippingName', - value : '', - anchor : '50%' - }, { - - xtype : 'textareafield', - name : 'comments', - id : _this.id + 'comments', - fieldLabel : 'Comments', - value : '' - }, { - fieldLabel : 'Status', - readOnly : true, - id : _this.id + 'shippingStatus', - value : 'Opened', - anchor : '50%' - } ] - }, { - xtype : 'fieldset', - title : 'Lab-Contacts', - collapsible : false, - defaultType : 'textfield', - layout : 'anchor', - defaults : { - anchor : '100%' - }, - items : [ this.labContactsSendingCombo, this.labContactsReturnCombo ] - }, { - border : 0, - html : BUI.getWarningHTML("These informations are relevant for all shipments") - }, { - xtype : 'fieldset', - title : 'Courier accounts details for return', - collapsible : false, - layout : 'anchor', - defaults : { - anchor : '100%' - }, - items : [ { - xtype : 'textfield', - labelWidth : 400, - fieldLabel : 'Courier company for return (if ESRF sends a dewar back)', - id : _this.id + 'returnCourier', - value : '' - }, { - xtype : 'textfield', - labelWidth : 400, - fieldLabel : 'Courier account', - id : _this.id + 'courierAccount', - value : '' - }, { - xtype : 'textfield', - labelWidth : 400, - fieldLabel : 'Billing reference', - id : _this.id + 'BillingReference', - value : '' - }, { - xtype : 'numberfield', - labelWidth : 400, - fieldLabel : 'Average Customs value of a dewar (Euro)', - id : _this.id + 'dewarAvgCustomsValue', - value : '' - }, { - xtype : 'numberfield', - labelWidth : 400, - fieldLabel : 'Average Transport value of a dewar (Euro)', - id : _this.id + 'dewarAvgTransportValue', - value : '' - } ] - } ], - buttons : buttons - }); - } - this.fillStores(); - if (this.showTitle) { - this.panel.setTitle('Create a new Shipment'); - } - return this.panel; -}; - -ShipmentForm.prototype.input = function() { - return { - proposal : DATADOC.getProposal_10() - - }; -}; - -ShipmentForm.prototype.test = function(targetId) { - var shipmentForm = new ShipmentForm({ - creationMode : true - - }); - BIOSAXS.proposal = new Proposal(shipmentForm.input().proposal); - shipmentForm.getPanel().render(targetId); -}; - -/** - * #onSaved - */ -function StockSolutionForm(args) { - this.id = BUI.id(); - this.actions = []; - - if (args != null) { - if (args.actions != null) { - this.actions = args.actions; - } - } - this.onSaved = new Event(this); -} - -StockSolutionForm.prototype.getStockSolution = function() { - if (this.stockSolution != null) { - this.stockSolution.concentration = Ext.getCmp(this.id + "stockSolution_concentration").getValue(); - this.stockSolution.storageTemperature = Ext.getCmp(this.id + "stockSolution_storageTemperature").getValue(); - this.stockSolution.volume = Ext.getCmp(this.id + "stockSolution_volume").getValue(); - this.stockSolution.comments = Ext.getCmp(this.id + "stockSolution_comments").getValue(); - this.stockSolution.name = Ext.getCmp(this.id + "stockSolution_name").getValue(); - this.stockSolution.bufferId = this.bufferCombo.getValue(); - - if (this.macromoleculeCombo.getValue() != null) { - this.stockSolution.macromoleculeId = this.macromoleculeCombo.getValue(); - } else { - this.stockSolution.macromolecule3VO = null; - } - - } else { - return { - concentration : Ext.getCmp(this.id + "stockSolution_concentration").getValue(), - storageTemperature : Ext.getCmp(this.id + "stockSolution_storageTemperature").getValue(), - volume : Ext.getCmp(this.id + "stockSolution_volume").getValue(), - comments : Ext.getCmp(this.id + "stockSolution_comments").getValue(), - name : Ext.getCmp(this.id + "stockSolution_name").getValue(), - bufferId : this.bufferCombo.getValue(), - macromoleculeId : this.macromoleculeCombo.getValue() - }; - } - return this.stockSolution; -}; - -StockSolutionForm.prototype.setStockSolution = function(stockSolution) { - if (stockSolution != null) { - if (stockSolution.macromoleculeId != null) { - this.macromoleculeCombo.setValue(stockSolution.macromoleculeId); - } - this.bufferCombo.setValue(stockSolution.bufferId); - Ext.getCmp(this.id + "stockSolution_concentration").setValue(this.stockSolution.concentration); - Ext.getCmp(this.id + "stockSolution_storageTemperature").setValue(this.stockSolution.storageTemperature); - Ext.getCmp(this.id + "stockSolution_volume").setValue(this.stockSolution.volume); - Ext.getCmp(this.id + "stockSolution_name").setValue(this.stockSolution.name); - Ext.getCmp(this.id + "stockSolution_comments").setValue(this.stockSolution.comments); - } -}; - -StockSolutionForm.prototype.getBufferCombo = function() { - this.bufferCombo = BIOSAXS_COMBOMANAGER.getComboBuffers(BIOSAXS.proposal.getBuffers(), { - labelWidth : 120, - margin : '0 0 10 0', - width : 220 - - }); - return this.bufferCombo; -}; - -StockSolutionForm.prototype.getMacromoleculeCombo = function() { - this.macromoleculeCombo = BIOSAXS_COMBOMANAGER.getComboMacromoleculeByMacromolecules(BIOSAXS.proposal.getMacromolecules(), { - labelWidth : 120, - margin : '0 0 10 0', - width : 220 - - }); - return this.macromoleculeCombo; -}; - -StockSolutionForm.prototype.refresh = function() { -}; - -StockSolutionForm.prototype._getTopPanel = function() { - return { - xtype : 'container', - layout : 'hbox', - border : 0, - items : [ { - xtype : 'container', - layout : 'hbox', - items : [ { - xtype : 'container', - flex : 1, - border : false, - layout : 'anchor', - defaultType : 'textfield', - items : [ - - this.getMacromoleculeCombo(), { - xtype : 'requiredtext', - id : this.id + 'stockSolution_name', - fieldLabel : 'Acronym', - labelWidth : 120, - width : 250 - }, { - xtype : 'requiredtext', - id : this.id + 'stockSolution_concentration', - fieldLabel : 'Conc. (mg/ml)', - labelWidth : 120, - width : 250 - }, - - { - id : this.id + 'stockSolution_storageTemperature', - fieldLabel : 'Storage Temp.(C)', - labelWidth : 120, - width : 250 - }, { - xtype : 'requiredtext', - id : this.id + 'stockSolution_volume', - fieldLabel : 'Volume in Well (µl)', - labelWidth : 120, - width : 250 - } ] - } ] - }, { - xtype : 'container', - flex : 1, - layout : 'anchor', - defaultType : 'textfield', - margin : '0 0 0 10', - items : [ this.getBufferCombo() ] - } ] - }; - -}; - -StockSolutionForm.prototype.getPanel = function(stockSolution) { - this.stockSolution = stockSolution; - this.panel = Ext.createWidget({ - xtype : 'container', - layout : 'vbox', - border : 0, - style : { - padding : '10px' - }, - fieldDefaults : { - labelAlign : 'left', - labelWidth : 50 - }, - items : [ this._getTopPanel(stockSolution), { - id : this.id + 'stockSolution_comments', - xtype : 'textareafield', - name : 'comments', - fieldLabel : 'Comments', - labelWidth : 120, - width : '100%' - } ] - }); - - this.setStockSolution(stockSolution); - return this.panel; -}; - -StockSolutionForm.prototype.input = function() { - return { - stock : { - "stockSolutionId" : 6, - "proposalId" : 3124, - "macromoleculeId" : 5933, - "bufferId" : 811, - "instructionSet3VO" : null, - "boxId" : 305861, - "storageTemperature" : "20", - "volume" : "300", - "concentration" : "1.2", - "comments" : "Buffer EDTA with A", - "name" : "A_EDTA_1.2", - "samples" : [], - "buffer" : "EDTA", - "macromolecule" : "A" - }, - proposal : new MeasurementGrid().input().proposal - }; -}; - -StockSolutionForm.prototype.test = function(targetId) { - var stockSolutionForm = new StockSolutionForm(); - BIOSAXS.proposal = new Proposal(stockSolutionForm.input().proposal); - var panel = stockSolutionForm.getPanel(new Shipment(stockSolutionForm.input().stock)); - panel.render(targetId); -}; - - -BoxWhiskerGraph.prototype.cleanArray = GenericGraph.prototype.cleanArray; -BoxWhiskerGraph.prototype.plotAxes = GenericGraph.prototype.plotAxes; -BoxWhiskerGraph.prototype.plotRuler = GenericGraph.prototype.plotRuler; -BoxWhiskerGraph.prototype.drawSVGVerticalText = GenericGraph.prototype.drawSVGVerticalText; -BoxWhiskerGraph.prototype.getClassColor = GenericGraph.prototype.getClassColor; -BoxWhiskerGraph.prototype.getDimensions = GenericGraph.prototype.getDimensions; -BoxWhiskerGraph.prototype.calculate = GenericGraph.prototype.calculate; -BoxWhiskerGraph.prototype.getMedian = GenericGraph.prototype.getMedian; -BoxWhiskerGraph.prototype.pointToPixel = GenericGraph.prototype.pointToPixel; -BoxWhiskerGraph.prototype.maxValueWhisker = GenericGraph.prototype.maxValueWhisker; -BoxWhiskerGraph.prototype.refresh = GenericGraph.prototype.refresh; - -/** - * Subclass of GenericGraph - * - * @maxBoxWidth - */ -function BoxWhiskerGraph(args){ - this.maxBoxWidth = 25; - - if (args == null){ - args = new Object(); - } - args["plotHorizontalByCluster"] = true; - - GenericGraph.call(this, args); - - if (args.maxBoxWidth != null){ - this.maxBoxWidth = args.maxBoxWidth; - } -}; - - - -/** -There are several different methods for calculating quartiles.[1] This calculator uses a method described by Moore and McCabe to find quartile values. -The same method also used by The TI-83 to calculate quartile values. -With this method, the first quartile is the median of the numbers below the median, the third quartile is the median of the numbers above the median. - -http://www.miniwebtool.com/quartile-calculator/ -http://www.alcula.com/calculators/statistics/box-plot/ -**/ -BoxWhiskerGraph.prototype.getQ1 = function(array){ - array = array.slice(0, array.length/2); - return this.getMedian(array); -}; - -BoxWhiskerGraph.prototype.getQ3 = function(array){ - array = array.slice((array.length + 1)/2); - return this.getMedian(array); - -}; - -BoxWhiskerGraph.prototype.getQ2 = function(array){ - return this.getMedian(array); -}; - -BoxWhiskerGraph.prototype.getBelowOutliers = function(belowLimit, array){ - var points = new Array(); - for (var i = 0; i < array.length; i++){ - if (array[i] <= belowLimit){ - points.push(array[i]); - } - } - return points; -}; - -BoxWhiskerGraph.prototype.getAboveOutliers = function(aboveLimit, array){ - var points = new Array(); - for (var i = 0; i < array.length; i++){ - if (array[i] >= aboveLimit){ - points.push(array[i]); - } - } - return points; -}; - - -BoxWhiskerGraph.prototype.minValueWhisker = function(belowLimit, q1, array){ - var points = new Array(); - - for (var i = 0; i < array.length; i++){ - if ((array[i] < q1) && (array[i]) > belowLimit){ - points.push(array[i]); - } - } - - if (points.length > 0){ - points.sort(function(a, b){return a - b;}); - return points[0]; - } - return null; -}; - -BoxWhiskerGraph.prototype.isNumber = function(value){ - if (value=="") return false; - - var d = parseInt(value); - if (!isNaN(d)) return true; else return false; - -}; - -BoxWhiskerGraph.prototype.plotBoxWhisker = function(boxProperties){ - if (this.maxBoxWidth != null){ - if (boxProperties.width > this.maxBoxWidth){ - boxProperties.x = boxProperties.x + (boxProperties.width/2) - (this.maxBoxWidth/2); - boxProperties.width = this.maxBoxWidth; - } - - } - - if (this.plotPoints){ - for(var i = 0; i < boxProperties.values.length; i++){ - var value = boxProperties.values[i]; - var toPixel = this.pointToPixel(value, boxProperties); - SVG.drawCircle(boxProperties.x + (boxProperties.width/2) + 2, toPixel, this.pointRadius, this.svg, [["fill", "green"], ["fill-opacity", this.fillOpacityPoint],['stroke-opacity', this.strokeOpacityPoint], ["stroke", "black"]]); - } - } - - - var boxColor = this.getClassColor(boxProperties.name); - var result = this.calculate(boxProperties.values); - /** Q1 **/ - - if (this.isNumber(result.Q1)){ - SVG.drawLine(boxProperties.x, this.pointToPixel(result.Q1, boxProperties), boxProperties.x + boxProperties.width, this.pointToPixel(result.Q1, boxProperties), this.svg, [["stroke", boxColor]]); - } - /** Q2 **/ - if (this.isNumber(result.Q2)){ - SVG.drawLine(boxProperties.x, this.pointToPixel(result.Q2, boxProperties), boxProperties.x + boxProperties.width, this.pointToPixel(result.Q2, boxProperties), this.svg, [["stroke", "blue"], ["stroke-width", "2"]]); - } - - /** Q3 **/ - if (this.isNumber(result.Q3)){ - SVG.drawLine(boxProperties.x, this.pointToPixel(result.Q3, boxProperties), boxProperties.x + boxProperties.width, this.pointToPixel(result.Q3, boxProperties), this.svg, [["stroke", boxColor]]); - } - - /** Concenting Q1 and Q3 **/ - if (this.isNumber(result.Q1)&&this.isNumber(result.Q3)){ - SVG.drawLine(boxProperties.x, this.pointToPixel(result.Q1, boxProperties), boxProperties.x, this.pointToPixel(result.Q3, boxProperties), this.svg, [["stroke", boxColor]]); - SVG.drawLine(boxProperties.x + boxProperties.width, this.pointToPixel(result.Q1, boxProperties), boxProperties.x + boxProperties.width, this.pointToPixel(result.Q3, boxProperties), this.svg, [["stroke", boxColor]]); - } - - /** min-whisker **/ - if (result["min-whisker"] != null){ - if (this.isNumber(result.Q1)){ - SVG.drawLine(boxProperties.x + (boxProperties.width/2) + 2, this.pointToPixel(result.Q1, boxProperties), boxProperties.x + (boxProperties.width/2) + 2, this.pointToPixel(result["min-whisker"], boxProperties), this.svg, [["stroke", boxColor]]); - } - SVG.drawLine(boxProperties.x,this.pointToPixel(result["min-whisker"], boxProperties), boxProperties.x + boxProperties.width, this.pointToPixel(result["min-whisker"], boxProperties) , this.svg, [["stroke", boxColor]]); - - } - - /** max-whisker **/ - if (result["max-whisker"] != null){ - if (this.isNumber(result.Q3)){ - SVG.drawLine(boxProperties.x + (boxProperties.width/2) + 2, this.pointToPixel(result.Q3, boxProperties), boxProperties.x + (boxProperties.width/2) + 2, this.pointToPixel(result["max-whisker"], boxProperties), this.svg, [["stroke", boxColor]]); - } - SVG.drawLine(boxProperties.x , this.pointToPixel(result["max-whisker"], boxProperties), boxProperties.x + boxProperties.width, this.pointToPixel(result["max-whisker"], boxProperties), this.svg, [["stroke", boxColor]]); - - } - - /** outliners **/ - if (result["above-outliers"] != null){ - for (var point in result["above-outliers"]){ - SVG.drawCircle(boxProperties.x + (boxProperties.width/2) + 2, this.pointToPixel(result["above-outliers"][point], boxProperties), this.pointRadius, this.svg, [["fill", "red"], ["fill-opacity", "1"]]); - //SVG.drawText(boxProperties.x + (boxProperties.width/2) - 2, this.pointToPixel(result["above-outliers"][point], boxProperties) + 2, "x",this.svg, [["fill", "black"], ["font-size", "x-small"]]); - } - } - if (result["below-outliers"] != null){ - for (var point in result["below-outliers"]){ - SVG.drawCircle(boxProperties.x + (boxProperties.width/2) + 2, this.pointToPixel(result["below-outliers"][point], boxProperties), this.pointRadius, this.svg, [["fill", "red"], ["fill-opacity", "1"]]); - //SVG.drawText(boxProperties.x + (boxProperties.width/2) - 2, this.pointToPixel(result["below-outliers"][point], boxProperties) + 2, "x",this.svg, [["fill", "black"], ["font-size", "x-small"]]); - } - } -}; - - - -BoxWhiskerGraph.prototype.plotClusterTitles = function(properties){ - /** Cluster Titles **/ - var posX = this.left + this.rulerWidth; - var data = this.data; - for(var i = 0; i < data.clusters.length; i++ ){ - /** title for the clusters **/ - posX = posX + this.interClustersSpace; - - /** Drawing title of classes **/ - var cluster = data.clusters[i]; - var posXClasses = posX; - for(var j = 0; j < cluster.classes.length; j++){ - //SVG.drawRectangle(posXClasses, this.height - (this.bottom + this.clusterTitleHeight + this.rulerHeight), properties.classWidth, this.rulerHeight, this.svg, [["fill", "green"]]); -// SVG.drawText(posXClasses + 1/6*properties.classWidth, this.height - (this.bottom + this.clusterTitleHeight + (this.rulerHeight*1/4)), cluster.classes[j].name, this.svg, [["style", "font-size:xx-small"]]); - var color = cluster.classes[j].color; - this.drawSVGVerticalText(posXClasses + 1/2*(properties.classWidth), this.height - (this.bottom - this.clusterTitleHeight + (this.rulerHeight)), cluster.classes[j].name , [["style", "font-size:x-small;"], ["fill", color]]); - - // SVG.drawRectangle(posXClasses, this.height - (this.bottom + this.clusterTitleHeight), properties.classWidth, 2, this.svg, [["fill", "black"]]); - posXClasses = posXClasses + properties.classWidth + this.interClassesSpace; - } - - var clusterTitleSpace = (data.clusters[i].classes.length * properties.classWidth) + ((data.clusters[i].classes.length - 1) * this.interClassesSpace); - //SVG.drawRectangle(posX, this.height - (this.bottom + this.clusterTitleHeight), clusterTitleSpace, this.clusterTitleHeight, this.svg, [["fill", "pink"]]); - SVG.drawRectangle(posX, this.height - (this.clusterTitleHeight+this.bottom), clusterTitleSpace, 1, this.svg, []); - -// var transform = ["translate", "(" + posX + 1/3*clusterTitleSpace +", " + this.height - (this.bottom + (this.clusterTitleHeight*1/4)) +")"], ["transform", "rotate(180)"]; -// var transform = ["transform", "translate(" + (posX + 1/3*clusterTitleSpace) +", " + (this.height - (this.bottom + (this.clusterTitleHeight*1/4))) + "), rotate(-90) "]; -// this.drawSVGVerticalText(posX + 1/3*clusterTitleSpace, this.height - (this.bottom + (this.clusterTitleHeight*1/4)),data.clusters[i].name, [["style", "font-size:x-small"]]); - SVG.drawText(posX , this.height - (this.bottom + (this.clusterTitleHeight*1/4)),data.clusters[i].name ,this.svg, [["style", "font-size:x-small"]]); - posX = posX + clusterTitleSpace; - } -}; - -BoxWhiskerGraph.prototype.plotWhisters = function(data, properties){ - var colors = ["yellow", "orange", "green"]; - var posX = this.left + this.rulerWidth; - for(var i = 0; i < data.clusters.length; i++ ){ - var cluster = data.clusters[i]; - /** inter cluster space **/ - //SVG.drawRectangle(posX, this.top, this.interClustersSpace, this.height - this.top - this.bottom - this.clusterTitleHeight - this.rulerHeight, this.svg, [["fill", "black"]]); - posX = posX + this.interClustersSpace; - - for (var j = 0; j < cluster.classes.length; j ++){ - - //SVG.drawRectangle(posX, this.top, properties.classWidth, this.height - this.top - this.bottom - this.clusterTitleHeight - this.rulerHeight, this.svg, [["fill", colors[j]]]); - this.plotBoxWhisker( - { - name : cluster.classes[j].name, - values : cluster.classes[j].values, - minPoint : properties.minPoint, - maxPoint : properties.maxPoint, - x : posX, - y : this.top, - width : properties.classWidth, - height : this.height - this.top - this.bottom - this.clusterTitleHeight - this.rulerHeight - - - } - ); - posX = posX + properties.classWidth; - //SVG.drawRectangle(posX, this.top, this.interClassesSpace, this.height - this.top - this.bottom - this.clusterTitleHeight - this.rulerHeight, this.svg, [["fill", "black"]]); - if (j < cluster.classes.length - 1){ - posX = posX + this.interClassesSpace; - } - } - } -}; - -BoxWhiskerGraph.prototype.draw = function(targetId, data){ - this.targetId = targetId; - var properties = (this.getDimensions(data)); - this.svg = SVG.createSVGCanvas(document.getElementById(this.targetId), [["width", this.width], ["height", this.height]]); - this.plotAxes(properties); - this.plotClusterTitles(properties); - this.plotWhisters(data,properties); -}; - -BoxWhiskerGraph.prototype.input = function(){ - return DATADOC.getBoxWhikerData(); -}; - -BoxWhiskerGraph.prototype.test = function(targetId){ - var plot = new BoxWhiskerGraph( - { - targetId : targetId, - height : 350, - width : 450, - maxBoxWidth : 25 - } - ); - plot.refresh(this.input()); -}; - - - - - - - -function DataSet(){ - this.json = null; - -}; - - -DataSet.prototype.loadFromJSON = function(json){ - if(json != null) { - if(this.validate(json)) { - this.json = json; - } - } -}; - - -DataSet.prototype.toJSON = function(json){ - return this.json; -}; - - -/** Abstract method to be override on childs classes **/ -DataSet.prototype.validate = function(json){ - if (true){ - return true; - } - /*else{ - throw "Data validation failed"; - }*/ -}; - - - - -Edge.prototype.getName = GraphItem.prototype.getName; -Edge.prototype.setName = GraphItem.prototype.setName; -Edge.prototype.getId = GraphItem.prototype.getId; - -function Edge(id, name, nodeSource, nodeTarget, args){ - GraphItem.prototype.constructor.call(this, id, name, args); - - this.sourceNode = nodeSource; - this.targetNode = nodeTarget; - -}; - -Edge.prototype.toJSON = function(){ - return {"index": this.id,"sourceIndex":this.sourceNode.getId(),"targetIndex":this.targetNode.getId(),"args":this.args}; -}; - -Edge.prototype.getNodeSource = function(){ - return this.sourceNode; -}; - -Edge.prototype.getNodeTarget = function(){ - return this.targetNode; -}; - -Edge.prototype.remove = function(){ - //Remove edge object in the nodes - this.getNodeSource().removeEdge(this); - this.getNodeTarget().removeEdge(this); - - this.deleted.notify(this); -}; - - -EdgeGraphFormatter.prototype.setProperties = ItemGraphFormatter.prototype.setProperties; -EdgeGraphFormatter.prototype.getDefault = ItemGraphFormatter.prototype.getDefault; -EdgeGraphFormatter.prototype.getSelected = ItemGraphFormatter.prototype.getSelected; -EdgeGraphFormatter.prototype.getOver = ItemGraphFormatter.prototype.getOver; -EdgeGraphFormatter.prototype.getDragging = ItemGraphFormatter.prototype.getDragging; -EdgeGraphFormatter.prototype.getId = ItemGraphFormatter.prototype.getId; -EdgeGraphFormatter.prototype.toJSON = ItemGraphFormatter.prototype.toJSON; -EdgeGraphFormatter.prototype.loadFromJSON = ItemGraphFormatter.prototype.loadFromJSON; -EdgeGraphFormatter.prototype._setEvents = ItemGraphFormatter.prototype._setEvents; - -function EdgeGraphFormatter(id, defaultFormat, selectedFormat, overFormat, dragginFormat){ - ItemGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, dragginFormat); - - this.getDefault().args.type = "EdgeGraphFormatter"; -}; - - - - -LineEdgeGraphFormatter.prototype.setProperties = EdgeGraphFormatter.prototype.setProperties; -LineEdgeGraphFormatter.prototype.getDefault = EdgeGraphFormatter.prototype.getDefault; -LineEdgeGraphFormatter.prototype.getSelected = EdgeGraphFormatter.prototype.getSelected; -LineEdgeGraphFormatter.prototype.getOver = EdgeGraphFormatter.prototype.getOver; -LineEdgeGraphFormatter.prototype.getDragging = EdgeGraphFormatter.prototype.getDragging; -LineEdgeGraphFormatter.prototype.getId = EdgeGraphFormatter.prototype.getId; -LineEdgeGraphFormatter.prototype.toJSON = EdgeGraphFormatter.prototype.toJSON; -LineEdgeGraphFormatter.prototype.loadFromJSON = EdgeGraphFormatter.prototype.loadFromJSON; -LineEdgeGraphFormatter.prototype._setEvents = EdgeGraphFormatter.prototype._setEvents; - -function LineEdgeGraphFormatter(id, defaultFormat, selectedFormat, overFormat, dragginFormat){ - EdgeGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, dragginFormat); - this.args.type = "LineEdgeGraphFormatter"; -}; - -/** DIRECTED **/ -DirectedLineEdgeGraphFormatter.prototype.setProperties = EdgeGraphFormatter.prototype.setProperties; -DirectedLineEdgeGraphFormatter.prototype.getDefault = EdgeGraphFormatter.prototype.getDefault; -DirectedLineEdgeGraphFormatter.prototype.getSelected = EdgeGraphFormatter.prototype.getSelected; -DirectedLineEdgeGraphFormatter.prototype.getOver = EdgeGraphFormatter.prototype.getOver; -DirectedLineEdgeGraphFormatter.prototype.getDragging = EdgeGraphFormatter.prototype.getDragging; -DirectedLineEdgeGraphFormatter.prototype.getId = EdgeGraphFormatter.prototype.getId; -DirectedLineEdgeGraphFormatter.prototype.toJSON = EdgeGraphFormatter.prototype.toJSON; -DirectedLineEdgeGraphFormatter.prototype.loadFromJSON = EdgeGraphFormatter.prototype.loadFromJSON; -DirectedLineEdgeGraphFormatter.prototype._setEvents = EdgeGraphFormatter.prototype._setEvents; - -function DirectedLineEdgeGraphFormatter(id, defaultFormat, selectedFormat, overFormat, dragginFormat){ - EdgeGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, dragginFormat); - this.args.type = "DirectedLineEdgeGraphFormatter"; - this.args.arrowSize = "3"; -}; -DirectedLineEdgeGraphFormatter.prototype.getArrowSize = function(){ - return this.args.arrowSize; -}; - -OdirectedLineEdgeGraphFormatter.prototype.setProperties = EdgeGraphFormatter.prototype.setProperties; -OdirectedLineEdgeGraphFormatter.prototype.getDefault = EdgeGraphFormatter.prototype.getDefault; -OdirectedLineEdgeGraphFormatter.prototype.getSelected = EdgeGraphFormatter.prototype.getSelected; -OdirectedLineEdgeGraphFormatter.prototype.getOver = EdgeGraphFormatter.prototype.getOver; -OdirectedLineEdgeGraphFormatter.prototype.getDragging = EdgeGraphFormatter.prototype.getDragging; -OdirectedLineEdgeGraphFormatter.prototype.getId = EdgeGraphFormatter.prototype.getId; -OdirectedLineEdgeGraphFormatter.prototype.toJSON = EdgeGraphFormatter.prototype.toJSON; -OdirectedLineEdgeGraphFormatter.prototype.loadFromJSON = EdgeGraphFormatter.prototype.loadFromJSON; -OdirectedLineEdgeGraphFormatter.prototype._setEvents = EdgeGraphFormatter.prototype._setEvents; - -function OdirectedLineEdgeGraphFormatter(id, defaultFormat, selectedFormat, overFormat, dragginFormat){ - EdgeGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, dragginFormat); - this.args.type = "DirectedLineEdgeGraphFormatter"; - this.args.arrowSize = "3"; -}; - -OdirectedLineEdgeGraphFormatter.prototype.getArrowSize = function(){ - return this.args.arrowSize; -}; - - -/** CUT (inhibition) **/ -CutDirectedLineEdgeGraphFormatter.prototype.setProperties = EdgeGraphFormatter.prototype.setProperties; -CutDirectedLineEdgeGraphFormatter.prototype.getDefault = EdgeGraphFormatter.prototype.getDefault; -CutDirectedLineEdgeGraphFormatter.prototype.getSelected = EdgeGraphFormatter.prototype.getSelected; -CutDirectedLineEdgeGraphFormatter.prototype.getOver = EdgeGraphFormatter.prototype.getOver; -CutDirectedLineEdgeGraphFormatter.prototype.getDragging = EdgeGraphFormatter.prototype.getDragging; -CutDirectedLineEdgeGraphFormatter.prototype.getId = EdgeGraphFormatter.prototype.getId; -CutDirectedLineEdgeGraphFormatter.prototype.toJSON = EdgeGraphFormatter.prototype.toJSON; -CutDirectedLineEdgeGraphFormatter.prototype.loadFromJSON = EdgeGraphFormatter.prototype.loadFromJSON; -CutDirectedLineEdgeGraphFormatter.prototype._setEvents = EdgeGraphFormatter.prototype._setEvents; - -function CutDirectedLineEdgeGraphFormatter(id, defaultFormat, selectedFormat, overFormat, dragginFormat){ - EdgeGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, dragginFormat); - this.args.type = "CutDirectedLineEdgeGraphFormatter"; -}; - - - - -BezierEdgeGraphFormatter.prototype.setProperties = EdgeGraphFormatter.prototype.setProperties; -BezierEdgeGraphFormatter.prototype.setProperties = EdgeGraphFormatter.prototype.setProperties; -BezierEdgeGraphFormatter.prototype.getDefault = EdgeGraphFormatter.prototype.getDefault; -BezierEdgeGraphFormatter.prototype.getSelected = EdgeGraphFormatter.prototype.getSelected; -BezierEdgeGraphFormatter.prototype.getOver = EdgeGraphFormatter.prototype.getOver; -BezierEdgeGraphFormatter.prototype.getDragging = EdgeGraphFormatter.prototype.getDragging; -BezierEdgeGraphFormatter.prototype.getId = EdgeGraphFormatter.prototype.getId; -BezierEdgeGraphFormatter.prototype.toJSON = EdgeGraphFormatter.prototype.toJSON; -BezierEdgeGraphFormatter.prototype.loadFromJSON = EdgeGraphFormatter.prototype.loadFromJSON; -BezierEdgeGraphFormatter.prototype._setEvents = EdgeGraphFormatter.prototype._setEvents; - -function BezierEdgeGraphFormatter(id, defaultFormat, selectedFormat, overFormat, dragginFormat){ - EdgeGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, dragginFormat); - this.args.type = "BezierEdgeGraphFormatter"; -}; - - - -DotDirectedLineEdgeGraphFormatter.prototype.setProperties = EdgeGraphFormatter.prototype.setProperties; -DotDirectedLineEdgeGraphFormatter.prototype.getDefault = EdgeGraphFormatter.prototype.getDefault; -DotDirectedLineEdgeGraphFormatter.prototype.getSelected = EdgeGraphFormatter.prototype.getSelected; -DotDirectedLineEdgeGraphFormatter.prototype.getOver = EdgeGraphFormatter.prototype.getOver; -DotDirectedLineEdgeGraphFormatter.prototype.getDragging = EdgeGraphFormatter.prototype.getDragging; -DotDirectedLineEdgeGraphFormatter.prototype.getId = EdgeGraphFormatter.prototype.getId; -DotDirectedLineEdgeGraphFormatter.prototype.toJSON = EdgeGraphFormatter.prototype.toJSON; -DotDirectedLineEdgeGraphFormatter.prototype.loadFromJSON = EdgeGraphFormatter.prototype.loadFromJSON; -DotDirectedLineEdgeGraphFormatter.prototype._setEvents = EdgeGraphFormatter.prototype._setEvents; - -function DotDirectedLineEdgeGraphFormatter(id, defaultFormat, selectedFormat, overFormat, dragginFormat){ - EdgeGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, dragginFormat); - this.args.type = "DotDirectedLineEdgeGraphFormatter"; -}; - - -OdotDirectedLineEdgeGraphFormatter.prototype.setProperties = EdgeGraphFormatter.prototype.setProperties; -OdotDirectedLineEdgeGraphFormatter.prototype.getDefault = EdgeGraphFormatter.prototype.getDefault; -OdotDirectedLineEdgeGraphFormatter.prototype.getSelected = EdgeGraphFormatter.prototype.getSelected; -OdotDirectedLineEdgeGraphFormatter.prototype.getOver = EdgeGraphFormatter.prototype.getOver; -OdotDirectedLineEdgeGraphFormatter.prototype.getDragging = EdgeGraphFormatter.prototype.getDragging; -OdotDirectedLineEdgeGraphFormatter.prototype.getId = EdgeGraphFormatter.prototype.getId; -OdotDirectedLineEdgeGraphFormatter.prototype.toJSON = EdgeGraphFormatter.prototype.toJSON; -OdotDirectedLineEdgeGraphFormatter.prototype.loadFromJSON = EdgeGraphFormatter.prototype.loadFromJSON; -OdotDirectedLineEdgeGraphFormatter.prototype._setEvents = EdgeGraphFormatter.prototype._setEvents; - -function OdotDirectedLineEdgeGraphFormatter(id, defaultFormat, selectedFormat, overFormat, dragginFormat){ - EdgeGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, dragginFormat); - this.args.type = "OdotDirectedLineEdgeGraphFormatter"; -}; - - - -function GraphDataset(){ - DataSet.prototype.constructor.call(this); - this.edges = new Object(); - this.vertices = new Object(); - this.verticesIndex = new Object(); - - //Events - this.newVertex = new Event(this); - this.vertexNameChanged = new Event(this); - this.vertexDeleted = new Event(this); - - this.newEdge = new Event(this); - this.edgeNameChanged = new Event(this); - this.edgeDeleted = new Event(this); - - this.json = new Object(); - this.json.vertices = new Array(); - this.json.edges = new Array(); - this.json.relations = new Array(); -}; - -GraphDataset.prototype.loadFromJSON = DataSet.prototype.loadFromJSON; -GraphDataset.prototype.toJSON = DataSet.prototype.toJSON; -GraphDataset.prototype.validate = DataSet.prototype.validate; - -/** Devuelve el numero de edges incidentes sobre el nodo con mas edges **/ -GraphDataset.prototype.getMaxClass = function(){ - var maxClassNode = 0; - for ( var node in this.vertices) { - if (this.vertices[node].getEdgesCount() > maxClassNode){ - maxClassNode = this.vertices[node].getEdgesCount(); - } - } - return maxClassNode; -}; - -/** Devuelve el numero de edges incidentes sobre el nodo con mas edges **/ -GraphDataset.prototype.getMinClass = function(){ - var minClassNode = Math.min(); - for ( var node in this.vertices) { - if (this.vertices[node].getEdgesCount() < minClassNode){ - minClassNode = this.vertices[node].getEdgesCount(); - } - } - return minClassNode; -}; - -GraphDataset.prototype.getVertexByName = function(nodeName){ - var results = new Array(); - - for (var vertexId in this.verticesIndex[nodeName]){ - var vertexByid = this.getVertexById(this.verticesIndex[nodeName][vertexId]); - results.push(vertexByid); - //* añadido nuevo porque fallaba el anterior codigo - return vertexByid - } - - if (results <= 1){ - return this.getVertexById(this.verticesIndex[nodeName]); - } - else{ - return results; - } -}; - -GraphDataset.prototype.getVertexById = function(id){ - return this.vertices[id]; -}; - -GraphDataset.prototype.toSIF = function(){ - var sifDataAdapter = new SifFileDataAdapter(); - return sifDataAdapter.toSIF(this); -}; - -GraphDataset.prototype.toSIFID = function(){ - var sifDataAdapter = new SifFileDataAdapter(); - return sifDataAdapter.toSIFID(this); -}; - -GraphDataset.prototype.toDOT = function(){ - var dotFileDataAdapter = new DotFileDataAdapter(); - return dotFileDataAdapter.toDOT(this); -}; - -GraphDataset.prototype.toDOTID = function(){ - var dotFileDataAdapter = new DotFileDataAdapter(); - return dotFileDataAdapter.toDOTID(this); -}; - -GraphDataset.prototype._addNode = function(nodeName, args){ - return new Vertex(this._getVerticesCount()-1, nodeName, args); -}; - -GraphDataset.prototype.addNode = function(nodeName, args){ - this.json.vertices.push(nodeName); - this._addVerticesIndex(nodeName, this._getVerticesCount() - 1); - var vertex = this._addNode(nodeName, args); - this.vertices[this._getVerticesCount()-1] = vertex; - this._setNodeEvents(vertex); - this.newVertex.notify(vertex); -}; - -GraphDataset.prototype._addVerticesIndex = function(nodeName, id){ - if (this.verticesIndex[nodeName] == null){ - this.verticesIndex[nodeName] = new Array(); - } - this.verticesIndex[nodeName].push(id); -}; - -GraphDataset.prototype.addEdge = function(edgeName, nodeSourceId, nodeTargetId, args){ - this.json.edges.push(edgeName); - var nodeSource = this.getVertexById(nodeSourceId); - var nodeTarget = this.getVertexById(nodeTargetId); - var index = this.getEdgesCount() - 1; - this.edges[index] = new Edge(index, edgeName, nodeSource, nodeTarget, args); - this.json.relations.push({"index": index, "sourceIndex": nodeSourceId, "targetIndex": nodeTargetId, "args": args }); - - nodeSource.addEdge(this.edges[index]); - nodeTarget.addEdge(this.edges[index]); - this._setEdgeEvents(this.edges[index]); - this.newEdge.notify(this.edges[index]); -}; - -GraphDataset.prototype.getVertices = function(){ - return this.vertices; -}; - -GraphDataset.prototype.getEdges = function(){ - return this.edges; -}; - -GraphDataset.prototype.getEdgeById = function(edgeId){ - return this.edges[edgeId]; -}; - -GraphDataset.prototype._getVerticesCount = function(){ - return this.json.vertices.length; -}; - - -GraphDataset.prototype.getVerticesCount = function(){ - var count = 0; - for ( var vertex in this.getVertices()) { - count ++; - } - return count; -}; - - -GraphDataset.prototype.getEdgesCount = function(){ - return this.json.edges.length; -}; - -GraphDataset.prototype.init = function(){ - this.edges = new Object(); - this.vertices = new Object(); -}; - -GraphDataset.prototype._setNodeEvents = function(node){ - var _this = this; - //NODE EVENTS - node.deleted.attach(function (sender, node){ - _this._removeNode(node); - }); - - node.nameChanged.attach(function (sender, args){ - var item = args.item; - var newName = item.name; - var indexes = _this.verticesIndex[args.previousName]; - for(var i = 0; i < indexes.length; i++){ - if(indexes[i] == item.id) - indexes.splice(i,1); - } - if(indexes.length == 0){ - delete _this.verticesIndex[args.previousName]; - } - _this._addVerticesIndex(newName, item.id); - _this.json.vertices[item.id] = newName; - _this.vertexNameChanged.notify(args); - }); -}; - -GraphDataset.prototype._setEdgeEvents = function(edge){ - var _this = this; - //EDGE EVENTS - edge.nameChanged.attach(function (sender, edge){ - _this.edgeNameChanged.notify(edge); - - }); - - edge.deleted.attach(function (sender, edge){ - _this._removeEdge(edge); - }); -}; - -GraphDataset.prototype._connectVerticesByName = function(nodeNameSource, nodeNameTarget){ - var source = this.getVertexByName(nodeNameSource); - var target = this.getVertexByName(nodeNameTarget); - - if ((source != null)&&(target!=null)){ - this.addEdge(source.getName() +"_" + target.getName(), source.getId(), target.getId(), {}); - } - else{ - if (source == null){ - console.log("No encontrado: " + nodeNameSource) - } - if (target == null){ - console.log("No encontrado: " + nodeNameTarget) - } - } -}; - -GraphDataset.prototype.loadFromJSON = function(json){ - var json = json; - this.init(); - this.json = new Object(); - this.json.vertices = new Array(); - this.json.edges = new Array(); - this.json.relations = new Array(); - - for ( var i = 0; i < json.nodes.length; i++) { - if (json.nodes[i] != null){ - var name = json.nodes[i]; - this.addNode(name); - } - else{ - this.json.vertices.push(null); - } - } - - for ( var i = 0; i < json.edges.length; i++) { - if (json.edges[i] != null){ - if (json.relations[i] != null){ - var name = json.edges[i]; - this.addEdge(name, json.relations[i].sourceIndex, json.relations[i].targetIndex, json.relations[i].args); - } - } - else{ - this.json.edges.push(null); - this.json.relations.push(null); - } - } -}; - -GraphDataset.prototype.prettyPrint = function(){ - for ( var node in this.vertices) { - console.log(this.vertices[node].getName() ); - for ( var j = 0; j < this.vertices[node].getEdgesIn().length; j++) { - console.log(" --> " + this.vertices[node].getEdgesIn()[j].getNodeTarget().getName() ); - } - } -}; - -GraphDataset.prototype._removeEdge = function(edge){ - this.json.edges[edge.getId()] = null; - this.json.relations[edge.getId()] = null; - - delete this.edges[edge.getId()]; - this.edgeDeleted.notify(edge); - - -}; - -GraphDataset.prototype._removeNode = function(node){ - this.json.vertices[node.getId()] = null; - delete this.vertices[node.getId()]; - this.vertexDeleted.notify(node); -}; - -GraphDataset.prototype.toJSON = function(){ - var json = new Object(); - var nodes = new Array(); - json.nodes = this.json.vertices; //nodes; - json.edges = this.json.edges; //edges; - json.relations = this.json.relations; - return json; -}; - -GraphDataset.prototype.clone = function(){ - var dsDataset = new GraphDataset(); - dsDataset.loadFromJSON(this.toJSON()); - return dsDataset; -}; -//GraphDataset.prototype.test = function(){ -// this.loadFromJSON(this.toJSON()); -//}; - -function labels(){ - var names = new Array(); - - var dataset = interactomeViewer.graphEditorWidget.dataset; - var layout = interactomeViewer.graphEditorWidget.layout; - - for ( var vertexId in interactomeViewer.graphEditorWidget.dataset.getVertices()) { - names.push(interactomeViewer.graphEditorWidget.dataset.getVertexById(vertexId).getName()); - } - - var sorted = (names.sort()); - console.log(sorted) - var distance = 0.01; - var altura = 0.6; - for ( var i = 0; i < names.length; i++) { - var id =dataset.getVertexByName(names[i]).getId(); - - layout.getNodeById(id).setCoordenates(distance, altura); - - - distance = parseFloat(distance) + parseFloat(0.03); - - altura = parseFloat(altura) + parseFloat(0.02); - - if (parseFloat(altura) == 0.9800000000000003){ - - altura = 0.6; - distance = distance - 0.51; - } - - } - - -}; - -function GraphItem(id, name, args){ - this.id = id; - this.name = name; - this.type = "NONE"; - - this.args = new Object(); - - - if (args!=null){ - this.args = args; - if (args.type !=null){ - this.type = args.type; - } - } - - //Events - this.nameChanged = new Event(this); - this.deleted = new Event(this); -} - -GraphItem.prototype.getName = function(){ - return this.name; -}; - -GraphItem.prototype.getId = function(){ - return this.id; -}; - -GraphItem.prototype.setName = function(name){ - var oldName = this.getName(); - this.name = name; - this.nameChanged.notify({"item": this, "previousName" : oldName}); -}; - - - - - -function LayoutDataset(){ - this.dataset = null; - this.vertices = new Object(); - this.changed = new Event(this); - - - this.args = new Object(); - - //RANDOM, CIRCLE - this.args.type = "CIRCLE"; -}; - -LayoutDataset.prototype.loadFromJSON = function(dataset, json){ - var _this = this; - this.vertices = new Object(); - this.dataset = dataset; //new GraphDataset(); - for ( var vertex in json) { - this.vertices[vertex] = new NodeLayout(vertex, json[vertex].x, json[vertex].y); - this.vertices[vertex].changed.attach(function (sender, item){ - _this.changed.notify(item); - }); - } - this._attachDatasetEvents(); -}; - - -LayoutDataset.prototype.toJSON = function(){ - var serialize = new Object(); - for ( var vertex in this.vertices) { - serialize[vertex] = new Object(); - serialize[vertex].x = this.vertices[vertex].x; - serialize[vertex].y = this.vertices[vertex].y; - } - serialize.dataset = new Object(); - serialize.dataset =this.dataset.toJSON(); - return serialize; -}; - -LayoutDataset.prototype.dataBind = function(graphDataset){ - this.dataset = graphDataset; - this._attachDatasetEvents(); - this._calculateLayout(); -}; - -LayoutDataset.prototype._removeVertex = function(vertexId){ - delete this.vertices[vertexId]; -}; - -LayoutDataset.prototype._attachDatasetEvents = function(){ - var _this = this; - - this.dataset.vertexDeleted.attach(function (sender, item){ - _this._removeVertex(item.getId()); - }); - - this.dataset.newVertex.attach(function (sender, item){ - _this.vertices[item.getId()] = new NodeLayout(item.getId(), 0.5, 0.5); - _this.vertices[item.getId()].changed.attach(function (sender, item){ - _this.changed.notify(item); - }); - }); -}; - -LayoutDataset.prototype.getType = function(){ - return this.args.type; -}; - -LayoutDataset.prototype._calculateLayoutVertices = function(type, count){ - - if (type == "CIRCLE"){ - var radius = 0.4; - var centerX = 0.5; - var centerY = 0.5; - var verticesCoordinates = new Array(); - for(var i = 0; i < count; i++){ - x = centerX + radius * Math.sin(i * 2 * Math.PI/count); - y = centerY + radius * Math.cos(i * 2 * Math.PI/count); - verticesCoordinates.push({'x':x,'y':y}); - } - return verticesCoordinates; - } -}; - - -LayoutDataset.prototype._calculateLayout = function(){ - var _this = this; - if (this.getType() == "RANDOM"){ - for ( var vertex in this.dataset.getVertices()) { - if (this.vertices[vertex] == null){ - this.vertices[vertex] = new NodeLayout(vertex, 0, 0); - } - this.vertices[vertex].setCoordinates(Math.random(), Math.random()); - this.vertices[vertex].changed.attach(function (sender, item){ - _this.changed.notify(item); - }); - } - } - - if ( this.getType() == "CIRCLE"){ - - var count = this.dataset._getVerticesCount(); - var verticesCoordinates = this._calculateLayoutVertices(this.getType(), count); - - var aux = 0; - for ( var vertex in this.dataset.getVertices()) { - if (this.vertices[vertex] == null){ - this.vertices[vertex] = new NodeLayout(vertex, 0, 0); - } - this.vertices[vertex].setCoordinates(verticesCoordinates[aux].x, verticesCoordinates[aux].y);//{"x":, "y":}; - aux++; - this.vertices[vertex].changed.attach(function (sender, item){ - _this.changed.notify(item); - }); - } - } - - - if (this.getType() == "SQUARE"){ - - var count = this.dataset._getVerticesCount(); - var xMin = 0.1; - var xMax = 0.9; - var yMin = 0.1; - var yMax = 0.9; - - var rows = Math.sqrt(count); - var step = (xMax - xMin) / rows; - - var verticesCoordinates = new Array(); - for(var i = 0; i < rows; i ++){ - for ( var j = 0; j < rows; j++) { - x = i * step + xMin; - y = j * step + yMin; - verticesCoordinates.push({'x':x,'y':y}); - } - } - - var aux = 0; - for ( var vertex in this.dataset.getVertices()) { - if (this.vertices[vertex] == null){ - this.vertices[vertex] = new NodeLayout(vertex, 0, 0); - } - this.vertices[vertex].setCoordinates(verticesCoordinates[aux].x, verticesCoordinates[aux].y);//{"x":, "y":}; - aux++; - this.vertices[vertex].changed.attach(function (sender, item){ - _this.changed.notify(item); - }); - } - } - -}; - -LayoutDataset.prototype.getNodeById = function(id){ - return this.vertices[id]; -}; - -LayoutDataset.prototype.getVerticesByArea = function(x1, y1, x2, y2){ - var vertices = new Array(); - for ( var vertex in this.dataset.getVertices()) { - if ((this.vertices[vertex].x >= x1)&&(this.vertices[vertex].x <= x2)){ - if ((this.vertices[vertex].y >= y1)&&(this.vertices[vertex].y <= y2)){ - vertices.push(this.vertices[vertex]); - } - } - } - return vertices; -}; - - - - -LayoutDataset.prototype.getLayout = function(type){ - - if (type == "CIRCLE"){ - this.args.type = "CIRCLE"; - this._calculateLayout(); - return; - } - - if (type == "SQUARE"){ - this.args.type = "SQUARE"; - this._calculateLayout(); - return; - } - - if (type == "RANDOM"){ - this.args.type = "RANDOM"; - this._calculateLayout(); - return; - } - - - var dotText = this.dataset.toDOTID(); - var url = "http://bioinfo.cipf.es/utils/ws/rest/network/layout/"+type+".coords"; - var _this = this; - - $.ajax({ - async: true, - type: "POST", - url: url, - dataType: "text", - data: { - dot :dotText - }, - cache: false, - success: function(data){ - var response = JSON.parse(data); - for ( var vertexId in response) { - _this.vertices[vertexId].setCoordinates(response[vertexId].x, response[vertexId].y); - } - } - }); - -// $.ajax({ -// async: true, -// type: "POST", -// url: url, -// dataType: "script", -// data: { -// dot :dotText -// }, -// cache: false, -// success: function(data){ -// var response = JSON.parse(data); -// for ( var vertexId in response) { -// _this.vertices[vertexId].setCoordinates(response[vertexId].x, response[vertexId].y); -// } -// } -// }); - -}; - -function NodeLayout(id, x, y, args){ - this.id = id; - this.x = x; - this.y = y; - this.changed = new Event(this); -}; - -NodeLayout.prototype.getId = function(id){ - return this.id; -}; - -NodeLayout.prototype.setCoordinates = function(x, y){ - this.x = x; - this.y = y; - this.changed.notify(this); -}; - - - -function NetworkDataSetFormatter(vertexFormatProperties, defaultEdgeProperties, args){ - DataSet.prototype.constructor.call(this); - - this.args = new Object(); - - this.vertices = new Object(); - this.edges = new Object(); - this.dataset = null; - this.maxClass = 0; - this.minClass = 0; - - /** Coordenates with default Setting */ - this.args.width = 1100; - this.args.height = 500; - - /** Optional parameters */ - this.args.backgroundColor = "#FFFFFF"; - this.args.backgroundImage = null; - this.args.backgroundImageHeight = null; - this.args.backgroundImageWidth = null; - this.args.backgroundImageX = null; - this.args.backgroundImageY = null; - - - this.args.balanceNodes = false; - this.args.nodesMaxSize = 3; - this.args.nodesMinSize = 0.5; - - - /** Zoom **/ - this.args.zoomScale = 1; - this.args.zoomScaleStepFactor = 0.4; - - if (args != null){ - if (args.backgroundImage != null){ - this.args.backgroundImage = args.backgroundImage; - } - - if (args.width != null){ - this.args.width = args.width; - } - - if (args.height != null){ - this.args.height = args.height; - this.args.svgHeight = args.height; - } - - if (args.svgHeight != null){ - this.args.svgHeight = args.svgHeight; - } - - if (args.backgroundColor != null){ - this.args.backgroundColor = args.backgroundColor; - } - - if(args.balanceNodes != null){ - this.args.balanceNodes = args.balanceNodes; - } - - if(args.nodesMaxSize != null){ - this.args.nodesMaxSize = args.nodesMaxSize; - } - if(args.nodesMinSize != null){ - this.args.nodesMinSize = args.nodesMinSize; - } - } - - this.args.defaultEdgeProperties = {"fill":"#000000", "radius":"1", "stroke":"#000000", "size":1, "title":{"fontSize":10, "fontColor":"#000000"}}; - this.args.vertexFormatProperties = { "fill":"#000000", "stroke":"#000000", "stroke-opacity":"#000000"}; - - if (vertexFormatProperties!= null){ - this.args.vertexFormatProperties = vertexFormatProperties; - } - if (defaultEdgeProperties!= null){ - this.args.defaultEdgeProperties = defaultEdgeProperties; - } - - /** Events **/ - this.changed = new Event(this); - this.edgeChanged = new Event(this); - this.resized = new Event(this); - this.backgroundImageChanged= new Event(this); - this.backgroundColorChanged= new Event(this); -}; - -NetworkDataSetFormatter.prototype.loadFromJSON = function(dataset, json){ - this.args = new Object(); - this.vertices = new Object(); - this.args = json; - this._setDataset(dataset); - var _this = this; - - for ( var vertex in json.vertices) { - this.addVertex(vertex, json.vertices[vertex]); - } - - for ( var edgeId in json.edges) { - this.addEdge(edgeId, json.edges[edgeId]); - } -}; - - -NetworkDataSetFormatter.prototype.toJSON = function(){ - - -// this.args.vertices = new Object(); -// this.args.edges = new Object(); -// -// for ( var vertex in this.vertices) { -// this.args.vertices[vertex] = this.getVertexById(vertex).toJSON(); -// } -// for ( var edge in this.edges) { -// this.args.edges[edge] = this.getEdgeById(edge).toJSON(); -// } -// -// return (this.args); - - - var serialize = new Object(); - serialize = JSON.parse(JSON.stringify(this.args)); - serialize.vertices = new Object(); - serialize.edges = new Object(); - - for ( var vertex in this.vertices) { - serialize.vertices[vertex] = this.getVertexById(vertex).toJSON(); - } - for ( var edge in this.edges) { - serialize.edges[edge] = this.getEdgeById(edge).toJSON(); - } - - return (serialize); -}; - - - -NetworkDataSetFormatter.prototype._getNodeSize = function(nodeId){ - if (this.isVerticesBalanced()){ - var total = this.maxClass - this.minClass; - if (total == 0){ total = 1;} - var nodeConnection = this.dataset.getVertexById(nodeId).getEdges().length; - return ((nodeConnection*this.args.nodesMaxSize)/total) + this.args.nodesMinSize; - } - return this.getVertexById(nodeId).getDefault().getSize(); -}; - -NetworkDataSetFormatter.prototype._recalculateSize = function(){ - if (this.isVerticesBalanced()){ - this.maxClass = this.dataset.getMaxClass(); - this.minClass = this.dataset.getMinClass(); - for ( var vertexIdAll in this.vertices) { - var size = this._getNodeSize(vertexIdAll); - this.vertices[vertexIdAll].getDefault().setSize(size); - this.vertices[vertexIdAll].getSelected().setSize(size); - this.vertices[vertexIdAll].getOver().setSize(size); - } - } -}; - - -NetworkDataSetFormatter.prototype.addVertex = function(vertexId, json){ - - - if (json == null){ - this.vertices[vertexId] = new CircleVertexGraphFormatter(vertexId, this.args.vertexFormatProperties.defaultFormat, this.args.vertexFormatProperties.selected, this.args.vertexFormatProperties.over, this.args.vertexFormatProperties.dragging); - } - else{ - - /** Cargo los attributos desde el json **/ - if (json.type == null){ - this.vertices[vertexId] = new CircleVertexGraphFormatter(vertexId, this.args.vertexFormatProperties.defaultFormat, this.args.vertexFormatProperties.selected, this.args.vertexFormatProperties.over, this.args.vertexFormatProperties.dragging); - } - - if ((json.type == "SquareVertexGraphFormatter")||(json.type == "SquareVertexNetworkFormatter")){ - this.vertices[vertexId] = new SquareVertexGraphFormatter(vertexId); - this.vertices[vertexId].loadFromJSON(json); - } - - if ((json.type == "CircleVertexGraphFormatter")||(json.type == "CircleVertexNetworkFormatter")){ - this.vertices[vertexId] = new CircleVertexGraphFormatter(vertexId); - this.vertices[vertexId].loadFromJSON(json); - } - - if ((json.type == "EllipseVertexGraphFormatter")||(json.type == "EllipseVertexNetworkFormatter")){ - this.vertices[vertexId] = new EllipseVertexGraphFormatter(vertexId); - this.vertices[vertexId].loadFromJSON(json); - } - - if ((json.type == "RectangleVertexGraphFormatter")||(json.type == "RectangleVertexNetworkFormatter")){ - this.vertices[vertexId] = new RectangleVertexGraphFormatter(vertexId); - this.vertices[vertexId].loadFromJSON(json); - } - - if ((json.type == "RoundedVertexGraphFormatter")||(json.type == "RoundedVertexNetworkFormatter")){ - this.vertices[vertexId] = new RoundedVertexGraphFormatter(vertexId); - this.vertices[vertexId].loadFromJSON(json); - } - - } - - - var _this = this; - this.vertices[vertexId].stateChanged.attach(function (sender, item){ - _this.changed.notify(item); - }); - - var size = this._getNodeSize(vertexId); - this.vertices[vertexId].defaultFormat.args.size = size; - this.vertices[vertexId].selected.args.size = size; - this.vertices[vertexId].over.args.size = size; - -}; - -NetworkDataSetFormatter.prototype.addEdge = function(edgeId, json){ - - /** Es un edge nuevo que le doy los atributos por defecto **/ - if (json == null){ - if (this.dataset.getEdgeById(edgeId).getNodeSource().getId() == this.dataset.getEdgeById(edgeId).getNodeTarget().getId()){ - this.edges[edgeId] = new BezierEdgeGraphFormatter(edgeId, this.args.defaultEdgeProperties.defaultFormat, this.args.defaultEdgeProperties.selected, this.args.defaultEdgeProperties.over, this.args.defaultEdgeProperties.dragging); - }else{ - this.edges[edgeId] = new DirectedLineEdgeGraphFormatter(edgeId, this.args.defaultEdgeProperties.defaultFormat, this.args.defaultEdgeProperties.selected, this.args.defaultEdgeProperties.over, this.args.defaultEdgeProperties.dragging); - } - } - else{ - /** Cargo los attributos desde el json **/ - if (json.type == null){ - this.edges[edgeId] = new LineEdgeGraphFormatter(edgeId, this.args.defaultEdgeProperties.defaultFormat, this.args.defaultEdgeProperties.selected, this.args.defaultEdgeProperties.over, this.args.defaultEdgeProperties.dragging); - } - - if ((json.type == "LineEdgeGraphFormatter")||(json.type == "LineEdgeNetworkFormatter")){ - this.edges[edgeId] = new LineEdgeGraphFormatter(edgeId); - this.edges[edgeId].loadFromJSON(json); - } - if ((json.type == "DirectedLineEdgeGraphFormatter")||(json.type == "DirectedLineEdgeNetworkFormatter")){ - this.edges[edgeId] = new DirectedLineEdgeGraphFormatter(edgeId); - this.edges[edgeId].loadFromJSON(json); - } - - if ((json.type == "BezierEdgeGraphFormatter")||(json.type == "BezierEdgeNetworkFormatter")){ - this.edges[edgeId] = new BezierEdgeGraphFormatter(edgeId); - this.edges[edgeId].loadFromJSON(json); - } - - - if ((json.type == "CutDirectedLineEdgeGraphFormatter")||(json.type == "CutDirectedLineEdgeNetworkFormatter")){ - this.edges[edgeId] = new CutDirectedLineEdgeGraphFormatter(edgeId); - this.edges[edgeId].loadFromJSON(json); - } - - if ((json.type == "DotDirectedLineEdgeGraphFormatter")||(json.type == "DotDirectedLineEdgeNetworkFormatter")){ - this.edges[edgeId] = new DotDirectedLineEdgeGraphFormatter(edgeId); - this.edges[edgeId].loadFromJSON(json); - } - if ((json.type == "OdotDirectedLineEdgeGraphFormatter")||(json.type == "OdotDirectedLineEdgeNetworkFormatter")){ - this.edges[edgeId] = new OdotDirectedLineEdgeGraphFormatter(edgeId); - this.edges[edgeId].loadFromJSON(json); - } - - if ((json.type == "OdirectedLineEdgeGraphFormatter")||(json.type == "OdirectedLineEdgeNetworkFormatter")){ - this.edges[edgeId] = new OdirectedLineEdgeGraphFormatter(edgeId); - this.edges[edgeId].loadFromJSON(json); - } - } - - var _this = this; - this.edges[edgeId].stateChanged.attach(function (sender, item){ - _this.edgeChanged.notify(item); - }); - - this._recalculateSize(); -}; - -NetworkDataSetFormatter.prototype._setDataset = function(dataset){ - this.dataset = dataset; - this.maxClass = dataset.getMaxClass(); - this.minClass = dataset.getMinClass(); - this._attachDatasetEvents(); -}; - -NetworkDataSetFormatter.prototype.changeEdgeType = function(edgeId, type){ - if ((type == "LineEdgeGraphFormatter")||(type == "LineEdgeNetworkFormatter")){ - this.edges[edgeId] = new LineEdgeGraphFormatter(edgeId, this.args.defaultEdgeProperties.defaultFormat, this.args.defaultEdgeProperties.selected, this.args.defaultEdgeProperties.over, this.args.defaultEdgeProperties.dragging); - - } - if ((type == "DirectedLineEdgeGraphFormatter")||(type == "DirectedLineEdgeNetworkFormatter")){ - this.edges[edgeId] = new DirectedLineEdgeGraphFormatter(edgeId, this.args.defaultEdgeProperties.defaultFormat, this.args.defaultEdgeProperties.selected, this.args.defaultEdgeProperties.over, this.args.defaultEdgeProperties.dragging); - } - - if ((type == "CutDirectedLineEdgeGraphFormatter")||(type == "CutDirectedLineEdgeNetworkFormatter")){ - this.edges[edgeId] = new CutDirectedLineEdgeGraphFormatter(edgeId, this.args.defaultEdgeProperties.defaultFormat, this.args.defaultEdgeProperties.selected, this.args.defaultEdgeProperties.over, this.args.defaultEdgeProperties.dragging); - } - - if ((type == "DotDirectedLineEdgeGraphFormatter")||(type == "DotDirectedLineEdgeNetworkFormatter")){ - this.edges[edgeId] = new DotDirectedLineEdgeGraphFormatter(edgeId, this.args.defaultEdgeProperties.defaultFormat, this.args.defaultEdgeProperties.selected, this.args.defaultEdgeProperties.over, this.args.defaultEdgeProperties.dragging); - } - - if ((type == "OdotDirectedLineEdgeGraphFormatter")||(type == "OdotDirectedLineEdgeNetworkFormatter")){ - this.edges[edgeId] = new OdotDirectedLineEdgeGraphFormatter(edgeId, this.args.defaultEdgeProperties.defaultFormat, this.args.defaultEdgeProperties.selected, this.args.defaultEdgeProperties.over, this.args.defaultEdgeProperties.dragging); - } - - if ((type == "OdirectedLineEdgeGraphFormatter")||(type == "OdirectedLineEdgeNetworkFormatter")){ - this.edges[edgeId] = new OdirectedLineEdgeGraphFormatter(edgeId, this.args.defaultEdgeProperties.defaultFormat, this.args.defaultEdgeProperties.selected, this.args.defaultEdgeProperties.over, this.args.defaultEdgeProperties.dragging); - } - - - - var _this = this; - this.edges[edgeId].stateChanged.attach(function (sender, item){ - _this.edgeChanged.notify(item); - }); - _this.edgeChanged.notify(this.edges[edgeId]); -}; -/* -classe = "SquareNetworkNodeFormatter"; -} -if (value == "circle"){ - classe = "CircleNetworkNodeFormatter"; - **/ -NetworkDataSetFormatter.prototype.changeNodeType = function(vertexId, type){ - var defaultFormat = JSON.parse(JSON.stringify(this.vertices[vertexId].getDefault())); - var selectedFormat = JSON.parse(JSON.stringify(this.vertices[vertexId].getSelected())); - var defaultFormat = JSON.parse(JSON.stringify(this.vertices[vertexId].getDefault())); - var defaultFormat = JSON.parse(JSON.stringify(this.vertices[vertexId].getDefault())); - - if ((type == "SquareVertexGraphFormatter")||(type == "SquareVertexNetworkFormatter")){ - this.vertices[vertexId] = new SquareVertexGraphFormatter(vertexId,defaultFormat, this.args.vertexFormatProperties.selected, this.args.vertexFormatProperties.over, this.args.vertexFormatProperties.dragging); - } - - if ((type == "CircleVertexGraphFormatter")||(type == "CircleVertexNetworkFormatter")){ - this.vertices[vertexId] = new CircleVertexGraphFormatter(vertexId, defaultFormat, this.args.vertexFormatProperties.selected, this.args.vertexFormatProperties.over, this.args.vertexFormatProperties.dragging); - } - - if ((type == "EllipseVertexGraphFormatter")||(type == "EllipseVertexNetworkFormatter")){ - this.vertices[vertexId] = new EllipseVertexGraphFormatter(vertexId, defaultFormat, this.args.vertexFormatProperties.selected, this.args.vertexFormatProperties.over, this.args.vertexFormatProperties.dragging); - } - - if ((type == "RectangleVertexGraphFormatter")||(type == "RectangleVertexNetworkFormatter")){ - this.vertices[vertexId] = new RectangleVertexGraphFormatter(vertexId, defaultFormat, this.args.vertexFormatProperties.selected, this.args.vertexFormatProperties.over, this.args.vertexFormatProperties.dragging); - } - - if ((type == "RoundedVertexGraphFormatter")||(type == "RoundedVertexNetworkFormatter")){ - this.vertices[vertexId] = new RoundedVertexGraphFormatter(vertexId, defaultFormat, this.args.vertexFormatProperties.selected, this.args.vertexFormatProperties.over, this.args.vertexFormatProperties.dragging); - } - - if ((type == "OctagonVertexGraphFormatter")||(type == "OctagonVertexNetworkhFormatter")){ - this.vertices[vertexId] = new OctagonVertexGraphFormatter(vertexId, defaultFormat, this.args.vertexFormatProperties.selected, this.args.vertexFormatProperties.over, this.args.vertexFormatProperties.dragging); - } - - - var _this = this; - this.vertices[vertexId].stateChanged.attach(function (sender, item){ - _this.changed.notify(item); - }); - _this.changed.notify(this.vertices[vertexId]); -}; - - - -NetworkDataSetFormatter.prototype.dataBind = function(networkDataSet){ - var _this = this; - this._setDataset(networkDataSet); - - for ( var vertex in this.dataset.getVertices()) { - this.addVertex(vertex); - } - - for ( var edge in this.dataset.getEdges()) { - this.addEdge(edge); - } -}; - -NetworkDataSetFormatter.prototype._removeEdge = function(edgeId){ - delete this.edges[edgeId]; -}; - -NetworkDataSetFormatter.prototype._removeVertex = function(vertexId){ - delete this.vertices[vertexId]; -}; - -NetworkDataSetFormatter.prototype._attachDatasetEvents = function(id){ - var _this = this; - this.dataset.vertexDeleted.attach(function (sender, item){ - _this._removeVertex(item.getId()); - }); - - this.dataset.edgeDeleted.attach(function (sender, item){ - _this._removeEdge(item.getId()); - }); - - this.dataset.newVertex.attach(function (sender, item){ - _this.addVertex(item.getId()); - }); - - this.dataset.newEdge.attach(function (sender, item){ - _this.addEdge(item.getId()); - }); -}; - - -NetworkDataSetFormatter.prototype.getVertexById = function(id){ - return this.vertices[id]; -}; - -NetworkDataSetFormatter.prototype.getEdgeById = function(id){ - return this.edges[id]; -}; - -NetworkDataSetFormatter.prototype.makeLabelsBigger = function(){ -for ( var vertexId in this.vertices) { - var fontSize = this.vertices[vertexId].getDefault().getFontSize() + 2; - this.vertices[vertexId].getDefault().setFontSize(fontSize); - } -}; - -NetworkDataSetFormatter.prototype.makeLabelsSmaller = function(){ - for ( var vertexId in this.vertices) { - var fontSize = this.vertices[vertexId].getDefault().getFontSize() - 2; - this.vertices[vertexId].getDefault().setFontSize(fontSize); - } -}; - - -NetworkDataSetFormatter.prototype.resize = function(width, height){ - this.args.width = width; - this.args.height = height; - this.resized.notify(); -}; - -/** ZOOM GETTERS **/ -NetworkDataSetFormatter.prototype.getZoomScaleStepFactor = function(){return this.args.zoomScaleStepFactor;}; -NetworkDataSetFormatter.prototype.setZoomScaleStepFactor = function(scaleFactor){this.args.zoomScaleStepFactor = scaleFactor;}; -NetworkDataSetFormatter.prototype.getZoomScale = function(){return this.args.zoomScale;}; -NetworkDataSetFormatter.prototype.setZoomScale = function(scale){this.args.zoomScale = scale;}; - -NetworkDataSetFormatter.prototype.getNodesMaxSize = function(){return this.args.nodesMaxSize;}; -NetworkDataSetFormatter.prototype.getNodesMinSize = function(){return this.args.nodesMinSize;}; - -/** SIZE SETTERS AND GETTERS **/ -NetworkDataSetFormatter.prototype.isVerticesBalanced = function(){return this.args.balanceNodes;}; -NetworkDataSetFormatter.prototype.getWidth = function(){return this.args.width;}; -NetworkDataSetFormatter.prototype.getHeight = function(){return this.args.height;}; - -/** OPTIONAL PARAMETERS GETTERS AND SETTERS **/ -NetworkDataSetFormatter.prototype.getBackgroundImage = function(){return this.args.backgroundImage;}; -NetworkDataSetFormatter.prototype.setBackgroundImage = function(value){this.args.backgroundImage = value; this.backgroundImageChanged.notify(this);}; - - -NetworkDataSetFormatter.prototype.getBackgroundImageWidth = function(){return this.args.backgroundImageWidth;}; -NetworkDataSetFormatter.prototype.setBackgroundImageWidth = function(value){this.args.backgroundImageWidth = value; this.backgroundImageChanged.notify(this);}; - -NetworkDataSetFormatter.prototype.getBackgroundImageHeight = function(){return this.args.backgroundImageHeight;}; -NetworkDataSetFormatter.prototype.setBackgroundImageHeight = function(value){this.args.backgroundImageHeight = value; this.backgroundImageChanged.notify(this);}; - -NetworkDataSetFormatter.prototype.getBackgroundImageX = function(){return this.args.backgroundImageX;}; -NetworkDataSetFormatter.prototype.setBackgroundImageX = function(value){this.args.backgroundImageX = value; this.backgroundImageChanged.notify(this);}; - -NetworkDataSetFormatter.prototype.getBackgroundImageY = function(){return this.args.backgroundImageX;}; -NetworkDataSetFormatter.prototype.setBackgroundImageY = function(value){this.args.backgroundImageY = value; this.backgroundImageChanged.notify(this);}; - - - -NetworkDataSetFormatter.prototype.getBackgroundColor = function(){return this.args.backgroundColor;}; -NetworkDataSetFormatter.prototype.setBackgroundColor = function(value){this.args.backgroundColor = value; this.backgroundColorChanged.notify(this);}; - -NetworkDataSetFormatter.prototype.getWidth = function(){return this.args.width;}; -NetworkDataSetFormatter.prototype.setWidth = function(value){this.args.width = value;}; - -NetworkDataSetFormatter.prototype.getHeight = function(){return this.args.height;}; -NetworkDataSetFormatter.prototype.setHeight = function(value){this.args.height = value;}; - - - - -Vertex.prototype.getName = GraphItem.prototype.getName; -Vertex.prototype.setName = GraphItem.prototype.setName; -Vertex.prototype.getId = GraphItem.prototype.getId; - -function Vertex(id, name, args){ - GraphItem.prototype.constructor.call(this, id, name, args); - this.edgesIn= new Array(); - this.edgesOut= new Array(); -}; - -Vertex.prototype.getEdges = function(){ - return this.edgesIn.concat(this.edgesOut); -}; - -Vertex.prototype.getEdgesCount = function(){ - return this.getEdges().length; -}; - -Vertex.prototype.getEdgesIn = function(){ - return this.edgesIn; -}; - -Vertex.prototype.getEdgesOut = function(){ - return this.edgesOut; -}; - -Vertex.prototype.addEdge = function(edge){ - if (edge.getNodeSource().getId() == this.id){ - this.edgesIn.push(edge); - } - else{ - this.edgesOut.push(edge); - } -}; - -Vertex.prototype.removeEdge = function(edge){ - for ( var i = 0; i < this.getEdgesIn().length; i++) { - var edgeIn = this.edgesIn[i]; - if (edgeIn.getId() == edge.getId()){ - this.edgesIn.splice(i, 1); - } - } - for ( var i = 0; i < this.getEdgesOut().length; i++) { - var edgeIn = this.edgesOut[i]; - if (edgeIn.getId() == edge.getId()){ - this.edgesOut.splice(i, 1); - } - } -}; - -Vertex.prototype.remove = function(){ - var edges = this.getEdges(); - for ( var i = 0; i < edges.length; i++) { - var edge = edges[i]; - edge.remove(); - } - this.deleted.notify(this); -}; - - - - - -VertexGraphFormatter.prototype.getDefault = ItemGraphFormatter.prototype.getDefault; -VertexGraphFormatter.prototype.getSelected = ItemGraphFormatter.prototype.getSelected; -VertexGraphFormatter.prototype.getOver = ItemGraphFormatter.prototype.getOver; -VertexGraphFormatter.prototype.getDragging = ItemGraphFormatter.prototype.getDragging; -VertexGraphFormatter.prototype.getId = ItemGraphFormatter.prototype.getId; -VertexGraphFormatter.prototype.toJSON = ItemGraphFormatter.prototype.toJSON; -VertexGraphFormatter.prototype.loadFromJSON = ItemGraphFormatter.prototype.loadFromJSON; -VertexGraphFormatter.prototype._setEvents = ItemGraphFormatter.prototype._setEvents; - - -function VertexGraphFormatter(defaultFormat, selectedFormat, overFormat, draggingFormat){ - ItemGraphFormatter.prototype.constructor.call(this, defaultFormat, selectedFormat, overFormat, draggingFormat); -}; - - -CircleVertexGraphFormatter.prototype.getDefault = VertexGraphFormatter.prototype.getDefault; -CircleVertexGraphFormatter.prototype.getSelected = VertexGraphFormatter.prototype.getSelected; -CircleVertexGraphFormatter.prototype.getOver = VertexGraphFormatter.prototype.getOver; -CircleVertexGraphFormatter.prototype.getDragging = VertexGraphFormatter.prototype.getDragging; -CircleVertexGraphFormatter.prototype.getId = VertexGraphFormatter.prototype.getId; -CircleVertexGraphFormatter.prototype.toJSON = VertexGraphFormatter.prototype.toJSON; -CircleVertexGraphFormatter.prototype.loadFromJSON = VertexGraphFormatter.prototype.loadFromJSON; -CircleVertexGraphFormatter.prototype._setEvents = VertexGraphFormatter.prototype._setEvents; - -function CircleVertexGraphFormatter(id, defaultFormat, selectedFormat, overFormat, draggingFormat){ - VertexGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, draggingFormat); - this.args.type = "CircleVertexGraphFormatter"; - if (defaultFormat != null){ - if (defaultFormat.radius != null){ - this.defaultFormat.args.radius = defaultFormat.radius; - } - } - - if (selectedFormat != null){ - if (selectedFormat.radius != null){ - this.selected.args.radius = selectedFormat.radius; - } - } - - if (overFormat != null){ - if (overFormat.radius != null){ - this.over.args.radius = overFormat.radius; - } - } - - if (draggingFormat != null){ - if (draggingFormat.radius != null){ - this.dragging.args.draggingFormat = draggingFormat.radius; - } - } -}; - - - -SquareVertexGraphFormatter.prototype.getDefault = VertexGraphFormatter.prototype.getDefault; -SquareVertexGraphFormatter.prototype.getSelected = VertexGraphFormatter.prototype.getSelected; -SquareVertexGraphFormatter.prototype.getOver = VertexGraphFormatter.prototype.getOver; -SquareVertexGraphFormatter.prototype.getDragging = VertexGraphFormatter.prototype.getDragging; -SquareVertexGraphFormatter.prototype.getId = VertexGraphFormatter.prototype.getId; -SquareVertexGraphFormatter.prototype.toJSON = VertexGraphFormatter.prototype.toJSON; -SquareVertexGraphFormatter.prototype.loadFromJSON = VertexGraphFormatter.prototype.loadFromJSON; -SquareVertexGraphFormatter.prototype._setEvents = VertexGraphFormatter.prototype._setEvents; - -function SquareVertexGraphFormatter(id, defaultFormat, selectedFormat, overFormat, draggingFormat){ - VertexGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, draggingFormat); - this.args.type = "SquareVertexGraphFormatter"; - if (defaultFormat != null){ - if (defaultFormat.radius != null){ - this.defaultFormat.args.radius = defaultFormat.radius; - } - } - - if (selectedFormat != null){ - if (selectedFormat.radius != null){ - this.selected.args.radius = selectedFormat.radius; - } - } - - if (overFormat != null){ - if (overFormat.radius != null){ - this.over.args.radius = overFormat.radius; - } - } - - if (draggingFormat != null){ - if (draggingFormat.radius != null){ - this.dragging.args.draggingFormat = draggingFormat.radius; - } - } -}; - - -EllipseVertexGraphFormatter.prototype.getDefault = VertexGraphFormatter.prototype.getDefault; -EllipseVertexGraphFormatter.prototype.getSelected = VertexGraphFormatter.prototype.getSelected; -EllipseVertexGraphFormatter.prototype.getOver = VertexGraphFormatter.prototype.getOver; -EllipseVertexGraphFormatter.prototype.getDragging = VertexGraphFormatter.prototype.getDragging; -EllipseVertexGraphFormatter.prototype.getId = VertexGraphFormatter.prototype.getId; -EllipseVertexGraphFormatter.prototype.toJSON = VertexGraphFormatter.prototype.toJSON; -EllipseVertexGraphFormatter.prototype.loadFromJSON = VertexGraphFormatter.prototype.loadFromJSON; -EllipseVertexGraphFormatter.prototype._setEvents = VertexGraphFormatter.prototype._setEvents; - -function EllipseVertexGraphFormatter(id, defaultFormat, selectedFormat, overFormat, draggingFormat){ - VertexGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, draggingFormat); - this.args.type = "EllipseVertexGraphFormatter"; - if (defaultFormat != null){ - if (defaultFormat.radius != null){ - this.defaultFormat.args.radius = defaultFormat.radius; - } - } - - if (selectedFormat != null){ - if (selectedFormat.radius != null){ - this.selected.args.radius = selectedFormat.radius; - } - } - - if (overFormat != null){ - if (overFormat.radius != null){ - this.over.args.radius = overFormat.radius; - } - } - - if (draggingFormat != null){ - if (draggingFormat.radius != null){ - this.dragging.args.draggingFormat = draggingFormat.radius; - } - } -}; - - - -RectangleVertexGraphFormatter.prototype.getDefault = ItemGraphFormatter.prototype.getDefault; -RectangleVertexGraphFormatter.prototype.getSelected = ItemGraphFormatter.prototype.getSelected; -RectangleVertexGraphFormatter.prototype.getOver = ItemGraphFormatter.prototype.getOver; -RectangleVertexGraphFormatter.prototype.getDragging = ItemGraphFormatter.prototype.getDragging; -RectangleVertexGraphFormatter.prototype.getId = ItemGraphFormatter.prototype.getId; -RectangleVertexGraphFormatter.prototype.toJSON = ItemGraphFormatter.prototype.toJSON; -RectangleVertexGraphFormatter.prototype.loadFromJSON = ItemGraphFormatter.prototype.loadFromJSON; -RectangleVertexGraphFormatter.prototype._setEvents = ItemGraphFormatter.prototype._setEvents; - -function RectangleVertexGraphFormatter(id, defaultFormat, selectedFormat, overFormat, draggingFormat){ - VertexGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, draggingFormat); - this.args.type = "RectangleVertexGraphFormatter"; - if (defaultFormat != null){ - if (defaultFormat.radius != null){ - this.defaultFormat.args.radius = defaultFormat.radius; - } - } - - if (selectedFormat != null){ - if (selectedFormat.radius != null){ - this.selected.args.radius = selectedFormat.radius; - } - } - - if (overFormat != null){ - if (overFormat.radius != null){ - this.over.args.radius = overFormat.radius; - } - } - - if (draggingFormat != null){ - if (draggingFormat.radius != null){ - this.dragging.args.draggingFormat = draggingFormat.radius; - } - } -}; - - - -RoundedVertexGraphFormatter.prototype.getDefault = ItemGraphFormatter.prototype.getDefault; -RoundedVertexGraphFormatter.prototype.getSelected = ItemGraphFormatter.prototype.getSelected; -RoundedVertexGraphFormatter.prototype.getOver = ItemGraphFormatter.prototype.getOver; -RoundedVertexGraphFormatter.prototype.getDragging = ItemGraphFormatter.prototype.getDragging; -RoundedVertexGraphFormatter.prototype.getId = ItemGraphFormatter.prototype.getId; -RoundedVertexGraphFormatter.prototype.toJSON = ItemGraphFormatter.prototype.toJSON; -RoundedVertexGraphFormatter.prototype.loadFromJSON = ItemGraphFormatter.prototype.loadFromJSON; -RoundedVertexGraphFormatter.prototype._setEvents = ItemGraphFormatter.prototype._setEvents; - -function RoundedVertexGraphFormatter(id, defaultFormat, selectedFormat, overFormat, draggingFormat){ - VertexGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, draggingFormat); - this.args.type = "RoundedVertexGraphFormatter"; - if (defaultFormat != null){ - if (defaultFormat.radius != null){ - this.defaultFormat.args.radius = defaultFormat.radius; - } - } - - if (selectedFormat != null){ - if (selectedFormat.radius != null){ - this.selected.args.radius = selectedFormat.radius; - } - } - - if (overFormat != null){ - if (overFormat.radius != null){ - this.over.args.radius = overFormat.radius; - } - } - - if (draggingFormat != null){ - if (draggingFormat.radius != null){ - this.dragging.args.draggingFormat = draggingFormat.radius; - } - } -}; - - - -OctagonVertexGraphFormatter.prototype.getDefault = ItemGraphFormatter.prototype.getDefault; -OctagonVertexGraphFormatter.prototype.getSelected = ItemGraphFormatter.prototype.getSelected; -OctagonVertexGraphFormatter.prototype.getOver = ItemGraphFormatter.prototype.getOver; -OctagonVertexGraphFormatter.prototype.getDragging = ItemGraphFormatter.prototype.getDragging; -OctagonVertexGraphFormatter.prototype.getId = ItemGraphFormatter.prototype.getId; -OctagonVertexGraphFormatter.prototype.toJSON = ItemGraphFormatter.prototype.toJSON; -OctagonVertexGraphFormatter.prototype.loadFromJSON = ItemGraphFormatter.prototype.loadFromJSON; -OctagonVertexGraphFormatter.prototype._setEvents = ItemGraphFormatter.prototype._setEvents; - -function OctagonVertexGraphFormatter(id, defaultFormat, selectedFormat, overFormat, draggingFormat){ - VertexGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, draggingFormat); - this.args.type = "OctagonVertexGraphFormatter"; - if (defaultFormat != null){ - if (defaultFormat.radius != null){ - this.defaultFormat.args.radius = defaultFormat.radius; - } - } - - if (selectedFormat != null){ - if (selectedFormat.radius != null){ - this.selected.args.radius = selectedFormat.radius; - } - } - - if (overFormat != null){ - if (overFormat.radius != null){ - this.over.args.radius = overFormat.radius; - } - } - - if (draggingFormat != null){ - if (draggingFormat.radius != null){ - this.dragging.args.draggingFormat = draggingFormat.radius; - } - } -}; - - - -var Colors = new function() -{ - this.hashColor = []; - this.getColorByScoreArrayValue = function (arrayScore) - { - var array = new Array(); - - for (var i = 0; i< arrayScore.length; i++) - { - - var color = this.getColorByScoreValue(arrayScore[i]) - array.push( color); - - } - return array; - }; - - this.getHexStringByScoreArrayValue = function (arrayScore) - { - var arrayColor = this.getColorByScoreArrayValue(arrayScore); - var arrayHex = new Array(); - for (var i = 0; i< arrayColor.length; i++) - { - arrayHex.push( arrayColor[i].HexString()); - } - return arrayHex; - }; - - this.getColorByScoreValue = function (score) - { - - var truncate = score.toString().substr(0,4); - if (this.hashColor[truncate]!=null) - { - return this.hashColor[truncate]; - } - - - if(isNaN(score)) { - return Colors.ColorFromRGB(0,0,0); - } - var value; - - var from, to; - if(score < 0.5) { - from = Colors.ColorFromRGB(0,0,255); - to = Colors.ColorFromRGB(255,255,255); - value = (score * 2); - } else { - from = Colors.ColorFromRGB(255,255,255); - to = Colors.ColorFromRGB(255,0,0); - value = (score * 2) - 1; - } - - var x = value; - var y = 1.0 - value; - var color = Colors.ColorFromRGB(y * from.Red() + x * to.Red(), y * from.Green() + x * to.Green(), y * from.Blue() + x * to.Blue()); - - this.hashColor[truncate] = color; - - return color; - }; - - this.ColorFromHSV = function(hue, sat, val) - { - var color = new Color(); - color.SetHSV(hue,sat,val); - return color; - }; - - this.ColorFromRGB = function(r, g, b) - { - var color = new Color(); - color.SetRGB(r,g,b); - return color; - }; - - this.ColorFromHex = function(hexStr) - { - var color = new Color(); - color.SetHexString(hexStr); - return color; - }; - - function Color() { - //Stored as values between 0 and 1 - var red = 0; - var green = 0; - var blue = 0; - - //Stored as values between 0 and 360 - var hue = 0; - - //Strored as values between 0 and 1 - var saturation = 0; - var value = 0; - - this.SetRGB = function(r, g, b) - { - red = r/255.0; - green = g/255.0; - blue = b/255.0; - calculateHSV(); - }; - - this.Red = function() { return Math.round(red*255); }; - - this.Green = function() { return Math.round(green*255); }; - - this.Blue = function() { return Math.round(blue*255); }; - - this.SetHSV = function(h, s, v) - { - hue = h; - saturation = s; - value = v; - calculateRGB(); - }; - - this.Hue = function() - { return hue; }; - - this.Saturation = function() - { return saturation; }; - - this.Value = function() - { return value; }; - - this.SetHexString = function(hexString) - { - if(hexString == null || typeof(hexString) != "string") - { - this.SetRGB(0,0,0); - return; - } - - if (hexString.substr(0, 1) == '#') - hexString = hexString.substr(1); - - if(hexString.length != 6) - { - this.SetRGB(0,0,0); - return; - } - - var r = parseInt(hexString.substr(0, 2), 16); - var g = parseInt(hexString.substr(2, 2), 16); - var b = parseInt(hexString.substr(4, 2), 16); - if (isNaN(r) || isNaN(g) || isNaN(b)) - { - this.SetRGB(0,0,0); - return; - } - - this.SetRGB(r,g,b); - }; - - this.HexString = function() - { - - var rStr = this.Red().toString(16); - if (rStr.length == 1) - rStr = '0' + rStr; - var gStr = this.Green().toString(16); - if (gStr.length == 1) - gStr = '0' + gStr; - var bStr = this.Blue().toString(16); - if (bStr.length == 1) - bStr = '0' + bStr; - return ('#' + rStr + gStr + bStr).toUpperCase(); - }; - - this.Complement = function() - { - var newHue = (hue >= 180) ? hue - 180 : hue + 180; - var newVal = (value * (saturation - 1) + 1); - var newSat = (value*saturation) / newVal; - var newColor = new Color(); - newColor.SetHSV(newHue, newSat, newVal); - return newColor; - } ; - - function calculateHSV() - { - var max = Math.max(Math.max(red, green), blue); - var min = Math.min(Math.min(red, green), blue); - - value = max; - - saturation = 0; - if(max != 0) - saturation = 1 - min/max; - - hue = 0; - if(min == max) - return; - - var delta = (max - min); - if (red == max) - hue = (green - blue) / delta; - else if (green == max) - hue = 2 + ((blue - red) / delta); - else - hue = 4 + ((red - green) / delta); - hue = hue * 60; - if(hue < 0) - hue += 360; - } - - function calculateRGB() - { - red = value; - green = value; - blue = value; - - if(value == 0 || saturation == 0) - return; - - var tHue = (hue / 60); - var i = Math.floor(tHue); - var f = tHue - i; - var p = value * (1 - saturation); - var q = value * (1 - saturation * f); - var t = value * (1 - saturation * (1 - f)); - switch(i) - { - case 0: - red = value; green = t; blue = p; - break; - case 1: - red = q; green = value; blue = p; - break; - case 2: - red = p; green = value; blue = t; - break; - case 3: - red = p; green = q; blue = value; - break; - case 4: - red = t; green = p; blue = value; - break; - default: - red = value; green = p; blue = q; - break; - } - } - } -} -(); -/* - * Clase gestiona la insercción, eliminación de los elementos en el DOM - * - * Vital hacerla SIEMPRE compatible hacia atrás - * - * Last update: 28-10-2010 - * - */ - - -var DOM = {}; - -DOM.createNewElement = function(type, nodeParent, attributes) -{ - - var node = document.createElement(type); - for (var i=0; i0) - { - parent.removeChild(parent.childNodes[0]); - - } -}; - -DOM.select = function(targetID) -{ - return document.getElementById(targetID); -// return $("#"+targetID); -}; -var Geometry = -{ - - /** From tow points obtains the angles formed with the cartesian side **/ - getAngleBetweenTwoPoints : function(x1, y1, x2, y2){ - //var m = (y1 - y2)/ (x1 - x2); - return Math.atan2(y2-y1, x2-x1); - }, - - getAdjacentSideOfRectangleRight : function(angle, hypotenuse){ - return Math.abs(Math.cos(angle)*hypotenuse); - }, - - getOppositeSideOfRectangleRight : function(angle, hypotenuse){ - return Math.abs(Math.sin(angle)*hypotenuse); - }, - - toDegree: function(radian){ - return radian*180/Math.PI; - - } - - -}; - -var SVG = -{ - svgns : 'http://www.w3.org/2000/svg', - xlinkns : "http://www.w3.org/1999/xlink", - - createSVGCanvas: function(parentNode, attributes) - { - - attributes.push( ['xmlns', SVG.svgns], ['xmlns:xlink', 'http://www.w3.org/1999/xlink']); - var svg = document.createElementNS("http://www.w3.org/2000/svg", "svg"); - this._setProperties(svg, attributes); - parentNode.appendChild( svg); - return svg; - - }, - - createRectangle : function (x, y, width, height, attributes){ - var rect = document.createElementNS(this.svgns, "rect"); - rect.setAttribute("x",x); - rect.setAttribute("y",y); - rect.setAttribute("width",width); - rect.setAttribute("height",height); - SVG._setProperties(rect, attributes); - return rect; - }, - - drawImage64 : function (x, y, width, height, base64, svgNode, attributes) { - var node = SVG.createImage64(x, y, width, height, base64, attributes); - svgNode.appendChild(node); - return node; - }, - - createImage64 : function (x, y, width, height, base64, attributes) { - var img = document.createElementNS(this.svgns, "image"); - img.setAttribute("x",x); - img.setAttribute("y",y); - img.setAttribute("width",width); - img.setAttribute("height",height); - img.setAttribute("xlink:href",base64); - SVG._setProperties(img, attributes); - return img; - }, - - createLine: function (x1, y1, x2, y2, attributes){ - var line = document.createElementNS(this.svgns,"line"); - line.setAttribute("x1",x1); - line.setAttribute("y1",y1); - line.setAttribute("x2", x2); - line.setAttribute("y2", y2); - SVG._setProperties(line, attributes); - return line; - }, - - createClip: function (id, nodeToClip, attributes){ - var clip = document.createElementNS(this.svgns,"clipPath"); - clip.setAttribute("id",id); - clip.appendChild(nodeToClip); - return clip; - }, - - drawClip : function (id, nodeToClip, svgNode) { - var node = SVG.createClip(id, nodeToClip); - svgNode.appendChild(node); - return node; - }, - - drawRectangle : function (cx, cy, width, height, svgNode, attributes) { - try{ - var node = SVG.createRectangle(cx, cy, width, height, attributes); - svgNode.appendChild(node); - } - catch(e){ - - console.log("-------------------- "); - console.log("Error on drawRectangle " + e); - console.log(attributes); - console.log("-------------------- "); - } - return node; - }, - - createEllipse : function (x, y, rx, ry, attributes){ - var rect = document.createElementNS(this.svgns, "ellipse"); - rect.setAttribute("cx",x); - rect.setAttribute("cy",y); - rect.setAttribute("rx",rx); - rect.setAttribute("ry",ry); - SVG._setProperties(rect, attributes); - return rect; - }, - - drawEllipse : function (cx, cy, rx, ry, svgNode, attributes) { - var node = SVG.createEllipse(cx, cy, rx, ry, attributes); - svgNode.appendChild(node); - return node; - }, - - drawImage : function (x, y, canvasSVG, attributes) { - var image = document.createElementNS(this.svgns, "image"); - image.setAttribute("x",x); - image.setAttribute("y",y); - canvasSVG.appendChild(image); - SVG._setProperties(image, attributes); - }, - - drawLine : function (x1, y1, x2, y2, nodeSVG, attributes) { - try{ - var line = SVG.createLine(x1, y1, x2, y2, attributes); - nodeSVG.appendChild(line); - }catch(e){ - } - return line; - }, - - - drawPath: function (d, nodeSVG, attributes) { - var path = SVG.createPath(d, attributes); - nodeSVG.appendChild(path); - return path; - }, - - createPoligon : function (points, attributes){ - var poligon = document.createElementNS(this.svgns, "polygon"); - poligon.setAttribute("points",points); - SVG._setProperties(poligon, attributes); - return poligon; - }, - - drawPoligon : function (points, canvasSVG, attributes){ - var poligon = SVG.createPoligon(points, attributes); - canvasSVG.appendChild(poligon); - return poligon; - }, - // - - createPath : function (d, attributes){ - var path = document.createElementNS(this.svgns, "path"); - path.setAttribute("d",d); - SVG._setProperties(path, attributes); - return path; - }, - - drawCircle : function (x, y, radio, canvasSVG, attributes) { - - var newText = document.createElementNS(this.svgns,"circle"); - newText.setAttribute("cx",x); - newText.setAttribute("cy",y); - newText.setAttribute("r",radio); - - canvasSVG.appendChild(newText); - attributes["cursor"] = "pointer"; - this._setProperties(newText, attributes); - return newText; - }, - - - _setProperties: function(node, attributes) - { - if (attributes instanceof Array){ - for (var i=0; i< attributes.length; i++) - { - node.setAttribute(attributes[i][0], attributes[i][1]); - } - } - else{ - for ( var key in attributes){ - node.setAttribute(key, attributes[key]); - } - } - }, - -/* drawPath: function(pointsArray, canvasSVG, attributes){ - var path = document.createElementNS(this.svgns,"polyline"); - path.setAttribute ('id', id); - - var d= pointsArray[0].x+ " "+ pointsArray[0].y; - for (var i=1; i< pointsArray.length; i++) - { - d=d+" "+pointsArray[i].x+" "+pointsArray[i].y; - } - path.setAttribute ('points', d); - canvasSVG.appendChild(path); - },*/ - - createText : function (x, y, text, attributes) { - var node = document.createElementNS(this.svgns,"text"); - node.setAttributeNS(null , "x",x); - node.setAttributeNS(null, "y",y); - - var textNode = document.createTextNode(text); - node.appendChild(textNode); - - this._setProperties(node, attributes); - return node; - }, - - drawText : function (x, y, text, canvasSVG, attributes) { - var text = SVG.createText(x, y, text, attributes); - canvasSVG.appendChild(text); - return text; - }, - - - - drawGroup: function(svgNode, attributes) - { - var group = SVG.createGroup(attributes); - svgNode.appendChild(group); - return group; - }, - - createGroup: function(attributes){ - var group = document.createElementNS(this.svgns,"g"); - this._setProperties(group, attributes); - return group; - } - -}; - - - -var CanvasToSVG = { - - convert: function(sourceCanvas, targetSVG, x, y, id, attributes) { - - var img = this._convert(sourceCanvas, targetSVG, x, y, id); - - for (var i=0; i< attributes.length; i++) - { - img.setAttribute(attributes[i][0], attributes[i][1]); - } - }, - - _convert: function(sourceCanvas, targetSVG, x, y, id) { - var svgNS = "http://www.w3.org/2000/svg"; - var xlinkNS = "http://www.w3.org/1999/xlink"; - // get base64 encoded png from Canvas - var image = sourceCanvas.toDataURL(); - - // must be careful with the namespaces - var svgimg = document.createElementNS(svgNS, "image"); - - svgimg.setAttribute('id', id); - - //svgimg.setAttribute('class', class); - //svgimg.setAttribute('xlink:href', image); - svgimg.setAttributeNS(xlinkNS, 'xlink:href', image); - - - - - svgimg.setAttribute('x', x ? x : 0); - svgimg.setAttribute('y', y ? y : 0); - svgimg.setAttribute('width', sourceCanvas.width); - svgimg.setAttribute('height', sourceCanvas.height); - //svgimg.setAttribute('cursor', 'pointer'); - svgimg.imageData = image; - - targetSVG.appendChild(svgimg); - return svgimg; - }, - - importSVG: function(sourceSVG, targetCanvas) { - svg_xml = sourceSVG;//(new XMLSerializer()).serializeToString(sourceSVG); - var ctx = targetCanvas.getContext('2d'); - - var img = new Image(); - img.src = "data:image/svg+xml;base64," + btoa(svg_xml); -// img.onload = function() { - ctx.drawImage(img, 0, 0); -// }; - } - -}; -/* -Graph.prototype.importSVG = function(sourceSVG, targetCanvas) { - sourceSVG = this._svg; - targetCanvas = document.createElementNS('canvas'); - // https://developer.mozilla.org/en/XMLSerializer - svg_xml = (new XMLSerializer()).serializeToString(sourceSVG); - var ctx = targetCanvas.getContext('2d'); - // this is just a JavaScript (HTML) image - var img = new Image(); - // http://en.wikipedia.org/wiki/SVG#Native_support - // https://developer.mozilla.org/en/DOM/window.btoa - img.src = "data:image/svg+xml;base64," + btoa(svg_xml); - img.onload = function() { - // after this, Canvas’ origin-clean is DIRTY - ctx.drawImage(img, 0, 0); - } -}; -*/ - -/* - -Normalizacion de datos para dibujar colores -Issues: - No sé como debería llamarse esta libreria - No sé si ya existe una funciçon en javascript que lo haga - - -*/ - - -var Normalizer = new function() -{ - this.normalizeArray = function (arrayData) - { - - return this.standardizeArray(this.normal(arrayData)); - -// var result = this._getMaxAndMin(arrayData); -// var min =result[0]; -// var max = result[1]; -// -// -// //los hacemos todos positivos -// for (var i = 0; i< arrayData.length; i++) -// { -// arrayData[i]= Math.abs(min) + parseFloat(arrayData[i]); -// } -// -// var result = this._getMaxAndMin(arrayData); -// var min =result[0]; -// var max = result[1]; -// -// -// var resultArray = new Array(); -// for (var i = 0; i< arrayData.length; i++) -// { -// resultArray.push(arrayData[i]*1/max); -// } -// return resultArray; - }; - - this.normal = function(arrayData){ - var mean = this._getMean(arrayData); - var deviation = this._getStdDeviation(arrayData); - var result = this._getMaxAndMin(arrayData); - var min = result[0]; - var max = result[1]; - - var resultArray = new Array(); - for (var i = 0; i< arrayData.length; i++) { - if (deviation!=0){ - resultArray.push((arrayData[i]-mean)/deviation); - }else{ - resultArray.push(arrayData[i]); - } - } - return resultArray; - }; - - this.standardizeArray = function(arrayData) - { - var result = this._getMaxAndMin(arrayData); - var min = result[0]; - var max = result[1]; - - var offset = ( min <= 0 ) ? Math.abs(min) : (-1 * min); - var resultArray = new Array(); - for (var i = 0; i< arrayData.length; i++) { - if(max + offset!=0){ - resultArray.push((arrayData[i] + offset) / (max + offset)); - }else{ - resultArray.push(arrayData[i]+offset); - } - } - return resultArray; - }; - - - this._getMean = function(arrayData) { - var sum = 0; - for (var i = 0; i< arrayData.length; i++) { - sum = sum + parseFloat(arrayData[i]); - } - return sum/arrayData.length; - }; - - this._getStdDeviation = function(arrayData) { - var mean = this._getMean(arrayData); - var acum = 0.0; - for(var i=0; i max) max = parseFloat(arrayData[i]); - } - - return [min, max]; - }; -}; -function GraphCanvas(componentID, targetNode, args) { - this.args = {}; - /** target */ - this.targetID = targetNode.id; - - /** id manage */ - this.id = componentID; - this.args.idGraph = this.id + "main"; - this.args.idBackgroundNode = this.id + "background"; - - this.args.idEdgesGraph = this.id + "edges"; - this.args.idNodesGraph = this.id + "vertices"; - this.args.idLabelGraph = this.id + "label"; - this.args.idBackground = this.id + "background"; - - /** Objects Graph **/ - this.dataset = null; - this.formatter = null; - this.layout = null; - - /** Drawing **/ - this.circleDefaultRadius = 2; - this.squareDefaultSide = this.circleDefaultRadius * 1.5; - - /** Directed Arrow **/ - this.arrowDefaultSize = this.circleDefaultRadius; - - /** Groups **/ - this.GraphGroup = null; - this.GraphNodeGroup = null; - this.GraphLabelGroup = null; - this.GraphBackground = null; - - /** SETTINGS FLAGS **/ - this.args.draggingCanvasEnabled = false; //Flag to set if the canvas can be dragged - this.args.multipleSelectionEnabled = false; - this.args.interactive = false; - this.args.labeled = false; - this.args.linkEnabled = false; - - /** If numberEdge > maxNumberEdgesMoving then only it will move edges when mouse up **/ - this.args.maxNumberEdgesMoving = 3; - this.args.maxNumberEdgesFiringEvents = 50; - - /** Linking edges **/ - this.args.linking = false; - this.linkStartX = 0; - this.linkStartY = 0; - this.linkSVGNode = null; - this.linkNodeSource = null; - this.linkNodeTarget = null; - - /** Dragging Control **/ - this.draggingElement = null; - this.dragging = false; - this.nMouseOffsetX = 0; - this.nMouseOffsetY = 0; - this.dragStartX = 0; - this.dragStartY = 0; - this.desplazamientoX = 0; - this.desplazamientoY = 0; - - /** Selection Control **/ - this.selecting = false; - this.selectorX = null; - this.selectorY = null; - this.selectorSVGNode = null; - - /** Node status **/ - this.args.isVertexSelected = {}; - this.args.selectedVertices = []; - - /** Edges status **/ - this.args.isEdgeSelected = {}; - //this.args.selectedEdges = []; - - if (args != null) { - if (args.multipleSelectionEnabled != null) { - this.args.multipleSelectionEnabled = args.multipleSelectionEnabled; - this.args.draggingCanvasEnabled = !(this.args.multipleSelectionEnabled); - } - if (args.draggingCanvasEnabled != null) { - this.args.draggingCanvasEnabled = args.draggingCanvasEnabled; - this.args.multipleSelectionEnabled = !(this.args.draggingCanvasEnabled); - } - if (args.interactive != null) { - this.args.interactive = args.interactive; - } - if (args.labeled != null) { - this.args.labeled = args.labeled; - } - - } - - /** Hashmap with the svg node labels **/ - this.svgLabels = {}; - - /** EVENTS **/ - this.onVertexOut = new Event(this); - this.onVertexOver = new Event(this); - this.onVertexSelect = new Event(this); - this.onEdgeSelect = new Event(this); - this.onCanvasClicked = new Event(this); - this.onVertexUp = new Event(this); -} - -GraphCanvas.prototype.showLabels = function(value) { - this.args.labeled = value; - this.removeLabels(); - if (value) { - this.renderLabels(); - } -}; - -GraphCanvas.prototype.getSelectedVertices = function() { - return this.args.selectedVertices; -}; - -GraphCanvas.prototype.getSelectedEdges = function() { - var selected = []; - for ( var selectedEdge in this.args.isEdgeSelected) { - selected.push(selectedEdge); - } - return selected; -}; - -GraphCanvas.prototype.createSVGDom = function(targetID, id, width, height, backgroundColor) { - var container = document.getElementById(targetID); - this._svg = SVG.createSVGCanvas(container, [ - [ "style", "background-color:" + backgroundColor + ";" ], [ "id", id ], [ "dragx", 0 ], [ "dragy", 0 ], - [ "height", this.getFormatter().getHeight() ], [ "width", this.getFormatter().getWidth() ] ]); - return this._svg; -}; - -/** MULTIPLE SELECTION **/ -GraphCanvas.prototype.isMultipleSelectionEnabled = function() { - return this.args.multipleSelectionEnabled; -}; - -GraphCanvas.prototype.setMultipleSelection = function(value) { - this.args.multipleSelectionEnabled = value; - this.args.draggingCanvasEnabled = (!value); -}; - -GraphCanvas.prototype.setSelecting = function(value) { - this.selecting = value; -}; - -/** linking **/ -GraphCanvas.prototype.setLinking = function(value) { - this.args.linkEnabled = value; - this.selecting = !value; - this.dragging = !value; -}; - -/** CANVAS MOVING **/ -GraphCanvas.prototype.setDraggingCanvas = function(value) { - this.args.draggingCanvasEnabled = value; - this.args.multipleSelectionEnabled = !value; -}; - -GraphCanvas.prototype.isDraggingCanvasEnabled = function() { - return this.args.draggingCanvasEnabled; -}; -/** ZOOM **/ -GraphCanvas.prototype.getScale = function() { - return this.getFormatter().getZoomScale(); -}; - -GraphCanvas.prototype.setScale = function(scale) { - var graphNode = document.getElementById(this.args.idGraph); - graphNode.setAttribute("transform", graphNode.getAttribute("transform").replace("scale(" + this.getScale() + ")", "scale(" + scale + ")")); - this.getFormatter().setZoomScale(scale); -}; - -GraphCanvas.prototype.zoomIn = function() { - this.setScale(this.getScale() + this.getFormatter().getZoomScaleStepFactor()); -}; - -GraphCanvas.prototype.zoomOut = function() { - this.setScale(this.getScale() - this.getFormatter().getZoomScaleStepFactor()); - -}; - -/** SVG COORDENATES **/ -GraphCanvas.prototype.getSVGCoordenates = function(evt) { - var p = this._svg.createSVGPoint(); - p.x = evt.clientX; - p.y = evt.clientY; - - var m = this._svg.getScreenCTM(document.documentElement); - p = p.matrixTransform(m.inverse()); - return p; -}; - -/** SVG EVENTS **/ -GraphCanvas.prototype.mouseClick = function(event) { - if (event.button == 0) { - if (!this.args.interactive) { - return; - } - - if (this.isVertex(event.target)) { - this.clickNode(this.getVertexIdFromSVGId(event.target.id)); - } - /** Como el evento mouseClick viene despues del mouse up es aqui donde manejo el tema de deseccionar los elementos que estoy dragging **/ - if (this.dragging) { - this.dragging = false; - } - } -}; - -GraphCanvas.prototype.mouseMove = function(evt) { - if (this.selecting) { - this.clearLabels(); - - var width = (this.getSVGCoordenates(evt).x - this.selectorX); - var height = (this.getSVGCoordenates(evt).y - this.selectorY); - if ((width > 0) && (height > 0)) { - this.displaySelection(this.selectorX, this.selectorY, width, height); - } - if ((width > 0) && (height < 0)) { - this.displaySelection(this.selectorX, this.getSVGCoordenates(evt).y, width, Math.abs(height)); - } - if ((width < 0) && (height < 0)) { - this.displaySelection(this.getSVGCoordenates(evt).x, this.getSVGCoordenates(evt).y, Math.abs(width), Math.abs(height)); - } - if ((width < 0) && (height > 0)) { - this.displaySelection(this.selectorX + width, this.selectorY, Math.abs(width), Math.abs(height)); - } - - var x1 = (parseFloat(this.selectorSVGNode.getAttribute("x")) - DOM.select(this.id).getAttribute("dragx")) / this.getFormatter().getWidth(); - var y1 = (parseFloat(this.selectorSVGNode.getAttribute("y")) - DOM.select(this.id).getAttribute("dragy")) / this.getFormatter().getHeight(); - var x2 = (x1 + parseFloat(this.selectorSVGNode.getAttribute("width") / this.getFormatter().getWidth())); - var y2 = (y1 + parseFloat(this.selectorSVGNode.getAttribute("height") / this.getFormatter().getHeight())); - - this.deselectNodes(this.getLayout()); - var verticesSelected = this.getLayout().getVerticesByArea(x1 / this.getFormatter().getZoomScale(), y1 / this.getFormatter().getZoomScale(), - x2 / this.getFormatter().getZoomScale(), y2 / this.getFormatter().getZoomScale()); - for ( var i = 0; i < verticesSelected.length; i++) { - this.selectNode(verticesSelected[i].getId()); - this.renderLabel(verticesSelected[i].getId()); - } - - } - var p = null; - if (this.args.linking) { - p = this.getSVGCoordenates(evt); - if (this.linkSVGNode != null) { - this.linkSVGNode.setAttribute("x2", p.x - 2); - this.linkSVGNode.setAttribute("y2", p.y - 2); - } - } - - if (this.dragging) { - p = this.getSVGCoordenates(evt); - p.x -= this.nMouseOffsetX; - p.y -= this.nMouseOffsetY; - this.desplazamientoX = (this.getSVGCoordenates(evt).x - this.dragStartX);// + parseFloat(DOM.select(this.id).getAttribute("dragx")); - this.desplazamientoY = (this.getSVGCoordenates(evt).y - this.dragStartY);// + parseFloat(DOM.select(this.id).getAttribute("dragy")); - - if (this.draggingElement != null) { - /** Click sobre el recct del banground que provoca que mueva todo el canvas **/ - if (this.isNodeCanvas(this.draggingElement)) { - - p = this.getSVGCoordenates(evt); - p.x = this.desplazamientoX; - p.y = this.desplazamientoY; - - this.draggingElement.setAttribute("dragx", p.x); - this.draggingElement.setAttribute("dragy", p.y); - this.draggingElement = document.getElementById(this.args.idGraph); - this.draggingElement.setAttribute("transform", "translate(" + p.x + "," + p.y + "), scale(" + this.getScale() + ")"); - - DOM.select(this.id).setAttribute("dragx", p.x); - DOM.select(this.id).setAttribute("dragy", p.y); - - if (this.NodeSVGbackgroundImage != null) { - this.NodeSVGbackgroundImage.setAttribute("dragx", p.x); - this.NodeSVGbackgroundImage.setAttribute("dragy", p.y); - } - } else { - if (this.isVertex(this.draggingElement)) { - this.selectNode(this.getVertexIdFromSVGId(this.draggingElement.id)); - this.desplazamientoX = this.desplazamientoX / this.getFormatter().getZoomScale(); - this.desplazamientoY = this.desplazamientoY / this.getFormatter().getZoomScale(); - this.moveSelectedNodes(this.desplazamientoX, this.desplazamientoY); - - this.dragStartX = this.getSVGCoordenates(evt).x; - this.dragStartY = this.getSVGCoordenates(evt).y; - } else { - if (this.isNodeBackground(this.draggingElement)) { - - this.draggingElement.setAttribute("dragx", p.x); - this.draggingElement.setAttribute("dragy", p.y); - this.draggingElement = document.getElementById(this.args.idGraph); - this.draggingElement.setAttribute("transform", "translate(" + p.x + "," + p.y + "), scale(" + this.getScale() + ")"); - } else { - this.draggingElement.setAttribute("transform", "translate(" + p.x + "," + p.y + ")"); - } - } - } - } - } -}; - -GraphCanvas.prototype.moveSelectedNodes = function(offsetX, offsetY) { - for ( var i = 0; i < this.getSelectedVertices().length; i++) { - - var nodeId = this.getSelectedVertices()[i]; - var svgNodeId = this.getSVGNodeId(nodeId); - - var x = parseFloat(DOM.select(svgNodeId).getAttribute("dragx")) + parseFloat(offsetX);// - parseFloat(DOM.select(this.id).getAttribute("dragx")); - var y = parseFloat(DOM.select(svgNodeId).getAttribute("dragy")) + parseFloat(offsetY);// + parseFloat(DOM.select(this.id).getAttribute("dragy")); - - this._movingNode(DOM.select(svgNodeId), x, y); - } -}; - -GraphCanvas.prototype.mouseDown = function(evt) { - if (event.button == 0) { - - /** if !no interactive mouse events do anything **/ - if (!this.args.interactive) { - return; - } - - var p = this.getSVGCoordenates(evt); - - /** When click on canvas or background deselect all **/ - if (this.isNodeCanvas(evt.target) || this.isNodeBackground(evt.target)) { - this.deselectNodes(); - this.deselectEdges(); - this.onCanvasClicked.notify(); - } - - /** if I am linking vertices **/ - if (this.args.linkEnabled) { - - if (!this.args.linking) { - this.args.linking = true; - if (this.isVertex(evt.target)) { - this.linkStartX = p.x; - this.linkStartY = p.y; - this.linkSVGNode = SVG.drawLine(p.x, p.y, p.x, p.y, this._svg, { - "stroke" : "#FF0000" - }); - this.linkNodeSource = this.getVertexIdFromSVGId(evt.target.id); - } - } else { - this.linkNodeTarget = this.getVertexIdFromSVGId(evt.target.id); - this.args.linking = false; - this.args.linkEnabled = false; - if (this.isVertex(evt.target)) { - this.getDataset().addEdge(this.linkNodeSource + "_" + this.linkNodeTarget, this.linkNodeSource, this.linkNodeTarget, {}); - } - this.linkSVGNode.parentNode.removeChild(this.linkSVGNode); - } - return; - } - - /** Id is a vertex or the canvas **/ - if (this.isVertex(evt.target) || this.isNodeCanvas(evt.target) || this.isNodeBackground(evt.target)) { - this._startDragging(evt); - } - /** if i is edge **/ - if (this.isEdge(evt.target)) { - this.selectEdge(this.getEdgeIdFromSVGId(evt.target.getAttribute("id"))); - } - - if (this.args.multipleSelectionEnabled) { - if (!this.dragging) { - this.setSelecting(true); - this.selectorX = p.x; - this.selectorY = p.y; - this.displaySelection(p.x, p.y, 1, 1); - } - } - - } - if (event.button == 1) { - this.setLinking(false); - this.setMultipleSelection(false); - this.selecting = false; - - /** Id is a vertex or the canvas **/ - if (this.isVertex(evt.target) || this.isNodeCanvas(evt.target) || this.isNodeBackground(evt.target)) { - this._startDragging(evt); - } - } -}; - -GraphCanvas.prototype.mouseUp = function(event) { - if (!this.args.interactive) { - return; - } - - if (this.dragging) { - this._stopDragging(event); - if (this.isVertex(event.target)) { - var vertexId = this.getVertexIdFromSVGId(event.target.id); - if (this.getDataset().getVertexById(vertexId).getEdges().length >= this.args.maxNumberEdgesMoving) { - this.moveEdge(vertexId); - } - } - } - - if (this.selecting) { - this.setSelecting(false); - - var x1 = (parseFloat(this.selectorSVGNode.getAttribute("x")) - DOM.select(this.id).getAttribute("dragx")) / this.getFormatter().getWidth(); - var y1 = (parseFloat(this.selectorSVGNode.getAttribute("y")) - DOM.select(this.id).getAttribute("dragy")) / this.getFormatter().getHeight(); - var x2 = (x1 + parseFloat(this.selectorSVGNode.getAttribute("width") / this.formatter.getWidth())); - var y2 = (y1 + parseFloat(this.selectorSVGNode.getAttribute("height") / this.formatter.getHeight())); - - var verticesSelected = this.getLayout().getVerticesByArea(x1 / this.getFormatter().getZoomScale, y1 / this.getFormatter().getZoomScale, - x2 / this.getFormatter().getZoomScale, y2 / this.getFormatter().getZoomScale); - - for ( var i = 0; i < verticesSelected.length; i++) { - this.selectNode(verticesSelected[i].getId()); - } - - if (this.selectorSVGNode != null) { - this._svg.removeChild(this.selectorSVGNode); - } - - if (this.args.labeled) { - this.clearLabels(); - this.renderLabels(); - } - - this.selectorSVGNode = null; - // this.renderLabels(); - } -}; - -/** SELECTION **/ -GraphCanvas.prototype.displaySelection = function(x, y, width, height) { - if (this.selectorSVGNode != null) { - this.selectorSVGNode.setAttribute("x", x); - this.selectorSVGNode.setAttribute("y", y); - this.selectorSVGNode.setAttribute("width", width); - this.selectorSVGNode.setAttribute("height", height); - } else { - this.selectorSVGNode = SVG.drawRectangle(x, y, width, height, this._svg, { - "fill" : "red", - "stroke" : "black", - "opacity" : "0.2", - "stroke-opacity" : "1" - }); - } -}; - -/** DRAGGING **/ -GraphCanvas.prototype._startDragging = function(evt) { - if (!this.isDraggingCanvasEnabled()) { - if (this.isNodeCanvas(evt.target)) { - this.draggingElement = null; - } - } - - if (this.isVertex(evt.target) || (this.isNodeBackground(evt.target) && (this.isDraggingCanvasEnabled()))|| (this.isNodeCanvas(evt.target) && (this.isDraggingCanvasEnabled()))) { - this.clearLabels(); - this.draggingElement = evt.target; - this.dragging = true; - var p = this.getSVGCoordenates(evt); - - this.nMouseOffsetX = p.x - parseInt(evt.target.getAttribute("dragx")); - this.nMouseOffsetY = p.y - parseInt(evt.target.getAttribute("dragy")); - - if (this.isVertex(evt.target)) { - this.dragStartX = parseInt(this.draggingElement.getAttribute("dragx")) * this.getFormatter().getZoomScale() - + parseFloat(DOM.select(this.id).getAttribute("dragx")); - this.dragStartY = parseInt(this.draggingElement.getAttribute("dragy")) * this.getFormatter().getZoomScale() - + parseFloat(DOM.select(this.id).getAttribute("dragy")); - } else { - this.dragStartX = p.x - parseInt(this.draggingElement.getAttribute("dragx"));// + parseFloat(DOM.select(this.id).getAttribute("dragx")); - this.dragStartY = p.y - parseInt(this.draggingElement.getAttribute("dragy"));// + parseFloat(DOM.select(this.id).getAttribute("dragy")); - } - } -}; - -GraphCanvas.prototype._stopDragging = function(event) { - this.nMouseOffsetX = 0; - this.nMouseOffsetX = 0; - /** despues del evento up viene el evento click entonces acabo el dragging en el mouseclick **/ - this.dragging = false; - this.draggingElement = null; - this.renderLabels(); - - this.setLinking(false); - this.setMultipleSelection(true); - this.selecting = false; - -}; - -/** Move the edges of the vertex with the vertexId indicado **/ -GraphCanvas.prototype.moveEdge = function(vertexId) { - var x = this.getLayout().getNodeById(vertexId).x * this.getFormatter().getWidth(); - var y = this.getLayout().getNodeById(vertexId).y * this.getFormatter().getHeight(); - - /** Moving edges out **/ - for ( var i = 0; i < this.getDataset().getVertexById(vertexId).getEdgesOut().length; i++) { - var edgeId = this.getDataset().getVertexById(vertexId).getEdgesOut()[i].getId(); - var svgEdgeId = this.getSVGEdgeId(edgeId); - var edgeFormatter = this.getFormatter().getEdgeById(edgeId); - if (edgeFormatter instanceof LineEdgeGraphFormatter) { - DOM.select(svgEdgeId + "_shadow").setAttribute("x2", x); - DOM.select(svgEdgeId + "_shadow").setAttribute("y2", y); - DOM.select(svgEdgeId).setAttribute("x2", x); - DOM.select(svgEdgeId).setAttribute("y2", y); - } - - if ((edgeFormatter instanceof DirectedLineEdgeGraphFormatter) || (edgeFormatter instanceof OdirectedLineEdgeGraphFormatter) - || (edgeFormatter instanceof OdotDirectedLineEdgeGraphFormatter) || (edgeFormatter instanceof DotDirectedLineEdgeGraphFormatter) - || (edgeFormatter instanceof CutDirectedLineEdgeGraphFormatter)) { - this.removeEdge(edgeId); - this.renderEdge(edgeId); - } - } - - /** Moving edges in **/ - for ( var i = 0; i < this.getDataset().getVertexById(vertexId).getEdgesIn().length; i++) { - var edgeId = this.getDataset().getVertexById(vertexId).getEdgesIn()[i].getId(); - var svgEdgeId = this.getSVGEdgeId(edgeId); - var edgeFormatter = this.getFormatter().getEdgeById(edgeId); - if (edgeFormatter instanceof LineEdgeGraphFormatter) { - DOM.select(svgEdgeId).setAttribute("x1", x); - DOM.select(svgEdgeId).setAttribute("y1", y); - DOM.select(svgEdgeId + "_shadow").setAttribute("x1", x); - DOM.select(svgEdgeId + "_shadow").setAttribute("y1", y); - } - - if ((edgeFormatter instanceof DirectedLineEdgeGraphFormatter) || (edgeFormatter instanceof OdirectedLineEdgeGraphFormatter) - || (edgeFormatter instanceof OdotDirectedLineEdgeGraphFormatter) || (edgeFormatter instanceof DotDirectedLineEdgeGraphFormatter) - || (edgeFormatter instanceof CutDirectedLineEdgeGraphFormatter)) { - this.removeEdge(edgeId); - this.renderEdge(edgeId); - } - - if (edgeFormatter instanceof BezierEdgeGraphFormatter) { - var radius = this.getFormatter().getVertexById(vertexId).getDefault().getSize() * this.getFormatter().getNodesMaxSize(); - var d = this.calculateCoordenatesBezier(radius, x, y); - DOM.select(svgEdgeId).setAttribute("d", d); - } - } -}; - -GraphCanvas.prototype.moveNode = function(vertexId) { - var x = this.getLayout().getNodeById(vertexId).x * this.getFormatter().getWidth(); - var y = this.getLayout().getNodeById(vertexId).y * this.getFormatter().getHeight(); - var svgNodeElement = DOM.select(this.getSVGNodeId(vertexId)); - - svgNodeElement.setAttribute("dragx", x); - svgNodeElement.setAttribute("dragy", y); - svgNodeElement.setAttribute("transform", "translate(" + x + "," + y + ")"); - - if (this.getDataset().getVertexById(vertexId).getEdges().length < this.args.maxNumberEdgesMoving) { - this.moveEdge(vertexId); - } -}; - -GraphCanvas.prototype._movingNode = function(svgNodeElement, x, y) { - var vertexId = this.getVertexIdFromSVGId(svgNodeElement.getAttribute("id")); - this.getLayout().getNodeById(vertexId).setCoordinates(x / this.getFormatter().getWidth(), y / this.getFormatter().getHeight()); - this.desplazamientoX = 0; - this.desplazamientoY = 0; - this.removeLabel(vertexId); - this.renderLabel(vertexId); -}; - -/** INIT **/ -GraphCanvas.prototype.init = function() { - - this._svg = this.createSVGDom(this.targetID, this.id, this.getFormatter().getWidth(), this.getFormatter().getHeight(), this.getFormatter() - .getBackgroundColor()); - this.GraphGroup = SVG.drawGroup(this._svg, [ [ "id", this.args.idGraph ], [ "transform", "translate(0,0), scale(1)" ] ]); - this.GraphBackground = SVG.drawGroup(this.GraphGroup, [ [ "id", this.args.idBackground ] ]); - this.GraphEdgeGroup = SVG.drawGroup(this.GraphGroup, [ [ "id", this.args.idEdgesGraph ] ]); - this.GraphNodeGroup = SVG.drawGroup(this.GraphGroup, [ [ "id", this.args.idNodesGraph ] ]); - this.GraphLabelGroup = SVG.drawGroup(this.GraphGroup, [ [ "id", this.args.idLabelGraph ] ]); - - if ((this.getFormatter().getBackgroundImage() != null) && (this.getFormatter().getBackgroundImage() != "")) { - this.setBackgroundImage(this.getFormatter().getBackgroundImage()); - } - /** SVG Events listener */ - var _this = this; - this._svg.addEventListener("click", function(event) { - _this.mouseClick(event); - }, false); - this._svg.addEventListener("mousemove", function(event) { - _this.mouseMove(event, _this); - }, false); - this._svg.addEventListener("mousedown", function(event) { - _this.mouseDown(event, _this); - }, false); - this._svg.addEventListener("mouseup", function(event) { - _this.mouseUp(event, _this); - }, false); -}; - -/* - GraphCanvas.prototype.backgroungToSVG = function(){ - var _this = this; - var canvas = document.createElement('canvas'); - canvas.setAttribute("id", "canvas"); - canvas.width = this.formatter.getWidth(); - canvas.height = this.formatter.getHeight(); - - this._svg.parentNode.parentNode.appendChild(canvas); - var ctx = document.getElementById('canvas').getContext('2d'); - var img = new Image(); - - img.src = this.formatter.getBackgroundImage(); - ctx.drawImage(img,0,0 ,_this.formatter.getWidth(), _this.formatter.getHeight()); - - - img.onload = function() { - canvas.parentNode.removeChild(canvas); - } - - this.NodeSVGbackgroundImage.setAttribute("xlink:href", document.getElementById("canvas").toDataURL()); - this.NodeSVGbackgroundImage.removeAttribute("href"); - - // - - };*/ - -GraphCanvas.prototype.setBackgroundImage = function() { - if (this.NodeSVGbackgroundImage != null) { - this.NodeSVGbackgroundImage.parentNode.removeChild(this.NodeSVGbackgroundImage); - } - $('#' + this.targetID).svg(); - $('#' + this.targetID).svg("get"); - - $('#' + this.targetID).svg("get")._svg = document.getElementById(this.id); - - var svg = $('#' + this.targetID).svg("get"); - this.NodeSVGbackgroundImage = svg.image(0, 0, this.getFormatter().getWidth(), this.getFormatter().getHeight(), this.getFormatter() - .getBackgroundImage()); - this.NodeSVGbackgroundImage.setAttribute("id", this.args.idBackgroundNode); - - this.NodeSVGbackgroundImage.setAttribute("x", 0); - this.NodeSVGbackgroundImage.setAttribute("y", 0); - - this.NodeSVGbackgroundImage.setAttribute("dragx", 0); - this.NodeSVGbackgroundImage.setAttribute("dragy", 0); - - if (this.getFormatter().args.backgroundImageHeight != null) { - this.NodeSVGbackgroundImage.setAttribute("height", this.getFormatter().args.backgroundImageHeight); - } - if (this.getFormatter().args.backgroundImageWidth != null) { - this.NodeSVGbackgroundImage.setAttribute("width", this.getFormatter().args.backgroundImageWidth); - } - - if (this.getFormatter().args.backgroundImageX != null) { - this.NodeSVGbackgroundImage.setAttribute("x", this.getFormatter().args.backgroundImageX); - } - if (this.getFormatter().args.backgroundImageY != null) { - this.NodeSVGbackgroundImage.setAttribute("y", this.getFormatter().args.backgroundImageY); - } - - this.GraphBackground.appendChild(this.NodeSVGbackgroundImage); - this.NodeSVGbackgroundImage.removeAttribute("href"); - this.NodeSVGbackgroundImage.setAttribute("xlink:href", this.getFormatter().getBackgroundImage()); -}; - -GraphCanvas.prototype.removeBackgroundImage = function() { - if (this.NodeSVGbackgroundImage != null) { - this.NodeSVGbackgroundImage.parentNode.removeChild(this.NodeSVGbackgroundImage); - } -}; - -GraphCanvas.prototype._setBackgroundColor = function(color) { - var attributes = [ [ "fill", color ] ]; - SVG.drawRectangle(0, 0, this.getFormatter().getWidth(), this.getFormatter().getHeight(), this.GraphBackground, attributes); -}; - -/** Serialize **/ -GraphCanvas.prototype.toJSON = function() { - var json = {}; - json.dataset = {}; - json.formatter = {}; - json.layout = {}; - json.dataset = this.getDataset().toJSON(); - json.formatter = this.getFormatter().toJSON(); - json.layout = this.getLayout().toJSON(); - return json; -}; - -GraphCanvas.prototype.toHTML = function() { - //this.backgroungToSVG(); - var html = this._svg.parentElement.innerHTML; - - var start = html.indexOf("") + 6; - - return html.substr(start, end); -}; - -/** DRAW **/ -GraphCanvas.prototype.draw = function(graphdataset, graphformatter, graphlayout) { - this.setDataset(graphdataset); - this.setFormatter(graphformatter); - this.setLayout(graphlayout); - - var _this = this; - this.getFormatter().changed.attach(function(sender, item) { - _this.removeNode(item.getId()); - _this.renderNode(item.getId()); - if (_this.args.labeled) { - _this.removeLabel(item.getId()); - _this.renderLabel(item.getId()); - } - - }); - //TODO - this.getFormatter().edgeChanged.attach(function(sender, item) { - _this.removeEdge(item.getId()); - _this.renderEdge(item.getId()); - }); - - this.getFormatter().resized.attach(function(sender, item) { - _this.resize(_this.getFormatter().getWidth(), _this.getFormatter().getHeight()); - }); - - this.getFormatter().backgroundImageChanged.attach(function(sender, item) { - _this.setBackgroundImage(_this.getFormatter().getBackgroundImage()); - }); - - this.getFormatter().backgroundColorChanged.attach(function(sender, item) { - _this._setBackgroundColor(_this.getFormatter().getBackgroundColor()); - }); - - this.getLayout().changed.attach(function(sender, item) { - _this.moveNode(item.getId()); - _this.moveEdge(item.getId()); - if (_this.args.labeled) { - _this.removeLabel(item.getId()); - _this.renderLabel(item.getId()); - } - }); - - this.getDataset().newVertex.attach(function(sender, item) { - - _this.renderNode(item.getId()); - if (_this.args.labeled) { - _this.renderLabel(item.getId()); - } - }); - - this.getDataset().newEdge.attach(function(sender, item) { - _this.renderEdge(item.getId()); - }); - - this.getDataset().vertexDeleted.attach(function(sender, item) { - _this.removeNode(item.getId()); - if (_this.args.labeled) { - _this.removeLabel(item.getId()); - } - }); - - this.getDataset().edgeDeleted.attach(function(sender, item) { - _this.removeEdge(item.getId()); - }); - - this.getDataset().vertexNameChanged.attach(function(sender, args) { - if (_this.args.labeled) { - _this.removeLabel(args.item.getId()); - _this.removeLabel(args.item.getId()); - _this.renderLabel(args.item.getId()); - } - }); - this.init(); - this.render(); -}; - -GraphCanvas.prototype.render = function() { - for ( var id in this.getDataset().getVertices()) { - this.renderNode(id); - } - this.renderLabels(); - this.renderEdges(); -}; - -GraphCanvas.prototype.renderLabels = function() { - if (this.args.labeled) { - for ( var id in this.getDataset().getVertices()) { - this.renderLabel(id); - } - } -}; - -GraphCanvas.prototype.removeLabels = function() { - for ( var id in this.getDataset().getVertices()) { - this.removeLabel(id); - } -}; - -/** Utilities method for nodes **/ -GraphCanvas.prototype.isNodeCanvas = function(node) { - return ((node.id == this.args.idGraph) || (node.id == this.id)); -}; - -GraphCanvas.prototype.isNodeBackground = function(node) { - return ((node.id == this.args.idBackgroundNode)); -}; - -GraphCanvas.prototype.isVertex = function(node) { - if (node.getAttribute("id") != null) { - if (node.getAttribute("id").indexOf("_v_") != -1) { - return true; - } - } - return false; -}; - -GraphCanvas.prototype.isLabel = function(node) { - if (node.getAttribute("id") != null) { - if (node.getAttribute("id").indexOf("_l_") != -1) { - return true; - } - } - return false; -}; - -GraphCanvas.prototype.isEdge = function(node) { - if (node.getAttribute("id") != null) { - if (node.getAttribute("id").indexOf("_e_") != -1) { - return true; - } - } - return false; -}; - -/** Resize **/ -GraphCanvas.prototype.resize = function(width, height) { - // this._svg.setAttribute("width", width); - // this._svg.setAttribute("height", height); - if (this.NodeSVGbackgroundImage != null) { - this.NodeSVGbackgroundImage.setAttribute("width", width); - this.NodeSVGbackgroundImage.setAttribute("height", height); - } - - this._svg.setAttribute("width", width); - this._svg.setAttribute("height", height); - - this.clearCanvas(); - this.render(); -}; - -GraphCanvas.prototype.clearCanvas = function() { - DOM.removeChilds(this.GraphEdgeGroup.getAttribute("id")); - DOM.removeChilds(this.GraphNodeGroup.getAttribute("id")); - this.clearLabels(); -}; - -GraphCanvas.prototype.clearLabels = function() { - DOM.removeChilds(this.GraphLabelGroup.getAttribute("id")); -}; - -/** ID'S converter **/ -GraphCanvas.prototype.getSVGNodeId = function(nodeId) { - return this.id + "_v_" + nodeId; -}; - -GraphCanvas.prototype.getSVGEdgeId = function(edgeId) { - return this.id + "_e_" + edgeId; -}; - -GraphCanvas.prototype.getSVGArrowEdgeId = function(edgeId) { - return this.id + "_arrow_" + edgeId; -}; - -GraphCanvas.prototype.getSVGLabelId = function(edgeId) { - return this.id + "_l_" + edgeId; -}; - -GraphCanvas.prototype.blinkVertexById = function(vertexId) { - $("#" + this.getSVGNodeId(vertexId)).fadeIn().fadeOut().fadeIn().fadeOut().fadeIn().fadeOut(); -}; - -GraphCanvas.prototype.getVertexIdFromSVGId = function(svgVertexId) { - return svgVertexId.replace(this.id, "").replace("_v_", ""); -}; - -GraphCanvas.prototype.getEdgeIdFromSVGId = function(svgEdgeId) { - return svgEdgeId.replace(this.id, "").replace("_e_", ""); -}; - -/** VERTEX **/ -GraphCanvas.prototype.getVertexById = function(id) { - return document.getElementById(this.getSVGNodeId(id)); -}; - -GraphCanvas.prototype.renderNodes = function() { - for ( var id in this.getDataset().getVertices()) { - this.renderNode(id); - } -}; - -GraphCanvas.prototype.overNode = function(nodeId) { - if (!this.args.interactive) { - return; - } - /** If selected we don't change the format **/ - if (this.args.isVertexSelected[nodeId] == null) { - var args = this.getFormatter().getVertexById(nodeId).getOver(); - args.args["cursor"] = 'pointer'; - this.changeVertexFormat(nodeId, args); - } -}; - -GraphCanvas.prototype.outNode = function(nodeId) { - if (!this.args.interactive) { - return; - } - - /** If selected we don't change the format **/ - if (this.args.isVertexSelected[nodeId] == null) { - this.changeVertexFormat(nodeId, this.getFormatter().getVertexById(nodeId).getDefault()); - } -}; - -GraphCanvas.prototype.overLabel = function(nodeId) { - this.overNode(nodeId); - // this.svgLabels[nodeId].setAttribute("cursor", "pointer"); -}; - -GraphCanvas.prototype.outLabel = function(nodeId) { - this.outNode(nodeId); - // this.svgLabels[nodeId].setAttribute("cursor", ""); -}; - -GraphCanvas.prototype.clickNode = function(nodeId) { - if (!this.args.interactive) { - return; - } - - /** si el evento se dispara oprque estaba dragging entonces no activo nada **/ - if (this.args.isVertexSelected[nodeId] == null) { - this.selectNode(nodeId); - } else { - this.deselectNode(nodeId); - } -}; - -GraphCanvas.prototype.selectNode = function(nodeId) { - for ( var i = 0; i < this.args.selectedVertices.length; i++) { - var format = this.getFormatter().getVertexById(nodeId).getSelected(); - format.opacity = 0.2; - this.changeVertexFormat(nodeId, this.getFormatter().getVertexById(nodeId).getSelected()); - } - - if (this.args.isVertexSelected[nodeId] == null) { - var format = this.getFormatter().getVertexById(nodeId).getSelected(); - format.opacity = 1; - this.changeVertexFormat(nodeId, this.getFormatter().getVertexById(nodeId).getSelected()); - this.args.selectedVertices.push(nodeId); - this.args.isVertexSelected[nodeId] = this.args.selectedVertices.length - 1; - this.onVertexSelect.notify(nodeId); - } -}; - -GraphCanvas.prototype.selectAllEdges = function() { - this.deselectNodes(); - this.deselectEdges(); - - for ( var edgesId in this.getDataset().edges) { - this.selectEdge(edgesId); - } -}; - -GraphCanvas.prototype.selectAllNodes = function() { - this.deselectNodes(); - this.deselectEdges(); - - for ( var vertexId in this.getDataset().vertices) { - this.selectNode(vertexId); - } -}; - -GraphCanvas.prototype.selectAll = function() { - this.deselectNodes(); - this.deselectEdges(); - - for ( var vertexId in this.getDataset().vertices) { - this.selectNode(vertexId); - } - - for ( var edgesId in this.getDataset().edges) { - this.selectEdge(edgesId); - } -}; - -GraphCanvas.prototype.selectEdge = function(edgeId) { - if (this.args.isEdgeSelected[edgeId] == null) { - this.changeEdgeFormat(edgeId, this.getFormatter().getEdgeById(edgeId).getSelected()); - //this.args.selectedEdges.push(edgeId); - this.args.isEdgeSelected[edgeId] = true; //this.args.selectedEdges.length - 1; - this.onEdgeSelect.notify(edgeId); - } -}; - -GraphCanvas.prototype.selectEdges = function(edges) { - - for ( var i = 0; i < edges.length; i++) { - this.selectEdge(edges[i]); - } -}; - -GraphCanvas.prototype.deselectNode = function(nodeId) { - if (this.args.isVertexSelected[nodeId] != null) { - this.changeVertexFormat(nodeId, this.getFormatter().getVertexById(nodeId).getDefault()); - this.args.selectedVertices.splice(this.args.isVertexSelected[nodeId], 1); - var index = this.args.isVertexSelected[nodeId]; - delete this.args.isVertexSelected[nodeId]; - - for ( var vertex in this.args.isVertexSelected) { - if (this.args.isVertexSelected[vertex] > index) { - this.args.isVertexSelected[vertex] = this.args.isVertexSelected[vertex] - 1; - } - } - } -}; - -GraphCanvas.prototype.deselectNodes = function() { - var selected = JSON.parse(JSON.stringify(this.getSelectedVertices())); - for ( var i = 0; i < selected.length; i++) { - this.deselectNode(selected[i]); - } -}; -GraphCanvas.prototype.selectNodes = function(idNodes) { - - for ( var i = 0; i < idNodes.length; i++) { - this.selectNode(idNodes[i]); - } - - // for ( var vertex in this.args.isVertexSelected) { - // if (this.args.isVertexSelected[vertex] > index){ - // this.args.isVertexSelected[vertex] = this.args.isVertexSelected[vertex] - 1; - // } - // } - -}; - -GraphCanvas.prototype.changeVertexFormat = function(nodeId, format) { - var svgNode = DOM.select(this.getSVGNodeId(nodeId)); - if (svgNode != null) { - var properties = format.toJSON(); - for ( var item in properties) { - svgNode.setAttribute(item, properties[item]); - } - - if (this.getFormatter().getVertexById(nodeId) instanceof CircleVertexGraphFormatter) { - var transform = "translate(" + svgNode.getAttribute("dragx") + "," + svgNode.getAttribute("dragy") + "), scale(" + format.getSize() + ")"; - svgNode.setAttribute("transform", transform); - } - } -}; - -GraphCanvas.prototype.renderLabel = function(nodeId) { - var x = Math.ceil(this.getLayout().getNodeById(nodeId).x * this.getFormatter().getWidth()); - var y = Math.ceil(this.getLayout().getNodeById(nodeId).y * this.getFormatter().getHeight()); - - var svgAttributesNode = JSON.parse(JSON.stringify(this.getFormatter().getVertexById(nodeId).getDefault().toJSON().title)); - svgAttributesNode.id = this.getSVGLabelId(this.getDataset().getVertexById(nodeId).getId()); - svgAttributesNode.dx = (-1) * (this.getDataset().getVertexById(nodeId).getName().length * svgAttributesNode["font-size"]) / 4 - 4; - - svgAttributesNode.dy = parseFloat((this.getFormatter().getVertexById(nodeId).getDefault().getSize())) - + parseFloat(svgAttributesNode["font-size"]) + parseFloat(this.getFormatter().getVertexById(nodeId).getDefault().getStrokeWidth()) - 4; - svgAttributesNode.dragx = Math.ceil(this.getLayout().getNodeById(nodeId).x * this.getFormatter().getWidth()); - - var gragy = parseFloat(this.getFormatter().getVertexById(nodeId).getDefault().getSize()) - + Math.ceil(this.getLayout().getNodeById(nodeId).y * this.getFormatter().getHeight()); - svgAttributesNode.dragy = gragy; - svgAttributesNode.transform = "translate(" + svgAttributesNode.dragx + "," + svgAttributesNode.dragy + ")";//, scale("+this.formatter.getVertexById(nodeId).getDefault().getSize()+")"; - - var nodeSVG = SVG.drawText(0, 0, this.getDataset().getVertexById(nodeId).getName(), this.GraphLabelGroup, svgAttributesNode); - - this.svgLabels[nodeId] = nodeSVG; - - /** Events for the SVG node **/ - var _this = this; - if (nodeSVG != null) { - nodeSVG.addEventListener("mouseover", function() { - _this.overLabel(nodeId); - }, false); - nodeSVG.addEventListener("mouseout", function() { - _this.outLabel(nodeId); - }, false); - } - -}; - -GraphCanvas.prototype.removeLabel = function(labelId) { - if (DOM.select(this.getSVGLabelId(labelId)) != null) { - DOM.select(this.getSVGLabelId(labelId)).parentNode.removeChild(DOM.select(this.getSVGLabelId(labelId))); - } -}; - -GraphCanvas.prototype.renderNode = function(nodeId) { - var svgAttributesNode = JSON.parse(JSON.stringify(this.getFormatter().getVertexById(nodeId).getDefault().toJSON())); - svgAttributesNode.dragx = Math.ceil(this.getLayout().getNodeById(nodeId).x * this.getFormatter().getWidth()); - svgAttributesNode.dragy = Math.ceil(this.getLayout().getNodeById(nodeId).y * this.getFormatter().getHeight()); - svgAttributesNode.transform = "translate(" + svgAttributesNode.dragx + "," + svgAttributesNode.dragy + ")"; - svgAttributesNode.id = this.getSVGNodeId(nodeId); - /*svgAttributesNode["stroke-width"] = 3 ; - svgAttributesNode["stroke-opacity"] = 1 ; - svgAttributesNode["fill-opacity"] = svgAttributesNode["opacity"] ; - svgAttributesNode["opacity"] = 1 ;*/ - this.circleDefaultRadius = this.getFormatter().getVertexById(nodeId).getDefault().getSize(); - var nodeSVG; - - if (this.getFormatter().getVertexById(nodeId) instanceof CircleVertexGraphFormatter) { - nodeSVG = SVG.drawCircle(0, 0, this.circleDefaultRadius, this.GraphNodeGroup, svgAttributesNode); - } - - if (this.getFormatter().getVertexById(nodeId) instanceof SquareVertexGraphFormatter) { - //nodeSVG = SVG.drawRectangle(0 - (this.circleDefaultRadius) ,0 - (this.formatter.getVertexById(nodeId).getDefault().getSize()) , (this.getFormatter().getVertexById(nodeId).getDefault().getSize()*2), (this.getFormatter().getVertexById(nodeId).getDefault().getSize()*2), this.GraphNodeGroup, svgAttributesNode); - nodeSVG = SVG.drawRectangle(0 - (this.circleDefaultRadius), 0 - (this.circleDefaultRadius), (this.circleDefaultRadius * 2), - (this.circleDefaultRadius * 2), this.GraphNodeGroup, svgAttributesNode); - } - - if (this.getFormatter().getVertexById(nodeId) instanceof EllipseVertexGraphFormatter) { - nodeSVG = SVG.drawEllipse(0, 0, this.circleDefaultRadius * 1.5, this.circleDefaultRadius, this.GraphNodeGroup, svgAttributesNode); - } - - if (this.getFormatter().getVertexById(nodeId) instanceof RectangleVertexGraphFormatter) { - //nodeSVG = SVG.drawRectangle(0 - (this.circleDefaultRadius) ,0 - ((this.circleDefaultRadius*2)/2) , (this.circleDefaultRadius*2), (this.circleDefaultRadius), this.GraphNodeGroup, svgAttributesNode); - nodeSVG = SVG.drawRectangle(0 - (this.circleDefaultRadius * 1.5), 0 - (this.circleDefaultRadius), (this.circleDefaultRadius * 2 * 1.5), - (this.circleDefaultRadius * 2), this.GraphNodeGroup, svgAttributesNode); - - } - - if (this.getFormatter().getVertexById(nodeId) instanceof RoundedVertexGraphFormatter) { - svgAttributesNode.ry = 2;// this.formatter.getVertexById(nodeId).getDefault().getSize()/4; - svgAttributesNode.rx = 2;// this.formatter.getVertexById(nodeId).getDefault().getSize()/4; - nodeSVG = SVG.drawRectangle(0 - (this.circleDefaultRadius * 1.5), 0 - (this.circleDefaultRadius), (this.circleDefaultRadius * 2 * 1.5), - (this.circleDefaultRadius * 2), this.GraphNodeGroup, svgAttributesNode); - } - - // - - if (this.getFormatter().getVertexById(nodeId) instanceof OctagonVertexGraphFormatter) { - svgAttributesNode.ry = 2; - svgAttributesNode.rx = 2; - nodeSVG = SVG.drawRectangle(0 - (this.circleDefaultRadius * 1.5), 0 - (this.circleDefaultRadius), (this.circleDefaultRadius * 2 * 1.5), - (this.circleDefaultRadius * 2), this.GraphNodeGroup, svgAttributesNode); - } - - nodeSVG.internalId = nodeId; - // - var _this = this; - - /** Events for the SVG node **/ - if (nodeSVG != null) { - nodeSVG.addEventListener("mouseover", function() { - _this.onVertexOver.notify(nodeId); - _this.overNode(nodeId); - }, false); - nodeSVG.addEventListener("mouseout", function() { - _this.onVertexOut.notify(nodeId); - _this.outNode(nodeId); - }, false); - //nodeSVG.addEventListener("click", function(){_this.clickNode(nodeId);}, false); - // - nodeSVG.addEventListener("mouseup", function() { - _this.onVertexUp.notify(nodeId); - }, false); - } -}; - -GraphCanvas.prototype.removeNode = function(nodeId) { - DOM.select(this.getSVGNodeId(nodeId)).parentNode.removeChild(DOM.select(this.getSVGNodeId(nodeId))); - if (this.args.labeled) { - this.removeLabel(nodeId); - } -}; - -/** REMOVING **/ -GraphCanvas.prototype.removeSelected = function() { - /** El orden importa **/ - this.removeSelectedEdges(); - this.removeSelectedNode(); - -}; - -GraphCanvas.prototype.removeSelectedNode = function() { - var selected = JSON.parse(JSON.stringify(this.getSelectedVertices())); - this.deselectNodes(); - var sorted = selected.sort(function(a, b) { - return a - b - }); - for ( var i = 0; i < sorted.length; i++) { - if (this.getDataset().getVertexById(sorted[i]) != null) { - this.getDataset().getVertexById(sorted[i]).remove(); - } - } -}; - -/** EDGES **/ -GraphCanvas.prototype.removeEdge = function(edgeId) { - if (DOM.select(this.getSVGEdgeId(edgeId)) != null) { - DOM.select(this.getSVGEdgeId(edgeId)).parentNode.removeChild(DOM.select(this.getSVGEdgeId(edgeId))); - } - - if (DOM.select(this.getSVGEdgeId(edgeId) + "_shadow") != null) { - DOM.select(this.getSVGEdgeId(edgeId) + "_shadow").parentNode.removeChild(DOM.select(this.getSVGEdgeId(edgeId) + "_shadow")); - } - - if (DOM.select(this.getSVGArrowEdgeId(edgeId)) != null) { - DOM.select(this.getSVGArrowEdgeId(edgeId)).parentNode.removeChild(DOM.select(this.getSVGArrowEdgeId(edgeId))); - } -}; - -GraphCanvas.prototype.overEdge = function(edgeId) { - if ((!this.args.interactive) || this.dragging || this.selecting) { - return; - } - - /** If selected we don't change the format **/ - if (this.args.isEdgeSelected[edgeId] == null) { - var format = this.getFormatter().getEdgeById(edgeId).getOver(); - format.args["cursor"] = "pointer"; - this.changeEdgeFormat(edgeId, format); - } -}; - -GraphCanvas.prototype.outEdge = function(edgeId) { - if (!this.args.interactive) { - return; - } - - /** If selected we don't change the format **/ - if (this.args.isEdgeSelected[edgeId] == null) { - this.changeEdgeFormat(edgeId, this.getFormatter().getEdgeById(edgeId).getDefault()); - } -}; - -GraphCanvas.prototype.changeEdgeFormat = function(edgeId, format) { - var svgEdge = DOM.select(this.getSVGEdgeId(edgeId) + "_shadow"); - if (svgEdge != null) { - var properties = format.toJSON(); - for ( var item in properties) { - svgEdge.setAttribute(item, properties[item]); - } - } -}; - -GraphCanvas.prototype.deselectEdge = function(edgeID) { - if (this.args.isEdgeSelected[edgeID] != null) { - this.changeEdgeFormat(edgeID, this.getFormatter().getEdgeById(edgeID).getDefault()); - var index = this.args.isEdgeSelected[edgeID]; - delete this.args.isEdgeSelected[edgeID]; - } -}; - -GraphCanvas.prototype.deselectEdges = function() { - var selected = JSON.parse(JSON.stringify(this.getSelectedEdges())); - for ( var i = 0; i < selected.length; i++) { - this.deselectEdge(selected[i]); - } -}; - -GraphCanvas.prototype.removeSelectedEdges = function() { - var selected = JSON.parse(JSON.stringify(this.getSelectedEdges())); - this.deselectEdges(); - for ( var i = 0; i < selected.length; i++) { - if (this.getDataset().getEdgeById(selected[i]) != null) { - this.getDataset().getEdgeById(selected[i]).remove(); - } - } -}; - -GraphCanvas.prototype.renderEdge = function(edgeId) { - var svgAttributesEdge = this.getFormatter().getEdgeById(edgeId).getDefault().toJSON(); - var edge = this.getDataset().getEdgeById(edgeId); - - var svgNodeTarget = this.getVertexById(edge.getNodeTarget().getId()); - var svgNodeSource = this.getVertexById(edge.getNodeSource().getId()); - svgAttributesEdge.id = this.getSVGEdgeId(edge.getId()) + "_shadow"; - - var svgEdge = null; - - if (this.getFormatter().getEdgeById(edgeId) instanceof LineEdgeGraphFormatter) { - var coordenateSourceX = svgNodeSource.getAttribute("dragx"); - var coordenateSourceY = svgNodeSource.getAttribute("dragy"); - var coordenateTargetX = svgNodeTarget.getAttribute("dragx"); - var coordenateTargetY = svgNodeTarget.getAttribute("dragy"); - - SVG.drawLine(coordenateSourceX, coordenateSourceY, coordenateTargetX, coordenateTargetY, this.GraphEdgeGroup, svgAttributesEdge); - var attributesShadow = {}; - attributesShadow.id = this.getSVGEdgeId(edge.getId()); - attributesShadow["stroke-opacity"] = 0; - attributesShadow["stroke-width"] = 4; - attributesShadow["stroke"] = "black"; - svgEdge = SVG.drawLine(svgNodeSource.getAttribute("dragx"), svgNodeSource.getAttribute("dragy"), svgNodeTarget.getAttribute("dragx"), - svgNodeTarget.getAttribute("dragy"), this.GraphEdgeGroup, attributesShadow); - } - - if (this.getFormatter().getEdgeById(edgeId) instanceof BezierEdgeGraphFormatter) { - var nodeId = edge.getNodeTarget().getId(); - var nodeSize = this.formatter.getVertexById(nodeId).getDefault().getSize() * this.getFormatter().getNodesMaxSize(); - svgAttributesEdge.fill = "none"; - svgAttributesEdge.id = this.getSVGEdgeId(edgeId); - var d = this.calculateCoordenatesBezier(nodeSize, svgNodeSource.getAttribute("dragx"), svgNodeSource.getAttribute("dragy")); - svgEdge = SVG.drawPath(d, this.GraphEdgeGroup, svgAttributesEdge); - } - ; - - if ((this.getFormatter().getEdgeById(edgeId) instanceof DirectedLineEdgeGraphFormatter) - || (this.getFormatter().getEdgeById(edgeId) instanceof CutDirectedLineEdgeGraphFormatter) - || (this.getFormatter().getEdgeById(edgeId) instanceof DotDirectedLineEdgeGraphFormatter) - || (this.getFormatter().getEdgeById(edgeId) instanceof OdotDirectedLineEdgeGraphFormatter) - || (this.getFormatter().getEdgeById(edgeId) instanceof OdirectedLineEdgeGraphFormatter)) { - var coordenateSourceX = svgNodeSource.getAttribute("dragx"); - var coordenateSourceY = svgNodeSource.getAttribute("dragy"); - var coordenateTargetX = svgNodeTarget.getAttribute("dragx"); - var coordenateTargetY = svgNodeTarget.getAttribute("dragy"); - - var offset = parseFloat(this.getFormatter().getVertexById(this.getDataset().getEdgeById(edgeId).getNodeTarget().getId()).getDefault() - .getSize() - * this.circleDefaultRadius); - var point = this._calculateEdgePointerPosition(coordenateSourceX, coordenateSourceY, coordenateTargetX, coordenateTargetY, offset); - coordenateTargetX = point.x; - coordenateTargetY = point.y; - SVG.drawLine(coordenateSourceX, coordenateSourceY, coordenateTargetX, coordenateTargetY, this.GraphEdgeGroup, svgAttributesEdge); - - var attributesShadow = {}; - attributesShadow.id = this.getSVGEdgeId(edge.getId()); - attributesShadow["stroke-opacity"] = 0; - attributesShadow["stroke-width"] = 4; - attributesShadow["stroke"] = "black"; - svgEdge = SVG.drawLine(coordenateSourceX, coordenateSourceY, coordenateTargetX, coordenateTargetY, this.GraphEdgeGroup, attributesShadow); - } - - if (this.getFormatter().getEdgeById(edgeId) instanceof DirectedLineEdgeGraphFormatter - || (this.getFormatter().getEdgeById(edgeId) instanceof OdirectedLineEdgeGraphFormatter)) { - var coordenateSourceX = svgNodeSource.getAttribute("dragx"); - var coordenateSourceY = svgNodeSource.getAttribute("dragy"); - var coordenateTargetX = svgNodeTarget.getAttribute("dragx"); - var coordenateTargetY = svgNodeTarget.getAttribute("dragy"); - - var point = this._calculateEdgePointerPosition(coordenateSourceX, coordenateSourceY, coordenateTargetX, coordenateTargetY, offset); - coordenateTargetX = point.x; - coordenateTargetY = point.y; - - var angle = Geometry.toDegree(point.angle) + 90; - this.arrowDefaultSize = this.getFormatter().getEdgeById(edgeId).getArrowSize(); //getDefault().getArrowSize(); - var d = "-" + this.arrowDefaultSize + ",0 0,-" + parseFloat(this.arrowDefaultSize) * 2 + " " + this.arrowDefaultSize + ",0"; - - var attributes; - - if (this.getFormatter().getEdgeById(edgeId) instanceof DirectedLineEdgeGraphFormatter) { - attributes = [ - [ "fill", this.getFormatter().getEdgeById(edgeId).getDefault().getStroke() ], - [ "stroke", this.getFormatter().getEdgeById(edgeId).getDefault().getStroke() ], [ "id", this.getSVGArrowEdgeId(edgeId) ] ]; - } else { - attributes = [ - [ "fill", "#FFFFFF" ], [ "stroke", this.getFormatter().getEdgeById(edgeId).getDefault().getStroke() ], - [ "id", this.getSVGArrowEdgeId(edgeId) ] ]; - } - - var flechaSVGNode = SVG.drawPoligon(d, this.GraphEdgeGroup, attributes);//, ["transform", "rotate("+angle+"), translate(0,0)"]]); - flechaSVGNode.setAttribute("transform", " translate(" + coordenateTargetX + ", " + coordenateTargetY + "), rotate(" + angle + ")"); - } - ; - - if (this.getFormatter().getEdgeById(edgeId) instanceof CutDirectedLineEdgeGraphFormatter) { - var coordenateSourceX = svgNodeSource.getAttribute("dragx"); - var coordenateSourceY = svgNodeSource.getAttribute("dragy"); - var coordenateTargetX = svgNodeTarget.getAttribute("dragx"); - var coordenateTargetY = svgNodeTarget.getAttribute("dragy"); - - var point = this._calculateEdgePointerPosition(coordenateSourceX, coordenateSourceY, coordenateTargetX, coordenateTargetY, offset); - coordenateTargetX = point.x; - coordenateTargetY = point.y; - - var angle = Geometry.toDegree(point.angle) + 90; - - //this.arrowDefaultSize = 2; //getDefault().getArrowSize(); - var d = "-4,0 4,0 4,-2 -4,-2"; - - var flechaSVGNode = SVG.drawPoligon(d, this.GraphEdgeGroup, [ - [ "fill", this.getFormatter().getEdgeById(edgeId).getDefault().getStroke() ], - [ "stroke", this.getFormatter().getEdgeById(edgeId).getDefault().getStroke() ], [ "id", this.getSVGArrowEdgeId(edgeId) ] ]);//, ["transform", "rotate("+angle+"), translate(0,0)"]]); - flechaSVGNode.setAttribute("transform", " translate(" + coordenateTargetX + ", " + coordenateTargetY + "), rotate(" + angle + ")"); - } - ; - - if ((this.getFormatter().getEdgeById(edgeId) instanceof DotDirectedLineEdgeGraphFormatter) - || (this.getFormatter().getEdgeById(edgeId) instanceof OdotDirectedLineEdgeGraphFormatter)) { - var coordenateSourceX = svgNodeSource.getAttribute("dragx"); - var coordenateSourceY = svgNodeSource.getAttribute("dragy"); - var coordenateTargetX = svgNodeTarget.getAttribute("dragx"); - var coordenateTargetY = svgNodeTarget.getAttribute("dragy"); - var point = this._calculateEdgePointerPosition(coordenateSourceX, coordenateSourceY, coordenateTargetX, coordenateTargetY, offset); - coordenateTargetX = point.x; - coordenateTargetY = point.y; - var angle = Geometry.toDegree(point.angle) + 90; - // this.arrowDefaultSize = this.formatter.getEdgeById(edgeId).getArrowSize(); //getDefault().getArrowSize(); - var attributes = []; - if (this.getFormatter().getEdgeById(edgeId) instanceof OdotDirectedLineEdgeGraphFormatter) { - attributes = [ - [ "fill", "#FFFFFF" ], [ "stroke", this.getFormatter().getEdgeById(edgeId).getDefault().getStroke() ], - [ "id", this.getSVGArrowEdgeId(edgeId) ] ]; - } else { - attributes = [ - [ "fill", this.getFormatter().getEdgeById(edgeId).getDefault().getStroke() ], - [ "stroke", this.getFormatter().getEdgeById(edgeId).getDefault().getStroke() ], [ "id", this.getSVGArrowEdgeId(edgeId) ] ]; - } - var flechaSVGNode = SVG.drawCircle(0, 0, 4, this.GraphEdgeGroup, attributes); - flechaSVGNode.setAttribute("transform", " translate(" + coordenateTargetX + ", " + coordenateTargetY + "), rotate(" + angle + ")"); - } - ; - - var _this = this; - /** Events for the SVG edge **/ - if (svgEdge != null) { - if (this.getDataset().getEdgesCount() < this.args.maxNumberEdgesFiringEvents) { - svgEdge.addEventListener("mouseover", function() { - _this.overEdge(edgeId); - }, false); - svgEdge.addEventListener("mouseout", function() { - _this.outEdge(edgeId); - }, false); - } - } -}; - -GraphCanvas.prototype._calculateEdgePointerPosition = function(sourceX, sourceY, targetX, targetY, radius) { - var angle = Geometry.getAngleBetweenTwoPoints(sourceX, sourceY, targetX, targetY); - - /** Suponiendo el node source que este a la derecha **/ - if ((targetX - sourceX) < 0) { - var b = Geometry.getAdjacentSideOfRectangleRight(angle, radius); - targetX = parseFloat(targetX) + parseFloat(b); - arrowX = parseFloat(targetX) + parseFloat(b) + this.arrowDefaultSize / 2; - } else { - var b = Geometry.getAdjacentSideOfRectangleRight(angle, radius); - targetX = parseFloat(targetX) - parseFloat(b); - arrowX = parseFloat(targetX) - parseFloat(b) - this.arrowDefaultSize / 2; - } - - /** Suponiendo el node source que este a la arriba **/ - if ((targetY - sourceY) > 0) { - var a = Geometry.getOppositeSideOfRectangleRight(angle, radius); - targetY = parseFloat(targetY) - parseFloat(a); - arrowY = parseFloat(targetY) - parseFloat(a) - this.arrowDefaultSize / 2; - } else { - var a = Geometry.getOppositeSideOfRectangleRight(angle, radius); - targetY = parseFloat(targetY) + parseFloat(a); - arrowY = parseFloat(targetY) + parseFloat(a) - this.arrowDefaultSize / 2; - - } - - return { - "x" : arrowX, - "y" : arrowY, - "angle" : angle - }; -}; - -GraphCanvas.prototype.calculateCoordenatesBezier = function(nodeSize, x1, y1) { - var x11 = x1 - (nodeSize / 2); - var y11 = y1 - (nodeSize / 2); - - var x12 = parseFloat(x1) + parseFloat(nodeSize / 2); - var y12 = y1 - (nodeSize / 2); - - var curvePointX = (x12 - x11) / 2 + x11; - var curvePointY = y1 - (nodeSize * 2); - var d = "M" + x11 + "," + y11 + " T" + curvePointX + "," + curvePointY + " " + x12 + "," + y12; - return d; - -}; - -GraphCanvas.prototype.renderEdges = function() { - for ( var edge in this.getDataset().getEdges()) { - this.renderEdge(this.getDataset().getEdgeById(edge).getId()); - - } -}; - -GraphCanvas.prototype.getLastSelectedNode = function() { - var node = null; - if (this.getSelectedVertices().length > 0) { - var nodeId = this.getSelectedVertices()[this.getSelectedVertices().length - 1]; - node = this.getDataset().getVertexById(nodeId); - } - return node; -}; -/* - GraphCanvas.prototype.getNodeByNameAndIndex = function(node, index){ - var nodeId = this.getDataset().verticesIndex[node][index]; - var nodeItem = this.getDataset().getVertexById(nodeId); - return nodeItem; - }; - */ - -GraphCanvas.prototype.setDataset = function(dataset) { - this.dataset = dataset; -}; - -GraphCanvas.prototype.setFormatter = function(formatter) { - this.formatter = formatter; -}; - -GraphCanvas.prototype.setLayout = function(layout) { - this.layout = layout; -}; - -/** API **/ -GraphCanvas.prototype.getDataset = function() { - return this.dataset; -}; - -GraphCanvas.prototype.getFormatter = function() { - return this.formatter; -}; - -GraphCanvas.prototype.getLayout = function() { - return this.layout; -}; - -/** API DATASET **/ -GraphCanvas.prototype.addVertex = function(name, args) { - this.getDataset().addNode(name, args); -}; - -GraphCanvas.prototype.removeVertex = function(vertexId) { - this.getDataset().getVertexById(vertexId).remove(); -}; - -GraphCanvas.prototype.addEdge = function(edgeName, nodeSourceId, nodeTargetId, args) { - this.getDataset().addEdge(edgeName, nodeSourceId, nodeTargetId, args); -}; -/* - GraphCanvas.prototype.removeEdge = function(edgeId){ - this.getDataset().getEdgeById(edgeId).remove(); - }; - */ - -/** API FORMATTER **/ -GraphCanvas.prototype.getWidth = function() { - return this.getFormatter().getWidth(); -}; - -GraphCanvas.prototype.getHeight = function() { - return this.getFormatter().getHeight(); -}; - -GraphCanvas.prototype.getBackgroundImage = function() { - return this.getFormatter().getBackgroundImage(); -}; - -//GraphCanvas.prototype.setBackgroundImage = function(value){ -// this.getFormatter().setBackgroundImage(value); -//}; - -GraphCanvas.prototype.getBackgroundColor = function() { - return this.getFormatter().getBackgroundColor(); -}; - -GraphCanvas.prototype.setBackgroundColor = function() { - this.getFormatter().setBackgroundColor(value); -}; - -//GraphCanvas.prototype.setEdgeFill = function(edgeId, value){ -// this.getFormatter().getEdgeById(edgeId).getDefault().setFill(value); -//}; -// -//GraphCanvas.prototype.getEdgeFill = function(edgeId){ -// return this.getFormatter().getEdgeById(edgeId).getDefault().getFill(); -//}; - -/** VERTICES FORMATTER **/ -GraphCanvas.prototype.setVertexSize = function(vertexId, value) { - this.getFormatter().getVertexById(vertexId).getDefault().setSize(value); -}; - -GraphCanvas.prototype.getVertexSize = function(vertexId) { - return this.getFormatter().getVertexById(vertexId).getDefault().getSize(); -}; - -GraphCanvas.prototype.setVertexStroke = function(vertexId, value) { - this.getFormatter().getVertexById(vertexId).getDefault().setStroke(value); -}; - -GraphCanvas.prototype.getVertexStroke = function(vertexId) { - return this.getFormatter().getVertexById(vertexId).getDefault().getStroke(); -}; - -GraphCanvas.prototype.setVertexStrokeOpacity = function(vertexId, value) { - this.getFormatter().getVertexById(vertexId).getDefault().setStrokeOpacity(value); -}; - -GraphCanvas.prototype.getVertexStrokeOpacity = function(vertexId) { - return this.getFormatter().getVertexById(vertexId).getDefault().getStrokeOpacity(); -}; - -GraphCanvas.prototype.setVertexOpacity = function(vertexId, value) { - this.getFormatter().getVertexById(vertexId).getDefault().setOpacity(value); -}; - -GraphCanvas.prototype.getVertexOpacity = function(vertexId) { - return this.getFormatter().getVertexById(vertexId).getDefault().getOpacity(); -}; - -GraphCanvas.prototype.setVertexFill = function(vertexId, color) { - this.getFormatter().getVertexById(vertexId).getDefault().setFill(color); -}; - -GraphCanvas.prototype.getVertexFill = function(vertexId) { - return this.getFormatter().getVertexById(vertexId).getDefault().getFill(); -}; - -/** EDGES FORMATTER **/ -GraphCanvas.prototype.setEdgeSize = function(edgeId, value) { - this.getFormatter().getEdgeById(edgeId).getDefault().setSize(value); -}; - -GraphCanvas.prototype.getEdgeSize = function(edgeId) { - return this.getFormatter().getEdgeById(edgeId).getDefault().getSize(); -}; - -GraphCanvas.prototype.setEdgeStroke = function(edgeId, value) { - this.getFormatter().getEdgeById(edgeId).getDefault().setStroke(value); -}; - -GraphCanvas.prototype.getEdgeStroke = function(edgeId) { - return this.getFormatter().getEdgeById(edgeId).getDefault().getStroke(); -}; - -GraphCanvas.prototype.setEdgeStrokeOpacity = function(edgeId, value) { - this.getFormatter().getEdgeById(edgeId).getDefault().setStrokeOpacity(value); -}; - -GraphCanvas.prototype.getEdgeStrokeOpacity = function(edgeId) { - return this.getFormatter().getEdgeById(edgeId).getDefault().getStrokeOpacity(); -}; - -GraphCanvas.prototype.setEdgeFill = function(edgeId, color) { - this.getFormatter().getEdgeById(edgeId).getDefault().setFill(color); -}; - -GraphCanvas.prototype.getEdgeFill = function(edgeId) { - return this.getFormatter().getEdgeById(edgeId).getDefault().getFill(); -}; - -/** API LAYOUT **/ -GraphCanvas.prototype.setCoordinates = function(vertexId, x, y) { - return this.getLayout().getEdgeById(vertexId).setCoordinates(x, y); -}; - - - -function HPLCGraph(args) { - this.width = 600; - this.height = 600; - this.title = ''; - this.bbar = false; - this.plotInnerPanelPadding = 10; - this.plotPanelPadding = 5; - this.id = BUI.id(); - - this.hidePlots = null; - this.xlabel = ""; - this.scaled = false; - this.xParam = null; - this.showRangeSelector = true; - this.interactionModel = null; - - /** for each stat the max and minimum value when it is scaled in order to show correctly in the legend **/ - this.ranges = {}; - if (args != null) { - if (args.interactionModel != null) { - this.interactionModel = args.interactionModel; - } - if (args.width != null) { - this.width = args.width; - } - if (args.height != null) { - this.height = args.height; - } - if (args.bbar != null) { - this.bbar = args.bbar; - } - if (args.title != null) { - this.title = args.title; - } - if (args.plots != null) { - this.plots = args.plots; - } - - if (args.scaled != null) { - this.scaled = args.scaled; - } - if (args.xlabel != null) { - this.xlabel = args.xlabel; - } - if (args.xParam != null) { - this.xParam = args.xParam; - } - if (args.showRangeSelector != null) { - this.showRangeSelector = args.showRangeSelector; - } - } - - this.onZoomX = new Event(this); - this.onResetZoom = new Event(this); - this.dblclick = new Event(this); -} - -HPLCGraph.prototype.getMenu = function () { - var _this = this; - /** Actions buttons **/ - var actions = []; - - function toggle(item, pressed) { - if (pressed) { - _this.plots[item.param] = true; - } else { - delete _this.plots[item.param]; - } - _this.reloadData(this.hplcData); - } - - for (var i = 0; i < this.hplcData.length; i++) { - if (this.hplcData[i].showOnMenu != false) { - var param = this.hplcData[i].param; - var style = "style='padding:0 0px 0 5px;'"; - actions.push({ - text : "
" + BUI.getRectangleColorDIV(this.hplcData[i].color, 10, 10) + " " + this.hplcData[i].label + "
", - id : _this.id + param, - param : param, - enableToggle : true, - scope : this, - toggleHandler : toggle, - pressed : (_this.plots[param] != null) - }); - } - } - actions.push("-"); - - actions.push({ - text : "Scale", - enableToggle : true, - scope : this, - pressed : this.scaled, - icon : '../images/icon_graph.png', - toggleHandler : function (item, pressed) { - _this.scaled = pressed; - _this.reloadData(this.hplcData); - } - }); - - actions.push("->"); - actions.push({ - text : "Save", - scope : this, - icon : '../images/save.gif', - handler : function (item, pressed) { - var largeImage = document.createElement("img"); - largeImage.style.display = 'block'; - largeImage.style.width = 200 + "px"; - largeImage.style.height = 200 + "px"; - largeImage.setAttribute('src', Dygraph.Export.asCanvas(this.dygraphObject.dygraph).toDataURL()); - window.open(Dygraph.Export.asCanvas(this.dygraphObject.dygraph).toDataURL(), 'Image', ''); - } - }); - - return actions; -}; - -/** Looks for the maximum value and then divide everything but that value **/ -HPLCGraph.prototype.scaledData = function (data) { - for (var i = 0; i < data.length; i++) { - var values = this.getMaxAndMinValue(data[i]); - data[i] = this.divideValuesByMax(data[i], values.max); - this.ranges[data[i].label] = values; - } - return data; -}; - - -/** Given a stat float[] and a max number it will divide each value by max **/ -HPLCGraph.prototype.divideValuesByMax = function (stat, max) { - for (var j = 0; j < stat.data.length; j++) { - if (max != 0) { - stat.data[j] = Number(stat.data[j]) / max; - stat.std[j] = Number(stat.std[j]) / max; - } - } - return stat; -}; - -/** returns max value of a stat **/ -HPLCGraph.prototype.getMaxAndMinValue = function (stat) { - var max = 0; - var min = stat.data[0]; - for (var j = 0; j < stat.data.length; j++) { - if (Number(stat.data[j]) > max) { - max = Number(stat.data[j]); - } - if (Number(stat.std[j]) > max) { - max = Number(stat.std[j]); - } - if (Number(stat.data[j]) < min) { - min = Number(stat.data[j]); - } - } - return { - max : Number(max), - min : Number(min) - }; -}; - -HPLCGraph.prototype.getPoint = function (data, i) { - var point = [ 10, 10, 10 ]; - var y = parseFloat(data.data[i]); - var error = parseFloat(data.std[i]); - if (data.fdata == null) { - return [ y - error, y, y + error ]; - } else { - if (data.fstd != null) { - return [ data.fstd(y - error), data.fdata(y), data.fstd(y + error) ]; - } - return [ data.fdata(y) - error, data.fdata(y), data.fdata(y) + error ]; - } - return point; -}; - -HPLCGraph.prototype.reloadData = function(hplcData) { - this.panel.setLoading(false); - this.hplcData = hplcData; - - var data = hplcData; - - - /** In case of having peaks **/ - if (this.peaks != null) { - for (var peak in this.peaks) { - var values = []; - var std = []; - for (var i = 0; i < this.peaks[peak].length; i++) { - values.push(this.peaks[peak][i][1]); - std.push(this.peaks[peak][i][2]); - } - data.push({ - param : peak, - data : values, - showOnMenu : false, - fdata : function (a) { - var value = (Math.log(parseFloat(a))); - if (isNumber(value)) { - return value; - } - }, - fstd : function (a) { - var value = Math.log(Math.abs(parseFloat(a))); - if (isNumber(value)) - return value; - }, - std : std, - color : this.colorPeak[peak], - label : peak - }); - - } - } - - - if (this.scaled) { - data = this.scaledData(JSON.parse(JSON.stringify(hplcData))); - } - - var paramIndex = {}; - var parsed = []; - var j = 0; - for (var i = 0; i < data[0].data.length - 1; i++) { - var aux = []; - for (j = 0; j < data.length; j++) { - if (this.plots != null) { - if (this.plots[data[j].param] != null) { - aux.push(this.getPoint(data[j], i)); - paramIndex[data[j].param] = aux.length - 1; - } - } else { - aux.push([ data[j].data[i] - data[j].std[i], data[j].data[i], data[j].data[i] + data[j].std[i] ]); - } - } - parsed.push([]); - - var index = i; - if (this.xParam != null) { - index = parseFloat(data[this.xParam].data[i]); - } - - parsed[parsed.length - 1].push(index); - - for (j = 0; j < data.length; j++) { - if (this.plots != null) { - if (this.plots[data[j].param] != null) { - parsed[parsed.length - 1].push(aux[paramIndex[data[j].param]]); - } - } else { - parsed[parsed.length - 1].push(aux[j]); - } - } - } - - var colors = []; - var labels = [ "" ]; - for (j = 0; j < data.length; j++) { - if (this.plots != null) { - if (this.plots[data[j].param] != null) { - colors.push(data[j].color); - labels.push(data[j].label); - } - } else { - parsed[parsed.length - 1].push(aux[j]); - } - } - - this._renderDygraph(parsed, colors, labels); -}; - -HPLCGraph.prototype._renderDygraph = function (parsed, colors, labels) { - this.dygraphObject = new StdDevDyGraph(this.id, { - width : this.width, - height : this.height - 10, - xlabel : this.xlabel, - showRangeSelector : this.showRangeSelector, - interactionModel : this.interactionModel, - scaled : this.scaled, - ranges : this.ranges - }); - this.dygraphObject.draw(parsed, colors, labels); - - var _this = this; - this.dygraphObject.onZoomX.attach(function (sender, args) { - try { - _this.onZoomX.notify(args); - } catch (e) { - } - }); - - this.dygraphObject.onResetZoom.attach(function (sender, args) { - try { - _this.onResetZoom.notify(args); - } catch (e) { - } - }); - - this.dygraphObject.dblclick.attach(function (sender, args) { - try { - _this.dblclick.notify(args); - } catch (e) { - } - }); - -}; - -HPLCGraph.prototype.loadData = function (data) { - var _this = this; - this.reloadData(data); - this.panel.addDocked({ - xtype : 'toolbar', - items : this.getMenu() - }); - - - if (this.bbar == true){ - this.panel.addDocked({ - xtype : 'toolbar', - dock: 'bottom', - items : [ - { - xtype: 'numberfield', - id: 'main_field_start', - fieldLabel: 'Range from', - width: 170, - labelWidth : 70, - value: 0, - minValue: 0 - }, - { - xtype: 'numberfield', - id: 'main_field_end', - fieldLabel: 'to', - width: 130, - labelWidth : 30, - value: 0, - minValue: 0 - }, - { - xtype: 'button', - text: 'Go', - handler: function () { - var start = parseFloat(Ext.getCmp("main_field_start").getValue()); - var end = parseFloat(Ext.getCmp("main_field_end").getValue()); - - if (start < 0) { - start = 0; - } - if (end < 0) { - end = 0; - } - if (start > end) { - var aux = end; - end = start; - start = aux; - } - - _this.dygraphObject.dygraph.updateOptions({ isZoomedIgnoreProgrammaticZoom: true, dateWindow: [start, end] }); - } - } - ] - }); - } -}; - -HPLCGraph.prototype.getPanel = function () { - this.panel = Ext.create('Ext.panel.Panel', { - padding : this.plotPanelPadding, - width : this.width + 4 * this.plotInnerPanelPadding, - height : this.height + 4 * this.plotInnerPanelPadding, - items : [ { - html : "", - id : this.id, - width : this.width, - height : this.height - } ] - }); - - return this.panel; -}; - -HPLCGraph.prototype.input = function () { - return DATADOC.getHPLCData(); -}; - -HPLCGraph.prototype.getDataByFrameNumber = function (frameNumber) { - var data = {}; - data.frameNumber = frameNumber; - for (var key in this.hplcData){ - data[this.hplcData[key].label] = this.hplcData[key].data[frameNumber]; - } - console.log(data); - return data; -}; - - -HPLCGraph.prototype.test = function (targetId) { - var mainPlotPanel = new HPLCGraph({ - title : 'I0', - width : 800, - height : 400, - plots : { - "I0" : true, - "Rg" : true, - "Mass" : true - }, - xlabel : "HPLC Frames", - scaled : this.scaled, - interactionModel : { - 'dblclick' : function (event, g, context) { - } - } - }); - mainPlotPanel.getPanel().render(targetId); - mainPlotPanel.loadData(mainPlotPanel.input()); - -}; - - -function MergesHPLCGraph(args) { - HPLCGraph.prototype.constructor.call(this, args); - -// this.peakColors = ["#00FB42", "#00BA31", "#007C21", "#003E10"]; - this.peakColors = ["#DEBD00", "#6D9100", "#872900", "#0092CC"]; -} - - -MergesHPLCGraph.prototype.scaledData = HPLCGraph.prototype.scaledData; -MergesHPLCGraph.prototype.divideValuesByMax = HPLCGraph.prototype.divideValuesByMax; -MergesHPLCGraph.prototype.getMaxAndMinValue = HPLCGraph.prototype.getMaxAndMinValue; -MergesHPLCGraph.prototype.getPoint = HPLCGraph.prototype.getPoint; -MergesHPLCGraph.prototype.reloadData = HPLCGraph.prototype.reloadData; -MergesHPLCGraph.prototype._renderDygraph = HPLCGraph.prototype._renderDygraph; -MergesHPLCGraph.prototype.loadData = HPLCGraph.prototype.loadData; -MergesHPLCGraph.prototype.getPanel = HPLCGraph.prototype.getPanel; -MergesHPLCGraph.prototype.getDataByFrameNumber = HPLCGraph.prototype.getDataByFrameNumber; - - -MergesHPLCGraph.prototype.setPeaks = function (data) { - this.peaks = data; - /** get size of peaks **/ - this.peakKeys = []; - this.colorPeak = {}; - var colorCount = 1; - for (var key in this.peaks) { - if (this.peaks.hasOwnProperty(key)) { - var color = this.peakColors[colorCount % this.peakColors.length]; - colorCount = colorCount + 1; - this.peakKeys.push(key); - this.colorPeak[key] = color; - } - } - this.peakKeys.sort(); -}; - - -MergesHPLCGraph.prototype.getMenu = function () { - var _this = this; - /** Actions buttons **/ - var actions = []; - - function toggle(item, pressed) { - if (pressed) { - _this.plots[item.param] = true; - } else { - delete _this.plots[item.param]; - } - _this.reloadData(_this.hplcData); - } - - - /** Toolbar for peaks Average **/ - if (this.peaks != null) { - var items = []; - for (var i = 0; i < this.peakKeys.length; i++) { - var color = this.colorPeak[this.peakKeys[i]]; - items.push({ - text: "Peak #" + i + " " + this.peakKeys[i].replace("- ", " to #").replace(".0", "").replace(".0", "") + "", - peakid : this.peakKeys[i], - checked: false, - checkHandler: function (sender, pressed) { - var item = new Object(); - item.param = sender.peakid; - toggle(item, pressed); - } - }); - } - - var menu = Ext.create('Ext.menu.Menu', { - id: 'mainMenu', - style: { - overflow: 'visible' - }, - items: items - }); - var tb = Ext.create('Ext.toolbar.Toolbar'); - tb.add({ - text: 'Peaks Avg.', - menu: menu - }); - actions.push(tb); - } - - - - for (var i = 0; i < this.hplcData.length; i++) { - if (this.hplcData[i].showOnMenu != false) { - var param = this.hplcData[i].param; - var style = "style='padding:0 0px 0 5px;'"; - actions.push({ - text : "
" + BUI.getRectangleColorDIV(this.hplcData[i].color, 10, 10) + " " + this.hplcData[i].label + "
", - id : _this.id + param, - param : param, - enableToggle : true, - scope : this, - margin : 5, - toggleHandler : toggle, - pressed : (_this.plots[param] != null) - }); - } - } - - actions.push("->"); - actions.push({ - text : "Save", - scope : this, - icon : '../images/save.gif', - handler : function (item, pressed) { - var largeImage = document.createElement("img"); - largeImage.style.display = 'block'; - largeImage.style.width = 200 + "px"; - largeImage.style.height = 200 + "px"; - largeImage.setAttribute('src', Dygraph.Export.asCanvas(this.dygraphObject.dygraph).toDataURL()); - window.open(Dygraph.Export.asCanvas(this.dygraphObject.dygraph).toDataURL(), 'Image', ''); - } - }); - - return actions; -}; - - -MergesHPLCGraph.prototype.input = function () { - return DATADOC.getScatteringHPLCFrameData(); -}; - -MergesHPLCGraph.prototype.test = function (targetId) { - var mainPlotPanel = new MergesHPLCGraph({ - title : 'Scattering', - width : this.plotWidth, - height : 500, - showRangeSelector : false, - xParam : 0, - xlabel : "scattering_I", - plots : { - "scattering_I" : true, - "subtracted_I" : true, - "buffer_I" : true - } - }); - mainPlotPanel.getPanel().render(targetId); - mainPlotPanel.loadData(mainPlotPanel.input()); -}; - - -function NetworkWidget(args) { - this.id = "NetworkViewer_" + Math.random().toString().replace(".", "_"); - - this.label = true; - if (args != null) { - if (args.targetId != null) { - this.targetId = args.targetId; - } - if (args.label != null) { - this.label = args.label; - } - } - - this.onVertexOver = new Event(this); - this.onVertexOut = new Event(this); -} - -NetworkWidget.prototype.draw = function(dataset, formatter, layout) { - - this.graphCanvas = new GraphCanvas(this.id, document.getElementById(this.targetId), { - "labeled" : this.label, - "multipleSelectionEnabled" : false, - "draggingCanvasEnabled" : false - }); - this.graphCanvas.draw(dataset, formatter, layout); - - var _this = this; - this.graphCanvas.onVertexOver.attach(function(sender, nodeId) { - _this.onVertexOver.notify(nodeId); - }); - - this.graphCanvas.onVertexOut.attach(function(sender, nodeId) { - _this.onVertexOut.notify(nodeId); - }); -}; - -/** SELECT VERTICES BY NAME * */ -NetworkWidget.prototype.selectVertexByName = function(vertexName) { - var vertices = this.getDataset().getVertexByName(vertexName); - if (vertices != null) { - for ( var nodeId in vertices) { - if (vertices.hasOwnProperty(nodeId)) { - var vertexId = vertices[nodeId].getId(); - this.selectVertexById(vertexId); - } - } - } -}; - -NetworkWidget.prototype.selectVerticesByName = function(verticesName) { - for ( var i = 0; i < verticesName.length; i++) { - this.selectVertexByName(verticesName[i]); - } -}; - -/** SELECT VERTICES BY ID * */ -NetworkWidget.prototype.selectVertexById = function(vertexId) { - this.graphCanvas.selectNode(vertexId); - this.blinkVertexById(vertexId); -}; - -NetworkWidget.prototype.selectVerticesById = function(verticesId) { - for ( var i = 0; i < verticesId.length; i++) { - this.selectVertexById(verticesId[i]); - } -}; - -/** VECINDARIO * */ -NetworkWidget.prototype.selectNeighbourhood = function() { - this.selectEdgesFromVertices(); - this.selectAdjacent(); -}; - -/** DESELECT * */ -NetworkWidget.prototype.deselectNodes = function() { - this.graphCanvas.deselectNodes(); -}; - -/** SELECT ALL NODES * */ -NetworkWidget.prototype.selectAllNodes = function() { - this.getGraphCanvas().selectAllNodes(); -}; - -/** SELECT EVERYTHING * */ -NetworkWidget.prototype.selectAll = function() { - this.getGraphCanvas().selectAll(); -}; - -/** SELECT ALL EDGES * */ -NetworkWidget.prototype.selectAllEdges = function() { - this.getGraphCanvas().selectAllEdges(); -}; - -/** ZOOM * */ -NetworkWidget.prototype.setScale = function(value) { - this.graphCanvas.setScale(value); -}; - -NetworkWidget.prototype.getScale = function() { - return this.graphCanvas.getScale(value); -}; - -/** SELECT ADJACENT VERTICES FROM SELECTED VERTICES * */ -NetworkWidget.prototype.selectAdjacent = function() { - var selectedVertices = this.getGraphCanvas().getSelectedVertices(); - var edges = []; - for ( var i = 0; i < selectedVertices.length; i++) { - edges = edges.concat(this.getGraphCanvas().getDataset().getVertexById(selectedVertices[i]).getEdges()); - } - var vertices = []; - for ( i = 0; i < edges.length; i++) { - vertices.push(edges[i].getNodeSource().getId()); - vertices.push(edges[i].getNodeTarget().getId()); - } - - this.selectVerticesById(vertices); -}; - -/** SELECT EDGES FROM SELECTED VERTICES * */ -NetworkWidget.prototype.selectEdgesFromVertices = function() { - var selectedVertices = this.getGraphCanvas().getSelectedVertices(); - var edges = []; - for ( var i = 0; i < selectedVertices.length; i++) { - edges = edges.concat(this.getGraphCanvas().getDataset().getVertexById(selectedVertices[i]).getEdges()); - } - var edgesId = []; - for ( i = 0; i < edges.length; i++) { - edgesId.push(edges[i].getId()); - } - this.getGraphCanvas().selectEdges(edgesId); -}; - -/** BLINKING * */ -NetworkWidget.prototype.blinkVertexById = function(vertexId) { - this.graphCanvas.blinkVertexById(vertexId); -}; - -/** COMPONENTE CONEXA DE LOS NODOS SELECCIONADOS * */ -NetworkWidget.prototype.selectConnectedComponent = function() { - var elements = this.getConnectedComponent(); - this.selectVerticesById(elements.nodes); - this.graphCanvas.selectEdges(elements.edges); -}; - -NetworkWidget.prototype.getConnectedComponent = function() { - var nodosVisitados = {}; - var aristasVisitadas = {}; - var arrNodos = []; - var arrAristas = []; - var selectedGraphNodesId = this.getGraphCanvas().getSelectedVertices(); - for ( var i = 0; i < selectedGraphNodesId.length; i++) { - this.visitNode(selectedGraphNodesId[i], nodosVisitados, aristasVisitadas, arrNodos, arrAristas); - } - return { - nodes : arrNodos, - edges : arrAristas - }; -}; - -NetworkWidget.prototype.visitNode = function(nodeId, nodosVisitados, aristasVisitadas, arrNodos, arrAristas) { - nodosVisitados[nodeId] = true; - arrNodos.push(nodeId); - var nodeEdges = this.getDataset().getVertexById(nodeId).getEdges(); - for ( var j = 0; j < nodeEdges.length; j++) { - var edge = nodeEdges[j]; - var edgeId = edge.getId(); - if (aristasVisitadas[edgeId] == null) { - aristasVisitadas[edgeId] = true; - arrAristas.push(edgeId); - var nodeTargetId = edge.getNodeTarget().getId(); - if (nodosVisitados[nodeTargetId] == null) { - this.visitNode(nodeTargetId, nodosVisitados, aristasVisitadas, arrNodos, arrAristas); - } - var nodeSourceId = edge.getNodeSource().getId(); - if (nodosVisitados[nodeSourceId] == null) { - this.visitNode(nodeSourceId, nodosVisitados, aristasVisitadas, arrNodos, arrAristas); - } - } - } -}; - -/** COLLAPSE SELECTED VERTICES * */ -NetworkWidget.prototype.collapse = function() { - var selectedVertices = this.getGraphCanvas().getSelectedVertices(); - var xMin = -Infinity; - var xMax = Infinity; - var yMin = -Infinity; - var yMax = Infinity; - - for ( var i = 0; i < selectedVertices.length; i++) { - var vertex = this.getGraphCanvas().getLayout().getNodeById(selectedVertices[i]); - if (xMin < vertex.x) { - xMin = vertex.x; - } - if (xMax > vertex.x) { - xMax = vertex.x; - } - if (yMin < vertex.y) { - yMin = vertex.y; - } - if (yMax > vertex.y) { - yMax = vertex.y; - } - } - - var centerX = xMin - xMax; - var centerY = yMin - yMax; - var radius = (xMax - xMin) / 4; - - var count = selectedVertices.length; - var verticesCoordinates = []; - - for ( i = 0; i < selectedVertices.length; i++) { - x = centerX + radius * Math.sin(i * 2 * Math.PI / count); - y = centerY + radius * Math.cos(i * 2 * Math.PI / count); - verticesCoordinates.push({ - 'x' : x, - 'y' : y - }); - } - - for ( i = 0; i < selectedVertices.length; i++) { - this.getGraphCanvas().getLayout().getNodeById(selectedVertices[i]).setCoordinates(verticesCoordinates[i].x, verticesCoordinates[i].y); - } -}; - -/** SETTER FONT SIZE * */ -NetworkWidget.prototype.setVerticesFontSize = function(value) { - for ( var nodeId in this.getDataset().getVertices()) { - if (this.getDataset().getVertices().hasOwnProperty(nodeId)) { - this.getFormatter().getVertexById(nodeId).getDefault().setFontSize(value); - } - } -}; - -/** GETTERS * */ -NetworkWidget.prototype.getFormatter = function() { - return this.getGraphCanvas().getFormatter(); -}; -NetworkWidget.prototype.getLayout = function() { - return this.getGraphCanvas().getLayout(); -}; - -NetworkWidget.prototype.getDataset = function() { - return this.getGraphCanvas().getDataset(); -}; - -NetworkWidget.prototype.getGraphCanvas = function() { - return this.graphCanvas; -}; - -RangeWhiskerGraph.prototype.cleanArray = GenericGraph.prototype.cleanArray; -RangeWhiskerGraph.prototype.plotAxes = GenericGraph.prototype.plotAxes; -RangeWhiskerGraph.prototype.plotRuler = GenericGraph.prototype.plotRuler; -RangeWhiskerGraph.prototype.drawSVGVerticalText = GenericGraph.prototype.drawSVGVerticalText; -RangeWhiskerGraph.prototype.getClassColor = GenericGraph.prototype.getClassColor; -RangeWhiskerGraph.prototype.getDimensions = GenericGraph.prototype.getDimensions; -RangeWhiskerGraph.prototype.calculate = GenericGraph.prototype.calculate; -RangeWhiskerGraph.prototype.getMedian = GenericGraph.prototype.getMedian; -RangeWhiskerGraph.prototype.pointToPixel = GenericGraph.prototype.pointToPixel; -RangeWhiskerGraph.prototype.maxValueWhisker = GenericGraph.prototype.maxValueWhisker; -RangeWhiskerGraph.prototype.refresh = GenericGraph.prototype.refresh; - -/** - * Subclass of GenericGraph - * - * @plotHorizontalByCluster - */ -function RangeWhiskerGraph(args) { - this.maxBoxWidth = 25; - - if (args == null) { - args = {}; - } - args.plotHorizontalByCluster = false; - - GenericGraph.call(this, args); - - if (args.maxBoxWidth != null) { - this.maxBoxWidth = args.maxBoxWidth; - } -} - -RangeWhiskerGraph.prototype.refresh = function(data) { - document.getElementById(this.targetId).innerHTML = ""; - this.draw(this.targetId, data); -}; - -RangeWhiskerGraph.prototype.isNumber = function(value) { - if (value == "") - return false; - - var d = parseInt(value); - if (!isNaN(d)) - return true; - else - return false; - -}; - -/** - There are several different methods for calculating quartiles.[1] This calculator uses a method described by Moore and McCabe to find quartile values. - The same method also used by The TI-83 to calculate quartile values. - With this method, the first quartile is the median of the numbers below the median, the third quartile is the median of the numbers above the median. - - http://www.miniwebtool.com/quartile-calculator/ - http://www.alcula.com/calculators/statistics/box-plot/ - - **/ -RangeWhiskerGraph.prototype.getQ1 = function(array) { - array = array.slice(0, array.length / 2); - return this.getMedian(array); -}; - -RangeWhiskerGraph.prototype.getQ3 = function(array) { - array = array.slice((array.length + 1) / 2); - return this.getMedian(array); - -}; - -RangeWhiskerGraph.prototype.getQ2 = function(array) { - return this.getMedian(array); -}; - -RangeWhiskerGraph.prototype.getBelowOutliers = function(belowLimit, array) { - var points = []; - for ( var i = 0; i < array.length; i++) { - if (array[i] <= belowLimit) { - points.push(array[i]); - } - } - return points; -}; - -RangeWhiskerGraph.prototype.getAboveOutliers = function(aboveLimit, array) { - var points = []; - for ( var i = 0; i < array.length; i++) { - if (array[i] >= aboveLimit) { - points.push(array[i]); - } - } - return points; -}; - -RangeWhiskerGraph.prototype.minValueWhisker = function(belowLimit, q1, array) { - var points = []; - - for ( var i = 0; i < array.length; i++) { - if ((array[i] < q1) && (array[i]) > belowLimit) { - points.push(array[i]); - } - } - - if (points.length > 0) { - points.sort(function(a, b) { - return a - b; - }); - return points[0]; - } - return null; -}; - -//RangeWhiskerGraph.prototype.maxValueWhisker = function(aboveLimit, q3, array){ -// var points = []; -// for (var i = 0; i < array.length; i++){ -// if ((array[i] > q3) && (array[i]) <= aboveLimit){ -// points.push(array[i]); -// } -// } -// if (points.length > 0){ -// points.sort(function(a, b){return a - b;}); -// return points[points.length - 1]; -// } -// return null; -//}; - -RangeWhiskerGraph.prototype.drawPoints = function(boxProperties) { - if (this.plotPoints) { - for ( var i = 0; i < boxProperties.values.length; i++) { - var value = boxProperties.values[i]; - var toPixel = this.pointToPixel(value, boxProperties); - SVG.drawCircle(boxProperties.x, toPixel, this.pointRadius, this.svg, - [ - [ "fill", "green" ], [ "fill-opacity", this.fillOpacityPoint ], [ 'stroke-opacity', this.strokeOpacityPoint ], - [ "stroke", "black" ] ]); - } - } -}; - -RangeWhiskerGraph.prototype.plotRangeQ1Q3 = function(data, properties) { - var posX = this.left + this.rulerWidth; - - var boxBordersPointsQ1 = []; - var boxBordersPointsQ3 = []; - var Q2 = []; - - for ( var i = 0; i < data.clusters.length; i++) { - var cluster = data.clusters[i]; - /** inter cluster space **/ - posX = posX + this.interClustersSpace; - - for ( var j = 0; j < cluster.classes.length; j++) { - var ratio = properties.width / (properties.xValues.range); - var coorX = (cluster.x - properties.xValues.min) * ratio + this.rulerWidth; - var boxProperties = { - name : cluster.classes[j].name, - values : cluster.classes[j].values, - minPoint : properties.minPoint, - maxPoint : properties.maxPoint, - x : coorX + this.left, - y : this.top, - width : properties.classWidth, - height : this.height - this.top - this.bottom - this.clusterTitleHeight - this.rulerHeight - }; - - var result = this.calculate(boxProperties.values); - var boxColor = this.getClassColor(boxProperties.name); - - if (this.isNumber(result.Q1) && this.isNumber(result.Q3)) { - var x = boxProperties.x; - var y = this.pointToPixel(result.Q1, boxProperties); - if (boxBordersPointsQ1[cluster.classes[j].name] == null) { - boxBordersPointsQ1[cluster.classes[j].name] = []; - } - boxBordersPointsQ1[cluster.classes[j].name].push({ - x : x, - y : y - }); - - x = boxProperties.x; - y = this.pointToPixel(result.Q3, boxProperties); - if (boxBordersPointsQ3[cluster.classes[j].name] == null) { - boxBordersPointsQ3[cluster.classes[j].name] = []; - } - boxBordersPointsQ3[cluster.classes[j].name].push({ - x : x, - y : y - }); - } else { - - if (this.isNumber(result.Q2)) { - - if (boxBordersPointsQ1[cluster.classes[j].name] == null) { - boxBordersPointsQ1[cluster.classes[j].name] = []; - } - - if (boxBordersPointsQ3[cluster.classes[j].name] == null) { - boxBordersPointsQ3[cluster.classes[j].name] = []; - } - - boxBordersPointsQ1[cluster.classes[j].name].push({ - x : boxProperties.x, - y : this.pointToPixel(result.Q2, boxProperties) - }); - boxBordersPointsQ3[cluster.classes[j].name].push({ - x : boxProperties.x, - y : this.pointToPixel(result.Q2, boxProperties) - }); - } - } - } - } - for ( var classe in boxBordersPointsQ1) { - var points = boxBordersPointsQ1[classe]; - var pointsSVG = ""; - for (var k = 0; k < points.length; k++) { - pointsSVG = Number(points[k].x).toFixed(1) + "," + Number(points[k].y).toFixed(1) + " " + pointsSVG; - } - - points = boxBordersPointsQ3[classe]; - for (var z = points.length - 1; z >= 0; z--) { - pointsSVG = Number(points[z].x).toFixed(1) + "," + Number(points[z].y).toFixed(1) + " " + pointsSVG; - } - - SVG.drawPoligon(pointsSVG, this.svg, [ - [ "fill", this.getClassColor(classe) ], [ "opacity", "0.3" ], [ "stroke", "black" ], [ "stroke-width", 1 ] ]); - } -}; - -RangeWhiskerGraph.prototype.plotWhisters = function(data, properties) { - var colors = [ "yellow", "orange", "green" ]; - - this.plotRangeQ1Q3(data, properties); - var Q2 = {}; - - for ( var i = 0; i < data.clusters.length; i++) { - var cluster = data.clusters[i]; - for ( var j = 0; j < cluster.classes.length; j++) { - var ratio = properties.width / (properties.xValues.range); - var coorX = (cluster.x - properties.xValues.min) * ratio + this.left + this.rulerWidth - this.rulerStroke; - var boxProperties = { - name : cluster.classes[j].name, - values : cluster.classes[j].values, - minPoint : properties.minPoint, - maxPoint : properties.maxPoint, - x : coorX, - y : this.top, - width : properties.classWidth, - height : this.height - this.top - this.bottom - this.clusterTitleHeight - this.rulerHeight - }; - - this.drawPoints(boxProperties); - - /** PLOTTING Q2 **/ - var result = this.calculate(boxProperties.values); - var boxColor = this.getClassColor(boxProperties.name); - if (this.isNumber(result.Q2)) { - if (Q2[boxProperties.name] != null) { - SVG.drawLine(boxProperties.x, this.pointToPixel(result.Q2, boxProperties), Q2[boxProperties.name].x, Q2[boxProperties.name].y, - this.svg, [ [ "stroke", boxColor ], [ "stroke-width", "2" ] ]); - } - Q2[boxProperties.name] = { - x : boxProperties.x, - y : this.pointToPixel(result.Q2, boxProperties) - } - } - } - } - -}; - -RangeWhiskerGraph.prototype.draw = function(targetId, data) { - //this.calculate(data); - this.targetId = targetId; - var properties = (this.getDimensions(data)); - this.svg = SVG.createSVGCanvas(document.getElementById(this.targetId), [ [ "width", this.width ], [ "height", this.height ] ]); - this.plotAxes(properties); - this.plotWhisters(data, properties); -}; - -RangeWhiskerGraph.prototype.input = function() { - return DATADOC.getBoxWhikerData(); -}; - -RangeWhiskerGraph.prototype.test = function(targetId) { - var plot = new RangeWhiskerGraph({ - targetId : targetId, - height : 350, - width : 450, - maxBoxWidth : 25 - }); - plot.refresh(this.input()); -}; - -StdDevDyGraph.prototype.dblclick = DygraphWidget.prototype.dblclick; -StdDevDyGraph.prototype._createHTLMWrapper = DygraphWidget.prototype._createHTLMWrapper; -StdDevDyGraph.prototype.draw = DygraphWidget.prototype.draw; - -function StdDevDyGraph(targetId, args) { - this.scaled = false; - if (args == null) { - args = {}; - } - args.customBars = true; - DygraphWidget.prototype.constructor.call(this, targetId, args); -} - -StdDevDyGraph.prototype.input = function () { - return { - data : [ [ 1, [ 2, 3, 3.5 ], [ 4, 4.2, 5 ] ], [ 2, [ 5, 5.5, 5.7 ], [ 4, 4.2, 5 ] ] ], - colors : [ "blue", "red" ], - labels : [ "", 'data1', 'data2' ] - }; -}; - -StdDevDyGraph.prototype.test = function (targetId) { - var dygraphObject = new StdDevDyGraph(targetId, { - width : 500, - height : 400, - xlabel : "xLabel", - showRangeSelector : false - }); - - dygraphObject.draw(dygraphObject.input().data, dygraphObject.input().colors, dygraphObject.input().labels); -}; - - - -function AbinitioGrid(args) { - this.height = null; - this.width = null; - this.id = BUI.id(); - - if (args != null) { - if (args.height != null) { - this.height = args.height; - - if (args.width != null) { - this.width = args.width; - } - } - } - - this.onSelected = new Event(this); -}; - - -AbinitioGrid.prototype.refresh = function(subtractions){ - this.store.loadData(this._prepareData(subtractions)); -}; - -AbinitioGrid.prototype._prepareData = function(subtractions){ - /** Parsing data * */ - var models = []; - for (var l = 0; l < subtractions.length; l++) { - var subtraction = subtractions[l]; - for (var k = 0; k < subtraction.substractionToAbInitioModel3VOs.length; k++) { - var data = subtraction.substractionToAbInitioModel3VOs[k].abinitiomodel3VO; - if (data.averagedModel != null) { - models.push(data.averagedModel); - models[models.length - 1].type = "Reference"; - } - - if (data.shapeDeterminationModel != null) { - models.push(data.shapeDeterminationModel); - models[models.length - 1].type = "Refined"; - } - - if (data.modelList3VO != null) { - if (data.modelList3VO.modeltolist3VOs != null) { - for (var i = 0; i < data.modelList3VO.modeltolist3VOs.length; i++) { - models.push(data.modelList3VO.modeltolist3VOs[i].model3VO); - models[models.length - 1].type = "Model"; - } - } - } - } - } - return models; -}; - -AbinitioGrid.prototype.getPanel = function(){ - var _this = this; - - - var modelFields = [ "modelId", "type", "chiSqrt", "dmax", "firFile", "logFile", "fitFile", "pdbFile", "rfactor", "rg", "volume" ]; - Ext.define('AbinitioModel', { - extend : 'Ext.data.Model', - fields : modelFields - - }); - - /** - * Store in Memory - */ - this.store = Ext.create('Ext.data.Store', { - model : 'AbinitioModel', - autoload : true, - groupField : 'type' - }); - - - var groupingFeature = Ext.create('Ext.grid.feature.Grouping',{ - groupHeaderTpl: '{name} ({rows.length} model{[values.rows.length > 1 ? "s" : ""]})', - startCollapsed: true, - collapsible : true - }); - - this.grid = Ext.create('Ext.grid.Panel', { - collapsible : false, - resizable : true, - features: [groupingFeature], - autoscroll : true, - multiSelect : true, - store : this.store, - height : this.height, - width : this.width, - margin : 10, - columns : [ { - text : "Type", - dataindex : "type", - hidden : true, - renderer : function(a, b, record) { - return record.data.type; - }, - flex : 1 - }, - { - text : "ModelId", - dataindex : "modelId", - hidden : true, - renderer : function(a, b, record) { - return record.data.modelId; - - }, - flex : 1 - }, - - { - text : "chiSqrt", - dataindex : "chiSqrt", - renderer : function(a, b, record) { - if (record.data.dmax != null) { - return BUI.formatValuesUnits(record.data.chiSqrt, "", 12, this.decimals); - } - - }, - flex : 1 - }, - { - text : "Dmax", - dataindex : "dmax", - renderer : function(a, b, record) { - if (record.data.dmax != null) { - return BUI.formatValuesUnits(record.data.dmax, "nm", 12, this.decimals); - } - - }, - flex : 1 - }, { - text : "rFactor", - dataindex : "rfactor", - hidden : true, - renderer : function(a, b, record) { - if (record.data.rfactor != null) { - return record.data.rfactor; - } - }, - flex : 1 - }, { - text : "Rg", - dataindex : "rg", - renderer : function(a, b, record) { - if (record.data.rg != null) { - return BUI.formatValuesUnits(record.data.rg, "nm", 12, this.decimals); - } - - }, - flex : 1 - }, - { - text : "Volume", - dataindex : "volume", - renderer : function(a, b, record) { - if (record.raw.volume != null){ - return BUI.formatValuesUnits(record.raw.volume, '') + " nm3"; - } - }, - flex : 1 - }, - { - text : "PDB", - dataindex : "pdbFile", - renderer : function(a, b, record) { - if (record.data.pdbFile != null){ - return record.data.pdbFile.split("/")[record.data.pdbFile.split("/").length - 1]; - } - }, - flex : 1 - }, { - text : "Fir", - dataindex : "firFile", - renderer : function(a, b, record) { - if (record.data.firFile != null){ - return record.data.firFile.split("/")[record.data.firFile.split("/").length - 1]; - } - }, - flex : 1 - }, { - text : "LOG", - dataindex : "logFile", - hidden : true, - renderer : function(a, b, record) { - if (record.data.logFile != null){ - return record.data.logFile.split("/")[record.data.logFile.split("/").length - 1]; - } - }, - flex : 1 - } - ], - viewConfig : { - enableTextSelection : true, - preserveScrollOnRefresh : true, - stripeRows : true, - listeners : { - 'celldblclick' : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { - }, - 'cellclick' : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { - var models = []; - for (var i = 0; i < grid.getSelectionModel().selected.items.length; i++) { - models.push(grid.getSelectionModel().selected.items[i].raw); - } - _this.onSelected.notify(models); - } - } - } - }); - return this.grid; - -}; - - -function AdditiveGrid(args) { - this.onRemoveButtonClicked = new Event(this); -} - -AdditiveGrid.prototype.getBuffer = function() { - return this.buffer; -}; - -AdditiveGrid.prototype._getActions = function() { - var _this = this; - /** Actions buttons **/ - var actions = []; - - actions.push(Ext.create('Ext.Action', { - icon : '../images/add.png', - text : 'Add', - disabled : true, - alwaysEnabled : true, - handler : function(widget, event) { - _this.buffer.bufferhasadditive3VOs.push(BIOSAXS_BEANS.getBufferhasAdditive3VO()); - _this.refresh(_this.buffer, _this.experiment); - } - })); - - return actions; -}; - -AdditiveGrid.prototype.refresh = function(buffer, experiment) { - this.buffer = buffer; - this.experiment = experiment; - if (buffer) { - if (buffer.bufferhasadditive3VOs) { - this.features = buffer.bufferhasadditive3VOs; - } - } - this.experiment = experiment; - this.store.loadData(this._prepareData(), false); -}; - -AdditiveGrid.prototype.getAdditives = function() { - var additives = []; - for ( var i = 0; i < this.store.getCount(); i++) { - var bufferHasAdditive = BIOSAXS_BEANS.getBufferhasAdditive3VO(); - var data = this.store.getAt(i).getData(); - - bufferHasAdditive.additive3VO.name = data.name; - bufferHasAdditive.additive3VO.comments = data.comments; - bufferHasAdditive.additive3VO.additiveType = data.additiveType; - - bufferHasAdditive.bufferId = this.buffer.bufferId; - bufferHasAdditive.bufferHasAdditiveId = data.bufferHasAdditiveId; - bufferHasAdditive.quantity = data.quantity; - additives.push(bufferHasAdditive); - } - - return additives; -}; - -AdditiveGrid.prototype._getFields = function(buffers) { - var columns = [ - - { - header : 'Name', - dataIndex : 'name', - type : 'string', - editor : { - allowBlank : true - }, - flex : 1 - }, - { - header : 'Type', - name : 'additiveType', - dataIndex : 'additiveType', - editor : { - xtype : 'combobox', - typeAhead : true, - triggerAction : 'all', - selectOnTab : true, - store : BIOSAXS.proposal.getAdditiveTypes(), - lazyRender : true, - listClass : 'x-combo-list-small' - }, - flex : 0.6 - }, - { - header : 'Quantity', - dataIndex : 'quantity', - name : 'quantity', - editor : { - allowBlank : true - }, - type : 'string', - flex : 1 - }, - { - xtype : 'actioncolumn', - items : [ { - icon : '../images/cancel.png', - tooltip : 'Delete additive', - scope : this, - handler : function(grid, rowIndex, colIndex) { - if ((grid.getStore().getAt(rowIndex).data.bufferHasAdditiveId == null)|| (grid.getStore().getAt(rowIndex).data.bufferHasAdditiveId == "")) { - grid.getStore().removeAt(rowIndex); - } else { - this.onRemoveButtonClicked.notify({ - 'bufferId' : this.buffer.bufferId, - 'bufferHasAdditiveId' : this.store.getAt(rowIndex).data.bufferHasAdditiveId - }); - } - } - } ] - } ]; - - return columns; -}; - -AdditiveGrid.prototype._prepareData = function() { - var data = []; - if (this.features == null) { - this.features = []; - } - for (var i = 0; i < this.features.length; i++) { - var object = this.features[i]; - object.name = this.features[i].additive3VO.name; - object.additiveType = this.features[i].additive3VO.additiveType; - object.comments = this.features[i].additive3VO.comments; - object.additiveId = this.features[i].additive3VO.additiveId; - data.push(object); - } - return data; -}; - -AdditiveGrid.prototype.getPanel = function(buffer, experiment) { - this.buffer = buffer; - this.features = buffer.bufferhasadditive3VOs; - this.experiment = experiment; - return this._renderGrid(); -}; - -AdditiveGrid.prototype.getStore = function() { - var _this = this; - var store = Ext.create('Ext.data.Store', { - fields : [ "name", "additiveType", "comments", "additiveId", "bufferHasAdditiveId", "quantity" ], - autoload : false, - data : this._prepareData(), - listeners : { - update : function(store, record) { - record.index = _this.grid.getSelectionModel().getCurrentPosition().row; - _this.buffer.bufferhasadditive3VOs[record.index].additive3VO.name = record.data.name; - _this.buffer.bufferhasadditive3VOs[record.index].additive3VO.additiveType = record.data.additiveType; - _this.buffer.bufferhasadditive3VOs[record.index].additive3VO.comments = record.data.comments; - _this.buffer.bufferhasadditive3VOs[record.index].quantity = record.data.quantity; - } - } - }); - - // store.sort('bufferHasAdditiveId', 'ASC'); - store.loadData(this._prepareData(), false); - return store; -}; - -AdditiveGrid.prototype._renderGrid = function() { - var _this = this; - this.store = this.getStore(); - - var cellEditing = Ext.create('Ext.grid.plugin.CellEditing', { - clicksToEdit : 1 - }); - - this.grid = Ext.create('Ext.grid.Panel', { - dockedItems : [ { - xtype : 'toolbar', - items : this._getActions() - } ], - store : this.store, - height : 230, - title : "Additives", - width : "100%", - columns : _this._getFields(), - plugins : [ cellEditing ], - viewConfig : { - stripeRows : true, - listeners : { - itemcontextmenu : function(view, rec, node, index, e) { - e.stopEvent(); - contextMenu.showAt(e.getXY()); - return false; - } - } - }, - selModel : { - mode : 'SINGLE' - } - }); - - return this.grid; -}; - -AdditiveGrid.prototype.input = function() { -}; - -AdditiveGrid.prototype.test = function(targetId) { - var grid = new AdditiveGrid(); - var panel = grid.getPanel([]); - panel.render(targetId); - -}; - -/** AnalysisGrid **/ -function AnalysisGrid(args) { - var _this = this; - - this.id = BUI.id(); - if (Ext.get("mainPanel")){ - this.width = Ext.get("mainPanel").getWidth(); - } - else{ - this.width = this.maxWidth; - } - this.maxWidth = 1500; - - /** Visibles **/ - this.isGuinierTabVisible = true; - this.isGnomTabVisible = true; - this.isPorodTabVisible = true; - this.isScatteringPlotVisible = true; - this.isAbinitioTabVisible = true; - this.showButtonsVisible = true; - this.isI0Visible = true; - - this.margin = null; - this.grouped = true; - - this.hideNulls = false; - this.decimals = 4; - this.height = null; - this.sorters = [ { - property : 'conc', - direction : 'ASC' - } ]; - - if (args != null) { - if (args.grouped != null) { - this.grouped = args.grouped; - } - - if (args.showButtonsVisible != null) { - this.showButtonsVisible = args.showButtonsVisible; - } - if (args.isI0Visible != null) { - this.isI0Visible = args.isI0Visible; - } - if (args.sorters != null) { - this.sorters = args.sorters; - } - if (args.isScatteringPlotVisible != null) { - this.isScatteringPlotVisible = args.isScatteringPlotVisible; - } - if (args.isGuinierTabVisible != null) { - this.isGuinierTabVisible = args.isGuinierTabVisible; - } - if (args.isGnomTabVisible != null) { - this.isGnomTabVisible = args.isGnomTabVisible; - } - if (args.isPorodTabVisible != null) { - this.isPorodTabVisible = args.isPorodTabVisible; - } - if (args.hideNulls != null) { - this.hideNulls = args.hideNulls; - } - if (args.height != null) { - this.height = args.height; - } - if (args.width != null) { - this.width = args.width; - } - if (args.maxWidth != null) { - this.maxWidth = args.maxWidth; - } - } -} - -AnalysisGrid.prototype.refresh = function(data, args) { - this.store.loadData(this._prepareData(data), false); - if (args != null){ - if (args.experiment != null){ - this.experiment = args.experiment; - } - } -}; - -AnalysisGrid.prototype._getPorod = function() { - return { - text : 'Porod', - name : 'Porod', - columns : [ - { - text : 'Volume', - dataIndex : 'volumeEdna', - width : 75, - sortable : true, - hidden : !this.isPorodTabVisible, - renderer : function(val, y, sample) { - if (sample.raw.volume != null) - return BUI.formatValuesUnits(sample.raw.volume, '') + " nm3"; - } - }, - { - text : 'MM Vol. est.', - dataIndex : 'volumeEdna', - tooltip : '[Volume/2 - Volume/1.5] (Guinier)', - sortable : true, - width : 95, - hidden : !this.isPorodTabVisible, - renderer : function(val, y, sample) { - if (sample.raw.volume != null) - return Number(sample.raw.volume / 2).toFixed(1) + " - " + Number(sample.raw.volume / 1.5).toFixed(1)+ "kD"; - } - } ] - }; -}; - - -AnalysisGrid.prototype._getFramesColumn = function() { - var _this = this; - return { - text : 'Frames (Averaged/Total)', - dataIndex : 'datacollection', - name : 'datacollection', - sortable : true, - width : 150, - renderer : function(dataCollections, y, data) { - /** Bug of Webservices: frames count were swapped with frames averages **/ - if (data.raw.bufferAfterFramesCount){ - if (data.raw.bufferAfterFramesMerged){ - if (parseInt(data.raw.bufferAfterFramesMerged) > parseInt(data.raw.bufferAfterFramesCount)){ - var aux = parseInt(data.raw.bufferAfterFramesCount); - data.raw.bufferAfterFramesCount= data.raw.bufferAfterFramesMerged; - data.raw.bufferAfterFramesMerged = aux; - } - } - } - - if (data.raw.bufferBeforeFramesCount){ - if (data.raw.bufferBeforeFramesMerged){ - if (parseInt(data.raw.bufferBeforeFramesMerged) > parseInt(data.raw.bufferBeforeFramesCount)){ - var aux = parseInt(data.raw.bufferBeforeFramesCount); - data.raw.bufferBeforeFramesCount= data.raw.bufferBeforeFramesMerged; - data.raw.bufferBeforeFramesMerged = aux; - } - } - - } - - var bufferAcronym = data.raw.bufferAcronym; - var macromoleculeAcronym = data.raw.macromoleculeAcronym; - var bbmerges = data.raw.bufferBeforeFramesMerged; - var molmerges = data.raw.framesMerge; - var bamerges = data.raw.bufferAfterFramesMerged; - var totalframes = data.raw.framesCount; - var bufferId = data.raw.bufferId; - var macromoleculeId = data.raw.macromoleculeId; - var macromoleculeColor = null; - if (_this.experiment != null){ - macromoleculeColor = _this.experiment.macromoleculeColors[data.raw.macromoleculeId]; - } - - /** BUG in the database to be fixed **/ - try{ - if (totalframes != null){ - if(molmerges != null){ - if (parseFloat(totalframes) < parseFloat(molmerges)){ - var aux = totalframes; - totalframes = molmerges; - molmerges = aux; - } - } - } - } - catch(e){ - - } - - return BUI.getHTMLTableForFrameAveraged(bufferAcronym, - macromoleculeAcronym, - bbmerges, - molmerges, - bamerges, - totalframes, - bufferId, - macromoleculeId, - macromoleculeColor); - } - }; -}; - -AnalysisGrid.prototype._getColumns = function() { - var _this = this; - return [ - { - name : 'groupeField', - dataIndex : 'groupeField', - hidden : true - - }, - { - "header" : "subtractionId", - hidden : true, - "dataIndex" : "subtractionId", - "name" : "subtractionId" - }, - { - header : "Macromolecule", - dataIndex : "macromoleculeAcronym", - name : "macromoleculeAcronym", - renderer : function(val, y, sample) { - return '
' + val + '
' + - BUI.formatValuesUnits(sample.raw.conc, "mg/ml", 10, this.decimals) + '
' + - BUI.formatValuesUnits(sample.raw.exposureTemperature, "C", 10, this.decimals) + '
' - } - }, - { - header : "Order", - dataIndex : "priorityLevelId", - name : "priorityLevelId", - hidden : true - }, - { - header : "Code", - dataIndex : "code", - name : "code", - hidden : true - }, - { - header : "Conc.", - dataIndex : "conc", - width : 80, - name : "conc", - type : "number", - hidden : true, - renderer : function(val, y, sample) { - return BUI.formatValuesUnits(val, "mg/ml", 10, this.decimals); - } - }, - { - text : 'Scattering', - width : 100, - dataIndex : 'subtractionId', - name : 'subtractionId', - hidden : !this.isScatteringPlotVisible, - renderer : function(val, y, sample) { - var url = BUI.getURL() + '&type=scattering&subtractionId=' + sample.raw.subtractionId; - var event = "OnClick= window.open('" + url + "')"; - return ''; - } - }, - { - text : 'Kratky', - width : 100, - dataIndex : 'subtractionId', - hidden : true, - name : 'subtractionId', - renderer : function(val, y, sample) { - var url = BUI.getURL() + '&type=kratky&subtractionId=' + sample.raw.subtractionId; - var event = "OnClick= window.open('" + url + "')"; - return ''; - } - }, - this._getFramesColumn(), - { - text : 'File Name', - dataIndex : 'averageFilePath', - name : 'averageFilePath', - sortable : true, - width : 150, - hidden : true, - renderer : function(dataCollections, y, data) { - return BUI.getHTMLTableForPrefixes(data.raw.bufferBeforeAverageFilePath, data.raw.averageFilePath, - data.raw.bufferAfterAverageFilePath); - } - }, - - { - text : 'Guinier', - name : 'Guinier', - columns : [ - { - text : 'Rg', - dataIndex : 'rgGuinier', - name : 'rgGuinier', - hidden : !this.isGuinierTabVisible, - width : 75, - tooltip : 'In polymer physics, the radius of gyration is used to describe the dimensions of a polymer chain.', - sortable : true, - renderer : function(val, y, sample) { - if (sample.raw.rgGuinier != null) { - /** Show warning if rgGuinier and rgGnom differ more than 10% **/ - if (Math.abs(sample.raw.rgGuinier - sample.raw.rgGnom) > (sample.raw.rgGuinier * 0.1)) { - return "" + BUI.formatValuesUnits(sample.raw.rgGuinier, "") + ""; - - } - return BUI.formatValuesUnits(sample.raw.rgGuinier, "nm", 12, this.decimals); - } - } - }, - { - text : 'Points', - dataIndex : 'points', - sortable : true, - width : 100, - type : 'string', - hidden : !this.isGuinierTabVisible, - renderer : function(val, y, sample) { - if ((sample.raw.firstPointUsed == "") || (sample.raw.firstPointUsed == null)) - return; - return "" + sample.raw.firstPointUsed + " - " + sample.raw.lastPointUsed + " (" + - (sample.raw.lastPointUsed - sample.raw.firstPointUsed) + " )"; - } - }, - { - text : 'Quality', - dataIndex : 'quality', - hidden : !this.isGuinierTabVisible, - tooltip : 'Estimated data quality. 1.0 - means ideal quality, 0.0 - unusable data. In table format it is given in percent (100% - ideal quality, 0% - unusable data). Please note that this estimation is based only on the Guinier interval (very low angles).', - width : 60, - sortable : true, - renderer : function(val, y, sample) { - if (sample.raw.quality != null) { - val = sample.raw.quality; - if ((val != null) && (val != "")) { - return "" + (Number(val) * 100).toFixed(2) + " %"; - } - } - } - }, { - text : 'I(0)', - dataIndex : 'I0', - sortable : true, - hidden : !this.isI0Visible, - tooltip : 'Extrapolated scattering intensity at zero angle I(0) (forward scattering)', - width : 75, - type : 'string', - renderer : function(val, y, sample) { - if (sample.raw.I0 != null) { - return BUI.formatValuesErrorUnitsScientificFormat(sample.raw.I0, sample.raw.i0stdev, ""); - } - } - }, { - text : 'Aggregated', - tooltip : "If aggregation was detected from the slope of the data curve at low angles the value is '1', otherwise '0'.", - dataIndex : 'isagregated', - hidden : true, - width : 75, - renderer : function(val, y, sample) { - if ((sample.raw.isagregated != null)) { - if (val == true) { - return "Yes"; - } else { - return "No"; - } - } - } - }, { - text : 'Guinier', - sortable : true, - dataIndex : 'subtractionId', - type : 'string', - width : 100, - hidden : true, - renderer : function(val, y, sample) { - - if (sample.raw.subtractionId != null) { - var url = BUI.getURL() + '&type=guinier&subtractionId=' + sample.raw.subtractionId; - var event = "OnClick= window.open('" + url + "')"; - return ''; - } - } - } ] - }, - { - text : 'Gnom', - name : 'Gnom', - - columns : [ { - text : 'Rg', - dataIndex : 'rgGnom', - type : 'string', - width : 65, - hidden : !this.isGnomTabVisible, - sortable : true, - renderer : function(val, y, sample) { - /** Show warning if rgGuinier and rgGnom differ more than 10% **/ - if (sample.raw.rgGnom != null) { - if (Math.abs(sample.raw.rgGuinier - sample.raw.rgGnom) > (sample.raw.rgGuinier * 0.1)) { - return "" + BUI.formatValuesUnits(sample.raw.rgGnom, "") + ""; - - } - return BUI.formatValuesUnits(sample.raw.rgGnom, "nm"); - } - } - }, { - text : 'Total', - dataIndex : 'total', - width : 65, - hidden : !this.isGnomTabVisible, - sortable : true, - renderer : function(val, y, sample) { - if (sample.raw.total != null) - return BUI.formatValuesUnits(sample.raw.total, ''); - } - }, { - text : 'Dmax', - dataIndex : 'dmax', - sortable : true, - width : 75, - hidden : !this.isGnomTabVisible, - renderer : function(val, y, sample) { - if (sample.raw.dmax != null) - return BUI.formatValuesUnits(sample.raw.dmax, "") + " nm"; - } - }, { - text : 'P(r)', - sortable : true, - hidden : true, - width : 100, - dataIndex : 'subtractionId', - type : 'string', - renderer : function(val, y, sample) { - var url = BUI.getURL() + '&type=gnom&subtractionId=' + sample.raw.subtractionId; - var event = "OnClick= window.open('" + url + "')"; - return ''; - } - } ] - }, - this._getPorod(), - { - text : 'AbInitio Modeling', - name : 'AbInitio_modeling', - - columns : [ - { - text : 'NSD', - dataIndex : 'volumeEdna', - width : 100, - sortable : true, - hidden : true, - renderer : function(val, y, sample) { - var url = BUI.getNSDImageURL(sample.raw.modelListId); - var event = "OnClick= window.open('" + url + "')"; - return ''; - } - }, - { - text : 'Chi2', - dataIndex : 'volumeEdna', - sortable : true, - hidden : true, - width : 100, - renderer : function(val, y, sample) { - var url = BUI.getCHI2ImageURL(sample.raw.modelListId); - var event = "OnClick= window.open('" + url + "')"; - return ''; - } - }, - { - text : 'Pdb', - dataIndex : 'volumeEdna', - tooltip : 'Damaver: The program suite DAMAVER is a set of programs to align ab initio models, select the most typical one and build an averaged model. Dammif : Rapid ab initio shape determination by simulated annealing using a single phase dummy atom model. Dammin :Ab initio shape determination by simulated annealing using a single phase dummy atom model', - sortable : true, - width : 125, - hidden : !this.isAbinitioTabVisible, - renderer : function(val, y, sample) { - var html = new String(); - var split = null; - var url = null; - var file = sample.raw.averagedModel; - if (file != null) { - split = file.split("/"); - if (split.length > 0) { - url = BUI.getPdbURL() + '&type=AVERAGED&abInitioModelId=' + sample.raw.abInitioModelId; - html = html + ''+ split[split.length - 1] + '

'; - - } - } - - file = sample.raw.rapidShapeDeterminationModel; - if (file != null) { - split = file.split("/"); - if (split.length > 0) { - url = BUI.getPdbURL() + '&type=RAPIDSHAPEDETERMINATIONMODEL&abInitioModelId=' + sample.raw.abInitioModelId; - html = html + '' + split[split.length - 1] + '

'; - } - } - - file = sample.raw.shapeDeterminationModel; - if (file != null) { - split = file.split("/"); - if (split.length > 0) { - url = BUI.getPdbURL() + '&type=SHAPEDETERMINATIONMODEL&abInitioModelId=' + sample.raw.abInitioModelId; - html = html + ''+ split[split.length - 1] + ''; - } - } - return html; - } - }, - { - text : 'Damaver', - dataIndex : 'volumeEdna', - hidden : true, - tooltip : 'Damaver: The program suite DAMAVER is a set of programs to align ab initio models, select the most typical one and build an averaged model. ', - sortable : true, - width : 125, - renderer : function(val, y, sample) { - var file = sample.raw.averagedModel; - if (file != null) { - var split = file.split("/"); - if (split.length > 0) { - var url = BUI.getPdbURL() + '&type=AVERAGED&abInitioModelId=' + sample.raw.abInitioModelId; - return '' + split[split.length - 1]+ ''; - } - } - return sample.raw.averagedModel; - } - }, - { - text : 'Dammif', - dataIndex : 'volumeEdna', - hidden : true, - tooltip : 'Dammif : Rapid ab initio shape determination by simulated annealing using a single phase dummy atom model', - sortable : true, - width : 125, - renderer : function(val, y, sample) { - var file = sample.raw.rapidShapeDeterminationModel; - if (file != null) { - var split = file.split("/"); - if (split.length > 0) { - var url = BUI.getPdbURL() + '&type=RAPIDSHAPEDETERMINATIONMODEL&abInitioModelId=' + sample.raw.abInitioModelId; - return '' + split[split.length - 1] + ''; - } - } - return sample.raw.averagedModel; - } - }, - { - text : 'Dammin', - dataIndex : 'volumeEdna', - hidden : true, - tooltip : 'Dammin :Ab initio shape determination by simulated annealing using a single phase dummy atom model', - sortable : true, - width : 125, - renderer : function(val, y, sample) { - var file = sample.raw.shapeDeterminationModel; - if (file != null) { - var split = file.split("/"); - if (split.length > 0) { - var url = BUI.getPdbURL() + '&type=SHAPEDETERMINATIONMODEL&abInitioModelId=' + sample.raw.abInitioModelId; - return '' + split[split.length - 1]+ ''; - } - } - return sample.raw.averagedModel; - } - } ] - }, { - text : 'Time', - dataIndex : 'substractionCreationTime', - name : 'substractionCreationTime', - hidden : true, - width : 80, - sortable : true, - renderer : function(value, metaData, record, rowIndex, colIndex, store) { - try { - if (record.raw.substractionCreationTime != null) { - return moment(record.raw.substractionCreationTime).format('h:mm:ss a'); - } - } catch (e) { - return "NA"; - } - } - }, { - text : 'Date', - dataIndex : 'substractionCreationTime', - name : 'substractionCreationTime', - hidden : true, - width : 80, - sortable : true, - renderer : function(value, metaData, record, rowIndex, colIndex, store) { - try { - if (record.raw.substractionCreationTime != null) { - return moment(record.raw.substractionCreationTime).format("LL"); - } - } catch (e) { - return "NA"; - } - } - }, - this._getButtons()]; -}; - -AnalysisGrid.prototype._getButtons = function() { - return { - id : this.id + 'buttonPlot', - name : 'buttonPlot', - hidden : !this.showButtonsVisible, - width : 110, - sortable : false, - renderer : function(value, metaData, sample, rowIndex, colIndex, store) { - var html = ""; - - if (sample.raw.subtractionId) { - html = html + ""; - if (sample.raw.rapidShapeDeterminationModelId != null) { - html = html + ""; - } - } - return html + "
" + BUI.getGreenButton('Calibration', { - height : 20, - width : 100 - }) + "
" + BUI.getGreenButton('Primary Data Proc.', { - height : 20, - width : 100 - }) + "
" + BUI.getGreenButton('AbInitio Modeling', { - height : 20, - width : 100 - }) + "
"; - } - }; -}; -AnalysisGrid.prototype._prepareData = function(data) { - if (this.hideNulls) { - var result = []; - for ( var i = 0; i < data.length; i++) { - if (data[i].subtractionId != null) { - data[i].groupeField = data[i].macromoleculeAcronym + " " + data[i].bufferAcronym; - result.push(data[i]); - } - } - return result; - } else { - return data; - } -}; - -AnalysisGrid.prototype.getPanel = function(data) { - var _this = this; - var columns = this._getColumns(); - - var fields = JSON.parse(JSON.stringify(columns)); - fields.push({ - name : 'experimentId', - dataIndex : 'experimentId' - - }); - this.store = Ext.create('Ext.data.Store', { - fields : fields, - autoload : true, - data : this._prepareData(data), - groupField : 'groupeField' - }); - - this.store.sort(this.sorters); - - var features = []; - - if (this.grouped) { - features.push({ - ftype : 'grouping', - groupHeaderTpl : '{name}', - hideGroupedHeader : false, - startCollapsed : false - }); - } - - this.grid = Ext.create('Ext.grid.Panel', { - style : { - padding : 5 - }, - maxWidth : this.maxWidth, - width : this.width, - height : this.height, - store : this.store, - columns : columns, - resizable : true, - features : features, - viewConfig : { - preserveScrollOnRefresh : true, - stripeRows : true, - listeners : { - cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { - - if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonPlot') { - if (e.target.defaultValue == 'AbInitio Modeling') { - var url = BUI.getPDBVisualizerURL(record.raw.averagedModelId, record.raw.subtractionId, record.raw.experimentId); - window.open(url, "_blank"); - } - - if (e.target.defaultValue == 'Primary Data Proc.') { - _this._edit(record.raw); - } - - if (e.target.defaultValue == 'Calibration') { - _this._showCalibration(record.raw); - } - } - } - } - }, - selModel : { - mode : 'SINGLE' - } - }); - - return this.grid; - -}; - -AnalysisGrid.prototype._openVisualizarBySubstractionId = function(record) { - //experimentId, subtractionId) { - var experimentId = record.experimentId; - var subtractionId = record.subtractionId; - - var _this = this; - var adapter = new BiosaxsDataAdapter(); - this.subtractionId = subtractionId; - - adapter.onSuccess.attach(function(sender, data) { - var experiment = new Experiment(data); - var experimentList = new ExperimentList([ experiment ]); - var subtraction = experiment.getSubtractionById(_this.subtractionId); - _this.grid.setLoading(false); - _this._openVisualizer(experimentList, subtraction, record); - }); - this.grid.setLoading("ISPyB: Fetching experiment"); - adapter.getExperimentById(experimentId, "MEDIUM"); -}; - -AnalysisGrid.prototype._edit = function(record) { - var experimentId = record.experimentId; - var _this = this; - - if (BIOSAXS.proposal.macromolecules == null) { - this.grid.setLoading("ISPyB: Fetching proposal"); - BIOSAXS.proposal.onInitialized.attach(function(sender, data) { - _this._openVisualizarBySubstractionId(record); - }); - BIOSAXS.proposal.init(); - } else { - this._openVisualizarBySubstractionId(record); - } -}; - - -AnalysisGrid.prototype._showCalibration = function(row) { - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, data) { - Ext.create('Ext.window.Window', { - title : 'Calibration', - width : 900, - resizable : true, - height : 400, - modal : true, - frame : false, - draggable : true, - closable : true, - autoscroll : true, - paddin : 5, - layout : { - type : 'vbox', - align : 'stretch' - }, - items : [ new AnalysisGrid({ - isGuinierTabVisible : false, - isGnomTabVisible : false, - isPorodTabVisible : false, - isAbinitioTabVisible : false, - isI0Visible : true, - showButtonsVisible : false, - height : 350, - sorters : [ { - property : 'experimentId', - direction : 'DESC' - } ] - }).getPanel(data) ] - }).show(); - }); - adapter.getAnalysisCalibrationByProposalId(); -}; - -AnalysisGrid.prototype._openVisualizer = function(experimentList, subtraction, record) { - var merges = experimentList.getMergesByDataCollectionId(subtraction.dataCollectionId); - var mergeIdList = []; - for ( var i = 0; i < merges.length; i++) { - mergeIdList.push(merges[i].mergeId); - } - - var dataAdapter = new BiosaxsDataAdapter(); - dataAdapter.onSuccess.attach(function(sender, merges) { - var dataCollectionCurveVisualizer = new DataCollectionCurveVisualizer(); - dataCollectionCurveVisualizer.draw(); - dataCollectionCurveVisualizer.refresh(merges, experimentList, subtraction.dataCollectionId, record); - }); - - dataAdapter.onError.attach(function(sender, error) { - }); - - dataAdapter.getMergesByIdsList(mergeIdList); -}; - -AnalysisGrid.prototype.input = function() { - return { - data : DATADOC.getData_3367().slice(20, 30),//[{"total":"0.447505552082","bufferBeforeMeasurementId":22150,"sampleMergeId":12373,"averagedModelId":43636,"I0":"32.50776","proposalCode":"OPD","averagedModel":"/data/pyarch/bm29/opd29/1137/1d/damaver.pdb","bufferAfterMergeId":12374,"framesCount":"10","bufferBeforeMergeId":12372,"averageFilePath":" /data/pyarch/bm29/opd29/1137/1d/MG386_016_ave.dat","bufferAfterMeasurementId":22152,"rgGuinier":"2.96883","subtractedFilePath":" /data/pyarch/bm29/opd29/1137/1d/MG386_016_sub.dat","firstPointUsed":"30","chi2RgFilePath":"/data/pyarch/bm29/opd29/1137/1d/chi2_R.png","abInitioModelId":272,"macromoleculeId":112,"code":"_20.0_1.25","transmission":"100.0","timeStart":"2013-07-17 17:10:25.743734","bufferAcronym":"MES","quality":"0.87091","shapeDeterminationModelId":43638,"expermientComments":"[BsxCube] Generated from BsxCube","bufferId":707,"isagregated":"False","dmax":"10.390905","bufferBeforeAverageFilePath":" /data/pyarch/bm29/opd29/1137/1d/MG386_015_ave.dat","exposureTemperature":"20.0","subtractionId":5165,"conc":"1.25","rapidShapeDeterminationModel":"/data/pyarch/bm29/opd29/1137/1d/damfilt.pdb","experimentCreationDate":"Jul 17, 2013 5:08:37 PM","lastPointUsed":"92","modelListId":272,"framesMerge":"10","rapidShapeDeterminationModelId":43637,"bufferAfterAverageFilePath":" /data/pyarch/bm29/opd29/1137/1d/MG386_017_ave.dat","nsdFilePath":"/data/pyarch/bm29/opd29/1137/1d/nsd.png","bufferAfterFramesMerged":"10","experimentId":1137,"macromoleculeAcronym":"MG386","i0stdev":"0.05726128","sampleMeasurementId":22151,"measurementComments":"[1] MES","priorityLevelId":2,"bufferBeforeFramesMerged":"10","substractionCreationTime":"Jul 17, 2013 5:12:32 PM","proposalNumber":"29","rgGnom":"3.05242714339","volume":"44.1406","shapeDeterminationModel":"/data/pyarch/bm29/opd29/1137/1d/dammin.pdb","comments":null},{"total":"0.582641941147","bufferBeforeMeasurementId":22152,"sampleMergeId":12375,"averagedModelId":43809,"I0":"31.4366153846","proposalCode":"OPD","averagedModel":"/data/pyarch/bm29/opd29/1137/1d/damaver.pdb","bufferAfterMergeId":12376,"framesCount":"10","bufferBeforeMergeId":12374,"averageFilePath":" /data/pyarch/bm29/opd29/1137/1d/MG386_018_ave.dat","bufferAfterMeasurementId":22155,"rgGuinier":"2.95541","subtractedFilePath":" /data/pyarch/bm29/opd29/1137/1d/MG386_018_sub.dat","firstPointUsed":"21","chi2RgFilePath":"/data/pyarch/bm29/opd29/1137/1d/chi2_R.png","abInitioModelId":273,"macromoleculeId":112,"code":"_20.0_0.65000000000000002","transmission":"100.0","timeStart":"2013-07-17 17:12:55.837462","bufferAcronym":"MES","quality":"0.870164","shapeDeterminationModelId":43811,"expermientComments":"[BsxCube] Generated from BsxCube","bufferId":707,"isagregated":"False","dmax":"10.343935","bufferBeforeAverageFilePath":" /data/pyarch/bm29/opd29/1137/1d/MG386_017_ave.dat","exposureTemperature":"20.0","subtractionId":5166,"conc":"0.65000000000000002","rapidShapeDeterminationModel":"/data/pyarch/bm29/opd29/1137/1d/damfilt.pdb","experimentCreationDate":"Jul 17, 2013 5:08:37 PM","lastPointUsed":"80","modelListId":273,"framesMerge":"10","rapidShapeDeterminationModelId":43810,"bufferAfterAverageFilePath":" /data/pyarch/bm29/opd29/1137/1d/MG386_019_ave.dat","nsdFilePath":"/data/pyarch/bm29/opd29/1137/1d/nsd.png","bufferAfterFramesMerged":"10","experimentId":1137,"macromoleculeAcronym":"MG386","i0stdev":"0.100250461538","sampleMeasurementId":22154,"measurementComments":"[2] MES","priorityLevelId":4,"bufferBeforeFramesMerged":"10","substractionCreationTime":"Jul 17, 2013 5:15:02 PM","proposalNumber":"29","rgGnom":"2.9963404542","volume":"42.2255","shapeDeterminationModel":"/data/pyarch/bm29/opd29/1137/1d/dammin.pdb","comments":null}], - proposal : new ResultsAssemblyGrid().input().proposal - }; -}; - -AnalysisGrid.prototype.test = function(targetId) { - var analysisGrid = new AnalysisGrid(); - BIOSAXS.proposal = new Proposal(analysisGrid.input().proposal); - var panel = analysisGrid.getPanel(analysisGrid.input().data); - panel.render(targetId); -}; - - -function HPLCAnalysisGrid(args) { - AnalysisGrid.prototype.constructor.call(this, args); -} - -HPLCAnalysisGrid.prototype._edit = AnalysisGrid.prototype._edit; -HPLCAnalysisGrid.prototype._getColumns = AnalysisGrid.prototype._getColumns; -HPLCAnalysisGrid.prototype._openVisualizarBySubstractionId = AnalysisGrid.prototype._openVisualizarBySubstractionId; -HPLCAnalysisGrid.prototype._openVisualizer = AnalysisGrid.prototype._openVisualizer; -HPLCAnalysisGrid.prototype._prepareData = AnalysisGrid.prototype._prepareData; -HPLCAnalysisGrid.prototype._showCalibration = AnalysisGrid.prototype._showCalibration; -HPLCAnalysisGrid.prototype.getPanel = AnalysisGrid.prototype.getPanel; -HPLCAnalysisGrid.prototype.input = AnalysisGrid.prototype.input; -HPLCAnalysisGrid.prototype.test = AnalysisGrid.prototype.test; -HPLCAnalysisGrid.prototype.refresh = AnalysisGrid.prototype.refresh; -HPLCAnalysisGrid.prototype._getPorod = AnalysisGrid.prototype._getPorod; - -HPLCAnalysisGrid.prototype._getButtons = function() { - return { - id : this.id + 'buttonPlot', - name : 'buttonPlot', - hidden : !this.showButtonsVisible, - width : 110, - sortable : false, - renderer : function(value, metaData, sample, rowIndex, colIndex, store) { - var html = ""; - - if (sample.raw.subtractionId) { - if (sample.raw.rapidShapeDeterminationModelId != null) { - html = html + ""; - } - } - return html + "
" + BUI.getGreenButton('AbInitio Modeling', { - height : 20, - width : 100 - }) + "
"; - } - }; -}; - -HPLCAnalysisGrid.prototype._getFramesColumn = function() { - return { - text : 'Frames', - dataIndex : 'datacollection', - name : 'datacollection', - sortable : true, - width : 150, - renderer : function(dataCollections, y, data) { - molmerges = data.raw.framesMerge; - totalframes = data.raw.framesCount; - return molmerges +" - "+ totalframes; - } - }; -}; - - - - -/** - * Rigid body grid to show PDB, symmetry and multiplicity - * - * - * #onUploadFile click on upload file - */ -function AprioriRigidBodyGrid(args) { - - this.height = 250; - this.btnEditVisible = true; - this.btnRemoveVisible = true; - - if (args != null) { - if (args.height != null) { - this.height = args.height; - } - if (args.btnEditVisible != null) { - this.btnEditVisible = args.btnEditVisible; - } - if (args.btnRemoveVisible != null) { - this.btnRemoveVisible = args.btnRemoveVisible; - } - } - - /** Events **/ - this.onUploadFile = new Event(this); - this.onRemove = new Event(this); -} - -AprioriRigidBodyGrid.prototype._getColumns = function() { -}; - -AprioriRigidBodyGrid.prototype._getTopButtons = function() { - var _this = this; - /** Actions buttons **/ - var actions = []; - - /** ADD BUTTON **/ - actions.push(Ext.create('Ext.Action', { - icon : '../images/add.png', - text : 'Add', - disabled : false, - handler : function(widget, event) { - _this.onAddButtonClicked.notify(); - } - })); - - return actions; -}; - -AprioriRigidBodyGrid.prototype.refresh = function(macromolecule) { - this.macromolecule = macromolecule; - if (macromolecule != null){ - this.pdbStore.loadData(macromolecule.structure3VOs); - } -}; - -AprioriRigidBodyGrid.prototype._prepareData = function() { - var data = []; - for ( var i = 0; i < this.features.length; i++) { - data.push(this.features[i]); - } - return data; -}; - -AprioriRigidBodyGrid.prototype._getPlugins = function() { - var _this = this; - var plugins = []; - plugins.push(Ext.create('Ext.grid.plugin.RowEditing', { - clicksToEdit : 1, - listeners : { - validateedit : function(grid, e) { - /** Comments are always updatable* */ - e.record.raw.symmetry = e.newValues.symmetry; - e.record.raw.multiplicity = e.newValues.multiplicity; - - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, proposal) { - BIOSAXS.proposal.setItems(proposal); - _this.refresh(BIOSAXS.proposal.getMacromoleculeById(_this.macromolecule.macromoleculeId)); - _this.panel.setLoading(false); - }); - adapter.onError.attach(function() { - alert("Error"); - }); - - _this.panel.setLoading(); - adapter.saveStructure(e.record.raw); - } - } - })); - return plugins; -}; - -AprioriRigidBodyGrid.prototype.getPanel = function() { - var _this = this; - - this.pdbStore = Ext.create('Ext.data.Store', { - fields : [ 'filePath', 'structureId', 'structureType', 'symmetry', 'structureId', 'name', 'multiplicity' ], - groupField : 'structureType', - sorters : { - property : 'structureId', - direction : 'DESC' - } - }); - - var groupingFeature = Ext.create('Ext.grid.feature.Grouping', { - groupHeaderTpl : Ext.create('Ext.XTemplate', - "
{name:this.formatName}
", { - formatName : function(name) { - return name; - } - }), - hideGroupedHeader : true, - startCollapsed : false - }); - - this.panel = Ext.create('Ext.grid.Panel', { - margin : "15 10 0 10", - height : this.height, - store : this.pdbStore, - plugins : _this._getPlugins(), - tbar : [ { - text : 'Add Modeling Option (PDB)', - icon : '../images/add.png', - handler : function() { - _this.onUploadFile.notify('PDB', 'Upload PDB File'); - } - } - - ], - columns : [ - { - text : "structureId", - flex : 0.2, - hidden : true, - dataIndex : 'structureId', - sortable : true - }, - { - text : "File", - flex : 0.5, - dataIndex : 'filePath', - sortable : true, - hidden : true - }, - { - text : "PDB", - flex : 0.4, - dataIndex : 'name', - sortable : true - }, - { - text : "Symmetry", - flex : 0.2, - dataIndex : 'symmetry', - sortable : true, - editor : { - xtype : 'combobox', - typeAhead : true, - triggerAction : 'all', - selectOnTab : true, - store : [ [ "P1", "P1" ], [ "P2", "P2" ], [ "P3", "P3" ], [ "P4", "P4" ], [ "P5", "P5" ], [ "P6", "P6" ], [ "P32", "P32" ], [ "P42", "P42" ], - [ "P52", "P52" ], [ "P62", "P62" ], [ "P222", "P222" ] ], - } - }, { - text : "Multiplicity", - flex : 0.2, - dataIndex : 'multiplicity', - sortable : true, - editor : { - xtype : 'textfield' - } - - }, { - text : "Subunit", - flex : 0.2, - dataIndex : 'isSubunit', - sortable : true, - hidden : true - }, { - text : "Type", - flex : 0.2, - dataIndex : 'structureType', - sortable : true, - hidden : true - }, - - { - id : this.id + 'REMOVE', - flex : 0.2, - sortable : false, - renderer : function(value, metaData, record, rowIndex, colIndex, store) { - return BUI.getRedButton('REMOVE'); - } - }, ], - - listeners : { - itemdblclick : function(dataview, record, item, e) { - _this._editExperiment(record.raw.experimentId); - }, - cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { - - if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'REMOVE') { - var dataAdapter = new BiosaxsDataAdapter(); - dataAdapter.onSuccess.attach(function() { - _this.panel.setLoading(false); - _this.onRemove.notify(); - }); - _this.panel.setLoading("Removing PDB file"); - dataAdapter.removeStructure(record.data.structureId); - } - - } - }, - viewConfig : { - getRowClass : function(record, rowIdx, params, store) { - if (record.raw.isSubunit != null) { - return "blue-row"; - } - } - } - }); - - return this.panel; -}; - - - - -AprioriRigidBodyGrid.prototype.input = function() { - return { - proposal : DATADOC.getProposal_10(), - dewars : DATADOC.getDewars_10() - - }; -}; - -AprioriRigidBodyGrid.prototype.test = function(targetId) { - var AprioriRigidBodyGrid = new AprioriRigidBodyGrid({ - height : 150 - }); - BIOSAXS.proposal = new Proposal(AprioriRigidBodyGrid.input().proposal); - var panel = AprioriRigidBodyGrid.getPanel(AprioriRigidBodyGrid.input().dewars); - panel.render(targetId); - -}; - -/** - * It shows buffer grid with a top bar with "Add" button - * - * @height - * @searchBar - * @collapsed - * @width - */ -function BufferGrid(args) { - this.height = 500; - this.searchBar = false; - this.tbar = false; - this.collapsed = false; - this.id = BUI.id(); - - if (args != null) { - if (args.height != null) { - this.height = args.height; - } - if (args.searchBar != null) { - this.searchBar = args.searchBar; - } - - if (args.tbar != null) { - this.tbar = args.tbar; - } - - if (args.collapsed != null) { - this.collapsed = args.collapsed; - } - - if (args.width != null) { - this.width = args.width; - } - } -} - -BufferGrid.prototype._edit = function(bufferId) { - var _this = this; - var window = new BufferWindow(); - window.onSuccess.attach(function(sender, proposal) { - _this.store.loadData(BIOSAXS.proposal.getBuffers()); - }); - window.draw(BIOSAXS.proposal.getBufferById(bufferId)); -}; - -BufferGrid.prototype.refresh = function(buffers) { - this.store.loadData(this._prepareData(buffers), false); -}; - -BufferGrid.prototype._prepareData = function(buffers) { - return buffers; -}; - -BufferGrid.prototype._getTbar = function() { - var _this = this; - var actions = []; - - actions.push(Ext.create('Ext.Action', { - icon : '../images/add.png', - text : 'Add buffer', - disabled : false, - handler : function(widget, event) { - var window = new BufferWindow(); - window.onSuccess.attach(function(sender) { - _this.store.loadData(BIOSAXS.proposal.getBuffers()); - }); - window.draw({}); - } - })); - return actions; -}; - -BufferGrid.prototype.getPanel = function(buffers) { - var _this = this; - - this.store = Ext.create('Ext.data.Store', { - fields : [ 'bufferId', 'acronym', 'name', 'composition' ], - data : buffers - }); - - this.store.sort('acronym'); - - var type = 'Ext.grid.Panel'; - if (this.searchBar == true) { - type = 'Ext.ux.LiveSearchGridPanel'; - } - - this.grid = Ext.create(type, { - title : 'Buffers', - collapsible : true, - collapsed : this.collapsed, - store : this.store, - height : this.height, - width : this.width, - columns : [ - /*{ - text : '', - dataIndex : 'bufferId', - width : 20, - renderer : function(val, y, sample) { - return BUI.getRectangleColorDIV(BIOSAXS.proposal.bufferColors[val], 10, 10); - } - },*/ - { - text : 'Acronym', - dataIndex : 'acronym', - flex : 1 - }, { - text : 'Name', - dataIndex : 'name', - flex : 1, - hidden : true - }, { - text : 'Composition', - dataIndex : 'composition', - flex : 1, - hidden : true - }, { - id : _this.id + 'buttonEditBuffer', - width : 80, - sortable : false, - renderer : function(value, metaData, record, rowIndex, colIndex, store) { - return BUI.getGreenButton('EDIT'); - } - }, { - id : _this.id + 'buttonRemoveBuffer', - width : 85, - hidden : true, - sortable : false, - renderer : function(value, metaData, record, rowIndex, colIndex, store) { - return BUI.getRedButton('REMOVE'); - } - } ], - flex : 1, - viewConfig : { - stripeRows : true, - listeners : { - 'celldblclick' : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { - _this._edit(record.data.bufferId); - }, - 'cellclick' : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { - if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonEditBuffer') { - _this._edit(record.data.bufferId); - } - - if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonRemoveBuffer') { - BUI.showBetaWarning(); - } - } - - } - } - }); - - /** Adding the tbar **/ - if (this.tbar) { - this.grid.addDocked({ - xtype : 'toolbar', - items : this._getTbar() - }); - } - return this.grid; -}; - -BufferGrid.prototype.input = function() { - return new MacromoleculeGrid().input(); -}; - -BufferGrid.prototype.test = function(targetId) { - var bufferGrid = new BufferGrid({ - width : 800, - height : 350, - collapsed : false, - tbar : true - }); - - BIOSAXS.proposal = new Proposal(bufferGrid.input().proposal); - var panel = bufferGrid.getPanel(BIOSAXS.proposal.macromolecules); - panel.render(targetId); -}; - -/** - * A shipment may contains one or more cases where stock solutions and sample plates are stored - * - * @height - * @btnEditVisible - * @btnRemoveVisible - * - * #onEditButtonClicked - * #onAddButtonClicked - * #onRemoveButtonClicked - * #onDuplicateButtonClicked - */ -function CaseGrid(args) { - - this.height = 100; - this.btnEditVisible = true; - this.btnRemoveVisible = true; - - if (args != null) { - if (args.height != null) { - this.height = args.height; - } - if (args.btnEditVisible != null) { - this.btnEditVisible = args.btnEditVisible; - } - if (args.btnRemoveVisible != null) { - this.btnRemoveVisible = args.btnRemoveVisible; - } - } - - /** Events **/ - this.onEditButtonClicked = new Event(this); - this.onAddButtonClicked = new Event(this); - this.onRemoveButtonClicked = new Event(this); - this.onDuplicateButtonClicked = new Event(this); -} - -CaseGrid.prototype._getColumns = function() { - var _this = this; - - function actionItemRenderer(value, meta, record, rowIx, ColIx, store) { - /*if (record.raw.specimen3VOs.length > 0){ - return 'x-hide-display'; - }*/ - } - - var columns = [ - { - header : 'Code', - dataIndex : 'code', - name : 'code', - type : 'string', - flex : 1 - }, - { - header : 'Bar Code', - dataIndex : 'barCode', - name : 'barCode', - type : 'string', - flex : 1, - hidden : true - }, - { - header : 'BeamLine', - dataIndex : 'barCode', - type : 'string', - flex : 1, - renderer : function(comp, val, record) { - if (record.raw.sessionVO != null) { - return "" + record.raw.sessionVO.beamlineName + ""; - } - } - }, - { - header : 'Session', - dataIndex : 'barCode', - type : 'string', - flex : 1, - renderer : function(comp, val, record) { - if (record.raw.sessionVO != null) { - return "" + moment(record.raw.sessionVO.startDate).format("MMM Do YY") + ""; - } - } - }, - { - header : 'Status', - dataIndex : 'dewarStatus', - name : 'dewarStatus', - type : 'string', - flex : 1, - renderer : function(comp, val, record) { - if (new String(record.raw.dewarStatus).toUpperCase() == 'OPENED') { - return "" + new String(record.raw.dewarStatus).toUpperCase() + ""; - } - return "" + new String(record.raw.dewarStatus).toUpperCase() + ""; - } - }, - { - header : 'Type', - dataIndex : 'type', - name : 'type', - type : 'string', - flex : 1, - hidden : true - }, - { - header : 'Sample Plates', - dataIndex : 'plates', - name : 'plates', - type : 'string', - flex : 1, - hidden : true - }, - { - header : 'Stock Solutions', - dataIndex : 'plates', - name : 'plates', - type : 'string', - width : 100, - renderer : function(comp, val, record) { - var html = "
"; - var stockSolutions = BIOSAXS.proposal.getStockSolutionsByDewarId(record.raw.dewarId); - html = html + "" + stockSolutions.length + " x"; - return html + "
"; - } - }, { - header : 'isStorageDewar', - dataIndex : 'isStorageDewar', - name : 'isStorageDewar', - type : 'string', - flex : 1, - hidden : true - }, { - header : 'Tracking Number From Synchrotron', - dataIndex : 'trackingNumberFromSynchrotron', - name : 'trackingNumberFromSynchrotron', - type : 'string', - flex : 1, - hidden : true - }, { - header : 'Tracking Number To Synchrotron', - dataIndex : 'trackingNumberToSynchrotron', - name : 'trackingNumberToSynchrotron', - type : 'string', - flex : 1, - hidden : true - }, { - header : 'Storage Location', - dataIndex : 'storageLocation', - name : 'storageLocation', - type : 'string', - flex : 1, - hidden : false - }, { - header : 'Comments', - dataIndex : 'comments', - name : 'comments', - type : 'string', - flex : 1, - hidden : false - } - ]; - - if (this.btnEditVisible) { - columns.push({ - id : _this.id + 'buttonEdit', - text : '', - hidden : !_this.btnEditVisible, - width : 85, - sortable : false, - renderer : function(value, metaData, record, rowIndex, colIndex, store) { - return BUI.getGreenButton('EDIT'); - } - }); - } - - if (this.btnRemoveVisible) { - columns.push({ - id : _this.id + 'buttonRemove', - width : 85, - sortable : false, - renderer : function(value, metaData, record, rowIndex, colIndex, store) { - if (_this.btnRemoveVisible) { - return BUI.getRedButton('REMOVE'); - } - } - }); - } - - columns.push({ - dataIndex : 'comments', - type : 'string', - width : 85, - hidden : false, - renderer : function(comp, val, record) { - return BUI.getBlueButton("LABELS", { - href : BUI.getPrintcomponentURL(record.raw.dewarId) - }); - } - }); - - return columns; -}; - -CaseGrid.prototype._getTopButtons = function() { - var _this = this; - /** Actions buttons **/ - var actions = []; - - /** ADD BUTTON **/ - actions.push(Ext.create('Ext.Action', { - icon : '../images/add.png', - text : 'Add', - disabled : false, - handler : function(widget, event) { - _this.onAddButtonClicked.notify(); - } - })); - - return actions; -}; - -CaseGrid.prototype.refresh = function(dewars) { - this.features = dewars; - this.store.loadData(this._prepareData(dewars), false); -}; - -CaseGrid.prototype._sort = function(store) { - store.sort('dewarId', 'DESC'); -}; - -CaseGrid.prototype._prepareData = function() { - var data = []; - for ( var i = 0; i < this.features.length; i++) { - data.push(this.features[i]); - } - return data; -}; - -CaseGrid.prototype.getPanel = function(dewars, plates) { - this.features = dewars; - this.plates = plates; - return this._renderGrid(); -}; - -CaseGrid.prototype._edit = function(dewar) { - var _this = this; - var caseWindow = new CaseWindow(); - /**SAVED **/ - caseWindow.onSaved.attach(function(sender, dewar) { - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, shipment) { - _this.refresh(shipment.dewarVOs); - }); - adapter.saveCase(BIOSAXS.proposal.getShipmentByDewarId(dewar.dewarId).shippingId, dewar); - }); - caseWindow.draw(dewar); -}; - -CaseGrid.prototype._getStoreFields = function(data) { - return [ { - name : 'dewarId', - type : 'string' - }, { - name : 'barCode', - type : 'string' - }, { - name : 'code', - type : 'string' - }, { - name : 'comments', - type : 'string' - }, { - name : 'dewarStatus', - type : 'string' - }, { - name : 'isStorageDewar', - type : 'string' - }, { - name : 'plates', - type : 'string' - }, { - name : 'transportValue', - type : 'string' - }, { - name : 'trackingNumberFromSynchrotron', - type : 'string' - }, { - name : 'trackingNumberToSynchrotron', - type : 'string' - }, { - name : 'timeStamp', - type : 'string' - }, { - name : 'storageLocation', - type : 'string' - }, { - name : 'type', - type : 'string' - } - - ]; -}; - -CaseGrid.prototype._renderGrid = function() { - var _this = this; - - /** Store **/ - var data = this._prepareData(); - this.store = Ext.create('Ext.data.Store', { - fields : this._getStoreFields(data), - autoload : true, - data : data - }); - this._sort(this.store); - - this.store.loadData(data, false); - - this.grid = Ext.create('Ext.grid.Panel', { - style : { - padding : 5 - }, - height : this.height, - store : this.store, - columns : this._getColumns(), - viewConfig : { - stripeRows : true, - listeners : { - itemdblclick : function(dataview, record, item, e) { - _this._edit(record.raw); - }, - cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { - if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonEdit') { - _this._edit(record.raw); - } - - if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonRemove') { - _this.onRemoveButtonClicked.notify(_this.store.getAt(rowIndex).raw.dewarId); - } - } - } - }, - selModel : { - mode : 'SINGLE' - } - }); - - var actions = _this._getTopButtons(); - this.grid.addDocked({ - xtype : 'toolbar', - items : actions - }); - - this.grid.getSelectionModel().on({ - selectionchange : function(sm, selections) { - if (selections.length) { - for ( var i = 0; i < actions.length; i++) { - if (actions[i].enable) { - actions[i].enable(); - } - } - } else { - for ( var i = 0; i < actions.length; i++) { - if (actions[i].alwaysEnabled == false) { - if (actions[i].disable) { - actions[i].disable(); - } - } - } - } - } - }); - return this.grid; -}; - -CaseGrid.prototype.input = function() { - return { - proposal : DATADOC.getProposal_10(), - dewars : DATADOC.getDewars_10() - - }; -}; - -CaseGrid.prototype.test = function(targetId) { - var CaseGrid = new CaseGrid({ - height : 150 - }); - BIOSAXS.proposal = new Proposal(CaseGrid.input().proposal); - var panel = CaseGrid.getPanel(CaseGrid.input().dewars); - panel.render(targetId); - -}; - -/** - * A shipment may contains one or more cases where stock solutions and sample plates are stored - * - * @height - * @btnEditVisible - * @btnRemoveVisible - * - * #onEditButtonClicked - * #onAddButtonClicked - * #onRemoveButtonClicked - * #onDuplicateButtonClicked - */ -function ExampleGrid(args) { - - this.height = 100; - this.btnEditVisible = true; - this.btnRemoveVisible = true; - - if (args != null) { - if (args.height != null) { - this.height = args.height; - } - if (args.btnEditVisible != null) { - this.btnEditVisible = args.btnEditVisible; - } - if (args.btnRemoveVisible != null) { - this.btnRemoveVisible = args.btnRemoveVisible; - } - } - - /** Events **/ - this.onEditButtonClicked = new Event(this); - this.onAddButtonClicked = new Event(this); - this.onRemoveButtonClicked = new Event(this); - this.onDuplicateButtonClicked = new Event(this); -} - -ExampleGrid.prototype._getColumns = function() { - var _this = this; - - function actionItemRenderer(value, meta, record, rowIx, ColIx, store) { - /*if (record.raw.specimen3VOs.length > 0){ - return 'x-hide-display'; - }*/ - } - - var columns = [ - { - header : 'Code', - dataIndex : 'code', - name : 'code', - type : 'string', - flex : 1 - }, - { - header : 'Bar Code', - dataIndex : 'barCode', - name : 'barCode', - type : 'string', - flex : 1, - hidden : true - }, - { - header : 'BeamLine', - dataIndex : 'barCode', - type : 'string', - flex : 1, - renderer : function(comp, val, record) { - if (record.raw.sessionVO != null) { - return "" + record.raw.sessionVO.beamlineName + ""; - } - } - }, - { - header : 'Session', - dataIndex : 'barCode', - type : 'string', - flex : 1, - renderer : function(comp, val, record) { - if (record.raw.sessionVO != null) { - return "" + moment(record.raw.sessionVO.startDate).format("MMM Do YY") + ""; - } - } - }, - { - header : 'Status', - dataIndex : 'dewarStatus', - name : 'dewarStatus', - type : 'string', - flex : 1, - renderer : function(comp, val, record) { - if (new String(record.raw.dewarStatus).toUpperCase() == 'OPENED') { - return "" + new String(record.raw.dewarStatus).toUpperCase() + ""; - } - return "" + new String(record.raw.dewarStatus).toUpperCase() + ""; - } - }, - { - header : 'Type', - dataIndex : 'type', - name : 'type', - type : 'string', - flex : 1, - hidden : true - }, - { - header : 'Sample Plates', - dataIndex : 'plates', - name : 'plates', - type : 'string', - flex : 1, - hidden : true - }, - { - header : 'Stock Solutions', - dataIndex : 'plates', - name : 'plates', - type : 'string', - width : 100, - renderer : function(comp, val, record) { - var html = "
"; - var stockSolutions = BIOSAXS.proposal.getStockSolutionsByDewarId(record.raw.dewarId); - html = html + "" + stockSolutions.length + " x"; - return html + "
"; - } - }, { - header : 'isStorageDewar', - dataIndex : 'isStorageDewar', - name : 'isStorageDewar', - type : 'string', - flex : 1, - hidden : true - }, { - header : 'Tracking Number From Synchrotron', - dataIndex : 'trackingNumberFromSynchrotron', - name : 'trackingNumberFromSynchrotron', - type : 'string', - flex : 1, - hidden : true - }, { - header : 'Tracking Number To Synchrotron', - dataIndex : 'trackingNumberToSynchrotron', - name : 'trackingNumberToSynchrotron', - type : 'string', - flex : 1, - hidden : true - }, { - header : 'Storage Location', - dataIndex : 'storageLocation', - name : 'storageLocation', - type : 'string', - flex : 1, - hidden : false - }, { - header : 'Comments', - dataIndex : 'comments', - name : 'comments', - type : 'string', - flex : 1, - hidden : false - } - ]; - - if (this.btnEditVisible) { - columns.push({ - id : _this.id + 'buttonEdit', - text : '', - hidden : !_this.btnEditVisible, - width : 85, - sortable : false, - renderer : function(value, metaData, record, rowIndex, colIndex, store) { - return BUI.getGreenButton('EDIT'); - } - }); - } - - if (this.btnRemoveVisible) { - columns.push({ - id : _this.id + 'buttonRemove', - width : 85, - sortable : false, - renderer : function(value, metaData, record, rowIndex, colIndex, store) { - if (_this.btnRemoveVisible) { - return BUI.getRedButton('REMOVE'); - } - } - }); - } - - columns.push({ - dataIndex : 'comments', - type : 'string', - width : 85, - hidden : false, - renderer : function(comp, val, record) { - return BUI.getBlueButton("LABELS", { - href : BUI.getPrintcomponentURL(record.raw.dewarId) - }); - } - }); - - return columns; -}; - -ExampleGrid.prototype._getTopButtons = function() { - var _this = this; - /** Actions buttons **/ - var actions = []; - - /** ADD BUTTON **/ - actions.push(Ext.create('Ext.Action', { - icon : '../images/add.png', - text : 'Add', - disabled : false, - handler : function(widget, event) { - _this.onAddButtonClicked.notify(); - } - })); - - return actions; -}; - -ExampleGrid.prototype.refresh = function(dewars) { - this.features = dewars; - this.store.loadData(this._prepareData(dewars), false); -}; - -ExampleGrid.prototype._sort = function(store) { - store.sort('dewarId', 'DESC'); -}; - -ExampleGrid.prototype._prepareData = function() { - var data = []; - for ( var i = 0; i < this.features.length; i++) { - data.push(this.features[i]); - } - return data; -}; - -ExampleGrid.prototype.getPanel = function() { - this.grid = Ext.create('Ext.grid.Panel', { - style : { - padding : 5 - }, - height : this.height, - store : this.store, - columns : this._getColumns(), - viewConfig : { - stripeRows : true, - listeners : { - itemdblclick : function(dataview, record, item, e) { - _this._edit(record.raw); - }, - cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { - if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonEdit') { - _this._edit(record.raw); - } - - if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonRemove') { - _this.onRemoveButtonClicked.notify(_this.store.getAt(rowIndex).raw.dewarId); - } - } - } - }, - selModel : { - mode : 'SINGLE' - } - }); - - var actions = _this._getTopButtons(); - this.grid.addDocked({ - xtype : 'toolbar', - items : actions - }); - - this.grid.getSelectionModel().on({ - selectionchange : function(sm, selections) { - if (selections.length) { - for ( var i = 0; i < actions.length; i++) { - if (actions[i].enable) { - actions[i].enable(); - } - } - } else { - for ( var i = 0; i < actions.length; i++) { - if (actions[i].alwaysEnabled == false) { - if (actions[i].disable) { - actions[i].disable(); - } - } - } - } - } - }); - return this.grid; -}; - - -ExampleGrid.prototype._getStoreFields = function(data) { - return [ { - name : 'dewarId', - type : 'string' - }, { - name : 'barCode', - type : 'string' - }, { - name : 'code', - type : 'string' - }, { - name : 'comments', - type : 'string' - }, { - name : 'dewarStatus', - type : 'string' - }, { - name : 'isStorageDewar', - type : 'string' - }, { - name : 'plates', - type : 'string' - }, { - name : 'transportValue', - type : 'string' - }, { - name : 'trackingNumberFromSynchrotron', - type : 'string' - }, { - name : 'trackingNumberToSynchrotron', - type : 'string' - }, { - name : 'timeStamp', - type : 'string' - }, { - name : 'storageLocation', - type : 'string' - }, { - name : 'type', - type : 'string' - } - - ]; -}; - -ExampleGrid.prototype._renderGrid = function() { - var _this = this; - - /** Store **/ - var data = this._prepareData(); - this.store = Ext.create('Ext.data.Store', { - fields : this._getStoreFields(data), - autoload : true, - data : data - }); - this._sort(this.store); - - this.store.loadData(data, false); - - this.grid = Ext.create('Ext.grid.Panel', { - style : { - padding : 5 - }, - height : this.height, - store : this.store, - columns : this._getColumns(), - viewConfig : { - stripeRows : true, - listeners : { - itemdblclick : function(dataview, record, item, e) { - _this._edit(record.raw); - }, - cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { - if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonEdit') { - _this._edit(record.raw); - } - - if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonRemove') { - _this.onRemoveButtonClicked.notify(_this.store.getAt(rowIndex).raw.dewarId); - } - } - } - }, - selModel : { - mode : 'SINGLE' - } - }); - - var actions = _this._getTopButtons(); - this.grid.addDocked({ - xtype : 'toolbar', - items : actions - }); - - this.grid.getSelectionModel().on({ - selectionchange : function(sm, selections) { - if (selections.length) { - for ( var i = 0; i < actions.length; i++) { - if (actions[i].enable) { - actions[i].enable(); - } - } - } else { - for ( var i = 0; i < actions.length; i++) { - if (actions[i].alwaysEnabled == false) { - if (actions[i].disable) { - actions[i].disable(); - } - } - } - } - } - }); - return this.grid; -}; - -ExampleGrid.prototype.input = function() { - return { - proposal : DATADOC.getProposal_10(), - dewars : DATADOC.getDewars_10() - - }; -}; - -ExampleGrid.prototype.test = function(targetId) { - var ExampleGrid = new ExampleGrid({ - height : 150 - }); - BIOSAXS.proposal = new Proposal(ExampleGrid.input().proposal); - var panel = ExampleGrid.getPanel(ExampleGrid.input().dewars); - panel.render(targetId); - -}; - - -/** - * Shows a list of experiment AKA data acquisitions - * @height - * @sorters - * @minHeight - * @gridType: Ext.ux.LiveSearchGridPanel or Ext.grid.Panel - * @tbar true or false - * @grouping true or false - * @width - * @title - * #onEditButtonClicked - */ -function ExperimentGrid(args) { - this.width = "100%"; - this.height = 700; - this.minHeight = 500; - - this.id = BUI.id(); - this.gridType = 'Ext.grid.Panel'; //'Ext.ux.LiveSearchGridPanel'; - this.tbar = false; - this.hideHeaders = false; - this.grouping = true; - this.title = null; - - this.filtered = null; - - /** maximum row count **/ - this.limit = 100; - - this.sessionIdFilter = null; - - /** if not null filtered by date **/ - this.date = null; - - this.sorters = [ { - property : 'date', - direction : 'DESC' - }, { - property : 'time', - direction : 'DESC' - } ]; - - this.dates = {}; - if (args != null) { - if (args.height != null) { - this.height = args.height; - } - if (args.limit != null) { - this.limit = args.limit; - } - if (args.sorters != null) { - this.sorters = args.sorters; - } - if (args.sessionId != null){ - this.sessionIdFilter = args.sessionId; - } - if (args.filtered != null) { - this.filtered = args.filtered; - } - if (args.minHeight != null) { - this.minHeight = args.minHeight; - } - if (args.gridType != null) { - this.gridType = args.gridType; - } - if (args.tbar != null) { - this.tbar = args.tbar; - } - if (args.grouping != null) { - this.grouping = args.grouping; - } - if (args.width != null) { - this.width = args.width; - } - if (args.title != null) { - this.title = args.title; - } - - } - /** Events **/ - this.onEditButtonClicked = new Event(this); -} - -ExperimentGrid.prototype._getFilterTypes = function() { - return []; -}; - -ExperimentGrid.prototype._prepareData = function(rows) { - var data = []; - var count = 0; - - rows.sort(function(a,b){return b.experimentId - a.experimentId;}); - for ( var i = 0; i < rows.length; i++) { - var row = rows[i]; - this.dates[moment(row.creationDate).format("YYYYMMDD")] = moment(row.creationDate).format("MMM Do YY"); - if ( - ( this.filtered == null || row.experimentType == this.filtered ) && (count < this.limit || this.limit == null ) && (this.sessionIdFilter == null || this.date != null || (this.date == null && this.sessionIdFilter == row.sessionId)) - ){ - - data.push({ - experimentId : row.experimentId, - status : row.status, - dataAcquisitionFilePath : row.dataAcquisitionFilePath, - type : row.experimentType, - name : row.name, - macromolecules_names : row.macromolecules, - percentageAnalysed : { - value : (row.dataCollectionDoneCount / row.dataCollectionCount) * 100, - text : row.dataCollectionDoneCount + " of " + row.dataCollectionCount - }, - percentageCollected : { - value : (row.measurementDoneCount / row.measurementCount) * 100, - text : row.measurementDoneCount + " of " + row.measurementCount - }, - percentageMerged : { - value : (row.measurementAveragedCount / row.measurementCount) * 100, - text : row.measurementAveragedCount + " of " + row.measurementCount - }, - date : moment(row.creationDate).format("YYYYMMDD"), - time : moment(row.creationDate).format("YYYYMMDDHHmmss"), - creationDate : row.creationDate - }); - count ++; - } - } - return data; -}; - -ExperimentGrid.prototype.getPanel = function(experiments) { - this.features = experiments; - return this._renderGrid(experiments); -}; - -ExperimentGrid.prototype.refresh = function(experiments) { - this.experiments = experiments; - var filtered = []; - for ( var i = 0; i < experiments.length; i++) { - if (experiments[i].experimentType != "TEMPLATE") { - filtered.push(experiments[i]); - } - } - - this.parsedData = this._prepareData(filtered); - - var day = {}; - var dates = []; - for ( var i = 0; i < this.experiments.length; i++) { - var date = moment(this.experiments[i].creationDate).format("MMM Do YYYY"); - if (day[date] == null){ - dates.push({ - date : date, - value : moment(this.experiments[i].creationDate) - }); - day[date] = true; - } - } - - this.storeDate.loadData(dates, false); - this.store.loadData(this.parsedData, false); - - /** If it has already been filtered by date we keep the filter **/ - if (this.date != null){ - this._filterByDate(this.date); - } -}; - -ExperimentGrid.prototype._getTopButtons = function() { - var _this = this; - /** Actions buttons **/ - var actions = []; - - actions.push(Ext.create('Ext.Action', { - icon : '../images/calendar_icon.png', - text : 'Show Calendar', - disabled : false, - handler : function(widget, event) { - var window = Ext.create('Ext.window.Window', { - title : 'Calendar', - width : 600, - height : 600, - modal : true, - closable : true, - layout : { - type : 'vbox', - align : 'stretch' - }, - items : [ { - xtype : 'label', - html : 'Click on a data acquisition to select:', - margin : '5 5 5 5' - }, { - html : '
', - margin : '5 5 5 5' - } - - ] - }).show(); - - var calendarWidget = new CalendarWidget({ - height : 450 - }); - var aux = _this.limit; - /** we remove the limit temporarily **/ - _this.limit = null; - _this.sessionIdFilter = null; - calendarWidget.loadData(_this._prepareData(_this.experiments)); - _this.limit = aux; - calendarWidget.draw('calendar'); - calendarWidget.onClick.attach(function(sender, date) { - date = moment(date, "YYYY-MM-DD"); - _this._filterByDate(date); - window.close(); - - }); - - } - })); - this.storeDate = Ext.create('Ext.data.ArrayStore', { - fields: ['date', 'value'], - data : [] - }); - - this.dateMenu = Ext.create('Ext.form.field.ComboBox', { - hideLabel: true, - store: this.storeDate, - displayField: 'date', - typeAhead: true, - queryMode: 'local', - margin : '0 0 0 30', - triggerAction: 'all', - emptyText:'Select a date...', - selectOnFocus:true, - width:135, - listeners:{ - scope: this, - 'select': function (a,b,c){ - _this.limit = null; - _this._filterByDate(moment(b[0].raw.value, "YYYY-MM-DD")); - } - } - }); - - actions.push(this.dateMenu); - - actions.push("->"); - if (_this.filtered != null){ - actions.push({ - html : "Experiment Type: " + _this.filtered +"" - }); - } - else{ - actions.push({ - html : "Experiment Type: ALL" - }); - } - return actions; -}; - -/** - * Date format: "YYYY-MM-DD" - */ -ExperimentGrid.prototype._filterByDate = function(date) { - var experimentsFiltered = []; - /** Getting all the experiments of date**/ - for ( var i = 0; i < this.experiments.length; i++) { - var experiment = this.experiments[i]; - if (experiment.creationDate != null) { - var experimentDate = moment(experiment.creationDate); - if (experimentDate.year() == date.year()) { - if (experimentDate.month() == date.month()) { - if (experimentDate.date() == date.date()) { - experimentsFiltered.push(experiment); - } - } - } - } - } - var parsedData = this._prepareData(experimentsFiltered); - this.store.loadData(parsedData); - this.date = date; -}; - -/** Only for templates **/ -ExperimentGrid.prototype._removeExperimentById = function(experimentId) { - var _this = this; - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(evt, args) { - _this.grid.setLoading(false); - document.getElementById(BIOSAXS.targetId).innerHTML = ""; - BIOSAXS.start(BIOSAXS.targetId); - }); - this.grid.setLoading("Removing experiment "); - adapter.removeExperimentById(experimentId); -}; - -ExperimentGrid.prototype._renderGrid = function() { - var _this = this; - - /** Store **/ - this.store = Ext.create('Ext.data.Store', { - fields : this._getColumns(), - groupField : 'date', - autoload : true, - data : [], - remoteSort: false, - sorters : this.sorters - }); - - - - - var groupingFeature = Ext.create('Ext.grid.feature.Grouping', { - groupHeaderTpl : Ext.create('Ext.XTemplate', - "
{name:this.formatName}
", { - formatName : function(name) { - return _this.dates[name]; - } - }), - hideGroupedHeader : true, - startCollapsed : false - }); - - this.features = []; - if (this.grouping) { - this.features.push(groupingFeature); - } - - /** Grid **/ - this.grid = Ext.create(this.gridType, { - hideHeaders : this.hideHeaders, - resizable : true, - title : this.title, - width : this.width, - minHeight : this.minHeight, - height : this.height, - features : this.features, - store : this.store, - columns : this._getColumns(), - selModel : { - mode : 'SINGLE' - }, - viewConfig : { - stripeRows : true, - getRowClass : function(record, rowIdx, params, store) { - if (record.raw.type == "TEMPLATE") { - return "template-color-row"; - } - if ((record.raw.type == "CALIBRATION") && (record.raw.status == "FINISHED")) { - return "blue-row"; - } - }, - listeners : { - itemdblclick : function(dataview, record, item, e) { - _this._editExperiment(record.raw.experimentId); - }, - cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { - if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'GO') { - _this._editExperiment(record.raw.experimentId); - } - - if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'REMOVE') { - _this._removeExperimentById(record.raw.experimentId); - } - - } - } - } - }); - - var actions = _this._getTopButtons(); - - if (this.tbar) { - this.grid.addDocked({ - xtype : 'toolbar', - items : actions - }); - this.grid.getSelectionModel().on({ - selectionchange : function(sm, selections) { - if (selections.length) { - for ( var i = 0; i < actions.length; i++) { - if (actions[i].enable) { - actions[i].enable(); - } - } - } else { - for ( var i = 0; i < actions.length; i++) { - if (actions[i].alwaysEnabled == false) { - if (actions[i].disable) { - actions[i].disable(); - } - } - } - } - } - }); - } - - return this.grid; -}; - -ExperimentGrid.prototype._getColumns = function() { - var _this = this; - function actionItemRenderer(value, meta, record, rowIx, ColIx, store) { - if (record.raw.buffer3VOs != null) { - if (record.raw.buffer3VOs.length > 0) { - return 'x-hide-display'; - } - } - - if (record.data.platesCount > 0) { - return 'x-hide-display'; - } - } - - return [ - { - text : 'experimentId', - dataIndex : 'experimentId', - name : 'experimentId', - type : 'string', - hidden : true - }, - { - xtype : 'rownumberer', - width : 40 - }, - { - text : 'Name', - dataIndex : 'name', - name : 'name', - type : 'string', - flex : 1 - }, - - { - text : 'Type', - dataIndex : 'type', - name : 'type', - type : 'string', - flex : 1, - renderer : function(val) { - if (val == "CALIBRATION") { - return "" + val + ""; - } - - return val; - } - }, - { - text : 'Macromolecules', - name : 'macromolecules_names', - dataIndex : 'macromolecules_names', - flex : 1, - renderer : function(val) { - if (val != null) { - return " " + val + ""; - } - return " Information not available"; - } - }, - { - text : 'Buffers', - dataIndex : 'buffer_names', - name : 'buffer_names', - flex : 1, - hidden : true, - renderer : function(val) { - return "Buffer/s: " + val + ""; - } - }, - { - text : 'Status', - dataIndex : 'status', - name : 'status', - type : 'string', - flex : 1, - renderer : function(val, x, sample) { - if (sample.raw.type == "TEMPLATE") { - return "READY"; - } - if (sample.raw.status == "ABORTED") { - return "" + val + ""; - } - return "" + val + ""; - } - }, - { - text : 'Download', - dataIndex : 'creationDate', - name : 'creationDate', - renderer : function(val, x, sample) { - if (sample != null) { - if (sample.raw.type == "HPLC") { - return; - } - return BUI.getZipHTMLByExperimentId(sample.raw.experimentId, sample.raw.name); - } - }, - width : 100 - - }, - { - header : 'Measurements', - dataIndex : 'percentageCollected', - name : 'percentageCollected', - type : 'string', - renderer : function(val, y, sample) { - if ((sample.raw.type == "TEMPLATE") || (sample.raw.type == "HPLC")) { - return; - } - return "
" + BUI.getProgessBar(sample.raw.percentageCollected.value, sample.raw.percentageCollected.text) + "
"; - }, - width : 100 - }, - { - header : 'Averaged', - dataIndex : 'percentageMerged', - name : 'percentageMerged', - type : 'string', - renderer : function(val, y, sample) { - if ((sample.raw.type == "TEMPLATE") || (sample.raw.type == "HPLC")) { - return; - } - return "
" + BUI.getProgessBar(sample.raw.percentageMerged.value, sample.raw.percentageMerged.text) + "
"; - }, - width : 100 - }, - { - header : 'Subtractions', - dataIndex : 'percentageAnalysed', - name : 'percentageAnalysed', - type : 'string', - renderer : function(val, y, sample) { - if ((sample.raw.type == "TEMPLATE") || (sample.raw.type == "HPLC")) { - return; - } - return "
" + BUI.getProgessBar(sample.raw.percentageAnalysed.value, sample.raw.percentageAnalysed.text) + "
"; - }, - width : 100 - }, - - { - text : 'time', - dataIndex : 'time', - name : 'time', - hidden : true, - renderer : function(val) { - return val; - }, - width : 100 - - }, { - text : 'Date', - dataIndex : 'date', - name : 'date', - renderer : function(val) { - return val; - }, - width : 100 - - }, - - { - text : 'Time', - dataIndex : 'creationDate', - name : 'creationDate', - renderer : function(val) { - return moment(val).format(" HH:mm:ss"); - }, - width : 100 - - }, { - id : _this.id + 'GO', - width : 80, - sortable : false, - renderer : function(value, metaData, record, rowIndex, colIndex, store) { - return BUI.getGreenButton('GO'); - } - } ]; -}; - -/** Changes location.href in order to edit the experiment **/ -ExperimentGrid.prototype._editExperiment = function(experimentId) { - if (Ext.urlDecode(window.location.href).sessionId != null) { - location.href = 'viewProjectList.do?reqCode=display&experimentId=' + experimentId + '&sessionId='+ Ext.urlDecode(window.location.href).sessionId; - } else { - location.href = 'viewProjectList.do?reqCode=display&experimentId=' + experimentId; - } -}; - -ExperimentGrid.prototype.input = function() { - var experiments = DATADOC.getExperimentList_10(); - return { - experiments : experiments, - proposal : new MeasurementGrid().input().proposal - - }; -}; - -ExperimentGrid.prototype.test = function(targetId) { - var experimentGrid = new ExperimentGrid({ - height : 350, - minHeight : 350, - width : 1000 - - }); - BIOSAXS.proposal = new Proposal(experimentGrid.input().proposal); - var panel = experimentGrid.getPanel(experimentGrid.input().experiments); - experimentGrid.refresh(experimentGrid.input().experiments); - panel.render(targetId); -}; - -function FitStructureToExperimentDataGrid(args) { - this.height = null; - this.width = null; - this.id = BUI.id(); - - if (args != null) { - if (args.height != null) { - this.height = args.height; - - if (args.width != null) { - this.width = args.width; - } - } - } - - this.onSelected = new Event(this); -} - -FitStructureToExperimentDataGrid.prototype.getStructuresByMacromolecule = function(macromolecule) { - var structures = []; - if (macromolecule.structure3VOs != null) { - if (macromolecule.structure3VOs.length > 0) { - for (var i = 0; i < macromolecule.structure3VOs.length; i++) { - structures.push(macromolecule.structure3VOs[i]); - } - } - } - return structures; -}; - -FitStructureToExperimentDataGrid.prototype._prepareData = function(subtractions) { - var data = []; - for (var i = 0; i < subtractions.length; i++) { - - for (var j = 0; j < subtractions[i].fitStructureToExperimentalData3VOs.length; j++) { - var fit = subtractions[i].fitStructureToExperimentalData3VOs[j]; - data.push({ - fit : fit.fitFilePath, - fitStructureToExperimentalDataId : fit.fitStructureToExperimentalDataId, - mixtureToStructure3VOs : fit.mixtureToStructure3VOs, - subtractedFile : subtractions[i].substractedFilePath - }); - } - } - return data; - -}; - -FitStructureToExperimentDataGrid.prototype.refresh = function(subtractions) { - this.store.loadData(this._prepareData(subtractions), false); -}; - -FitStructureToExperimentDataGrid.prototype.getPanel = function() { - var _this = this; - this.store = Ext.create('Ext.data.Store', { - fields : [ 'fit', 'subtractedFile', 'structureId', 'fitFilePath', 'mixtureToStructure3VOs' ], - data : [] - }); - - this.store.sort([ { - property : 'name', - direction : 'ASC' - } ]); - - this.selModel = Ext.create('Ext.selection.RowModel', { - allowDeselect : true, - mode : 'MULTI', - listeners : { - selectionchange : function(sm, selections) { - var selected = []; - for (var i = 0; i < selections.length; i++) { - selected.push(selections[i].raw); - } - _this.onSelected.notify(selected); - } - } - }); - - this.panel = Ext.create('Ext.grid.Panel', { - store : this.store, - deferredRender : false, - width : this.width, - height : this.height, - margin : 10, - selModel : this.selModel, - columns : [ { - text : 'Name', - dataIndex : 'fit', - flex : 1, - renderer : function(val, b, record) { - return BUI.getFileName(val); - } - }, { - text : 'PDB', - dataIndex : 'mixtureToStructure3VOs', - flex : 1, - renderer : function(values, b, record) { - var html = ""; - for (var i = 0; i < values.length; i++) { - var structure = BIOSAXS.proposal.getStructuresById(values[i].structureId); - html = html + ""; - if (structure != null){ - html = html + ""; - } - html = html + ""; - } - return html + "
" + structure.name + "
"; - } - }, { - text : 'Volume Fraction', - dataIndex : 'mixtureToStructure3VOs', - flex : 1, - renderer : function(values, b, record) { - var html = ""; - for (var i = 0; i < values.length; i++) { - html = html + ""; - html = html + ""; - html = html + ""; - } - - return html + "
" + values[i].volumeFraction + "
"; - } - },{ - text : 'Subtraction', - dataIndex : 'subtractedFile', - flex : 1, - renderer : function(values, b, record) { - return values.split("/")[values.split("/").length - 1]; - } - },], - - viewConfig : { - enableTextSelection : true, - preserveScrollOnRefresh : true - }, - listeners : { - cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { - }, - afterrender : function() { - } - } - }); - return this.panel; -}; - -/** Static method **/ -FitStructureToExperimentDataGrid.prototype.RUNFitScattering = function(subtractionId, structureId) { - var _this = this; - /** Add to Workflow **/ - var adapter = new BiosaxsDataAdapter(); - var workflow = { - 'workflowTitle' : 'FitExperimentalDatatoStructure', - 'comments' : 'FitExperimentalDatatoStructure run from ISPyB for subtractionId: ' + subtractionId + ' and structureId ' + structureId - } - - var inputs = [ { - name : 'subtractionId', - value : subtractionId - }, { - name : 'structureId', - value : structureId - } ]; - - adapter.onSuccess.attach(function(sender, data) { - /** Add to Fit **/ - var adapter2 = new BiosaxsDataAdapter(); - var fit = { - 'workflowId' : data.workflowId, - 'subtractionId' : subtractionId, - 'structureId' : structureId, - 'comments' : 'Sent to workflow engine' - } - adapter2.onSuccess.attach(function(sender, fit) { - _this.refresh(_this.subtractionId, _this.macromolecule); - }); - adapter2.addFitStructureData(fit); - - }); - adapter.addWorkflow(workflow, inputs); - -}; +/** + * Example form + * + * @witdh + * @height + */ +function MolarityForm(args) { + this.id = BUI.id(); + this.width = 700; + this.height = 500; + + if (args != null) { + if (args.width != null) { + this.width = args.width; + } + if (args.height != null) { + this.height = args.height; + } + } + + this.onSave = new Event(this); + this.onClose = new Event(this); +} + -/** - * It shows buffer grid with a top bar with "Add" button - * - * @height - * @searchBar - * @collapsed - * @width - */ -function FrameGrid(args) { - this.height = 500; - this.width = 500; - this.searchBar = false; - this.tbar = false; - this.collapsed = false; - - - if (args != null) { - if (args.height != null) { - this.height = args.height; - } - - if (args.searchBar != null) { - this.searchBar = args.searchBar; - } - - if (args.tbar != null) { - this.tbar = args.tbar; - } - - if (args.collapsed != null) { - this.collapsed = args.collapsed; - } - - if (args.width != null) { - this.width = args.width; - } - } -} - -FrameGrid.prototype._edit = function(bufferId) { - var _this = this; - var window = new BufferWindow(); - window.onSuccess.attach(function(sender, proposal) { - _this.store.loadData(BIOSAXS.proposal.getBuffers()); - }); - window.draw(BIOSAXS.proposal.getBufferById(bufferId)); -}; - -FrameGrid.prototype.refresh = function(buffers, experimentId) { - this.experimentId = experimentId; - this.store.loadData(this._prepareData(buffers), false); -}; - -FrameGrid.prototype._prepareData = function(buffers) { - return buffers; -}; - -FrameGrid.prototype._getTbar = function() { - var _this = this; - var actions = []; - - actions.push(Ext.create('Ext.Action', { - icon : '../images/add.png', - text : 'Add Buffer', - disabled : false, - handler : function(widget, event) { - var window = new BufferWindow(); - window.onSuccess.attach(function(sender) { - _this.store.loadData(BIOSAXS.proposal.getBuffers()); - }); - window.draw({}); - } - })); - return actions; -}; - -FrameGrid.prototype.getPanel = function(buffers) { - var _this = this; - - this.store = Ext.create('Ext.data.Store', { - fields : [ 'frameNumber', 'I0', 'Rg', 'Mass', 'Vc', 'Qr', 'quality'], - data : buffers - }); - - this.store.sort('frameNumber'); - - var type = 'Ext.grid.Panel'; - if (this.searchBar == true) { - type = 'Ext.ux.LiveSearchGridPanel'; - } - - this.grid = Ext.create(type, { - store : this.store, - height : this.height, - width : this.width, - margin : 5, - columns : [{ - text : 'Frame', - dataIndex : 'frameNumber', - flex : 1 - },{ - text : 'I0', - dataIndex : 'I0', - flex : 1, - renderer : function(val, y, sample) { - return BUI.formatValuesUnits(sample.data.I0, "", 12, 3); - } - },{ - text : 'Rg', - dataIndex : 'Rg', - flex : 1, - renderer : function(val, y, sample) { - return BUI.formatValuesUnits(sample.data.Rg, "nm", 12, 3); - } - },{ - text : 'Mass', - dataIndex : 'Mass', - flex : 1, - renderer : function(val, y, sample) { - return BUI.formatValuesUnits(sample.data.Mass, "", 12, 3); - } - },{ - text : 'Vc', - dataIndex : 'Vc', - flex : 1, - renderer : function(val, y, sample) { - return BUI.formatValuesUnits(sample.data.Vc, "", 12, 3); - } - },{ - text : 'Qr', - dataIndex : 'Qr', - flex : 1, - renderer : function(val, y, sample) { - return BUI.formatValuesUnits(sample.data.Qr, "", 12, 3); - } - },{ - text : 'Quality', - dataIndex : 'quality', - flex : 1, - renderer : function(val, y, sample) { - return (Number(sample.data.quality) * 100).toFixed(2) + "%"; - } - }, - { - text : '', - renderer : function(val, x, sample) { - if (sample != null) { - return BUI.getZipHTMLByFrameRangeId(_this.experimentId, sample.raw.frameNumber, sample.raw.frameNumber); - } - }, - width : 100 - }], - flex : 1, - viewConfig : { - stripeRows : true - } - }); - - /** Adding the tbar **/ - if (this.tbar) { - this.grid.addDocked({ - xtype : 'toolbar', - items : this._getTbar() - }); - } - return this.grid; -}; - -FrameGrid.prototype.input = function() { - return []; -}; - -FrameGrid.prototype.test = function(targetId) { - var frameGrid = new FrameGrid({ - width : 800, - height : 350, - collapsed : false, - tbar : true - }); - - var panel = frameGrid.getPanel([]); - panel.render(targetId); -}; - - -function PeakGrid(args) { - this.height = 500; - this.searchBar = false; - this.tbar = false; - this.collapsed = false; - this.width = 500; - this.showExtendedColumns = false; - - if (args != null) { - if (args.showExtendedColumns != null) { - this.showExtendedColumns = args.showExtendedColumns; - } - if (args.height != null) { - this.height = args.height; - } - if (args.searchBar != null) { - this.searchBar = args.searchBar; - } - - if (args.tbar != null) { - this.tbar = args.tbar; - } - - if (args.collapsed != null) { - this.collapsed = args.collapsed; - } - - if (args.width != null) { - this.width = args.width; - } - } -} - - -PeakGrid.prototype.refresh = function(buffers) { - this.store.loadData(this._prepareData(buffers), false); -}; - -PeakGrid.prototype._prepareData = function(buffers) { - -// for ( var i = 0; i < buffers.length; i++) { -// buffers[i].name = "Peak #" + (i+1); +MolarityForm.prototype._getNumericWithHelp = function(type, fieldLabel, fieldName, help) { + return Ext.create('Ext.container.Container', { + margin : "10 0 0 10", + items : [ { + xtype : type, + fieldLabel : fieldLabel, + name : fieldName, + id : this.id + fieldName, + decimalPrecision : 6 + }, { + xtype : 'label', + forId : 'myFieldId', + text : help, + margin : "5 0 0 105", + cls : "inline-help" + } ] + }); +}; + + +MolarityForm.prototype._getItems = function() { + this.macromoleculeCombo = BIOSAXS_COMBOMANAGER.getComboMacromoleculeByMacromolecules(this.getMacromuleculesCandidates(this.macromolecule), { + width : 250, + labelWidth : 100, + margin : 10 + }); + + return [ { + xtype : 'container', + flex : 1, + margin : '10 0 0 10', + border : 0, + layout : 'anchor', + defaultType : 'requiredtext', + items : [ this.macromoleculeCombo, + this._getNumericWithHelp("textfield", "Ratio", "ratio", "Number in assymmetric units") + ] + } ]; +}; + +MolarityForm.prototype._persist = function() { + var _this = this; + var macromoleculeId = this.macromoleculeCombo.getValue(); + var ratio = Ext.getCmp(this.id + "ratio").getValue(); + var comments = "Not used yet"; + var dataAdapter = new BiosaxsDataAdapter(); + this.panel.setLoading("Saving"); + dataAdapter.onSuccess.attach(function(sender, args) { + _this.onSave.notify(); + }); + dataAdapter.saveStoichiometry(this.macromolecule.macromoleculeId, macromoleculeId, ratio, comments); +}; + +MolarityForm.prototype._getButtons = function() { + var _this = this; + + function onClose() { + _this.onClose.notify(); + } + + return [ { + text : 'Save', + handler : function() { + _this._persist(); + } + }, { + text : 'Cancel', + handler : function() { + onClose(); + } + } ]; +}; + +MolarityForm.prototype.getPanel = function() { + this.panel = Ext.create('Ext.form.Panel', { +// width : null, + height : this.height, + margin : 2, + border : 1, + defaultType : 'requiredtext', + items : this._getItems(), + buttons : this._getButtons() + }); + return this.panel; +}; + +/** macromolecules contains all macromolecules except this one **/ +MolarityForm.prototype.getMacromuleculesCandidates = function(macromolecule) { + var macromolecules = []; + if ( BIOSAXS.proposal.macromolecules){ + for (var i = 0; i < BIOSAXS.proposal.macromolecules.length; i++) { + var m = BIOSAXS.proposal.macromolecules[i]; + if (this.macromolecule != null){ + if (m.macromoleculeId != this.macromolecule.macromoleculeId) { + macromolecules.push(m); + } + } + } + } + return macromolecules; +}; + + +/** It populates the form **/ +MolarityForm.prototype.refresh = function(macromolecule) { + this.macromolecule = macromolecule; + +}; + + +MolarityForm.prototype.input = function() { + return {}; +}; + + +/** It populates the form **/ +MolarityForm.prototype.test = function(targetId) { + var macromoleculeForm = new MolarityForm(); + var panel = macromoleculeForm.getPanel(); + panel.render(targetId); +}; + +///** +// * +// * @witdh +// * @height +// */ +//function MacromoleculeForm(args) { +// this.id = BUI.id(); +// this.width = 700; +// this.height = 500; +// +// if (args != null) { +// if (args.width != null) { +// this.width = args.width; +// } +// if (args.height != null) { +// this.height = args.height; +// } +// } +// +// this.onClose = new Event(this); +//} +// +//MacromoleculeForm.prototype.getMacromolecule = function() { +// this.macromolecule.name = Ext.getCmp(this.panel.getItemId()).getValues().name; +// this.macromolecule.acronym = Ext.getCmp(this.panel.getItemId()).getValues().acronym; +// this.macromolecule.comments = Ext.getCmp(this.panel.getItemId()).getValues().comments; +// this.macromolecule.extintionCoefficient = Ext.getCmp(this.panel.getItemId()).getValues().extintionCoefficient; +// this.macromolecule.molecularMass = Ext.getCmp(this.panel.getItemId()).getValues().molecularMass; +// return this.macromolecule; +//}; +// +//MacromoleculeForm.prototype.setMacromolecule = function(macromolecule) { +// this.pdbStore.loadData(macromolecule.structure3VOs); +// +//}; +// +//MacromoleculeForm.prototype.getForm = function(macromolecule) { +// this.panel = Ext.createWidget('form', { +// frame : false, +// border : 0, +// padding : 15, +// width : 550, +// height : 350, +// items : [ { +// xtype : 'container', +// layout : 'hbox', +// items : [ { +// xtype : 'container', +// flex : 1, +// border : false, +// layout : 'anchor', +// defaultType : 'requiredtext', +// items : [ { +// fieldLabel : 'Name', +// name : 'name', +// anchor : '95%', +// tooltip : "Name of the macromolecule", +// value : macromolecule.name +// }, { +// fieldLabel : 'Acronym', +// name : 'acronym', +// anchor : '95%', +// value : macromolecule.acronym +// } ] +// }, { +// xtype : 'container', +// flex : 1, +// layout : 'anchor', +// defaultType : 'textfield', +// items : [ { +// xtype : 'numberfield', +// fieldLabel : 'Mol. Mass (Da)', +// name : 'molecularMass', +// value : macromolecule.molecularMass, +// decimalPrecision : 6 +// }, { +// xtype : 'numberfield', +// fieldLabel : 'Extinction coef.', +// name : 'extintionCoefficient', +// value : macromolecule.extintionCoefficient, +// decimalPrecision : 6 +// } ] +// } ] +// }, { +// xtype : 'textareafield', +// name : 'comments', +// fieldLabel : 'Comments', +// value : macromolecule.comments, +// width : this.width - 10 +// }, +// +// { +// html : BUI.getWarningHTML("The macromolecule is unsaved. Save the macromolecule to get access to other tabs"), +// margin : "150 10 10 10", +// id : this.id + "unsavedWarning", +// hidden : !(!macromolecule.macromoleculeId) +// } +// +// ] +// }); +// return this.panel; +//}; +// +//MacromoleculeForm.prototype.save = function() { +// var _this = this; +// +// var adapter = new BiosaxsDataAdapter(); +// adapter.onSuccess.attach(function(sender, proposal) { +// BIOSAXS.proposal.setItems(proposal); +// _this.panel.setLoading(false); +// +// Ext.getCmp(_this.id + "assembly").enable() +// Ext.getCmp(_this.id + "advanced").enable(); +// Ext.getCmp(_this.id + "unsavedWarning").hide(); +// }); +// +// if (this.getMacromolecule().name == "") { +// BUI.showError("Name field is mandatory"); +// return; +// } +// if (this.getMacromolecule().acronym == "") { +// BUI.showError("Acroynm field is mandatory"); +// return; +// } +// +// /** Check if acronym is unique * */ +// if (this.getMacromolecule().macromoleculeId == null) { +// if (BIOSAXS.proposal.getMacromoleculeByAcronym(this.getMacromolecule().acronym) == null) { +// this.panel.setLoading("ISPyB: Saving Macromolecule") +// adapter.saveMacromolecule(this.getMacromolecule()); +// } else { +// alert("There is already an existing macromolecule with the same acronym"); +// +// } +// } else { +// this.panel.setLoading("ISPyB: Saving Macromolecule") +// adapter.saveMacromolecule(this.getMacromolecule()); +// } +// +//}; +// +//MacromoleculeForm.prototype.getPanel = function(macromolecule) { +// var _this = this; +// this.macromolecule = macromolecule; +// return Ext.createWidget('tabpanel', { +// height : this.height, +// margin : 5, +// plain : true, +// style : { +// padding : 5 +// }, +// items : [ { +// tabConfig : { +// title : "General", +// disabled : false +// }, +// items : [ this.getForm(macromolecule) ], +// bbar : [ "->", { +// text : 'Save', +// cls : 'btn-with-border', +// style : { +// +// border : 1 +// }, +// handler : function() { +// _this.save(); +// } +// }, { +// text : 'Close', +// cls : 'btn-with-border', +// handler : function() { +// _this.onClose.notify(); +// } +// } ] +// }, { +// tabConfig : { +// id : this.id + "assembly", +// title : "Assembly", +// tooltip : 'Description of subunits present in the macromolecule', +// // hidden : (!macromolecule.macromoleculeId), +// disabled : (!macromolecule.macromoleculeId) +// }, +// items : [ this.getMolarityGrid(macromolecule) ] +// }, { +// tabConfig : { +// id : this.id + "advanced", +// title : "Advanced Modeling", +// // hidden : (!macromolecule.macromoleculeId), +// tooltip : 'Definition of the description contacts and symetries', +// disabled : (!macromolecule.macromoleculeId) +// }, +// items : [ this.getRigidBodyForm(macromolecule) ] +// } ] +// }); +//}; +// +//MacromoleculeForm.prototype.getRigidBodyForm = function(macromolecule) { +// var _this = this; +// +// // [P1, P2, P3, P4 ,P5 ,P6 ,32, P42, P52, P62] + P222 +// var symmetry = Ext.create('Ext.data.Store', { +// fields : [ 's' ], +// data : [ { +// "s" : "P1" +// }, { +// "s" : "P2" +// }, { +// "s" : "P3" +// }, { +// "s" : "P4" +// }, { +// "s" : "P5" +// }, { +// "s" : "P6" +// }, { +// "s" : "P32" +// }, { +// "s" : "P42" +// }, { +// "s" : "P52" +// }, { +// "s" : "P62" +// }, { +// "s" : "P222" +// } ] +// }); +// +// if (macromolecule.symmetry == null) { +// macromolecule.symmetry = "P1"; +// } +// var comboBox = Ext.create('Ext.form.ComboBox', { +// fieldLabel : 'Symmetry', +// store : symmetry, +// id : 'comboSym', +// queryMode : 'local', +// displayField : 's', +// valueField : 's', +// value : macromolecule.symmetry, +// margin : "10 0 0 0", +// listeners : { +// change : function(combo, newValue, oldValue, eOpts) { +// macromolecule.symmetry = newValue; +// } +// } +// }); +// +// this.rigidBodyPanel = Ext.createWidget('form', { +// frame : false, +// border : 0, +// padding : 10, +// width : 550, +// height : 400, +// items : [ { +// xtype : 'container', +// layout : 'hbox', +// items : [ { +// xtype : 'container', +// border : false, +// layout : 'hbox', +// items : [ { +// xtype : 'label', +// forId : 'myFieldId', +// text : 'Contact Desc:', +// width : 105, +// margin : '0 0 0 0' +// }, { +// xtype : 'textfield', +// hideLabel : true, +// id : "contactsDescriptionFilePath", +// margin : '0 0 0 0', +// width : 300, +// value : macromolecule.contactsDescriptionFilePath +// }, { +// text : 'Upload', +// xtype : 'button', +// margin : "0 0 0 20", +// width : 100, +// handler : function() { +// _this._openUploadDialog(macromolecule.macromoleculeId, "CONTACTS", "Upload Contact Description File"); +// } +// } ] +// } ] +// }, +// +// comboBox, { +// xtype : 'checkbox', +// margin : '10 0 0 5', +// boxLabel : "I want rigid body modeling run on this stuff", +// checked : true, +// width : 300 +// }, _this.getPDBGrid(macromolecule) ] +// }); +// return this.rigidBodyPanel; +//}; +// +//MacromoleculeForm.prototype.update = function() { +// var _this = this; +// BIOSAXS.proposal.onInitialized.attach(function() { +// if (BIOSAXS.proposal != null) { +// var macromolecules = BIOSAXS.proposal.macromolecules; +// for (var i = 0; i < macromolecules.length; i++) { +// if (macromolecules[i].macromoleculeId == _this.macromolecule.macromoleculeId) { +// _this.macromolecule = macromolecules[i]; +// _this.setMacromolecule(_this.macromolecule); +// _this.molarityStore.loadData(_this.parseMolarityData(_this.macromolecule)); +// _this.pdbGrid.setLoading(false); +// Ext.getCmp("contactsDescriptionFilePath").setValue(_this.macromolecule.contactsDescriptionFilePath) +// _this.molarityGrid.setLoading(false); +// +// } +// } +// } +// }); +// this.molarityGrid.setLoading("Updating"); +// this.pdbGrid.setLoading("Updating"); +// BIOSAXS.proposal.init(); +//}; +// +///******************************************************************************* +// * MOLARITY GRID +// ******************************************************************************/ +// +//MacromoleculeForm.prototype.parseMolarityData = function(macromolecule) { +// var data = []; +// if (macromolecule.stoichiometry != null) { +// for (var i = 0; i < macromolecule.stoichiometry.length; i++) { +// data.push({ +// ratio : macromolecule.stoichiometry[i].ratio, +// acronym : macromolecule.stoichiometry[i].macromolecule3VO.acronym, +// comments : macromolecule.stoichiometry[i].macromolecule3VO.comments, +// stoichiometryId : macromolecule.stoichiometry[i].stoichiometryId, +// name : macromolecule.stoichiometry[i].macromolecule3VO.name +// }); +// } +// } +// return data; +//}; +// +//MacromoleculeForm.prototype.getMolarityGrid = function(macromolecule) { +// var _this = this; +// +// this.molarityStore = Ext.create('Ext.data.Store', { +// fields : [ 'acronym', 'ratio', 'comments', 'stoichiometryId', 'name' ], +// data : this.parseMolarityData(macromolecule), +// sorters : { +// property : 'ratio', +// direction : 'DESC' +// } +// }); +// +// this.molarityGrid = Ext.create('Ext.grid.Panel', { +// store : this.molarityStore, +// height : 350, +// padding : 5, +// columns : [ +// +// { +// text : 'Subunit', +// columns : [ { +// text : "Acronym", +// width : 100, +// hidden : false, +// dataIndex : 'acronym', +// sortable : true +// }, { +// text : "Name", +// width : 100, +// hidden : false, +// dataIndex : 'name', +// sortable : true +// }, { +// text : "Comments", +// width : 100, +// dataIndex : 'comments', +// sortable : true +// } ] +// }, { +// text : "Number
in assymmetric units", +// width : 150, +// dataIndex : 'ratio', +// tooltip : 'Number of times this subunit is present in the macromolecule', +// sortable : true +// }, { +// id : this.id + 'MOLARITY_REMOVE', +// flex : 0.1, +// sortable : false, +// renderer : function(value, metaData, record, rowIndex, colIndex, store) { +// return BUI.getRedButton('REMOVE'); +// } +// } ], +// listeners : { +// cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { +// if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'MOLARITY_REMOVE') { +// var dataAdapter = new BiosaxsDataAdapter(); +// dataAdapter.onSuccess.attach(function() { +// _this.molarityGrid.setLoading(false); +// _this.update(); +// }); +// dataAdapter.removeStoichiometry(record.data.stoichiometryId); +// _this.molarityGrid.setLoading("Removing Structure"); +// } +// } +// }, +// buttons : [ { +// text : 'Add molarity', +// handler : function() { +// +// function onClose() { +// w.destroy(); +// _this.update(); +// } +// var w = Ext.create('Ext.window.Window', { +// title : 'Molarity', +// height : 300, +// width : 500, +// modal : true, +// buttons : [ { +// text : 'Save', +// handler : function() { +// var macromoleculeId = (_this.macromoleculeCombo.getValue()); +// var ratio = Ext.getCmp(_this.id + "ratio").getValue(); +// var comments = ""; +// var dataAdapter = new BiosaxsDataAdapter(); +// dataAdapter.onSuccess.attach(function(sender, args) { +// _this.update(); +// w.destroy(); +// }); +// dataAdapter.saveStoichiometry(_this.macromolecule.macromoleculeId, macromoleculeId, ratio, comments); +// } +// }, { +// text : 'Cancel', +// handler : function() { +// onClose(); +// } +// } ], +// items : [ _this.getMolarityForm(macromolecule) ], +// listeners : { +// onEsc : function() { +// onClose(); +// }, +// close : function() { +// onClose(); +// } +// } +// }).show(); +// } +// } ] +// }); +// return this.molarityGrid; +//}; +// +///******************************************************************************* +// * PDB GRID +// ******************************************************************************/ +// +//MacromoleculeForm.prototype._openUploadDialog = function(macromoleculeId, type, title) { +// var _this = this; +// function onClose() { +// w.destroy(); +// _this.update(); +// } +// +// var w = Ext.create('Ext.window.Window', { +// title : title, +// height : 200, +// width : 400, +// modal : true, +// buttons : [ { +// text : 'Close', +// handler : function() { +// onClose(); +// } +// } ], +// layout : 'fit', +// items : { +// html : "" +// }, +// listeners : { +// onEsc : function() { +// onClose(); +// }, +// close : function() { +// onClose(); +// } +// } +// }).show(); +//}; +// +//MacromoleculeForm.prototype._getPlugins = function() { +// var _this = this; +// var plugins = []; +// // if (this.updateRowEnabled) { +// plugins.push(Ext.create('Ext.grid.plugin.RowEditing', { +// clicksToEdit : 1, +// listeners : { +// validateedit : function(grid, e) { +// /** Comments are always updatable* */ +// e.record.raw.symmetry = e.newValues.symmetry; +// e.record.raw.multiplicity = e.newValues.multiplicity; +// +// var adapter = new BiosaxsDataAdapter(); +// adapter.onSuccess.attach(function(sender, measurement) { +// // _this.grid.setLoading(false); +// }); +// adapter.onError.attach(function() { +// alert("Error"); +// // _this.grid.setLoading(false); +// }); +// +// // _this.grid.setLoading(); +// adapter.saveStructure(e.record.raw); +// } +// } +// })); +// // } +// return plugins; +//}; +// +//MacromoleculeForm.prototype.getPDBGrid = function(macromolecule) { +// var _this = this; +// +// var data = macromolecule.structure3VOs; +// +// // /** Getting PDB from subunits **/ +// // if (macromolecule != null){ +// // if (macromolecule.stoichiometry != null){ +// // for (var i =0; i < macromolecule.stoichiometry.length; i++){ +// // var stoichiometry = macromolecule.stoichiometry[i]; +// // if (stoichiometry.macromolecule3VO != null){ +// // if (stoichiometry.macromolecule3VO.structure3VOs != null){ +// // for (var j = 0; j < stoichiometry.macromolecule3VO.structure3VOs.length; +// // j++) { +// // var structure = stoichiometry.macromolecule3VO.structure3VOs[j]; +// // structure["isSubunit"] = stoichiometry.macromolecule3VO.acronym; +// // data.push(structure) +// // } +// // } +// // } +// // } +// // } +// // } +// +// this.pdbStore = Ext.create('Ext.data.Store', { +// fields : [ 'filePath', 'structureId', 'structureType', 'symmetry', 'structureId', 'name', 'multiplicity' ], +// data : macromolecule.structure3VOs, +// groupField : 'structureType', +// sorters : { +// property : 'structureId', +// direction : 'DESC' +// } +// }); +// +// var groupingFeature = Ext.create('Ext.grid.feature.Grouping', { +// groupHeaderTpl : Ext.create('Ext.XTemplate', +// "
{name:this.formatName}
", { +// formatName : function(name) { +// return name; +// } +// }), +// hideGroupedHeader : true, +// startCollapsed : false +// }); +// +// this.pdbGrid = Ext.create('Ext.grid.Panel', { +// margin : "15 0 0 5", +// height : 250, +// store : this.pdbStore, +// plugins : _this._getPlugins(), +// buttons : [ { +// // text : 'Add PDB file', +// text : 'Add Modeling Option', +// handler : function() { +// _this._openUploadDialog(macromolecule.macromoleculeId, "PDB", 'Upload PDB File'); +// } +// } +// +// ], +// columns : [ +// { +// text : "structureId", +// flex : 0.2, +// hidden : true, +// dataIndex : 'structureId', +// sortable : true +// }, +// { +// text : "File", +// flex : 0.5, +// dataIndex : 'filePath', +// sortable : true, +// hidden : true +// }, +// { +// text : "PDB", +// flex : 0.4, +// dataIndex : 'name', +// sortable : true +// }, +// { +// text : "Symmetry", +// flex : 0.4, +// dataIndex : 'symmetry', +// sortable : true, +// editor : { +// xtype : 'combobox', +// typeAhead : true, +// triggerAction : 'all', +// selectOnTab : true, +// store : [ [ "P1", "P1" ], [ "P2", "P2" ], [ "P3", "P3" ], [ "P4", "P4" ], [ "P5", "P5" ], [ "P6", "P6" ], [ "P32", "P32" ], [ "P42", "P42" ], +// [ "P52", "P52" ], [ "P62", "P62" ], [ "P222", "P222" ] ], +// } +// }, { +// text : "Multiplicity", +// flex : 0.4, +// dataIndex : 'multiplicity', +// sortable : true, +// editor : { +// xtype : 'textfield' +// } +// +// }, { +// text : "Subunit", +// flex : 0.2, +// dataIndex : 'isSubunit', +// sortable : true, +// hidden : true +// }, { +// text : "Type", +// flex : 0.2, +// dataIndex : 'structureType', +// sortable : true, +// hidden : true +// }, +// +// { +// id : this.id + 'REMOVE', +// flex : 0.2, +// hidden : true, +// sortable : false, +// renderer : function(value, metaData, record, rowIndex, colIndex, store) { +// return BUI.getRedButton('REMOVE'); +// } +// }, ], +// +// listeners : { +// itemdblclick : function(dataview, record, item, e) { +// _this._editExperiment(record.raw.experimentId); +// }, +// cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { +// +// if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'REMOVE') { +// var dataAdapter = new BiosaxsDataAdapter(); +// dataAdapter.onSuccess.attach(function() { +// _this.pdbGrid.setLoading(false); +// _this.update(); +// }); +// dataAdapter.removeStructure(record.data.structureId); +// _this.pdbGrid.setLoading("Removing PDB file"); +// } +// +// } +// }, +// viewConfig : { +// getRowClass : function(record, rowIdx, params, store) { +// if (record.raw.isSubunit != null) { +// return "blue-row"; +// } +// } +// } +// }); +// +// return this.pdbGrid; +//}; +// +//MacromoleculeForm.prototype.getMolarityForm = function(macromolecule) { +// var _this = this; +// var data = []; +// for (var i = 0; i < BIOSAXS.proposal.macromolecules.length; i++) { +// var m = BIOSAXS.proposal.macromolecules[i]; +// if (m.macromoleculeId != macromolecule.macromoleculeId) { +// data.push(m); +// } +// // } - /** Adding information of buffer **/ - if (buffers.length > 0){ - buffers.unshift({ - name : "BUFFER", - start : 0, - experimentId : buffers[0].experimentId, - end : buffers[0].start - 1 - }); - } - - - return buffers; -}; - -PeakGrid.prototype._getTbar = function() { - var _this = this; - var actions = []; +// this.macromoleculeCombo = BIOSAXS_COMBOMANAGER.getComboMacromoleculeByMacromolecules(data, { +// width : 250, +// labelWidth : 100, +// margin : 10 +// }); +// +// return Ext.createWidget('form', { +// +// frame : false, +// border : 0, +// // padding : 15, +// width : 550, +// height : 350, +// items : [ { +// xtype : 'container', +// flex : 1, +// border : false, +// layout : 'anchor', +// defaultType : 'requiredtext', +// items : [ this.macromoleculeCombo, { +// xtype : 'numberfield', +// name : 'Ratio', +// id : _this.id + "ratio", +// fieldLabel : 'Number in assymmetric units', +// value : 1, +// decimalPrecision : 6, +// margin : 10 +// }, { +// xtype : 'textareafield', +// name : 'comments', +// fieldLabel : 'Comments', +// margin : 10, +// width : 400, +// value : "" +// +// } ] +// } ] +// }); +// +//}; +// +///******************************************************************************* +// * JAVASCRIPT DOC +// ******************************************************************************/ +//MacromoleculeForm.prototype.input = function() { +// return { +// macromolecule : DATADOC.getMacromolecule_10() +// }; +//}; +// +//MacromoleculeForm.prototype.test = function(targetId) { +// var macromoleculeForm = new MacromoleculeForm(); +// var panel = macromoleculeForm.getPanel(macromoleculeForm.input().macromolecule); +// panel.render(targetId); +//}; + +function ResultSummaryForm() { + this.radiationDamageThreshold = BUI.getRadiationDamageThreshold(); + this.qualityThreshold = BUI.getQualityThreshold(); - actions.push(Ext.create('Ext.Action', { - icon : '../images/add.png', - text : 'Add Buffer', - disabled : false, - handler : function(widget, event) { - var window = new BufferWindow(); - window.onSuccess.attach(function(sender) { - _this.store.loadData(BIOSAXS.proposal.getBuffers()); - }); - window.draw({}); - } - })); - return actions; -}; + /** Data clusters with bufers **/ + this.clusterByBuffers = false; + + /** Visualization are groupeb by concenttrations. Ex: 4.5mg/ml and 4.7mg/ml will be clusterized together **/ + this.collapseConcentrations = true; + + this.summaryHeight = 350; + this.id = BUI.id(); -PeakGrid.prototype.getPanel = function(buffers) { var _this = this; - this.peakCount = 0; - this.store = Ext.create('Ext.data.Store', { - fields : ['name', {name:'start', type : 'int'}, {name:'end', type : 'int'}], - data : buffers - - }); - - this.store.sort('start'); - var type = 'Ext.grid.Panel'; - if (this.searchBar == true) { - type = 'Ext.ux.LiveSearchGridPanel'; - } - this.grid = Ext.create(type, { - store : this.store, - height : this.height, - width : this.width, - margin : 5, - columns : [ - { - text : '', - dataIndex : 'name', - width : 100, - hidden : _this.showExtendedColumns, - renderer : function(val, y, sample) { - if (sample.data.name == "BUFFER") - return "BUFFER"; - _this.peakCount++; - return "Peak #" + _this.peakCount; - } - - - }, - { - text : 'Peaks', - dataIndex : 'start', - sortable : true, - width : 100, - hidden: !_this.showExtendedColumns, - renderer : function(val, y, sample) { - return "From " + Number(sample.raw.start) + " to " + Number(sample.raw.end); - } - }, - { - text : 'Frames', - hidden : _this.showExtendedColumns, - columns : [ - { - text : 'Start', - dataIndex : 'start', - sortable : true, - hidden : _this.showExtendedColumns, - width : 100, - renderer : function(val, y, sample) { - return Number(val); - } - },{ - text : 'End', - dataIndex : 'end', - width : 100, - hidden : _this.showExtendedColumns, - renderer : function(val, y, sample) { - return Number(val); - } - },{ - text : 'Total', - dataIndex : 'end', - width : 100, - hidden : _this.showExtendedColumns, - renderer : function(val, y, sample) { - return "" + (sample.raw.end - sample.raw.start) + " frames"; - } - } - ] - } -// , -// { -// text : '', -// hidden : _this.showExtendedColumns, -// renderer : function(val, x, sample) { -// if (sample != null) { -// return BUI.getZipHTMLByFrameRangeId(sample.raw.experimentId, sample.raw.start, sample.raw.end); -// } -// }, -// width : 100 -// } - ], - flex : 1, - viewConfig : { - stripeRows : true, - getRowClass : function(record, rowIdx, params, store) { - if (record.raw.name == "BUFFER") { - return "blue-row"; - } - } - } + this.qualityControlResultsWidget = new QualityControlResultsWidget({ + qualityThreshold : this.radiationDamageThreshold, + radiationDamageThreshold : this.qualityThreshold + + }); + this.qualityControlResultsWidget.onQualityChanged.attach(function(sender, value) { + _this.qualityThreshold = value; + _this.thresholdsChanged(); }); - /** Adding the tbar **/ - if (this.tbar) { - this.grid.addDocked({ - xtype : 'toolbar', - items : this._getTbar() - }); - } - return this.grid; -}; + this.qualityControlResultsWidget.onRadiationDatamageChanged.attach(function(sender, value) { + _this.radiationDamageThreshold = value; + _this.thresholdsChanged(); -PeakGrid.prototype.input = function() { - return []; -}; + }); + this.concentrationHTMLTableWidget = new ConcentrationHTMLTableWidget({ + height : 250, + showBufferColumns : this.clusterByBuffers + }); -PeakGrid.prototype.test = function(targetId) { - var PeakGrid = new PeakGrid({ - width : 800, + this.plot = new BoxWhiskerGraph({ + targetId : _this.id + '_boxPlot', height : 350, - collapsed : false, - tbar : true + width : 450, + maxBoxWidth : 25 }); - var panel = PeakGrid.getPanel([]); - panel.render(targetId); -}; - -/** - * Macromolecule Grid showing macromolecules and adding anb updating buttons - * - * @height - * @maxHeight - * @width - * @cssFontStyle - * @searchBar makes this grid as Ext.ux.LiveSearchGridPanel - * @tbar top bar containing "Add" and "Update From SMIS" button - * @collapsed - * @collapsible - * @btnEditVisible - * @btnRemoveVisible - * @multiselect makes it multiselect using Ext.selection.CheckboxModel - * - * #onSelected - * #onMacromoleculesChanged - */ -function MacromoleculeGrid(args) { - this.height = 500; - this.width = 500; - this.id = BUI.id(); - this.maxHeight = this.height; + this.rangePlot = new RangeWhiskerGraph({ + targetId : _this.id + '_rangePlot', + height : 350, + width : 450, + maxBoxWidth : 25 + }); +} - this.searchBar = false; - this.tbar = false; +ResultSummaryForm.prototype.thresholdsChanged = function() { + var parsedData = this.prepareData(this.rawData); - this.collapsible = true; - this.collapsed = true; + var filtered = JSON.parse(JSON.stringify(parsedData)); + filtered.concentration.concentrations = []; + for ( var conc in parsedData.concentration.concentrations) { + if (parsedData.concentration.concentrations[conc].calculation.rgGnom.validNumber > 0) { + filtered.concentration.concentrations.push(parsedData.concentration.concentrations[conc]); + } + } - this.btnEditVisible = true; - this.btnRemoveVisible = false; - this.multiselect = false; + this.plotWhisker(filtered); + this.plotRange(filtered); - /** Font style applied to the acronym column **/ - this.cssFontStyle = null; + this.concentrationHTMLTableWidget.refresh(parsedData); +}; - if (args != null) { - if (args.height != null) { - this.height = args.height; - this.maxHeight = this.height; - } - if (args.maxHeight != null) { - this.maxHeight = args.maxHeight; - } - if (args.width != null) { - this.width = args.width; - } - if (args.cssFontStyle != null) { - this.cssFontStyle = args.cssFontStyle; - } +/** Given a frame object (object returned by analyzeFrames()) and depending of the threshold indicates if a datacollection has radiation damage **/ +ResultSummaryForm.prototype.hasRadiationDamage = function(frameObject) { + var bb = frameObject.bufferBeforeFramesMerged / frameObject.framesCount; + var ba = frameObject.bufferAfterFramesMerged / frameObject.framesCount; + var mol = frameObject.framesMerge / frameObject.framesCount; + return !((bb >= this.radiationDamageThreshold) && (ba >= this.radiationDamageThreshold) && (mol >= this.radiationDamageThreshold)); +}; - if (args.searchBar != null) { - this.searchBar = args.searchBar; - } - if (args.tbar != null) { - this.tbar = args.tbar; - } - if (args.collapsible != null) { - this.collapsible = args.collapsible; - } - if (args.collapsed != null) { - this.collapsed = args.collapsed; - } - if (args.btnEditVisible != null) { - this.btnEditVisible = args.btnEditVisible; - } - if (args.btnRemoveVisible != null) { - this.btnRemoveVisible = args.btnRemoveVisible; - } - if (args.multiselect != null) { - this.multiselect = args.multiselect; - } +/** Return (frameObject) an object with the information about the frames for a data collection**/ +ResultSummaryForm.prototype.analyzeFrames = function(dataCollectionRow) { + return { + bufferAfterFramesMerged : dataCollectionRow.bufferAfterFramesMerged, + bufferBeforeFramesMerged : dataCollectionRow.bufferBeforeFramesMerged, + framesCount : dataCollectionRow.framesCount, + framesMerge : dataCollectionRow.framesMerge + }; +}; + +ResultSummaryForm.prototype.detectDataCollectionWarnings = function(dataCollectionRow) { + var frameObject = this.analyzeFrames(dataCollectionRow); + var warnings = { + count : 0, + type : [] + }; + + if (this.hasRadiationDamage(frameObject)) { + warnings.count = warnings.count + 1; + warnings.type.push("RADIATION DAMAGE"); } - this.onSelected = new Event(); - - this.onMacromoleculesChanged = new Event(); -} + if (Number(dataCollectionRow.quality) < this.qualityThreshold) { + warnings.count = 1;//warnings.count + 1; + warnings.type.push("Quality <" + this.qualityThreshold); + } + return warnings; +}; +/** Return array composed by {concentration} objects **/ +ResultSummaryForm.prototype.getConcentrations = function(data) { + var concentrationsId = {}; + for ( var i = 0; i < data.length; i++) { + var warning = this.detectDataCollectionWarnings(data[i]); + var id = data[i].conc;// + "_" + data[i].bufferId; + if (this.clusterByBuffers) { + id = data[i].conc + "_" + data[i].bufferId; + } + if (concentrationsId[id] == null) { + concentrationsId[id] = { + id : id, + concentration : Number(data[i].conc).toFixed(2), + bufferId : data[i].bufferId, + bufferAcronym : data[i].bufferAcronym, + rgGuinier : [], + i0Guinier : [], + rgGnom : [], + dMax : [], + quality : [], + frames : [], + frames_warning : warning.count + }; + } else { + concentrationsId[id].frames_warning = concentrationsId[id].frames_warning + warning.count; + } + concentrationsId[id].frames.push(data[i]); -MacromoleculeGrid.prototype.edit = function(macromolecule) { - var _this = this; - var window = new MacromoleculeWindow(); - window.onSave.attach(function(sender) { - _this.store.loadData(BIOSAXS.proposal.getMacromolecules()); - _this.onMacromoleculesChanged.notify(); - }); - window.draw(macromolecule); -}; + if (warning.count == 0) { + concentrationsId[id].rgGuinier.push(data[i].rgGuinier); + concentrationsId[id].i0Guinier.push(data[i].I0); + concentrationsId[id].quality.push(data[i].quality); + concentrationsId[id].rgGnom.push(data[i].rgGnom); + concentrationsId[id].dMax.push(data[i].dmax); + } -MacromoleculeGrid.prototype.getTbar = function() { - var _this = this; - var actions = []; + } + var concentrations = []; + for ( var item in concentrationsId) { + if (concentrationsId.hasOwnProperty(item)) { + concentrations.push({ + concentration : concentrationsId[item].concentration, + id : item, + bufferId : Number(concentrationsId[item].bufferId).toFixed(2), + bufferAcronym : concentrationsId[item].bufferAcronym, + rgGuinier : concentrationsId[item].rgGuinier, + /** Frames **/ + frames : concentrationsId[item].frames, + frames_warning : concentrationsId[item].frames_warning, + /** Calculation **/ + calculation : { + rgGuinier : BUI.getStandardDeviation(concentrationsId[item].rgGuinier), + i0Guinier : BUI.getStandardDeviation(concentrationsId[item].i0Guinier), + quality : BUI.getStandardDeviation(concentrationsId[item].quality), + rgGnom : BUI.getStandardDeviation(concentrationsId[item].rgGnom), + dMax : BUI.getStandardDeviation(concentrationsId[item].dMax) - actions.push(Ext.create('Ext.Action', { - icon : '../images/add.png', - text : 'Add macromolecule', - disabled : false, - handler : function(widget, event) { - _this.edit(); - } - })); - actions.push("->"); - actions.push(Ext.create('Ext.Action', { - icon : '../images/folder_go.png', - text : 'Update From SMIS', - tooltip : "Retrieve all the macromolecules of your proposal from SMIS database", - disabled : false, - handler : function(widget, event) { - _this.grid.setLoading("Connecting to SMIS"); - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, data) { - BIOSAXS.proposal.setMacromolecules(data.macromolecules); - _this.refresh(BIOSAXS.proposal.macromolecules); - _this.grid.setLoading(false); - }); - adapter.onError.attach(function(sender, data) { - _this.grid.setLoading(false); + } }); - adapter.updateDataBaseFromSMIS(); } - })); - return actions; -}; + } -MacromoleculeGrid.prototype.deselectAll = function() { - this.grid.getSelectionModel().deselectAll(); + return { + concentrations : concentrations + }; }; -MacromoleculeGrid.prototype.selectById = function(macromoleculeId) { - this.grid.getSelectionModel().deselectAll(); - for ( var i = 0; i < this.grid.getStore().data.items.length; i++) { - var item = this.grid.getStore().data.items[i].raw; - if (item.macromoleculeId == macromoleculeId) { - this.grid.getSelectionModel().select(i); +ResultSummaryForm.prototype.prepareData = function(data) { + /** Return array composed by {acronym, bufferId} objects **/ + function getBuffers(data) { + var buffersId = {}; + for ( var i = 0; i < data.length; i++) { + buffersId[data[i].bufferId] = data[i].acronym; + } + var buffers = []; + for ( var id in buffersId) { + if (buffersId.hasOwnProperty(id)) { + buffers.push({ + acronym : buffersId[id], + bufferId : id + }); + } } + return buffers; } -}; -MacromoleculeGrid.prototype.refresh = function(macromolecules) { - this.store.loadData(macromolecules, false); + /** Get a string with all the concentrations **/ + function getConcentrationString(parseConcentrations) { + var concentrations = []; + for ( var i = 0; i < parseConcentrations.concentrations.length; i++) { + concentrations.push(parseConcentrations.concentrations[i].concentration + " mg/ml "); + } + return concentrations.toString(); + } + + var parseConcentrations = this.getConcentrations(data); + + return { + dataCollectionCount : data.length, + buffers : getBuffers(data), + concentration : parseConcentrations, + concentrationLabel : getConcentrationString(parseConcentrations) + }; }; -MacromoleculeGrid.prototype.getColumns = function() { - var _this = this; - var columns = [ - { - text : 'Acronym', - dataIndex : 'acronym', - id : this.id + "acronym", - flex : 1, - renderer : function(value, metaData, record, rowIndex, colIndex, store) { - if (_this.cssFontStyle != null) { - return "" + value + ""; +ResultSummaryForm.prototype.getDataForWhisker = function(data) { + var clusters = []; + + var concentrations = {}; + var i = 0; + var conc = 0; + if (this.collapseConcentrations) { + for (i = 0; i < data.concentration.concentrations.length; i++) { + conc = data.concentration.concentrations[i]; + var concentration = Number(conc.concentration).toFixed(0); + if (concentrations[concentration] == null) { + concentrations[concentration] = {}; + concentrations[concentration].concentration = concentration; + concentrations[concentration].calculation = {}; + concentrations[concentration].calculation.rgGuinier = {}; + concentrations[concentration].calculation.rgGuinier.values = []; + concentrations[concentration].calculation.rgGuinier.values = conc.calculation.rgGuinier.values; + concentrations[concentration].calculation.rgGnom = {}; + concentrations[concentration].calculation.rgGnom.values = []; + concentrations[concentration].calculation.rgGnom.values = conc.calculation.rgGnom.values; + } else { + concentrations[concentration].calculation.rgGuinier.values = concentrations[concentration].calculation.rgGuinier.values + .concat(conc.calculation.rgGuinier.values); + concentrations[concentration].calculation.rgGnom.values = concentrations[concentration].calculation.rgGnom.values + .concat(conc.calculation.rgGnom.values); } - return value; } - }, { - text : 'Name', - dataIndex : 'name', - id : this.id + "name", - flex : 1, - hidden : true - } ]; - if (this.btnEditVisible) { - columns.push({ - id : _this.id + 'buttonEditMacromolecule', - width : 85, - sortable : false, - renderer : function(value, metaData, record, rowIndex, colIndex, store) { - if (_this.btnEditVisible) { - return BUI.getGreenButton('EDIT'); - } - return null; + /** From object to array **/ + var array = []; + for ( var key in concentrations) { + if (concentrations.hasOwnProperty(key)) { + array.push(concentrations[key]); } + } + data.concentration.concentrations = array; + } + + for (i = 0; i < data.concentration.concentrations.length; i++) { + conc = data.concentration.concentrations[i]; + clusters.push({ + name : conc.concentration + "mg/ml", + concentration : Number(conc.concentration), + x : Number(conc.concentration), + classes : [] + }); + clusters[clusters.length - 1].classes.push({ + name : "Guinier", + color : '#9A2EFE', + values : conc.calculation.rgGuinier.values + + }); + clusters[clusters.length - 1].classes.push({ + name : "P(r)", + color : '#2E64FE', + values : conc.calculation.rgGnom.values + }); } - if (this.btnRemoveVisible) { - columns.push({ - id : _this.id + 'buttonRemoveMacromolecule', - width : 85, - sortable : false, - renderer : function(value, metaData, record, rowIndex, colIndex, store) { - if (_this.btnRemoveVisible) { - return BUI.getRedButton('REMOVE'); - } - return null; - } + return { + clusters : clusters.sort(function(a, b) { + return a.concentration - b.concentration; + }) + }; +}; + +ResultSummaryForm.prototype.getDataForWhiskerQuality = function(data) { + var clusters = []; + + for ( var i = 0; i < data.concentration.concentrations.length; i++) { + var conc = data.concentration.concentrations[i]; + clusters.push({ + name : conc.concentration + "mg/ml", + classes : [] + }); + clusters[clusters.length - 1].classes.push({ + name : "Quality", + values : conc.calculation.quality.values + }); } - return columns; + return { + clusters : clusters + }; +}; + +ResultSummaryForm.prototype.plotQualityWhisker = function(parsedData) { + this.qualityPlot.refresh(this.getDataForWhiskerQuality(parsedData)); }; +ResultSummaryForm.prototype.plotWhisker = function(parsedData) { + this.plot.refresh(this.getDataForWhisker(parsedData)); +}; -/** Returns the grid **/ -MacromoleculeGrid.prototype.getPanel = function() { +ResultSummaryForm.prototype.plotRange = function(parsedData) { + this.rangePlot.refresh(this.getDataForWhisker(parsedData)); +}; + +ResultSummaryForm.prototype.getPanel = function(data) { var _this = this; + _this.rawData = data; + var parsedData = this.prepareData(data); - this.store = Ext.create('Ext.data.Store', { - fields : [ 'macromoleculeId', 'name', 'acronym' ] - }); + this.panel = Ext + .createWidget( + 'form', + { + bodyPadding : 20, + frame : false, + border : 0, + width : this.width, + height : this.summaryHeight + 1000, + items : [ { + xtype : 'container', + layout : 'hbox', + items : [ { + xtype : 'container', + flex : 1, + border : false, + layout : 'anchor', + defaultType : 'textfield', + items : [ + _this.qualityControlResultsWidget.getPanel(), + { + xtype : 'fieldset', + width : 950, + margin : "20, 0 0 0", + height : this.summaryHeight + 500, + items : [ + { + html : "
Concentration Analysis
", + border : 0, + width : 900 - var type = 'Ext.grid.Panel'; - if (this.searchBar == true) { - type = 'Ext.ux.LiveSearchGridPanel'; - } + }, + { + html : this.concentrationHTMLTableWidget.getPanel(parsedData), + border : 0, + width : 900, + margin : "10, 0 0 10" - this.selModel = Ext.create('Ext.selection.RowModel', { -// allowDeselect : true, -// mode : 'MULTI', - listeners : { - selectionchange : function(sm, selections) { - var selected = []; - for (var i = 0; i < selections.length; i++) { - selected.push(selections[i].raw); - } - _this.onSelected.notify(selected); - } - } - }); + }, + { + xtype : 'container', + layout : 'vbox', + border : 5, + items : [ + { + html : "
Rg based on Guinier and Gnom
", + border : 8, + width : 900, + margin : "20 0 0 10" - this.grid = Ext.create(type, { - id : this.id, - title : 'Macromolecules', - collapsible : this.collapsible, - collapsed : this.collapsed, - store : this.store, - height : this.height, - maxHeight : this.maxHeight, - selModel : this.selModel, - columns : this.getColumns(), - width : this.width, - viewConfig : { - stripeRows : true, - listeners : { - 'celldblclick' : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { - _this.edit(BIOSAXS.proposal.getMacromoleculeById(record.data.macromoleculeId)); - }, - 'cellclick' : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { - if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonEditMacromolecule') { - _this.edit(BIOSAXS.proposal.getMacromoleculeById(record.data.macromoleculeId)); - } - if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonRemoveMacromolecule') { - BUI.showBetaWarning(); - } - } + }, { + xtype : 'container', + layout : 'hbox', + items : [ { + xtype : 'container', + layout : 'vbox', + items : [ { + html : "
", + border : 0, + width : this.plot.width, + padding : "10 0 0 20", + listeners : { + afterrender : function() { + _this.plotWhisker(parsedData); - } - } - }); + } + } + }, { + html : "
Concentration (mg/ml)
", + width : 450, + border : 0, + margin : "-50 0 0 0" + + } ] + }, { + xtype : 'container', + layout : 'vbox', + items : [ { + html : "
", + border : 0, + width : this.rangePlot.width, + padding : "10 0 0 10", + listeners : { + afterrender : function() { + _this.plotRange(parsedData); + } + } + }, { + html : "
Concentration (mg/ml)
", + width : 450, + border : 0, + margin : "-50 0 0 0" + + } ] + } ] + } ] + } ] + } ] + } + + ] + } ] + }); + return this.panel; - /** Adding the tbar **/ - if (this.tbar) { - this.grid.addDocked({ - xtype : 'toolbar', - items : this.getTbar() - }); - } - return this.grid; }; -MacromoleculeGrid.prototype.input = function() { +ResultSummaryForm.prototype.input = function() { return { - proposal : DATADOC.getProposal_10() + data : DATADOC.getData_3367() }; }; -MacromoleculeGrid.prototype.test = function(targetId) { - var macromoleculeGrid = new MacromoleculeGrid({ - width : 800, - height : 350, - collapsed : false, - tbar : true - }); - - BIOSAXS.proposal = new Proposal(macromoleculeGrid.input().proposal); - var panel = macromoleculeGrid.getPanel(BIOSAXS.proposal.macromolecules); +ResultSummaryForm.prototype.test = function(targetId) { + var resultSummaryForm = new ResultSummaryForm(); + var panel = resultSummaryForm.getPanel(resultSummaryForm.input().data); panel.render(targetId); + }; -/** - * Shows measurements with their attributes as specimen, exposure temperature, - * volume to load, transmission etc... - * - * @addBtnMultipleEdit: if true add a button for changing measurements' - * parameters by choosing multiple measurements. It opens - * MultipleEditMeasurementGridWindow - * @collapsed: if true it doesn't show buffer's measurements - * @editor: hashmap containing columns to be edited. It is an array of a json - * like editor of Ext - * @selModel - * @collapseBtnEnable - * @removeBtnEnabled Ext.selection.RowModel - * @addBtnEnable - * @updateRowEnabled true/false if update plugin is set to the grid - * @showTitle - * @title - * @isStatusColumnHidden - * @isPriorityColumnHidden - * @isTimeColumnHidden - * @estimateTime - * @margin - * @tbar - * @height - * @maxHeight - * @minHeight - * @width - * @maxWidth - * @resizable - * @experimentColorBased when colors for buffers and macromolecules are not - * selected by the proposal but by experiment, so the - * number of colors is smaller +/** + * Example form + * + * @witdh + * @height + */ +function RigibBodyModelingForm(args) { + this.id = BUI.id(); + this.width = 700; + this.height = 500; + + if (args != null) { + if (args.width != null) { + this.width = args.width; + } + if (args.height != null) { + this.height = args.height; + } + } + + var _this = this; + this.rigidBodyGrid = new AprioriRigidBodyGrid(); + + this.rigidBodyGrid.onUploadFile.attach(function(sender, type, title){ + _this._openUploadDialog(_this.macromolecule.macromoleculeId, type, title); + }); + + this.rigidBodyGrid.onRemove.attach(function(sender, type, title){ + _this._update(); + }); + + this.onSave = new Event(this); + +} + +RigibBodyModelingForm.prototype._getItems = function() { + var _this = this; + + + return [ { + xtype : 'label', + forId : 'myFieldId', + text : 'Information for model fit, mixture analysis and rigid body modeling', + margin : '15 0 20 10', + cls : "inline-help" + }, + this.rigidBodyGrid.getPanel(), + { + xtype : 'label', + forId : 'myFieldId', + text : 'Distance restraints may be imposed on the model using contacts conditions file (OPTIONAL)', + margin : '25 0 5 10', + cls : "inline-help" + }, + { + xtype : 'container', + layout : 'hbox', + items : [ { + xtype : 'container', + border : false, + layout : 'hbox', + items : [ { + xtype : 'label', + forId : 'myFieldId', + text : 'Contact Description File: (Optional)', + width : 150, + margin : '10 0 0 10' + }, { + id : this.id + "contactsDescriptionFilePath", + xtype : 'textfield', + hideLabel : true, + margin : '10 0 0 0', + width : 200, + disabled: true + }, { + text : 'Upload', + xtype : 'button', + margin : "10 0 0 10", + width : 80, + handler : function() { + + _this._openUploadDialog(_this.macromolecule.macromoleculeId, "CONTACTS", "Upload Contact Description File"); + } + }, { + text : 'Remove', + id : _this.id + "_remove", + xtype : 'button', + margin : "10 0 0 10", + width : 80, + handler : function() { + _this.macromolecule.contactsDescriptionFilePath = null; + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, proposal) { + /** Updating the proposal global object **/ + BIOSAXS.proposal.setItems(proposal); + _this.panel.setLoading(false); + + var saved = BIOSAXS.proposal.getMacromoleculeByAcronym(_this.macromolecule.acronym); + /** Refreshing to check that everything is ok **/ + _this.refresh(saved); + _this.onSave.notify(saved); + }); + + _this.panel.setLoading("Saving Macromolecule") + adapter.saveMacromolecule(_this.macromolecule); + } + } ] + } ] + }, { + xtype : 'panel', + html : "Go to SASREF manual for further information", + margin : "10 0 0 160", + border : 0 + + }, + { + xtype : 'checkbox', + margin : '10 0 0 5', + boxLabel : "I want rigid body modeling run on this stuff", + checked : true, + width : 300 + } ] + +}; + +/** Because update is a jsp page we don't know if the user has uploaded a file or not then we need to refresh **/ +RigibBodyModelingForm.prototype._update = function(macromoleculeId, type, title) { + var _this = this; + BIOSAXS.proposal.onInitialized.attach(function() { + if (BIOSAXS.proposal != null) { + _this.refresh(BIOSAXS.proposal.getMacromoleculeById(_this.macromolecule.macromoleculeId)); + _this.panel.setLoading(false); + } + }); + this.panel.setLoading(); + BIOSAXS.proposal.init(); +}; + +RigibBodyModelingForm.prototype._openUploadDialog = function(macromoleculeId, type, title) { + var _this = this; + function onClose() { + w.destroy(); + _this._update(); + + } + + var w = Ext.create('Ext.window.Window', { + title : title, + height : 200, + width : 400, + modal : true, + buttons : [ { + text : 'Close', + handler : function() { + onClose(); + } + } ], + layout : 'fit', + items : { + html : "" + }, + listeners : { + onEsc : function() { + onClose(); + }, + close : function() { + onClose(); + } + } + }).show(); +}; + +RigibBodyModelingForm.prototype._getButtons = function() { + return []; +}; + +RigibBodyModelingForm.prototype.getPanel = function() { + var _this = this; + this.panel = Ext.create('Ext.form.Panel', { + width : this.width, + height : this.height, + margin : 10, + border : 1, + defaultType : 'textfield', + items : this._getItems(), + buttons : this._getButtons(), + listeners : { + afterrender : function(){ + _this._populate(); + + } + } + }); + return this.panel; +}; + +/** Populates could be call when the DOM is not filled yet **/ +RigibBodyModelingForm.prototype._populate = function() { + if (this.macromolecule != null){ + if (Ext.getCmp(this.id + "contactsDescriptionFilePath") != null){ + if (this.macromolecule.contactsDescriptionFilePath != null){ + Ext.getCmp(this.id + "contactsDescriptionFilePath").setValue(this.macromolecule.contactsDescriptionFilePath); + Ext.getCmp(this.id + "_remove").enable(); + } + else{ + Ext.getCmp(this.id + "_remove").disable(); + Ext.getCmp(this.id + "contactsDescriptionFilePath").setValue(""); + + } + } + } +}; + +/** It populates the form * */ +RigibBodyModelingForm.prototype.refresh = function(macromolecule) { + this.macromolecule = macromolecule; + this.rigidBodyGrid.refresh(macromolecule); + this._populate(); +}; + +RigibBodyModelingForm.prototype.input = function() { + return {}; +}; + + +/** It populates the form **/ +RigibBodyModelingForm.prototype.test = function(targetId) { + var macromoleculeForm = new RigibBodyModelingForm(); + var panel = macromoleculeForm.getPanel(); + panel.render(targetId); +}; + + +/** + * Same form as MX part * - * #onClick #onSelected #onRemoved #onUpdateTime #onMeasurementChanged - * #onExperimentChanged + * @creationMode if true a create button appears instead of save + * @showTitle true or false */ -function MeasurementGrid(args) { +function ShipmentForm(args) { this.id = BUI.id(); - this.height = 500; - this.width = 900; - - this.maxWidth = 1200; - this.maxHeight = 600; - this.minHeight = 500; - - this.unitsFontSize = 9; - this.title = "Measurements"; - this.estimateTime = false; - this.collapsed = true; - this.tbar = true; - + this.creationMode = false; this.showTitle = true; - this.resizable = true; - this.updateRowEnabled = true; - - /** - * Hash map containing the keys of the editable columns. Ex: - * 'exposureTemperature' * - */ - this.editor = { - comments : { - xtype : 'textfield', - allowBlank : true + if (args != null) { + if (args.creationMode != null) { + this.creationMode = args.creationMode; } - }; + if (args.showTitle != null) { + this.showTitle = args.showTitle; + } + } - this.isTimeColumnHidden = false; - this.isStatusColumnHidden = false; - this.isPriorityColumnHidden = true; - this.margin = "0 0 0 0"; + this.onSaved = new Event(this); +} - this.addBtnEnable = true; - this.sorter = [ { - property : 'priority', - direction : 'ASC' - } ]; +ShipmentForm.prototype.fillStores = function() { + this.panel.setLoading("Loading Labcontacts from database"); + this.labContactForSendingStore.loadData(BIOSAXS.proposal.getLabcontacts(), false); + this.labContactForReturnStore.loadData(BIOSAXS.proposal.getLabcontacts(), false); + this.panel.setLoading(false); + if (this.shipment != null) { + this.setShipment(this.shipment); + } +}; - this.removeBtnEnabled = false; - this.collapseBtnEnable = true; - this.addBtnMultipleEdit = false; - this.sortingBtnEnable = false; +ShipmentForm.prototype.draw = function(targetId) { + this.getPanel().render(targetId); +}; +ShipmentForm.prototype.setShipment = function(shipment) { + this.shipment = shipment; var _this = this; - this.selModel = Ext.create('Ext.selection.RowModel', { - allowDeselect : true, - mode : 'MULTI', - listeners : { - selectionchange : function(sm, selections) { - var selected = []; - for (var i = 0; i < selections.length; i++) { - selected.push(selections[i].raw); - } - _this.onSelected.notify(selected); - } - } - }); - - if (args != null) { - - if (args.selModel != null) { - this.selModel = args.selModel; - } + Ext.getCmp(_this.id + "shippingName").setValue(shipment.json.shippingName); + Ext.getCmp(_this.id + "shippingStatus").setValue(shipment.json.shippingStatus); + Ext.getCmp(_this.id + "comments").setValue(shipment.json.comments); + if (shipment.json.sendingLabContactVO != null) { + this.labContactsSendingCombo.setValue(shipment.json.sendingLabContactVO.labContactId); + } + if (shipment.json.returnLabContactVO != null) { + this.labContactsReturnCombo.setValue(shipment.json.returnLabContactVO.labContactId); + } - if (args.removeBtnEnabled != null) { - this.removeBtnEnabled = args.removeBtnEnabled; - } +}; - if (args.addBtnMultipleEdit != null) { - this.addBtnMultipleEdit = args.addBtnMultipleEdit; - } +ShipmentForm.prototype._saveShipment = function() { + var _this = this; + var shippingId = null; + if (this.shipment != null) { + shippingId = this.shipment.json.shippingId; + } + var json = { + shippingId : shippingId, + name : Ext.getCmp(_this.id + "shippingName").getValue(), + status : Ext.getCmp(_this.id + "shippingStatus").getValue(), + sendingLabContactId : Ext.getCmp(_this.id + "shipmentform_sendingLabContactId").getValue(), + returnLabContactId : Ext.getCmp(_this.id + "returnLabContactId").getValue(), + returnCourier : Ext.getCmp(_this.id + "returnCourier").getValue(), + courierAccount : Ext.getCmp(_this.id + "courierAccount").getValue(), + BillingReference : Ext.getCmp(_this.id + "BillingReference").getValue(), + dewarAvgCustomsValue : Ext.getCmp(_this.id + "dewarAvgCustomsValue").getValue(), + dewarAvgTransportValue : Ext.getCmp(_this.id + "dewarAvgTransportValue").getValue(), + comments : Ext.getCmp(_this.id + "comments").getValue() + }; - // if (args.experimentColorBased != null) { - // this.experimentColorBased = args.experimentColorBased; - // } + var dataAdapter = new BiosaxsDataAdapter(); + dataAdapter.onSuccess.attach(function(sender, shipment) { + window.location = BUI.getShippingURL(shipment.shippingId); + }); - if (args.collapsed != null) { - this.collapsed = args.collapsed; - } - if (args.resizable != null) { - this.resizable = args.resizable; - } + dataAdapter.onError.attach(function(sender, error) { + _this.onError.notify(error); + }); - if (args.editor != null) { - this.editor = args.editor; - } + /** Cheking params **/ + if (json.name == "") { + BUI.showError("Name field is mandatory"); + return; + } - if (args.collapseBtnEnable != null) { - this.collapseBtnEnable = args.collapseBtnEnable; - } + if (json.sendingLabContactId == null) { + BUI.showError("Lab contact for sending field is mandatory"); + return; + } - if (args.addBtnEnable != null) { - this.addBtnEnable = args.addBtnEnable; - } - if (args.sortingBtnEnable != null) { - this.sortingBtnEnable = args.sortingBtnEnable; - } + if (json.returnLabContactId == null) { + BUI.showError("Lab contact for return field is mandatory"); + return; + } - if (args.isPriorityColumnHidden != null) { - this.isPriorityColumnHidden = args.isPriorityColumnHidden; - } + dataAdapter.createShipment(json.shippingId, json.name, json.status, json.comments, json.sendingLabContactId, json.returnLabContactId, + json.returnCourier, json.courierAccount, json.BillingReference, json.dewarAvgCustomsValue, json.dewarAvgTransportValue); - if (args.width != null) { - this.width = args.width; - } +}; - if (args.updateRowEnabled != null) { - this.updateRowEnabled = args.updateRowEnabled; - } +ShipmentForm.prototype.getPanel = function(shipment) { + var _this = this; + this.shipment = shipment; + var required = '*'; + var buttons = []; - if (args.showTitle != null) { - this.showTitle = args.showTitle; - if (this.showTitle == false) { - this.title = null; + if (_this.creationMode) { + buttons.push({ + text : 'Create', + scope : this, + handler : function() { + _this._saveShipment(); } - } - - if (args.height != null) { - this.height = args.height; - } - - if (args.maxHeight != null) { - this.maxHeight = args.maxHeight; - } - - if (args.minHeight != null) { - this.minHeight = args.minHeight; - } + }); + } else { + buttons.push({ + text : 'Save', + scope : this, + handler : function() { + _this._saveShipment(); + } + }); - if (args.maxWidth != null) { - this.maxWidth = args.maxWidth; - } + } - if (args.isStatusColumnHidden != null) { - this.isStatusColumnHidden = args.isStatusColumnHidden; - } - if (args.isTimeColumnHidden != null) { - this.isTimeColumnHidden = args.isTimeColumnHidden; - } + this.labContactForSendingStore = Ext.create('Ext.data.Store', { + fields : [ 'cardName', 'labContactId' ] + }); - if (args.title != null) { - this.title = args.title; - } - if (args.estimateTime != null) { - this.estimateTime = args.estimateTime; - } + this.labContactForReturnStore = Ext.create('Ext.data.Store', { + fields : [ 'cardName', 'labContactId' ] + }); - if (args.margin != null) { - this.margin = args.margin; - } + // Create the combo box, attached to the states data store + this.labContactsSendingCombo = Ext.create('Ext.form.ComboBox', { + id : _this.id + "shipmentform_sendingLabContactId", + fieldLabel : 'Lab contact for sending', + afterLabelTextTpl : required, + store : this.labContactForSendingStore, + queryMode : 'local', + labelWidth : 200, + displayField : 'cardName', + valueField : 'labContactId' + }); - if (args.tbar != null) { - this.tbar = args.tbar; + this.labContactsReturnCombo = Ext.create('Ext.form.ComboBox', { + id : _this.id + "returnLabContactId", + fieldLabel : 'If No, Lab-Contact for Return', + afterLabelTextTpl : required, + store : this.labContactForReturnStore, + queryMode : 'local', + labelWidth : 200, + displayField : 'cardName', + valueField : 'labContactId', + listeners : { + change : function(x, newValue) { + for ( var i = 0; i < x.getStore().data.items.length; i++) { + if (x.getStore().data.items[i].raw.labContactId == newValue) { + Ext.getCmp(_this.id + "returnCourier").setValue(x.getStore().data.items[i].raw.defaultCourrierCompany); + Ext.getCmp(_this.id + "courierAccount").setValue(x.getStore().data.items[i].raw.courierAccount); + Ext.getCmp(_this.id + "BillingReference").setValue(x.getStore().data.items[i].raw.billingReference); + Ext.getCmp(_this.id + "dewarAvgCustomsValue").setValue(x.getStore().data.items[i].raw.dewarAvgCustomsValue); + Ext.getCmp(_this.id + "dewarAvgTransportValue").setValue(x.getStore().data.items[i].raw.dewarAvgTransportValue); + } + } + } } + }); - if (args.sorter != null) { - this.sorter = args.sorter; - } + if (this.panel == null) { + this.panel = Ext.create('Ext.form.Panel', { + bodyPadding : 5, + width : 600, + border : 1, + items : [ { + xtype : 'fieldset', + title : 'Details', + collapsible : false, + defaultType : 'textfield', + layout : 'anchor', + defaults : { + anchor : '100%' + }, + items : [ { + xtype : 'requiredtext', + fieldLabel : 'Shipment Label', + allowBlank : false, + name : 'shippingName', + id : _this.id + 'shippingName', + value : '', + anchor : '50%' + }, { + xtype : 'textareafield', + name : 'comments', + id : _this.id + 'comments', + fieldLabel : 'Comments', + value : '' + }, { + fieldLabel : 'Status', + readOnly : true, + id : _this.id + 'shippingStatus', + value : 'Opened', + anchor : '50%' + } ] + }, { + xtype : 'fieldset', + title : 'Lab-Contacts', + collapsible : false, + defaultType : 'textfield', + layout : 'anchor', + defaults : { + anchor : '100%' + }, + items : [ this.labContactsSendingCombo, this.labContactsReturnCombo ] + }, { + border : 0, + html : BUI.getWarningHTML("These informations are relevant for all shipments") + }, { + xtype : 'fieldset', + title : 'Courier accounts details for return', + collapsible : false, + layout : 'anchor', + defaults : { + anchor : '100%' + }, + items : [ { + xtype : 'textfield', + labelWidth : 400, + fieldLabel : 'Courier company for return (if ESRF sends a dewar back)', + id : _this.id + 'returnCourier', + value : '' + }, { + xtype : 'textfield', + labelWidth : 400, + fieldLabel : 'Courier account', + id : _this.id + 'courierAccount', + value : '' + }, { + xtype : 'textfield', + labelWidth : 400, + fieldLabel : 'Billing reference', + id : _this.id + 'BillingReference', + value : '' + }, { + xtype : 'numberfield', + labelWidth : 400, + fieldLabel : 'Average Customs value of a dewar (Euro)', + id : _this.id + 'dewarAvgCustomsValue', + value : '' + }, { + xtype : 'numberfield', + labelWidth : 400, + fieldLabel : 'Average Transport value of a dewar (Euro)', + id : _this.id + 'dewarAvgTransportValue', + value : '' + } ] + } ], + buttons : buttons + }); } - this.onClick = new Event(this); - this.onSelected = new Event(this); - this.onRemoved = new Event(this); - this.onUpdateTime = new Event(this); - this.onMeasurementChanged = new Event(this); - this.onExperimentChanged = new Event(this); -} - -MeasurementGrid.prototype._sortBy = function(sort) { - var _this = this; - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, data) { - _this.onExperimentChanged.notify(data); - _this.grid.setLoading(false); - // wizardWidget.window.close(); - }); - adapter.onError.attach(function(sender, data) { - _this.grid.setLoading(false); - alert("Oops, there was a problem"); - }); - _this.grid.setLoading("Sorting"); - adapter.sortMeasurements(this.experiments.experiments[0].experimentId, sort); + this.fillStores(); + if (this.showTitle) { + this.panel.setTitle('Create a new Shipment'); + } + return this.panel; }; -MeasurementGrid.prototype._getMenu = function() { - var _this = this; - if (this.tbar) { +ShipmentForm.prototype.input = function() { + return { + proposal : DATADOC.getProposal_10() - var items = []; - if (_this.addBtnEnable) { - items.push({ - icon : '../images/add.png', - text : 'Add measurements', - handler : function() { - _this._openAddMeasurementWindow(); - } - }); - } + }; +}; - if (_this.addBtnMultipleEdit) { - items.push({ - icon : '../images/Edit_16x16_01.png', - text : 'Multiple Edit', - handler : function() { - var multipleEditMeasurementGridWindow = new MultipleEditMeasurementGridWindow(); - multipleEditMeasurementGridWindow.onExperimentChanged.attach(function(sender, data) { - _this.onExperimentChanged.notify(data); - }); +ShipmentForm.prototype.test = function(targetId) { + var shipmentForm = new ShipmentForm({ + creationMode : true - multipleEditMeasurementGridWindow.draw(_this.measurements, _this.experiments); + }); + BIOSAXS.proposal = new Proposal(shipmentForm.input().proposal); + shipmentForm.getPanel().render(targetId); +}; + +/** + * #onSaved + */ +function StockSolutionForm(args) { + this.id = BUI.id(); + this.actions = []; - } - }); + if (args != null) { + if (args.actions != null) { + this.actions = args.actions; } + } + this.onSaved = new Event(this); +} - items.push("->"); +StockSolutionForm.prototype.getStockSolution = function() { + if (this.stockSolution != null) { + this.stockSolution.concentration = Ext.getCmp(this.id + "stockSolution_concentration").getValue(); + this.stockSolution.storageTemperature = Ext.getCmp(this.id + "stockSolution_storageTemperature").getValue(); + this.stockSolution.volume = Ext.getCmp(this.id + "stockSolution_volume").getValue(); + this.stockSolution.comments = Ext.getCmp(this.id + "stockSolution_comments").getValue(); + this.stockSolution.name = Ext.getCmp(this.id + "stockSolution_name").getValue(); + this.stockSolution.bufferId = this.bufferCombo.getValue(); - if (_this.sortingBtnEnable) { - var split = Ext.create('Ext.button.Split', { - text : 'Sort by', - // handle a click on the button itself - handler : function() { - // alert("The button was clicked"); - }, - menu : new Ext.menu.Menu({ - items : [ - { - text : 'First Created First Measured', - handler : function() { - _this._sortBy("FIFO"); - } - }, "-", { - text : 'Default', - handler : function() { - _this._sortBy("DEFAULT"); - } - } ] - }) - }); - items.push(split); + if (this.macromoleculeCombo.getValue() != null) { + this.stockSolution.macromoleculeId = this.macromoleculeCombo.getValue(); + } else { + this.stockSolution.macromolecule3VO = null; } - if (_this.collapseBtnEnable) { - items.push({ - text : 'Collapse buffers', - enableToggle : true, - scope : this, - toggleHandler : function(item, pressed) { - this.collapsed = pressed; - this.grid.getStore().loadData(this._prepareData(this.measurements, this.experiments), false); - }, - pressed : this.collapsed - }); + } else { + return { + concentration : Ext.getCmp(this.id + "stockSolution_concentration").getValue(), + storageTemperature : Ext.getCmp(this.id + "stockSolution_storageTemperature").getValue(), + volume : Ext.getCmp(this.id + "stockSolution_volume").getValue(), + comments : Ext.getCmp(this.id + "stockSolution_comments").getValue(), + name : Ext.getCmp(this.id + "stockSolution_name").getValue(), + bufferId : this.bufferCombo.getValue(), + macromoleculeId : this.macromoleculeCombo.getValue() + }; + } + return this.stockSolution; +}; + +StockSolutionForm.prototype.setStockSolution = function(stockSolution) { + if (stockSolution != null) { + if (stockSolution.macromoleculeId != null) { + this.macromoleculeCombo.setValue(stockSolution.macromoleculeId); } - var tb = Ext.create('Ext.toolbar.Toolbar', { - items : items - }); - return tb; + this.bufferCombo.setValue(stockSolution.bufferId); + Ext.getCmp(this.id + "stockSolution_concentration").setValue(this.stockSolution.concentration); + Ext.getCmp(this.id + "stockSolution_storageTemperature").setValue(this.stockSolution.storageTemperature); + Ext.getCmp(this.id + "stockSolution_volume").setValue(this.stockSolution.volume); + Ext.getCmp(this.id + "stockSolution_name").setValue(this.stockSolution.name); + Ext.getCmp(this.id + "stockSolution_comments").setValue(this.stockSolution.comments); } - return null; }; -/** Opens WizardWidget for adding new measurements * */ -MeasurementGrid.prototype._openAddMeasurementWindow = function(measurements, experiments) { - var _this = this; - var wizardWidget = new WizardWidget(); - wizardWidget.onFinished.attach(function(sender, result) { - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, data) { - _this.onExperimentChanged.notify(data); - _this.grid.setLoading(false); - wizardWidget.window.close(); - }); - wizardWidget.current.setLoading("ISPyB: Adding measurements"); - adapter.addMeasurements(result.name, "comments", result.data, _this.experiments.experiments[0].experimentId); +StockSolutionForm.prototype.getBufferCombo = function() { + this.bufferCombo = BIOSAXS_COMBOMANAGER.getComboBuffers(BIOSAXS.proposal.getBuffers(), { + labelWidth : 120, + margin : '0 0 10 0', + width : 220 + }); + return this.bufferCombo; +}; - wizardWidget.draw(null, new MeasurementCreatorStepWizardForm(BIOSAXS.proposal.getMacromolecules(), { - noNext : true - })); +StockSolutionForm.prototype.getMacromoleculeCombo = function() { + this.macromoleculeCombo = BIOSAXS_COMBOMANAGER.getComboMacromoleculeByMacromolecules(BIOSAXS.proposal.getMacromolecules(), { + labelWidth : 120, + margin : '0 0 10 0', + width : 220 + + }); + return this.macromoleculeCombo; }; -/******************************************************************************* - * Opens WizardWidget for adding new measurements - * - * @Measurements - * @Experiments experimentList Object - ******************************************************************************/ -MeasurementGrid.prototype._prepareData = function(measurements, experiments) { - var data = []; - for (var i = 0; i < measurements.length; i++) { - var measurement = measurements[i]; - var specimen = experiments.getSampleById(measurement.specimenId); - var buffer = experiments.getBufferById(specimen.bufferId); - measurement.buffer_acronym = buffer.acronym; - measurement.bufferId = buffer.bufferId; - measurement.volume = specimen.volume; - if (specimen.macromolecule3VO != null) { - measurement.acronym = specimen.macromolecule3VO.acronym; - measurement.macromoleculeId = specimen.macromolecule3VO.macromoleculeId; - } - measurement.concentration = specimen.concentration; - if (measurement.run3VO != null) { - measurement.energy = measurement.run3VO.energy; - measurement.expExposureTemperature = measurement.run3VO.exposureTemperature; - measurement.storageTemperature = measurement.run3VO.storageTemperature; - measurement.timePerFrame = measurement.run3VO.timePerFrame; - measurement.radiationAbsolute = measurement.run3VO.radiationAbsolute; - measurement.radiationRelative = measurement.run3VO.radiationRelative; - measurement.status = "DONE"; +StockSolutionForm.prototype.refresh = function() { +}; - try { - if (measurement.run3VO.timeStart != null) { - if (measurement.run3VO.timeStart != "") { - measurement.miliseconds = moment(measurement.run3VO.timeStart).format("X"); - } - } - } catch (E) { - console.log(E); - } - } +StockSolutionForm.prototype._getTopPanel = function() { + return { + xtype : 'container', + layout : 'hbox', + border : 0, + items : [ { + xtype : 'container', + layout : 'hbox', + items : [ { + xtype : 'container', + flex : 1, + border : false, + layout : 'anchor', + defaultType : 'textfield', + items : [ - if (experiments.getDataCollectionByMeasurementId(measurement.measurementId).length > 0) { - var measurementtodatacollection3VOs = experiments.getDataCollectionByMeasurementId(measurement.measurementId)[0].measurementtodatacollection3VOs; - for (var k = 0; k < measurementtodatacollection3VOs.length; k++) { - if (measurementtodatacollection3VOs[k].dataCollectionOrder == 1) { - var specimenBuffer = experiments.getSampleById(experiments.getMeasurementById(measurementtodatacollection3VOs[k].measurementId).specimenId); - if (specimenBuffer.sampleplateposition3VO != null) { - measurement.bufferSampleplateposition3VO = specimenBuffer.sampleplateposition3VO; - measurement.bufferSampleplate = (experiments.getSamplePlateById(specimenBuffer.sampleplateposition3VO.samplePlateId)); - } - } - } - } + this.getMacromoleculeCombo(), { + xtype : 'requiredtext', + id : this.id + 'stockSolution_name', + fieldLabel : 'Acronym', + labelWidth : 120, + width : 250 + }, { + xtype : 'requiredtext', + id : this.id + 'stockSolution_concentration', + fieldLabel : 'Conc. (mg/ml)', + labelWidth : 120, + width : 250 + }, - if (this.collapsed) { - /** If collapsed only the samples * */ - if (specimen.macromolecule3VO != null) { - data.push(measurement); - } - } else { - data.push(measurement); - } + { + id : this.id + 'stockSolution_storageTemperature', + fieldLabel : 'Storage Temp.(C)', + labelWidth : 120, + width : 250 + }, { + xtype : 'requiredtext', + id : this.id + 'stockSolution_volume', + fieldLabel : 'Volume in Well (µl)', + labelWidth : 120, + width : 250 + } ] + } ] + }, { + xtype : 'container', + flex : 1, + layout : 'anchor', + defaultType : 'textfield', + margin : '0 0 0 10', + items : [ this.getBufferCombo() ] + } ] + }; - } - return data; }; -/** - * Refresh data grid with the measurements and the experiments - * - * @measurements array with measurement3VO objects - * @experiments array with experiments objects - */ -MeasurementGrid.prototype.refresh = function(measurements, experiments) { - this.experiments = experiments; - this.measurements = measurements; - this.store.loadData(this._prepareData(measurements, experiments), false); +StockSolutionForm.prototype.getPanel = function(stockSolution) { + this.stockSolution = stockSolution; + this.panel = Ext.createWidget({ + xtype : 'container', + layout : 'vbox', + border : 0, + style : { + padding : '10px' + }, + fieldDefaults : { + labelAlign : 'left', + labelWidth : 50 + }, + items : [ this._getTopPanel(stockSolution), { + id : this.id + 'stockSolution_comments', + xtype : 'textareafield', + name : 'comments', + fieldLabel : 'Comments', + labelWidth : 120, + width : '100%' + } ] + }); + + this.setStockSolution(stockSolution); + return this.panel; +}; + +StockSolutionForm.prototype.input = function() { + return { + stock : { + "stockSolutionId" : 6, + "proposalId" : 3124, + "macromoleculeId" : 5933, + "bufferId" : 811, + "instructionSet3VO" : null, + "boxId" : 305861, + "storageTemperature" : "20", + "volume" : "300", + "concentration" : "1.2", + "comments" : "Buffer EDTA with A", + "name" : "A_EDTA_1.2", + "samples" : [], + "buffer" : "EDTA", + "macromolecule" : "A" + }, + proposal : new MeasurementGrid().input().proposal + }; +}; + +StockSolutionForm.prototype.test = function(targetId) { + var stockSolutionForm = new StockSolutionForm(); + BIOSAXS.proposal = new Proposal(stockSolutionForm.input().proposal); + var panel = stockSolutionForm.getPanel(new Shipment(stockSolutionForm.input().stock)); + panel.render(targetId); }; + + +BoxWhiskerGraph.prototype.cleanArray = GenericGraph.prototype.cleanArray; +BoxWhiskerGraph.prototype.plotAxes = GenericGraph.prototype.plotAxes; +BoxWhiskerGraph.prototype.plotRuler = GenericGraph.prototype.plotRuler; +BoxWhiskerGraph.prototype.drawSVGVerticalText = GenericGraph.prototype.drawSVGVerticalText; +BoxWhiskerGraph.prototype.getClassColor = GenericGraph.prototype.getClassColor; +BoxWhiskerGraph.prototype.getDimensions = GenericGraph.prototype.getDimensions; +BoxWhiskerGraph.prototype.calculate = GenericGraph.prototype.calculate; +BoxWhiskerGraph.prototype.getMedian = GenericGraph.prototype.getMedian; +BoxWhiskerGraph.prototype.pointToPixel = GenericGraph.prototype.pointToPixel; +BoxWhiskerGraph.prototype.maxValueWhisker = GenericGraph.prototype.maxValueWhisker; +BoxWhiskerGraph.prototype.refresh = GenericGraph.prototype.refresh; /** - * Set status bar to busy (refreshing icon) + * Subclass of GenericGraph * - * @msg message to be displayed on the bar + * @maxBoxWidth */ -MeasurementGrid.prototype._showStatusBarBusy = function(msg) { - var statusBar = Ext.getCmp(this.id + 'basic-statusbar'); - statusBar.setStatus({ - text : msg, - iconCls : 'x-status-busy', - clear : false - }); +function BoxWhiskerGraph(args){ + this.maxBoxWidth = 25; + + if (args == null){ + args = new Object(); + } + args["plotHorizontalByCluster"] = true; + + GenericGraph.call(this, args); + + if (args.maxBoxWidth != null){ + this.maxBoxWidth = args.maxBoxWidth; + } }; + + /** - * Set status bar to ready (ok icon) - * - * @msg message to be displayed on the bar - */ -MeasurementGrid.prototype._showStatusBarReady = function(msg) { - var statusBar = Ext.getCmp(this.id + 'basic-statusbar'); - statusBar.setStatus({ - text : msg, - iconCls : 'x-status-valid', - clear : false - }); +There are several different methods for calculating quartiles.[1] This calculator uses a method described by Moore and McCabe to find quartile values. +The same method also used by The TI-83 to calculate quartile values. +With this method, the first quartile is the median of the numbers below the median, the third quartile is the median of the numbers above the median. + +http://www.miniwebtool.com/quartile-calculator/ +http://www.alcula.com/calculators/statistics/box-plot/ +**/ +BoxWhiskerGraph.prototype.getQ1 = function(array){ + array = array.slice(0, array.length/2); + return this.getMedian(array); }; -/** - * If updateRowEnabled returns an array with Ext.grid.plugin.RowEditing - */ -MeasurementGrid.prototype._getPlugins = function() { - var _this = this; - var plugins = []; - if (this.updateRowEnabled) { - plugins.push(Ext.create('Ext.grid.plugin.RowEditing', { - clicksToEdit : 1, - listeners : { - validateedit : function(grid, e) { - /** Setting values * */ - for ( var key in _this.editor) { - e.record.raw[key] = e.newValues[key]; - } - /** Comments are always updatable* */ - e.record.raw.comments = e.newValues.comments; +BoxWhiskerGraph.prototype.getQ3 = function(array){ + array = array.slice((array.length + 1)/2); + return this.getMedian(array); + +}; - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, measurement) { - _this.onMeasurementChanged.notify(measurement); - _this.grid.setLoading(false); - }); - adapter.onError.attach(function() { - alert("Error"); - _this.grid.setLoading(false); - }); +BoxWhiskerGraph.prototype.getQ2 = function(array){ + return this.getMedian(array); +}; - _this.grid.setLoading(); - adapter.saveMeasurement(e.record.raw, _this.experiments.experiments[0]); - } - } - })); +BoxWhiskerGraph.prototype.getBelowOutliers = function(belowLimit, array){ + var points = new Array(); + for (var i = 0; i < array.length; i++){ + if (array[i] <= belowLimit){ + points.push(array[i]); + } } - return plugins; + return points; }; -/** - * @key name of the columns mathing the this.editor[key] - */ -MeasurementGrid.prototype._getEditor = function(key) { - if (this.editor[key] != null) { - return this.editor[key]; +BoxWhiskerGraph.prototype.getAboveOutliers = function(aboveLimit, array){ + var points = new Array(); + for (var i = 0; i < array.length; i++){ + if (array[i] >= aboveLimit){ + points.push(array[i]); + } } - return null; + return points; }; -MeasurementGrid.prototype.getPanel = function(measurements, experiments) { - this.experiments = experiments; - this.measurements = measurements; - var _this = this; - this.store = Ext.create('Ext.data.Store', { - fields : [ { - name : 'miliseconds', - type : 'int' - }, 'priority', 'bufferId', { - name : 'exposureTemperature', - type : 'numeric' - }, 'volumeToLoad','code', 'transmission', 'viscosity', 'waitTime', 'flow', 'buffer_acronym', 'macromoleculeId', 'acronym', 'concentration', 'extraFlowTime', 'volume', 'energy', - 'expExposureTemperature', 'storageTemperature', 'timePerFrame', 'radiationAbsolute', 'radiationRelative', 'status', 'comments' ], - data : this._prepareData(measurements, experiments) - }); - this.store.sort(this.sorter); - var bbar = {}; - try { - bbar = Ext.create('Ext.ux.StatusBar', { - id : _this.id + 'basic-statusbar', - defaultText : 'Ready', - text : 'Ready', - iconCls : 'x-status-valid', - items : [] - }); - } catch (exp) { - console.log("bbar error"); + +BoxWhiskerGraph.prototype.minValueWhisker = function(belowLimit, q1, array){ + var points = new Array(); + + for (var i = 0; i < array.length; i++){ + if ((array[i] < q1) && (array[i]) > belowLimit){ + points.push(array[i]); + } + } + + if (points.length > 0){ + points.sort(function(a, b){return a - b;}); + return points[0]; } + return null; +}; - this.grid = Ext - .create( - 'Ext.grid.Panel', - { - id : this.id, - title : this.title, - store : this.store, - selModel : this.selModel, - plugins : this._getPlugins(), - resizable : this.resizable, - margin : this.margin, - maxHeight : this.maxHeight, - minHeight : this.minHeight, - maxWidth : this.maxWidth, - width : this.width, - tbar : this._getMenu(), - columns : [ - { - text : 'Order', - dataIndex : 'priority', - width : 50, - hidden : _this.isPriorityColumnHidden, - sortable : true +BoxWhiskerGraph.prototype.isNumber = function(value){ + if (value=="") return false; - }, - { - text : 'Run Number', - dataIndex : 'code', - width : 50, - hidden : true, - sortable : true + var d = parseInt(value); + if (!isNaN(d)) return true; else return false; - }, +}; - { - text : 'Specimen', - columns : [ +BoxWhiskerGraph.prototype.plotBoxWhisker = function(boxProperties){ + if (this.maxBoxWidth != null){ + if (boxProperties.width > this.maxBoxWidth){ + boxProperties.x = boxProperties.x + (boxProperties.width/2) - (this.maxBoxWidth/2); + boxProperties.width = this.maxBoxWidth; + } + + } - { - text : '', - dataIndex : 'macromoleculeId', - width : 30, - renderer : function(val, y, sample) { - if (val != null) { - if (_this.experiments == null) { - return BUI.getRectangleColorDIV(BIOSAXS.proposal.macromoleculeColors[val], 10, 10); - } else { - return BUI.getRectangleColorDIV(_this.experiments.macromoleculeColors[val], 10, 10); - } - } - }, - sortable : true - }, - { - text : 'Macromo.', - dataIndex : 'acronym', - width : 80, - renderer : function(val, y, sample) { - return val; - }, - sortable : true - }, - { - text : 'Conc. ', - dataIndex : 'concentration', - width : 80, - renderer : function(val, y, sample) { - if (sample.raw.macromoleculeId == null) { - return ""; - } - if (isNaN(val)) - return val; + if (this.plotPoints){ + for(var i = 0; i < boxProperties.values.length; i++){ + var value = boxProperties.values[i]; + var toPixel = this.pointToPixel(value, boxProperties); + SVG.drawCircle(boxProperties.x + (boxProperties.width/2) + 2, toPixel, this.pointRadius, this.svg, [["fill", "green"], ["fill-opacity", this.fillOpacityPoint],['stroke-opacity', this.strokeOpacityPoint], ["stroke", "black"]]); + } + } + - if (val != 0) { - return BUI.formatValuesUnits(val, '', { - fontSize : 16, - decimals : 3, - unitsFontSize : this.unitsFontSize - }); - } else { - return; - } + var boxColor = this.getClassColor(boxProperties.name); + var result = this.calculate(boxProperties.values); + /** Q1 **/ + + if (this.isNumber(result.Q1)){ + SVG.drawLine(boxProperties.x, this.pointToPixel(result.Q1, boxProperties), boxProperties.x + boxProperties.width, this.pointToPixel(result.Q1, boxProperties), this.svg, [["stroke", boxColor]]); + } + /** Q2 **/ + if (this.isNumber(result.Q2)){ + SVG.drawLine(boxProperties.x, this.pointToPixel(result.Q2, boxProperties), boxProperties.x + boxProperties.width, this.pointToPixel(result.Q2, boxProperties), this.svg, [["stroke", "blue"], ["stroke-width", "2"]]); + } + + /** Q3 **/ + if (this.isNumber(result.Q3)){ + SVG.drawLine(boxProperties.x, this.pointToPixel(result.Q3, boxProperties), boxProperties.x + boxProperties.width, this.pointToPixel(result.Q3, boxProperties), this.svg, [["stroke", boxColor]]); + } + + /** Concenting Q1 and Q3 **/ + if (this.isNumber(result.Q1)&&this.isNumber(result.Q3)){ + SVG.drawLine(boxProperties.x, this.pointToPixel(result.Q1, boxProperties), boxProperties.x, this.pointToPixel(result.Q3, boxProperties), this.svg, [["stroke", boxColor]]); + SVG.drawLine(boxProperties.x + boxProperties.width, this.pointToPixel(result.Q1, boxProperties), boxProperties.x + boxProperties.width, this.pointToPixel(result.Q3, boxProperties), this.svg, [["stroke", boxColor]]); + } + + /** min-whisker **/ + if (result["min-whisker"] != null){ + if (this.isNumber(result.Q1)){ + SVG.drawLine(boxProperties.x + (boxProperties.width/2) + 2, this.pointToPixel(result.Q1, boxProperties), boxProperties.x + (boxProperties.width/2) + 2, this.pointToPixel(result["min-whisker"], boxProperties), this.svg, [["stroke", boxColor]]); + } + SVG.drawLine(boxProperties.x,this.pointToPixel(result["min-whisker"], boxProperties), boxProperties.x + boxProperties.width, this.pointToPixel(result["min-whisker"], boxProperties) , this.svg, [["stroke", boxColor]]); + + } + + /** max-whisker **/ + if (result["max-whisker"] != null){ + if (this.isNumber(result.Q3)){ + SVG.drawLine(boxProperties.x + (boxProperties.width/2) + 2, this.pointToPixel(result.Q3, boxProperties), boxProperties.x + (boxProperties.width/2) + 2, this.pointToPixel(result["max-whisker"], boxProperties), this.svg, [["stroke", boxColor]]); + } + SVG.drawLine(boxProperties.x , this.pointToPixel(result["max-whisker"], boxProperties), boxProperties.x + boxProperties.width, this.pointToPixel(result["max-whisker"], boxProperties), this.svg, [["stroke", boxColor]]); + + } + + /** outliners **/ + if (result["above-outliers"] != null){ + for (var point in result["above-outliers"]){ + SVG.drawCircle(boxProperties.x + (boxProperties.width/2) + 2, this.pointToPixel(result["above-outliers"][point], boxProperties), this.pointRadius, this.svg, [["fill", "red"], ["fill-opacity", "1"]]); + //SVG.drawText(boxProperties.x + (boxProperties.width/2) - 2, this.pointToPixel(result["above-outliers"][point], boxProperties) + 2, "x",this.svg, [["fill", "black"], ["font-size", "x-small"]]); + } + } + if (result["below-outliers"] != null){ + for (var point in result["below-outliers"]){ + SVG.drawCircle(boxProperties.x + (boxProperties.width/2) + 2, this.pointToPixel(result["below-outliers"][point], boxProperties), this.pointRadius, this.svg, [["fill", "red"], ["fill-opacity", "1"]]); + //SVG.drawText(boxProperties.x + (boxProperties.width/2) - 2, this.pointToPixel(result["below-outliers"][point], boxProperties) + 2, "x",this.svg, [["fill", "black"], ["font-size", "x-small"]]); + } + } +}; - }, - sortable : true - }, - { - text : '', - dataIndex : 'bufferId', - width : 30, - hidden : false, - renderer : function(val, y, sample) { - if (val != null) { - var color = '#FFCCFF'; - if (_this.experiments != null) { - var dc = _this.experiments.getDataCollectionByMeasurementId(sample.raw.measurementId); - if (dc != null) { - if (dc.length > 0) { - color = _this.experiments.getSpecimenColorByBufferId(_this.experiments - .getMeasurementById(dc[0].measurementtodatacollection3VOs[0].measurementId).specimenId); - } - } - } else { - color = BIOSAXS.proposal.bufferColors[val]; - } - return BUI.getRectangleColorDIV(color, 10, 10); - } - }, - sortable : true - }, - { - text : 'Buffer', - dataIndex : 'buffer_acronym', - width : 120, - renderer : function(val, y, sample) { - if (sample.raw.bufferSampleplateposition3VO != null) { - return BIOSAXS.proposal.getBufferById(sample.raw.bufferId).acronym + " Plate: [" - + sample.raw.bufferSampleplate.slotPositionColumn + ", " - + BUI.getSamplePlateLetters()[sample.raw.bufferSampleplateposition3VO.rowNumber - 1] + "-" - + sample.raw.bufferSampleplateposition3VO.columnNumber + "]"; - } - return val; - }, - sortable : true - }, { - text : 'Position', - width : 100, - hidden : true, - renderer : function(val, y, sample) { - if (_this.experiments != null) { - return BUI.getSamplePositionHTML(_this.experiments.getSampleById(sample.raw.specimenId), _this.experiments.experiments[0]); - } - } - } ] - }, - { - text : 'Parameters', - columns : [ - { - text : 'Ex. Flow. time (s)', - dataIndex : 'extraFlowTime', - width : 100, - hidden : true, - renderer : function(val, y, sample) { - return BUI.formatValuesUnits(val, 's', { - fontSize : 12, - decimals : 0, - unitsFontSize : this.unitsFontSize - }); - } - }, - { - text : 'Exp. Temp.', - dataIndex : 'exposureTemperature', - width : 70, - renderer : function(val, y, sample) { - if (Number(val)) { - return BUI.formatValuesUnits(val, 'C', { - fontSize : 12, - decimals : 2, - unitsFontSize : this.unitsFontSize - }); - } - return null; - }, - sortable : true, - editor : this._getEditor("exposureTemperature") - }, - { - text : 'Vol. Load', - dataIndex : 'volumeToLoad', - width : 60, - hidden : false, - editor : this._getEditor("volumeToLoad"), - renderer : function(val, y, sample) { - // return val+ " µl"; - return BUI.formatValuesUnits(val, 'µl', { - fontSize : 12, - decimals : 2, - unitsFontSize : this.unitsFontSize - }); - } - }, - { - text : 'Volume in Well', - dataIndex : 'volume', - hidden : true, - editor : this._getEditor("volume"), - width : 80, - renderer : function(val, y, sample) { - // return val + "(µl)"; - return BUI.formatValuesUnits(val, 'µl', { - fontSize : 12, - decimals : 2, - unitsFontSize : this.unitsFontSize - }); - } - }, - { - text : 'Trans.', - dataIndex : 'transmission', - width : 60, - editor : this._getEditor("transmission"), - renderer : function(val, y, sample) { - return BUI.formatValuesUnits(val, '%', { - fontSize : 12, - decimals : 0, - unitsFontSize : this.unitsFontSize - }); - } - }, - { - text : 'Wait T.', - dataIndex : 'waitTime', - editor : this._getEditor("waitTime"), - width : 50, - renderer : function(val, y, sample) { - // if (val != 0) { - return BUI.formatValuesUnits(val, 's', { - fontSize : 12, - decimals : 0, - unitsFontSize : this.unitsFontSize - }); - // } - } - }, - { - text : 'Flow', - dataIndex : 'flow', - editor : this._getEditor("flow"), - width : 50, - renderer : function(val, y, sample) { - if (val == true) { - return "yes"; - } - return null; - } - }, - { - text : 'Viscosity', - dataIndex : 'viscosity', - tooltip : 'The viscosity of a fluid is a measure of its resistance to gradual deformation by shear stress or tensile stress. For liquids, it corresponds to the informal notion of "thickness"', - editor : this._getEditor("viscosity"), - width : 50, - renderer : function(val, y, sample) { - return val; - } - } ] - }, { - text : 'Status', - dataIndex : 'status', - width : 50, - hidden : _this.isStatusColumnHidden, - renderer : function(val, y, sample) { - if (val != null) { - if (val == 'DONE') { - return "" + val + " "; - } - } - } - }, { - text : 'Time', - dataIndex : 'time', - width : 80, - hidden : _this.isTimeColumnHidden, - renderer : function(val, y, sample) { - if (sample.raw.run3VO != null) { - if (sample.raw.run3VO.timeStart != null) { - if (sample.raw.run3VO.timeStart != "") { - var m = moment(sample.raw.run3VO.timeStart); - return m.format("hh:mm:ss a"); - } - } - } - } - }, { - text : 'Energy', - dataIndex : 'energy', - width : 100, - hidden : true - }, { - text : 'Real Exp. Temp.(C)', - width : 100, - dataIndex : 'expExposureTemperature', - hidden : true - }, { - text : 'Storage Temp.(C)', - width : 100, - dataIndex : 'storageTemperature', - hidden : true - }, { - text : 'Time/Frame (s)', - width : 100, - dataIndex : 'timePerFrame', - hidden : true - }, { - text : 'Radiation Relative', - dataIndex : 'radiationRelative', - width : 100, - hidden : true - }, { - text : 'Radiation Absolute', - dataIndex : 'radiationAbsolute', - width : 100, - hidden : true - }, { - text : 'Comments', - dataIndex : 'comments', - flex : 1, - hidden : false, - editor : this._getEditor("comments") - }, { - id : _this.id + 'buttonRemoveSample', - text : '', - hidden : !_this.removeBtnEnabled, - width : 100, - sortable : false, - renderer : function(value, metaData, record, rowIndex, colIndex, store) { - if (record.raw.macromoleculeId != null) { - if (_this.removeBtnEnabled) { - return BUI.getRedButton('REMOVE'); - } - } - } - } ], - bbar : bbar, - viewConfig : { - stripeRows : true, - getRowClass : function(record, index, rowParams, store) { - if (record.data.status == "DONE") { - return 'green-row'; - } - }, - listeners : { - 'itemclick' : function(grid, record, item, index, e, eOpts) { - _this.onClick.notify({ - specimen : record.raw - }); - }, - 'cellclick' : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { - if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonRemoveSample') { - grid.getStore().removeAt(rowIndex); +BoxWhiskerGraph.prototype.plotClusterTitles = function(properties){ + /** Cluster Titles **/ + var posX = this.left + this.rulerWidth; + var data = this.data; + for(var i = 0; i < data.clusters.length; i++ ){ + /** title for the clusters **/ + posX = posX + this.interClustersSpace; + + /** Drawing title of classes **/ + var cluster = data.clusters[i]; + var posXClasses = posX; + for(var j = 0; j < cluster.classes.length; j++){ + //SVG.drawRectangle(posXClasses, this.height - (this.bottom + this.clusterTitleHeight + this.rulerHeight), properties.classWidth, this.rulerHeight, this.svg, [["fill", "green"]]); +// SVG.drawText(posXClasses + 1/6*properties.classWidth, this.height - (this.bottom + this.clusterTitleHeight + (this.rulerHeight*1/4)), cluster.classes[j].name, this.svg, [["style", "font-size:xx-small"]]); + var color = cluster.classes[j].color; + this.drawSVGVerticalText(posXClasses + 1/2*(properties.classWidth), this.height - (this.bottom - this.clusterTitleHeight + (this.rulerHeight)), cluster.classes[j].name , [["style", "font-size:x-small;"], ["fill", color]]); + + // SVG.drawRectangle(posXClasses, this.height - (this.bottom + this.clusterTitleHeight), properties.classWidth, 2, this.svg, [["fill", "black"]]); + posXClasses = posXClasses + properties.classWidth + this.interClassesSpace; + } + + var clusterTitleSpace = (data.clusters[i].classes.length * properties.classWidth) + ((data.clusters[i].classes.length - 1) * this.interClassesSpace); + //SVG.drawRectangle(posX, this.height - (this.bottom + this.clusterTitleHeight), clusterTitleSpace, this.clusterTitleHeight, this.svg, [["fill", "pink"]]); + SVG.drawRectangle(posX, this.height - (this.clusterTitleHeight+this.bottom), clusterTitleSpace, 1, this.svg, []); + +// var transform = ["translate", "(" + posX + 1/3*clusterTitleSpace +", " + this.height - (this.bottom + (this.clusterTitleHeight*1/4)) +")"], ["transform", "rotate(180)"]; +// var transform = ["transform", "translate(" + (posX + 1/3*clusterTitleSpace) +", " + (this.height - (this.bottom + (this.clusterTitleHeight*1/4))) + "), rotate(-90) "]; +// this.drawSVGVerticalText(posX + 1/3*clusterTitleSpace, this.height - (this.bottom + (this.clusterTitleHeight*1/4)),data.clusters[i].name, [["style", "font-size:x-small"]]); + SVG.drawText(posX , this.height - (this.bottom + (this.clusterTitleHeight*1/4)),data.clusters[i].name ,this.svg, [["style", "font-size:x-small"]]); + posX = posX + clusterTitleSpace; + } +}; + +BoxWhiskerGraph.prototype.plotWhisters = function(data, properties){ + var colors = ["yellow", "orange", "green"]; + var posX = this.left + this.rulerWidth; + for(var i = 0; i < data.clusters.length; i++ ){ + var cluster = data.clusters[i]; + /** inter cluster space **/ + //SVG.drawRectangle(posX, this.top, this.interClustersSpace, this.height - this.top - this.bottom - this.clusterTitleHeight - this.rulerHeight, this.svg, [["fill", "black"]]); + posX = posX + this.interClustersSpace; + + for (var j = 0; j < cluster.classes.length; j ++){ + + //SVG.drawRectangle(posX, this.top, properties.classWidth, this.height - this.top - this.bottom - this.clusterTitleHeight - this.rulerHeight, this.svg, [["fill", colors[j]]]); + this.plotBoxWhisker( + { + name : cluster.classes[j].name, + values : cluster.classes[j].values, + minPoint : properties.minPoint, + maxPoint : properties.maxPoint, + x : posX, + y : this.top, + width : properties.classWidth, + height : this.height - this.top - this.bottom - this.clusterTitleHeight - this.rulerHeight + + + } + ); + posX = posX + properties.classWidth; + //SVG.drawRectangle(posX, this.top, this.interClassesSpace, this.height - this.top - this.bottom - this.clusterTitleHeight - this.rulerHeight, this.svg, [["fill", "black"]]); + if (j < cluster.classes.length - 1){ + posX = posX + this.interClassesSpace; + } + } + } +}; + +BoxWhiskerGraph.prototype.draw = function(targetId, data){ + this.targetId = targetId; + var properties = (this.getDimensions(data)); + this.svg = SVG.createSVGCanvas(document.getElementById(this.targetId), [["width", this.width], ["height", this.height]]); + this.plotAxes(properties); + this.plotClusterTitles(properties); + this.plotWhisters(data,properties); +}; + +BoxWhiskerGraph.prototype.input = function(){ + return DATADOC.getBoxWhikerData(); +}; + +BoxWhiskerGraph.prototype.test = function(targetId){ + var plot = new BoxWhiskerGraph( + { + targetId : targetId, + height : 350, + width : 450, + maxBoxWidth : 25 + } + ); + plot.refresh(this.input()); +}; + + + + + + + +function DataSet(){ + this.json = null; + +}; + + +DataSet.prototype.loadFromJSON = function(json){ + if(json != null) { + if(this.validate(json)) { + this.json = json; + } + } +}; + + +DataSet.prototype.toJSON = function(json){ + return this.json; +}; + + +/** Abstract method to be override on childs classes **/ +DataSet.prototype.validate = function(json){ + if (true){ + return true; + } + /*else{ + throw "Data validation failed"; + }*/ +}; + + + + +Edge.prototype.getName = GraphItem.prototype.getName; +Edge.prototype.setName = GraphItem.prototype.setName; +Edge.prototype.getId = GraphItem.prototype.getId; + +function Edge(id, name, nodeSource, nodeTarget, args){ + GraphItem.prototype.constructor.call(this, id, name, args); + + this.sourceNode = nodeSource; + this.targetNode = nodeTarget; + +}; + +Edge.prototype.toJSON = function(){ + return {"index": this.id,"sourceIndex":this.sourceNode.getId(),"targetIndex":this.targetNode.getId(),"args":this.args}; +}; + +Edge.prototype.getNodeSource = function(){ + return this.sourceNode; +}; + +Edge.prototype.getNodeTarget = function(){ + return this.targetNode; +}; + +Edge.prototype.remove = function(){ + //Remove edge object in the nodes + this.getNodeSource().removeEdge(this); + this.getNodeTarget().removeEdge(this); + + this.deleted.notify(this); +}; + + +EdgeGraphFormatter.prototype.setProperties = ItemGraphFormatter.prototype.setProperties; +EdgeGraphFormatter.prototype.getDefault = ItemGraphFormatter.prototype.getDefault; +EdgeGraphFormatter.prototype.getSelected = ItemGraphFormatter.prototype.getSelected; +EdgeGraphFormatter.prototype.getOver = ItemGraphFormatter.prototype.getOver; +EdgeGraphFormatter.prototype.getDragging = ItemGraphFormatter.prototype.getDragging; +EdgeGraphFormatter.prototype.getId = ItemGraphFormatter.prototype.getId; +EdgeGraphFormatter.prototype.toJSON = ItemGraphFormatter.prototype.toJSON; +EdgeGraphFormatter.prototype.loadFromJSON = ItemGraphFormatter.prototype.loadFromJSON; +EdgeGraphFormatter.prototype._setEvents = ItemGraphFormatter.prototype._setEvents; + +function EdgeGraphFormatter(id, defaultFormat, selectedFormat, overFormat, dragginFormat){ + ItemGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, dragginFormat); + + this.getDefault().args.type = "EdgeGraphFormatter"; +}; + + + + +LineEdgeGraphFormatter.prototype.setProperties = EdgeGraphFormatter.prototype.setProperties; +LineEdgeGraphFormatter.prototype.getDefault = EdgeGraphFormatter.prototype.getDefault; +LineEdgeGraphFormatter.prototype.getSelected = EdgeGraphFormatter.prototype.getSelected; +LineEdgeGraphFormatter.prototype.getOver = EdgeGraphFormatter.prototype.getOver; +LineEdgeGraphFormatter.prototype.getDragging = EdgeGraphFormatter.prototype.getDragging; +LineEdgeGraphFormatter.prototype.getId = EdgeGraphFormatter.prototype.getId; +LineEdgeGraphFormatter.prototype.toJSON = EdgeGraphFormatter.prototype.toJSON; +LineEdgeGraphFormatter.prototype.loadFromJSON = EdgeGraphFormatter.prototype.loadFromJSON; +LineEdgeGraphFormatter.prototype._setEvents = EdgeGraphFormatter.prototype._setEvents; + +function LineEdgeGraphFormatter(id, defaultFormat, selectedFormat, overFormat, dragginFormat){ + EdgeGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, dragginFormat); + this.args.type = "LineEdgeGraphFormatter"; +}; + +/** DIRECTED **/ +DirectedLineEdgeGraphFormatter.prototype.setProperties = EdgeGraphFormatter.prototype.setProperties; +DirectedLineEdgeGraphFormatter.prototype.getDefault = EdgeGraphFormatter.prototype.getDefault; +DirectedLineEdgeGraphFormatter.prototype.getSelected = EdgeGraphFormatter.prototype.getSelected; +DirectedLineEdgeGraphFormatter.prototype.getOver = EdgeGraphFormatter.prototype.getOver; +DirectedLineEdgeGraphFormatter.prototype.getDragging = EdgeGraphFormatter.prototype.getDragging; +DirectedLineEdgeGraphFormatter.prototype.getId = EdgeGraphFormatter.prototype.getId; +DirectedLineEdgeGraphFormatter.prototype.toJSON = EdgeGraphFormatter.prototype.toJSON; +DirectedLineEdgeGraphFormatter.prototype.loadFromJSON = EdgeGraphFormatter.prototype.loadFromJSON; +DirectedLineEdgeGraphFormatter.prototype._setEvents = EdgeGraphFormatter.prototype._setEvents; + +function DirectedLineEdgeGraphFormatter(id, defaultFormat, selectedFormat, overFormat, dragginFormat){ + EdgeGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, dragginFormat); + this.args.type = "DirectedLineEdgeGraphFormatter"; + this.args.arrowSize = "3"; +}; +DirectedLineEdgeGraphFormatter.prototype.getArrowSize = function(){ + return this.args.arrowSize; +}; + +OdirectedLineEdgeGraphFormatter.prototype.setProperties = EdgeGraphFormatter.prototype.setProperties; +OdirectedLineEdgeGraphFormatter.prototype.getDefault = EdgeGraphFormatter.prototype.getDefault; +OdirectedLineEdgeGraphFormatter.prototype.getSelected = EdgeGraphFormatter.prototype.getSelected; +OdirectedLineEdgeGraphFormatter.prototype.getOver = EdgeGraphFormatter.prototype.getOver; +OdirectedLineEdgeGraphFormatter.prototype.getDragging = EdgeGraphFormatter.prototype.getDragging; +OdirectedLineEdgeGraphFormatter.prototype.getId = EdgeGraphFormatter.prototype.getId; +OdirectedLineEdgeGraphFormatter.prototype.toJSON = EdgeGraphFormatter.prototype.toJSON; +OdirectedLineEdgeGraphFormatter.prototype.loadFromJSON = EdgeGraphFormatter.prototype.loadFromJSON; +OdirectedLineEdgeGraphFormatter.prototype._setEvents = EdgeGraphFormatter.prototype._setEvents; + +function OdirectedLineEdgeGraphFormatter(id, defaultFormat, selectedFormat, overFormat, dragginFormat){ + EdgeGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, dragginFormat); + this.args.type = "DirectedLineEdgeGraphFormatter"; + this.args.arrowSize = "3"; +}; + +OdirectedLineEdgeGraphFormatter.prototype.getArrowSize = function(){ + return this.args.arrowSize; +}; + + +/** CUT (inhibition) **/ +CutDirectedLineEdgeGraphFormatter.prototype.setProperties = EdgeGraphFormatter.prototype.setProperties; +CutDirectedLineEdgeGraphFormatter.prototype.getDefault = EdgeGraphFormatter.prototype.getDefault; +CutDirectedLineEdgeGraphFormatter.prototype.getSelected = EdgeGraphFormatter.prototype.getSelected; +CutDirectedLineEdgeGraphFormatter.prototype.getOver = EdgeGraphFormatter.prototype.getOver; +CutDirectedLineEdgeGraphFormatter.prototype.getDragging = EdgeGraphFormatter.prototype.getDragging; +CutDirectedLineEdgeGraphFormatter.prototype.getId = EdgeGraphFormatter.prototype.getId; +CutDirectedLineEdgeGraphFormatter.prototype.toJSON = EdgeGraphFormatter.prototype.toJSON; +CutDirectedLineEdgeGraphFormatter.prototype.loadFromJSON = EdgeGraphFormatter.prototype.loadFromJSON; +CutDirectedLineEdgeGraphFormatter.prototype._setEvents = EdgeGraphFormatter.prototype._setEvents; + +function CutDirectedLineEdgeGraphFormatter(id, defaultFormat, selectedFormat, overFormat, dragginFormat){ + EdgeGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, dragginFormat); + this.args.type = "CutDirectedLineEdgeGraphFormatter"; +}; + + + + +BezierEdgeGraphFormatter.prototype.setProperties = EdgeGraphFormatter.prototype.setProperties; +BezierEdgeGraphFormatter.prototype.setProperties = EdgeGraphFormatter.prototype.setProperties; +BezierEdgeGraphFormatter.prototype.getDefault = EdgeGraphFormatter.prototype.getDefault; +BezierEdgeGraphFormatter.prototype.getSelected = EdgeGraphFormatter.prototype.getSelected; +BezierEdgeGraphFormatter.prototype.getOver = EdgeGraphFormatter.prototype.getOver; +BezierEdgeGraphFormatter.prototype.getDragging = EdgeGraphFormatter.prototype.getDragging; +BezierEdgeGraphFormatter.prototype.getId = EdgeGraphFormatter.prototype.getId; +BezierEdgeGraphFormatter.prototype.toJSON = EdgeGraphFormatter.prototype.toJSON; +BezierEdgeGraphFormatter.prototype.loadFromJSON = EdgeGraphFormatter.prototype.loadFromJSON; +BezierEdgeGraphFormatter.prototype._setEvents = EdgeGraphFormatter.prototype._setEvents; + +function BezierEdgeGraphFormatter(id, defaultFormat, selectedFormat, overFormat, dragginFormat){ + EdgeGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, dragginFormat); + this.args.type = "BezierEdgeGraphFormatter"; +}; + + + +DotDirectedLineEdgeGraphFormatter.prototype.setProperties = EdgeGraphFormatter.prototype.setProperties; +DotDirectedLineEdgeGraphFormatter.prototype.getDefault = EdgeGraphFormatter.prototype.getDefault; +DotDirectedLineEdgeGraphFormatter.prototype.getSelected = EdgeGraphFormatter.prototype.getSelected; +DotDirectedLineEdgeGraphFormatter.prototype.getOver = EdgeGraphFormatter.prototype.getOver; +DotDirectedLineEdgeGraphFormatter.prototype.getDragging = EdgeGraphFormatter.prototype.getDragging; +DotDirectedLineEdgeGraphFormatter.prototype.getId = EdgeGraphFormatter.prototype.getId; +DotDirectedLineEdgeGraphFormatter.prototype.toJSON = EdgeGraphFormatter.prototype.toJSON; +DotDirectedLineEdgeGraphFormatter.prototype.loadFromJSON = EdgeGraphFormatter.prototype.loadFromJSON; +DotDirectedLineEdgeGraphFormatter.prototype._setEvents = EdgeGraphFormatter.prototype._setEvents; + +function DotDirectedLineEdgeGraphFormatter(id, defaultFormat, selectedFormat, overFormat, dragginFormat){ + EdgeGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, dragginFormat); + this.args.type = "DotDirectedLineEdgeGraphFormatter"; +}; + + +OdotDirectedLineEdgeGraphFormatter.prototype.setProperties = EdgeGraphFormatter.prototype.setProperties; +OdotDirectedLineEdgeGraphFormatter.prototype.getDefault = EdgeGraphFormatter.prototype.getDefault; +OdotDirectedLineEdgeGraphFormatter.prototype.getSelected = EdgeGraphFormatter.prototype.getSelected; +OdotDirectedLineEdgeGraphFormatter.prototype.getOver = EdgeGraphFormatter.prototype.getOver; +OdotDirectedLineEdgeGraphFormatter.prototype.getDragging = EdgeGraphFormatter.prototype.getDragging; +OdotDirectedLineEdgeGraphFormatter.prototype.getId = EdgeGraphFormatter.prototype.getId; +OdotDirectedLineEdgeGraphFormatter.prototype.toJSON = EdgeGraphFormatter.prototype.toJSON; +OdotDirectedLineEdgeGraphFormatter.prototype.loadFromJSON = EdgeGraphFormatter.prototype.loadFromJSON; +OdotDirectedLineEdgeGraphFormatter.prototype._setEvents = EdgeGraphFormatter.prototype._setEvents; + +function OdotDirectedLineEdgeGraphFormatter(id, defaultFormat, selectedFormat, overFormat, dragginFormat){ + EdgeGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, dragginFormat); + this.args.type = "OdotDirectedLineEdgeGraphFormatter"; +}; + + + +function GraphDataset(){ + DataSet.prototype.constructor.call(this); + this.edges = new Object(); + this.vertices = new Object(); + this.verticesIndex = new Object(); + + //Events + this.newVertex = new Event(this); + this.vertexNameChanged = new Event(this); + this.vertexDeleted = new Event(this); + + this.newEdge = new Event(this); + this.edgeNameChanged = new Event(this); + this.edgeDeleted = new Event(this); + + this.json = new Object(); + this.json.vertices = new Array(); + this.json.edges = new Array(); + this.json.relations = new Array(); +}; + +GraphDataset.prototype.loadFromJSON = DataSet.prototype.loadFromJSON; +GraphDataset.prototype.toJSON = DataSet.prototype.toJSON; +GraphDataset.prototype.validate = DataSet.prototype.validate; + +/** Devuelve el numero de edges incidentes sobre el nodo con mas edges **/ +GraphDataset.prototype.getMaxClass = function(){ + var maxClassNode = 0; + for ( var node in this.vertices) { + if (this.vertices[node].getEdgesCount() > maxClassNode){ + maxClassNode = this.vertices[node].getEdgesCount(); + } + } + return maxClassNode; +}; + +/** Devuelve el numero de edges incidentes sobre el nodo con mas edges **/ +GraphDataset.prototype.getMinClass = function(){ + var minClassNode = Math.min(); + for ( var node in this.vertices) { + if (this.vertices[node].getEdgesCount() < minClassNode){ + minClassNode = this.vertices[node].getEdgesCount(); + } + } + return minClassNode; +}; + +GraphDataset.prototype.getVertexByName = function(nodeName){ + var results = new Array(); + + for (var vertexId in this.verticesIndex[nodeName]){ + var vertexByid = this.getVertexById(this.verticesIndex[nodeName][vertexId]); + results.push(vertexByid); + //* añadido nuevo porque fallaba el anterior codigo + return vertexByid + } + + if (results <= 1){ + return this.getVertexById(this.verticesIndex[nodeName]); + } + else{ + return results; + } +}; + +GraphDataset.prototype.getVertexById = function(id){ + return this.vertices[id]; +}; + +GraphDataset.prototype.toSIF = function(){ + var sifDataAdapter = new SifFileDataAdapter(); + return sifDataAdapter.toSIF(this); +}; + +GraphDataset.prototype.toSIFID = function(){ + var sifDataAdapter = new SifFileDataAdapter(); + return sifDataAdapter.toSIFID(this); +}; + +GraphDataset.prototype.toDOT = function(){ + var dotFileDataAdapter = new DotFileDataAdapter(); + return dotFileDataAdapter.toDOT(this); +}; + +GraphDataset.prototype.toDOTID = function(){ + var dotFileDataAdapter = new DotFileDataAdapter(); + return dotFileDataAdapter.toDOTID(this); +}; + +GraphDataset.prototype._addNode = function(nodeName, args){ + return new Vertex(this._getVerticesCount()-1, nodeName, args); +}; + +GraphDataset.prototype.addNode = function(nodeName, args){ + this.json.vertices.push(nodeName); + this._addVerticesIndex(nodeName, this._getVerticesCount() - 1); + var vertex = this._addNode(nodeName, args); + this.vertices[this._getVerticesCount()-1] = vertex; + this._setNodeEvents(vertex); + this.newVertex.notify(vertex); +}; + +GraphDataset.prototype._addVerticesIndex = function(nodeName, id){ + if (this.verticesIndex[nodeName] == null){ + this.verticesIndex[nodeName] = new Array(); + } + this.verticesIndex[nodeName].push(id); +}; + +GraphDataset.prototype.addEdge = function(edgeName, nodeSourceId, nodeTargetId, args){ + this.json.edges.push(edgeName); + var nodeSource = this.getVertexById(nodeSourceId); + var nodeTarget = this.getVertexById(nodeTargetId); + var index = this.getEdgesCount() - 1; + this.edges[index] = new Edge(index, edgeName, nodeSource, nodeTarget, args); + this.json.relations.push({"index": index, "sourceIndex": nodeSourceId, "targetIndex": nodeTargetId, "args": args }); + + nodeSource.addEdge(this.edges[index]); + nodeTarget.addEdge(this.edges[index]); + this._setEdgeEvents(this.edges[index]); + this.newEdge.notify(this.edges[index]); +}; + +GraphDataset.prototype.getVertices = function(){ + return this.vertices; +}; + +GraphDataset.prototype.getEdges = function(){ + return this.edges; +}; + +GraphDataset.prototype.getEdgeById = function(edgeId){ + return this.edges[edgeId]; +}; + +GraphDataset.prototype._getVerticesCount = function(){ + return this.json.vertices.length; +}; + + +GraphDataset.prototype.getVerticesCount = function(){ + var count = 0; + for ( var vertex in this.getVertices()) { + count ++; + } + return count; +}; + + +GraphDataset.prototype.getEdgesCount = function(){ + return this.json.edges.length; +}; + +GraphDataset.prototype.init = function(){ + this.edges = new Object(); + this.vertices = new Object(); +}; + +GraphDataset.prototype._setNodeEvents = function(node){ + var _this = this; + //NODE EVENTS + node.deleted.attach(function (sender, node){ + _this._removeNode(node); + }); + + node.nameChanged.attach(function (sender, args){ + var item = args.item; + var newName = item.name; + var indexes = _this.verticesIndex[args.previousName]; + for(var i = 0; i < indexes.length; i++){ + if(indexes[i] == item.id) + indexes.splice(i,1); + } + if(indexes.length == 0){ + delete _this.verticesIndex[args.previousName]; + } + _this._addVerticesIndex(newName, item.id); + _this.json.vertices[item.id] = newName; + _this.vertexNameChanged.notify(args); + }); +}; + +GraphDataset.prototype._setEdgeEvents = function(edge){ + var _this = this; + //EDGE EVENTS + edge.nameChanged.attach(function (sender, edge){ + _this.edgeNameChanged.notify(edge); + + }); + + edge.deleted.attach(function (sender, edge){ + _this._removeEdge(edge); + }); +}; + +GraphDataset.prototype._connectVerticesByName = function(nodeNameSource, nodeNameTarget){ + var source = this.getVertexByName(nodeNameSource); + var target = this.getVertexByName(nodeNameTarget); + + if ((source != null)&&(target!=null)){ + this.addEdge(source.getName() +"_" + target.getName(), source.getId(), target.getId(), {}); + } + else{ + if (source == null){ + console.log("No encontrado: " + nodeNameSource) + } + if (target == null){ + console.log("No encontrado: " + nodeNameTarget) + } + } +}; + +GraphDataset.prototype.loadFromJSON = function(json){ + var json = json; + this.init(); + this.json = new Object(); + this.json.vertices = new Array(); + this.json.edges = new Array(); + this.json.relations = new Array(); + + for ( var i = 0; i < json.nodes.length; i++) { + if (json.nodes[i] != null){ + var name = json.nodes[i]; + this.addNode(name); + } + else{ + this.json.vertices.push(null); + } + } + + for ( var i = 0; i < json.edges.length; i++) { + if (json.edges[i] != null){ + if (json.relations[i] != null){ + var name = json.edges[i]; + this.addEdge(name, json.relations[i].sourceIndex, json.relations[i].targetIndex, json.relations[i].args); + } + } + else{ + this.json.edges.push(null); + this.json.relations.push(null); + } + } +}; + +GraphDataset.prototype.prettyPrint = function(){ + for ( var node in this.vertices) { + console.log(this.vertices[node].getName() ); + for ( var j = 0; j < this.vertices[node].getEdgesIn().length; j++) { + console.log(" --> " + this.vertices[node].getEdgesIn()[j].getNodeTarget().getName() ); + } + } +}; + +GraphDataset.prototype._removeEdge = function(edge){ + this.json.edges[edge.getId()] = null; + this.json.relations[edge.getId()] = null; + + delete this.edges[edge.getId()]; + this.edgeDeleted.notify(edge); + + +}; + +GraphDataset.prototype._removeNode = function(node){ + this.json.vertices[node.getId()] = null; + delete this.vertices[node.getId()]; + this.vertexDeleted.notify(node); +}; + +GraphDataset.prototype.toJSON = function(){ + var json = new Object(); + var nodes = new Array(); + json.nodes = this.json.vertices; //nodes; + json.edges = this.json.edges; //edges; + json.relations = this.json.relations; + return json; +}; + +GraphDataset.prototype.clone = function(){ + var dsDataset = new GraphDataset(); + dsDataset.loadFromJSON(this.toJSON()); + return dsDataset; +}; +//GraphDataset.prototype.test = function(){ +// this.loadFromJSON(this.toJSON()); +//}; + +function labels(){ + var names = new Array(); + + var dataset = interactomeViewer.graphEditorWidget.dataset; + var layout = interactomeViewer.graphEditorWidget.layout; + + for ( var vertexId in interactomeViewer.graphEditorWidget.dataset.getVertices()) { + names.push(interactomeViewer.graphEditorWidget.dataset.getVertexById(vertexId).getName()); + } + + var sorted = (names.sort()); + console.log(sorted) + var distance = 0.01; + var altura = 0.6; + for ( var i = 0; i < names.length; i++) { + var id =dataset.getVertexByName(names[i]).getId(); + + layout.getNodeById(id).setCoordenates(distance, altura); + + + distance = parseFloat(distance) + parseFloat(0.03); + + altura = parseFloat(altura) + parseFloat(0.02); + + if (parseFloat(altura) == 0.9800000000000003){ + + altura = 0.6; + distance = distance - 0.51; + } + + } + + +}; + +function GraphItem(id, name, args){ + this.id = id; + this.name = name; + this.type = "NONE"; + + this.args = new Object(); + + + if (args!=null){ + this.args = args; + if (args.type !=null){ + this.type = args.type; + } + } + + //Events + this.nameChanged = new Event(this); + this.deleted = new Event(this); +} + +GraphItem.prototype.getName = function(){ + return this.name; +}; + +GraphItem.prototype.getId = function(){ + return this.id; +}; + +GraphItem.prototype.setName = function(name){ + var oldName = this.getName(); + this.name = name; + this.nameChanged.notify({"item": this, "previousName" : oldName}); +}; + + + + + +function LayoutDataset(){ + this.dataset = null; + this.vertices = new Object(); + this.changed = new Event(this); + + + this.args = new Object(); + + //RANDOM, CIRCLE + this.args.type = "CIRCLE"; +}; + +LayoutDataset.prototype.loadFromJSON = function(dataset, json){ + var _this = this; + this.vertices = new Object(); + this.dataset = dataset; //new GraphDataset(); + for ( var vertex in json) { + this.vertices[vertex] = new NodeLayout(vertex, json[vertex].x, json[vertex].y); + this.vertices[vertex].changed.attach(function (sender, item){ + _this.changed.notify(item); + }); + } + this._attachDatasetEvents(); +}; + + +LayoutDataset.prototype.toJSON = function(){ + var serialize = new Object(); + for ( var vertex in this.vertices) { + serialize[vertex] = new Object(); + serialize[vertex].x = this.vertices[vertex].x; + serialize[vertex].y = this.vertices[vertex].y; + } + serialize.dataset = new Object(); + serialize.dataset =this.dataset.toJSON(); + return serialize; +}; + +LayoutDataset.prototype.dataBind = function(graphDataset){ + this.dataset = graphDataset; + this._attachDatasetEvents(); + this._calculateLayout(); +}; + +LayoutDataset.prototype._removeVertex = function(vertexId){ + delete this.vertices[vertexId]; +}; + +LayoutDataset.prototype._attachDatasetEvents = function(){ + var _this = this; + + this.dataset.vertexDeleted.attach(function (sender, item){ + _this._removeVertex(item.getId()); + }); + + this.dataset.newVertex.attach(function (sender, item){ + _this.vertices[item.getId()] = new NodeLayout(item.getId(), 0.5, 0.5); + _this.vertices[item.getId()].changed.attach(function (sender, item){ + _this.changed.notify(item); + }); + }); +}; + +LayoutDataset.prototype.getType = function(){ + return this.args.type; +}; + +LayoutDataset.prototype._calculateLayoutVertices = function(type, count){ + + if (type == "CIRCLE"){ + var radius = 0.4; + var centerX = 0.5; + var centerY = 0.5; + var verticesCoordinates = new Array(); + for(var i = 0; i < count; i++){ + x = centerX + radius * Math.sin(i * 2 * Math.PI/count); + y = centerY + radius * Math.cos(i * 2 * Math.PI/count); + verticesCoordinates.push({'x':x,'y':y}); + } + return verticesCoordinates; + } +}; + + +LayoutDataset.prototype._calculateLayout = function(){ + var _this = this; + if (this.getType() == "RANDOM"){ + for ( var vertex in this.dataset.getVertices()) { + if (this.vertices[vertex] == null){ + this.vertices[vertex] = new NodeLayout(vertex, 0, 0); + } + this.vertices[vertex].setCoordinates(Math.random(), Math.random()); + this.vertices[vertex].changed.attach(function (sender, item){ + _this.changed.notify(item); + }); + } + } + + if ( this.getType() == "CIRCLE"){ + + var count = this.dataset._getVerticesCount(); + var verticesCoordinates = this._calculateLayoutVertices(this.getType(), count); + + var aux = 0; + for ( var vertex in this.dataset.getVertices()) { + if (this.vertices[vertex] == null){ + this.vertices[vertex] = new NodeLayout(vertex, 0, 0); + } + this.vertices[vertex].setCoordinates(verticesCoordinates[aux].x, verticesCoordinates[aux].y);//{"x":, "y":}; + aux++; + this.vertices[vertex].changed.attach(function (sender, item){ + _this.changed.notify(item); + }); + } + } + + + if (this.getType() == "SQUARE"){ + + var count = this.dataset._getVerticesCount(); + var xMin = 0.1; + var xMax = 0.9; + var yMin = 0.1; + var yMax = 0.9; + + var rows = Math.sqrt(count); + var step = (xMax - xMin) / rows; + + var verticesCoordinates = new Array(); + for(var i = 0; i < rows; i ++){ + for ( var j = 0; j < rows; j++) { + x = i * step + xMin; + y = j * step + yMin; + verticesCoordinates.push({'x':x,'y':y}); + } + } + + var aux = 0; + for ( var vertex in this.dataset.getVertices()) { + if (this.vertices[vertex] == null){ + this.vertices[vertex] = new NodeLayout(vertex, 0, 0); + } + this.vertices[vertex].setCoordinates(verticesCoordinates[aux].x, verticesCoordinates[aux].y);//{"x":, "y":}; + aux++; + this.vertices[vertex].changed.attach(function (sender, item){ + _this.changed.notify(item); + }); + } + } + +}; + +LayoutDataset.prototype.getNodeById = function(id){ + return this.vertices[id]; +}; + +LayoutDataset.prototype.getVerticesByArea = function(x1, y1, x2, y2){ + var vertices = new Array(); + for ( var vertex in this.dataset.getVertices()) { + if ((this.vertices[vertex].x >= x1)&&(this.vertices[vertex].x <= x2)){ + if ((this.vertices[vertex].y >= y1)&&(this.vertices[vertex].y <= y2)){ + vertices.push(this.vertices[vertex]); + } + } + } + return vertices; +}; + + + + +LayoutDataset.prototype.getLayout = function(type){ + + if (type == "CIRCLE"){ + this.args.type = "CIRCLE"; + this._calculateLayout(); + return; + } + + if (type == "SQUARE"){ + this.args.type = "SQUARE"; + this._calculateLayout(); + return; + } + + if (type == "RANDOM"){ + this.args.type = "RANDOM"; + this._calculateLayout(); + return; + } + + + var dotText = this.dataset.toDOTID(); + var url = "http://bioinfo.cipf.es/utils/ws/rest/network/layout/"+type+".coords"; + var _this = this; + + $.ajax({ + async: true, + type: "POST", + url: url, + dataType: "text", + data: { + dot :dotText + }, + cache: false, + success: function(data){ + var response = JSON.parse(data); + for ( var vertexId in response) { + _this.vertices[vertexId].setCoordinates(response[vertexId].x, response[vertexId].y); + } + } + }); + +// $.ajax({ +// async: true, +// type: "POST", +// url: url, +// dataType: "script", +// data: { +// dot :dotText +// }, +// cache: false, +// success: function(data){ +// var response = JSON.parse(data); +// for ( var vertexId in response) { +// _this.vertices[vertexId].setCoordinates(response[vertexId].x, response[vertexId].y); +// } +// } +// }); + +}; + +function NodeLayout(id, x, y, args){ + this.id = id; + this.x = x; + this.y = y; + this.changed = new Event(this); +}; + +NodeLayout.prototype.getId = function(id){ + return this.id; +}; + +NodeLayout.prototype.setCoordinates = function(x, y){ + this.x = x; + this.y = y; + this.changed.notify(this); +}; + + + +function NetworkDataSetFormatter(vertexFormatProperties, defaultEdgeProperties, args){ + DataSet.prototype.constructor.call(this); + + this.args = new Object(); + + this.vertices = new Object(); + this.edges = new Object(); + this.dataset = null; + this.maxClass = 0; + this.minClass = 0; + + /** Coordenates with default Setting */ + this.args.width = 1100; + this.args.height = 500; + + /** Optional parameters */ + this.args.backgroundColor = "#FFFFFF"; + this.args.backgroundImage = null; + this.args.backgroundImageHeight = null; + this.args.backgroundImageWidth = null; + this.args.backgroundImageX = null; + this.args.backgroundImageY = null; + + + this.args.balanceNodes = false; + this.args.nodesMaxSize = 3; + this.args.nodesMinSize = 0.5; + + + /** Zoom **/ + this.args.zoomScale = 1; + this.args.zoomScaleStepFactor = 0.4; + + if (args != null){ + if (args.backgroundImage != null){ + this.args.backgroundImage = args.backgroundImage; + } + + if (args.width != null){ + this.args.width = args.width; + } + + if (args.height != null){ + this.args.height = args.height; + this.args.svgHeight = args.height; + } + + if (args.svgHeight != null){ + this.args.svgHeight = args.svgHeight; + } + + if (args.backgroundColor != null){ + this.args.backgroundColor = args.backgroundColor; + } + + if(args.balanceNodes != null){ + this.args.balanceNodes = args.balanceNodes; + } + + if(args.nodesMaxSize != null){ + this.args.nodesMaxSize = args.nodesMaxSize; + } + if(args.nodesMinSize != null){ + this.args.nodesMinSize = args.nodesMinSize; + } + } + + this.args.defaultEdgeProperties = {"fill":"#000000", "radius":"1", "stroke":"#000000", "size":1, "title":{"fontSize":10, "fontColor":"#000000"}}; + this.args.vertexFormatProperties = { "fill":"#000000", "stroke":"#000000", "stroke-opacity":"#000000"}; + + if (vertexFormatProperties!= null){ + this.args.vertexFormatProperties = vertexFormatProperties; + } + if (defaultEdgeProperties!= null){ + this.args.defaultEdgeProperties = defaultEdgeProperties; + } + + /** Events **/ + this.changed = new Event(this); + this.edgeChanged = new Event(this); + this.resized = new Event(this); + this.backgroundImageChanged= new Event(this); + this.backgroundColorChanged= new Event(this); +}; + +NetworkDataSetFormatter.prototype.loadFromJSON = function(dataset, json){ + this.args = new Object(); + this.vertices = new Object(); + this.args = json; + this._setDataset(dataset); + var _this = this; + + for ( var vertex in json.vertices) { + this.addVertex(vertex, json.vertices[vertex]); + } + + for ( var edgeId in json.edges) { + this.addEdge(edgeId, json.edges[edgeId]); + } +}; + + +NetworkDataSetFormatter.prototype.toJSON = function(){ + + +// this.args.vertices = new Object(); +// this.args.edges = new Object(); +// +// for ( var vertex in this.vertices) { +// this.args.vertices[vertex] = this.getVertexById(vertex).toJSON(); +// } +// for ( var edge in this.edges) { +// this.args.edges[edge] = this.getEdgeById(edge).toJSON(); +// } +// +// return (this.args); + + + var serialize = new Object(); + serialize = JSON.parse(JSON.stringify(this.args)); + serialize.vertices = new Object(); + serialize.edges = new Object(); + + for ( var vertex in this.vertices) { + serialize.vertices[vertex] = this.getVertexById(vertex).toJSON(); + } + for ( var edge in this.edges) { + serialize.edges[edge] = this.getEdgeById(edge).toJSON(); + } + + return (serialize); +}; + + + +NetworkDataSetFormatter.prototype._getNodeSize = function(nodeId){ + if (this.isVerticesBalanced()){ + var total = this.maxClass - this.minClass; + if (total == 0){ total = 1;} + var nodeConnection = this.dataset.getVertexById(nodeId).getEdges().length; + return ((nodeConnection*this.args.nodesMaxSize)/total) + this.args.nodesMinSize; + } + return this.getVertexById(nodeId).getDefault().getSize(); +}; + +NetworkDataSetFormatter.prototype._recalculateSize = function(){ + if (this.isVerticesBalanced()){ + this.maxClass = this.dataset.getMaxClass(); + this.minClass = this.dataset.getMinClass(); + for ( var vertexIdAll in this.vertices) { + var size = this._getNodeSize(vertexIdAll); + this.vertices[vertexIdAll].getDefault().setSize(size); + this.vertices[vertexIdAll].getSelected().setSize(size); + this.vertices[vertexIdAll].getOver().setSize(size); + } + } +}; + + +NetworkDataSetFormatter.prototype.addVertex = function(vertexId, json){ + + + if (json == null){ + this.vertices[vertexId] = new CircleVertexGraphFormatter(vertexId, this.args.vertexFormatProperties.defaultFormat, this.args.vertexFormatProperties.selected, this.args.vertexFormatProperties.over, this.args.vertexFormatProperties.dragging); + } + else{ + + /** Cargo los attributos desde el json **/ + if (json.type == null){ + this.vertices[vertexId] = new CircleVertexGraphFormatter(vertexId, this.args.vertexFormatProperties.defaultFormat, this.args.vertexFormatProperties.selected, this.args.vertexFormatProperties.over, this.args.vertexFormatProperties.dragging); + } + + if ((json.type == "SquareVertexGraphFormatter")||(json.type == "SquareVertexNetworkFormatter")){ + this.vertices[vertexId] = new SquareVertexGraphFormatter(vertexId); + this.vertices[vertexId].loadFromJSON(json); + } + + if ((json.type == "CircleVertexGraphFormatter")||(json.type == "CircleVertexNetworkFormatter")){ + this.vertices[vertexId] = new CircleVertexGraphFormatter(vertexId); + this.vertices[vertexId].loadFromJSON(json); + } + + if ((json.type == "EllipseVertexGraphFormatter")||(json.type == "EllipseVertexNetworkFormatter")){ + this.vertices[vertexId] = new EllipseVertexGraphFormatter(vertexId); + this.vertices[vertexId].loadFromJSON(json); + } + + if ((json.type == "RectangleVertexGraphFormatter")||(json.type == "RectangleVertexNetworkFormatter")){ + this.vertices[vertexId] = new RectangleVertexGraphFormatter(vertexId); + this.vertices[vertexId].loadFromJSON(json); + } + + if ((json.type == "RoundedVertexGraphFormatter")||(json.type == "RoundedVertexNetworkFormatter")){ + this.vertices[vertexId] = new RoundedVertexGraphFormatter(vertexId); + this.vertices[vertexId].loadFromJSON(json); + } + + } + + + var _this = this; + this.vertices[vertexId].stateChanged.attach(function (sender, item){ + _this.changed.notify(item); + }); + + var size = this._getNodeSize(vertexId); + this.vertices[vertexId].defaultFormat.args.size = size; + this.vertices[vertexId].selected.args.size = size; + this.vertices[vertexId].over.args.size = size; + +}; + +NetworkDataSetFormatter.prototype.addEdge = function(edgeId, json){ + + /** Es un edge nuevo que le doy los atributos por defecto **/ + if (json == null){ + if (this.dataset.getEdgeById(edgeId).getNodeSource().getId() == this.dataset.getEdgeById(edgeId).getNodeTarget().getId()){ + this.edges[edgeId] = new BezierEdgeGraphFormatter(edgeId, this.args.defaultEdgeProperties.defaultFormat, this.args.defaultEdgeProperties.selected, this.args.defaultEdgeProperties.over, this.args.defaultEdgeProperties.dragging); + }else{ + this.edges[edgeId] = new DirectedLineEdgeGraphFormatter(edgeId, this.args.defaultEdgeProperties.defaultFormat, this.args.defaultEdgeProperties.selected, this.args.defaultEdgeProperties.over, this.args.defaultEdgeProperties.dragging); + } + } + else{ + /** Cargo los attributos desde el json **/ + if (json.type == null){ + this.edges[edgeId] = new LineEdgeGraphFormatter(edgeId, this.args.defaultEdgeProperties.defaultFormat, this.args.defaultEdgeProperties.selected, this.args.defaultEdgeProperties.over, this.args.defaultEdgeProperties.dragging); + } + + if ((json.type == "LineEdgeGraphFormatter")||(json.type == "LineEdgeNetworkFormatter")){ + this.edges[edgeId] = new LineEdgeGraphFormatter(edgeId); + this.edges[edgeId].loadFromJSON(json); + } + if ((json.type == "DirectedLineEdgeGraphFormatter")||(json.type == "DirectedLineEdgeNetworkFormatter")){ + this.edges[edgeId] = new DirectedLineEdgeGraphFormatter(edgeId); + this.edges[edgeId].loadFromJSON(json); + } + + if ((json.type == "BezierEdgeGraphFormatter")||(json.type == "BezierEdgeNetworkFormatter")){ + this.edges[edgeId] = new BezierEdgeGraphFormatter(edgeId); + this.edges[edgeId].loadFromJSON(json); + } + + + if ((json.type == "CutDirectedLineEdgeGraphFormatter")||(json.type == "CutDirectedLineEdgeNetworkFormatter")){ + this.edges[edgeId] = new CutDirectedLineEdgeGraphFormatter(edgeId); + this.edges[edgeId].loadFromJSON(json); + } + + if ((json.type == "DotDirectedLineEdgeGraphFormatter")||(json.type == "DotDirectedLineEdgeNetworkFormatter")){ + this.edges[edgeId] = new DotDirectedLineEdgeGraphFormatter(edgeId); + this.edges[edgeId].loadFromJSON(json); + } + if ((json.type == "OdotDirectedLineEdgeGraphFormatter")||(json.type == "OdotDirectedLineEdgeNetworkFormatter")){ + this.edges[edgeId] = new OdotDirectedLineEdgeGraphFormatter(edgeId); + this.edges[edgeId].loadFromJSON(json); + } + + if ((json.type == "OdirectedLineEdgeGraphFormatter")||(json.type == "OdirectedLineEdgeNetworkFormatter")){ + this.edges[edgeId] = new OdirectedLineEdgeGraphFormatter(edgeId); + this.edges[edgeId].loadFromJSON(json); + } + } + + var _this = this; + this.edges[edgeId].stateChanged.attach(function (sender, item){ + _this.edgeChanged.notify(item); + }); + + this._recalculateSize(); +}; + +NetworkDataSetFormatter.prototype._setDataset = function(dataset){ + this.dataset = dataset; + this.maxClass = dataset.getMaxClass(); + this.minClass = dataset.getMinClass(); + this._attachDatasetEvents(); +}; + +NetworkDataSetFormatter.prototype.changeEdgeType = function(edgeId, type){ + if ((type == "LineEdgeGraphFormatter")||(type == "LineEdgeNetworkFormatter")){ + this.edges[edgeId] = new LineEdgeGraphFormatter(edgeId, this.args.defaultEdgeProperties.defaultFormat, this.args.defaultEdgeProperties.selected, this.args.defaultEdgeProperties.over, this.args.defaultEdgeProperties.dragging); + + } + if ((type == "DirectedLineEdgeGraphFormatter")||(type == "DirectedLineEdgeNetworkFormatter")){ + this.edges[edgeId] = new DirectedLineEdgeGraphFormatter(edgeId, this.args.defaultEdgeProperties.defaultFormat, this.args.defaultEdgeProperties.selected, this.args.defaultEdgeProperties.over, this.args.defaultEdgeProperties.dragging); + } + + if ((type == "CutDirectedLineEdgeGraphFormatter")||(type == "CutDirectedLineEdgeNetworkFormatter")){ + this.edges[edgeId] = new CutDirectedLineEdgeGraphFormatter(edgeId, this.args.defaultEdgeProperties.defaultFormat, this.args.defaultEdgeProperties.selected, this.args.defaultEdgeProperties.over, this.args.defaultEdgeProperties.dragging); + } + + if ((type == "DotDirectedLineEdgeGraphFormatter")||(type == "DotDirectedLineEdgeNetworkFormatter")){ + this.edges[edgeId] = new DotDirectedLineEdgeGraphFormatter(edgeId, this.args.defaultEdgeProperties.defaultFormat, this.args.defaultEdgeProperties.selected, this.args.defaultEdgeProperties.over, this.args.defaultEdgeProperties.dragging); + } + + if ((type == "OdotDirectedLineEdgeGraphFormatter")||(type == "OdotDirectedLineEdgeNetworkFormatter")){ + this.edges[edgeId] = new OdotDirectedLineEdgeGraphFormatter(edgeId, this.args.defaultEdgeProperties.defaultFormat, this.args.defaultEdgeProperties.selected, this.args.defaultEdgeProperties.over, this.args.defaultEdgeProperties.dragging); + } + + if ((type == "OdirectedLineEdgeGraphFormatter")||(type == "OdirectedLineEdgeNetworkFormatter")){ + this.edges[edgeId] = new OdirectedLineEdgeGraphFormatter(edgeId, this.args.defaultEdgeProperties.defaultFormat, this.args.defaultEdgeProperties.selected, this.args.defaultEdgeProperties.over, this.args.defaultEdgeProperties.dragging); + } + + + + var _this = this; + this.edges[edgeId].stateChanged.attach(function (sender, item){ + _this.edgeChanged.notify(item); + }); + _this.edgeChanged.notify(this.edges[edgeId]); +}; +/* +classe = "SquareNetworkNodeFormatter"; +} +if (value == "circle"){ + classe = "CircleNetworkNodeFormatter"; + **/ +NetworkDataSetFormatter.prototype.changeNodeType = function(vertexId, type){ + var defaultFormat = JSON.parse(JSON.stringify(this.vertices[vertexId].getDefault())); + var selectedFormat = JSON.parse(JSON.stringify(this.vertices[vertexId].getSelected())); + var defaultFormat = JSON.parse(JSON.stringify(this.vertices[vertexId].getDefault())); + var defaultFormat = JSON.parse(JSON.stringify(this.vertices[vertexId].getDefault())); + + if ((type == "SquareVertexGraphFormatter")||(type == "SquareVertexNetworkFormatter")){ + this.vertices[vertexId] = new SquareVertexGraphFormatter(vertexId,defaultFormat, this.args.vertexFormatProperties.selected, this.args.vertexFormatProperties.over, this.args.vertexFormatProperties.dragging); + } + + if ((type == "CircleVertexGraphFormatter")||(type == "CircleVertexNetworkFormatter")){ + this.vertices[vertexId] = new CircleVertexGraphFormatter(vertexId, defaultFormat, this.args.vertexFormatProperties.selected, this.args.vertexFormatProperties.over, this.args.vertexFormatProperties.dragging); + } + + if ((type == "EllipseVertexGraphFormatter")||(type == "EllipseVertexNetworkFormatter")){ + this.vertices[vertexId] = new EllipseVertexGraphFormatter(vertexId, defaultFormat, this.args.vertexFormatProperties.selected, this.args.vertexFormatProperties.over, this.args.vertexFormatProperties.dragging); + } + + if ((type == "RectangleVertexGraphFormatter")||(type == "RectangleVertexNetworkFormatter")){ + this.vertices[vertexId] = new RectangleVertexGraphFormatter(vertexId, defaultFormat, this.args.vertexFormatProperties.selected, this.args.vertexFormatProperties.over, this.args.vertexFormatProperties.dragging); + } + + if ((type == "RoundedVertexGraphFormatter")||(type == "RoundedVertexNetworkFormatter")){ + this.vertices[vertexId] = new RoundedVertexGraphFormatter(vertexId, defaultFormat, this.args.vertexFormatProperties.selected, this.args.vertexFormatProperties.over, this.args.vertexFormatProperties.dragging); + } + + if ((type == "OctagonVertexGraphFormatter")||(type == "OctagonVertexNetworkhFormatter")){ + this.vertices[vertexId] = new OctagonVertexGraphFormatter(vertexId, defaultFormat, this.args.vertexFormatProperties.selected, this.args.vertexFormatProperties.over, this.args.vertexFormatProperties.dragging); + } + + + var _this = this; + this.vertices[vertexId].stateChanged.attach(function (sender, item){ + _this.changed.notify(item); + }); + _this.changed.notify(this.vertices[vertexId]); +}; + + + +NetworkDataSetFormatter.prototype.dataBind = function(networkDataSet){ + var _this = this; + this._setDataset(networkDataSet); + + for ( var vertex in this.dataset.getVertices()) { + this.addVertex(vertex); + } + + for ( var edge in this.dataset.getEdges()) { + this.addEdge(edge); + } +}; + +NetworkDataSetFormatter.prototype._removeEdge = function(edgeId){ + delete this.edges[edgeId]; +}; + +NetworkDataSetFormatter.prototype._removeVertex = function(vertexId){ + delete this.vertices[vertexId]; +}; + +NetworkDataSetFormatter.prototype._attachDatasetEvents = function(id){ + var _this = this; + this.dataset.vertexDeleted.attach(function (sender, item){ + _this._removeVertex(item.getId()); + }); + + this.dataset.edgeDeleted.attach(function (sender, item){ + _this._removeEdge(item.getId()); + }); + + this.dataset.newVertex.attach(function (sender, item){ + _this.addVertex(item.getId()); + }); + + this.dataset.newEdge.attach(function (sender, item){ + _this.addEdge(item.getId()); + }); +}; + + +NetworkDataSetFormatter.prototype.getVertexById = function(id){ + return this.vertices[id]; +}; + +NetworkDataSetFormatter.prototype.getEdgeById = function(id){ + return this.edges[id]; +}; + +NetworkDataSetFormatter.prototype.makeLabelsBigger = function(){ +for ( var vertexId in this.vertices) { + var fontSize = this.vertices[vertexId].getDefault().getFontSize() + 2; + this.vertices[vertexId].getDefault().setFontSize(fontSize); + } +}; + +NetworkDataSetFormatter.prototype.makeLabelsSmaller = function(){ + for ( var vertexId in this.vertices) { + var fontSize = this.vertices[vertexId].getDefault().getFontSize() - 2; + this.vertices[vertexId].getDefault().setFontSize(fontSize); + } +}; + + +NetworkDataSetFormatter.prototype.resize = function(width, height){ + this.args.width = width; + this.args.height = height; + this.resized.notify(); +}; + +/** ZOOM GETTERS **/ +NetworkDataSetFormatter.prototype.getZoomScaleStepFactor = function(){return this.args.zoomScaleStepFactor;}; +NetworkDataSetFormatter.prototype.setZoomScaleStepFactor = function(scaleFactor){this.args.zoomScaleStepFactor = scaleFactor;}; +NetworkDataSetFormatter.prototype.getZoomScale = function(){return this.args.zoomScale;}; +NetworkDataSetFormatter.prototype.setZoomScale = function(scale){this.args.zoomScale = scale;}; + +NetworkDataSetFormatter.prototype.getNodesMaxSize = function(){return this.args.nodesMaxSize;}; +NetworkDataSetFormatter.prototype.getNodesMinSize = function(){return this.args.nodesMinSize;}; + +/** SIZE SETTERS AND GETTERS **/ +NetworkDataSetFormatter.prototype.isVerticesBalanced = function(){return this.args.balanceNodes;}; +NetworkDataSetFormatter.prototype.getWidth = function(){return this.args.width;}; +NetworkDataSetFormatter.prototype.getHeight = function(){return this.args.height;}; + +/** OPTIONAL PARAMETERS GETTERS AND SETTERS **/ +NetworkDataSetFormatter.prototype.getBackgroundImage = function(){return this.args.backgroundImage;}; +NetworkDataSetFormatter.prototype.setBackgroundImage = function(value){this.args.backgroundImage = value; this.backgroundImageChanged.notify(this);}; + + +NetworkDataSetFormatter.prototype.getBackgroundImageWidth = function(){return this.args.backgroundImageWidth;}; +NetworkDataSetFormatter.prototype.setBackgroundImageWidth = function(value){this.args.backgroundImageWidth = value; this.backgroundImageChanged.notify(this);}; + +NetworkDataSetFormatter.prototype.getBackgroundImageHeight = function(){return this.args.backgroundImageHeight;}; +NetworkDataSetFormatter.prototype.setBackgroundImageHeight = function(value){this.args.backgroundImageHeight = value; this.backgroundImageChanged.notify(this);}; + +NetworkDataSetFormatter.prototype.getBackgroundImageX = function(){return this.args.backgroundImageX;}; +NetworkDataSetFormatter.prototype.setBackgroundImageX = function(value){this.args.backgroundImageX = value; this.backgroundImageChanged.notify(this);}; + +NetworkDataSetFormatter.prototype.getBackgroundImageY = function(){return this.args.backgroundImageX;}; +NetworkDataSetFormatter.prototype.setBackgroundImageY = function(value){this.args.backgroundImageY = value; this.backgroundImageChanged.notify(this);}; + + + +NetworkDataSetFormatter.prototype.getBackgroundColor = function(){return this.args.backgroundColor;}; +NetworkDataSetFormatter.prototype.setBackgroundColor = function(value){this.args.backgroundColor = value; this.backgroundColorChanged.notify(this);}; + +NetworkDataSetFormatter.prototype.getWidth = function(){return this.args.width;}; +NetworkDataSetFormatter.prototype.setWidth = function(value){this.args.width = value;}; + +NetworkDataSetFormatter.prototype.getHeight = function(){return this.args.height;}; +NetworkDataSetFormatter.prototype.setHeight = function(value){this.args.height = value;}; + + + + +Vertex.prototype.getName = GraphItem.prototype.getName; +Vertex.prototype.setName = GraphItem.prototype.setName; +Vertex.prototype.getId = GraphItem.prototype.getId; + +function Vertex(id, name, args){ + GraphItem.prototype.constructor.call(this, id, name, args); + this.edgesIn= new Array(); + this.edgesOut= new Array(); +}; + +Vertex.prototype.getEdges = function(){ + return this.edgesIn.concat(this.edgesOut); +}; + +Vertex.prototype.getEdgesCount = function(){ + return this.getEdges().length; +}; + +Vertex.prototype.getEdgesIn = function(){ + return this.edgesIn; +}; + +Vertex.prototype.getEdgesOut = function(){ + return this.edgesOut; +}; + +Vertex.prototype.addEdge = function(edge){ + if (edge.getNodeSource().getId() == this.id){ + this.edgesIn.push(edge); + } + else{ + this.edgesOut.push(edge); + } +}; + +Vertex.prototype.removeEdge = function(edge){ + for ( var i = 0; i < this.getEdgesIn().length; i++) { + var edgeIn = this.edgesIn[i]; + if (edgeIn.getId() == edge.getId()){ + this.edgesIn.splice(i, 1); + } + } + for ( var i = 0; i < this.getEdgesOut().length; i++) { + var edgeIn = this.edgesOut[i]; + if (edgeIn.getId() == edge.getId()){ + this.edgesOut.splice(i, 1); + } + } +}; + +Vertex.prototype.remove = function(){ + var edges = this.getEdges(); + for ( var i = 0; i < edges.length; i++) { + var edge = edges[i]; + edge.remove(); + } + this.deleted.notify(this); +}; + + + + + +VertexGraphFormatter.prototype.getDefault = ItemGraphFormatter.prototype.getDefault; +VertexGraphFormatter.prototype.getSelected = ItemGraphFormatter.prototype.getSelected; +VertexGraphFormatter.prototype.getOver = ItemGraphFormatter.prototype.getOver; +VertexGraphFormatter.prototype.getDragging = ItemGraphFormatter.prototype.getDragging; +VertexGraphFormatter.prototype.getId = ItemGraphFormatter.prototype.getId; +VertexGraphFormatter.prototype.toJSON = ItemGraphFormatter.prototype.toJSON; +VertexGraphFormatter.prototype.loadFromJSON = ItemGraphFormatter.prototype.loadFromJSON; +VertexGraphFormatter.prototype._setEvents = ItemGraphFormatter.prototype._setEvents; + + +function VertexGraphFormatter(defaultFormat, selectedFormat, overFormat, draggingFormat){ + ItemGraphFormatter.prototype.constructor.call(this, defaultFormat, selectedFormat, overFormat, draggingFormat); +}; + + +CircleVertexGraphFormatter.prototype.getDefault = VertexGraphFormatter.prototype.getDefault; +CircleVertexGraphFormatter.prototype.getSelected = VertexGraphFormatter.prototype.getSelected; +CircleVertexGraphFormatter.prototype.getOver = VertexGraphFormatter.prototype.getOver; +CircleVertexGraphFormatter.prototype.getDragging = VertexGraphFormatter.prototype.getDragging; +CircleVertexGraphFormatter.prototype.getId = VertexGraphFormatter.prototype.getId; +CircleVertexGraphFormatter.prototype.toJSON = VertexGraphFormatter.prototype.toJSON; +CircleVertexGraphFormatter.prototype.loadFromJSON = VertexGraphFormatter.prototype.loadFromJSON; +CircleVertexGraphFormatter.prototype._setEvents = VertexGraphFormatter.prototype._setEvents; + +function CircleVertexGraphFormatter(id, defaultFormat, selectedFormat, overFormat, draggingFormat){ + VertexGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, draggingFormat); + this.args.type = "CircleVertexGraphFormatter"; + if (defaultFormat != null){ + if (defaultFormat.radius != null){ + this.defaultFormat.args.radius = defaultFormat.radius; + } + } + + if (selectedFormat != null){ + if (selectedFormat.radius != null){ + this.selected.args.radius = selectedFormat.radius; + } + } + + if (overFormat != null){ + if (overFormat.radius != null){ + this.over.args.radius = overFormat.radius; + } + } + + if (draggingFormat != null){ + if (draggingFormat.radius != null){ + this.dragging.args.draggingFormat = draggingFormat.radius; + } + } +}; + + + +SquareVertexGraphFormatter.prototype.getDefault = VertexGraphFormatter.prototype.getDefault; +SquareVertexGraphFormatter.prototype.getSelected = VertexGraphFormatter.prototype.getSelected; +SquareVertexGraphFormatter.prototype.getOver = VertexGraphFormatter.prototype.getOver; +SquareVertexGraphFormatter.prototype.getDragging = VertexGraphFormatter.prototype.getDragging; +SquareVertexGraphFormatter.prototype.getId = VertexGraphFormatter.prototype.getId; +SquareVertexGraphFormatter.prototype.toJSON = VertexGraphFormatter.prototype.toJSON; +SquareVertexGraphFormatter.prototype.loadFromJSON = VertexGraphFormatter.prototype.loadFromJSON; +SquareVertexGraphFormatter.prototype._setEvents = VertexGraphFormatter.prototype._setEvents; + +function SquareVertexGraphFormatter(id, defaultFormat, selectedFormat, overFormat, draggingFormat){ + VertexGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, draggingFormat); + this.args.type = "SquareVertexGraphFormatter"; + if (defaultFormat != null){ + if (defaultFormat.radius != null){ + this.defaultFormat.args.radius = defaultFormat.radius; + } + } + + if (selectedFormat != null){ + if (selectedFormat.radius != null){ + this.selected.args.radius = selectedFormat.radius; + } + } + + if (overFormat != null){ + if (overFormat.radius != null){ + this.over.args.radius = overFormat.radius; + } + } + + if (draggingFormat != null){ + if (draggingFormat.radius != null){ + this.dragging.args.draggingFormat = draggingFormat.radius; + } + } +}; + + +EllipseVertexGraphFormatter.prototype.getDefault = VertexGraphFormatter.prototype.getDefault; +EllipseVertexGraphFormatter.prototype.getSelected = VertexGraphFormatter.prototype.getSelected; +EllipseVertexGraphFormatter.prototype.getOver = VertexGraphFormatter.prototype.getOver; +EllipseVertexGraphFormatter.prototype.getDragging = VertexGraphFormatter.prototype.getDragging; +EllipseVertexGraphFormatter.prototype.getId = VertexGraphFormatter.prototype.getId; +EllipseVertexGraphFormatter.prototype.toJSON = VertexGraphFormatter.prototype.toJSON; +EllipseVertexGraphFormatter.prototype.loadFromJSON = VertexGraphFormatter.prototype.loadFromJSON; +EllipseVertexGraphFormatter.prototype._setEvents = VertexGraphFormatter.prototype._setEvents; + +function EllipseVertexGraphFormatter(id, defaultFormat, selectedFormat, overFormat, draggingFormat){ + VertexGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, draggingFormat); + this.args.type = "EllipseVertexGraphFormatter"; + if (defaultFormat != null){ + if (defaultFormat.radius != null){ + this.defaultFormat.args.radius = defaultFormat.radius; + } + } + + if (selectedFormat != null){ + if (selectedFormat.radius != null){ + this.selected.args.radius = selectedFormat.radius; + } + } + + if (overFormat != null){ + if (overFormat.radius != null){ + this.over.args.radius = overFormat.radius; + } + } + + if (draggingFormat != null){ + if (draggingFormat.radius != null){ + this.dragging.args.draggingFormat = draggingFormat.radius; + } + } +}; + + + +RectangleVertexGraphFormatter.prototype.getDefault = ItemGraphFormatter.prototype.getDefault; +RectangleVertexGraphFormatter.prototype.getSelected = ItemGraphFormatter.prototype.getSelected; +RectangleVertexGraphFormatter.prototype.getOver = ItemGraphFormatter.prototype.getOver; +RectangleVertexGraphFormatter.prototype.getDragging = ItemGraphFormatter.prototype.getDragging; +RectangleVertexGraphFormatter.prototype.getId = ItemGraphFormatter.prototype.getId; +RectangleVertexGraphFormatter.prototype.toJSON = ItemGraphFormatter.prototype.toJSON; +RectangleVertexGraphFormatter.prototype.loadFromJSON = ItemGraphFormatter.prototype.loadFromJSON; +RectangleVertexGraphFormatter.prototype._setEvents = ItemGraphFormatter.prototype._setEvents; + +function RectangleVertexGraphFormatter(id, defaultFormat, selectedFormat, overFormat, draggingFormat){ + VertexGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, draggingFormat); + this.args.type = "RectangleVertexGraphFormatter"; + if (defaultFormat != null){ + if (defaultFormat.radius != null){ + this.defaultFormat.args.radius = defaultFormat.radius; + } + } + + if (selectedFormat != null){ + if (selectedFormat.radius != null){ + this.selected.args.radius = selectedFormat.radius; + } + } + + if (overFormat != null){ + if (overFormat.radius != null){ + this.over.args.radius = overFormat.radius; + } + } + + if (draggingFormat != null){ + if (draggingFormat.radius != null){ + this.dragging.args.draggingFormat = draggingFormat.radius; + } + } +}; + + + +RoundedVertexGraphFormatter.prototype.getDefault = ItemGraphFormatter.prototype.getDefault; +RoundedVertexGraphFormatter.prototype.getSelected = ItemGraphFormatter.prototype.getSelected; +RoundedVertexGraphFormatter.prototype.getOver = ItemGraphFormatter.prototype.getOver; +RoundedVertexGraphFormatter.prototype.getDragging = ItemGraphFormatter.prototype.getDragging; +RoundedVertexGraphFormatter.prototype.getId = ItemGraphFormatter.prototype.getId; +RoundedVertexGraphFormatter.prototype.toJSON = ItemGraphFormatter.prototype.toJSON; +RoundedVertexGraphFormatter.prototype.loadFromJSON = ItemGraphFormatter.prototype.loadFromJSON; +RoundedVertexGraphFormatter.prototype._setEvents = ItemGraphFormatter.prototype._setEvents; + +function RoundedVertexGraphFormatter(id, defaultFormat, selectedFormat, overFormat, draggingFormat){ + VertexGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, draggingFormat); + this.args.type = "RoundedVertexGraphFormatter"; + if (defaultFormat != null){ + if (defaultFormat.radius != null){ + this.defaultFormat.args.radius = defaultFormat.radius; + } + } + + if (selectedFormat != null){ + if (selectedFormat.radius != null){ + this.selected.args.radius = selectedFormat.radius; + } + } + + if (overFormat != null){ + if (overFormat.radius != null){ + this.over.args.radius = overFormat.radius; + } + } + + if (draggingFormat != null){ + if (draggingFormat.radius != null){ + this.dragging.args.draggingFormat = draggingFormat.radius; + } + } +}; + + + +OctagonVertexGraphFormatter.prototype.getDefault = ItemGraphFormatter.prototype.getDefault; +OctagonVertexGraphFormatter.prototype.getSelected = ItemGraphFormatter.prototype.getSelected; +OctagonVertexGraphFormatter.prototype.getOver = ItemGraphFormatter.prototype.getOver; +OctagonVertexGraphFormatter.prototype.getDragging = ItemGraphFormatter.prototype.getDragging; +OctagonVertexGraphFormatter.prototype.getId = ItemGraphFormatter.prototype.getId; +OctagonVertexGraphFormatter.prototype.toJSON = ItemGraphFormatter.prototype.toJSON; +OctagonVertexGraphFormatter.prototype.loadFromJSON = ItemGraphFormatter.prototype.loadFromJSON; +OctagonVertexGraphFormatter.prototype._setEvents = ItemGraphFormatter.prototype._setEvents; + +function OctagonVertexGraphFormatter(id, defaultFormat, selectedFormat, overFormat, draggingFormat){ + VertexGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, draggingFormat); + this.args.type = "OctagonVertexGraphFormatter"; + if (defaultFormat != null){ + if (defaultFormat.radius != null){ + this.defaultFormat.args.radius = defaultFormat.radius; + } + } + + if (selectedFormat != null){ + if (selectedFormat.radius != null){ + this.selected.args.radius = selectedFormat.radius; + } + } + + if (overFormat != null){ + if (overFormat.radius != null){ + this.over.args.radius = overFormat.radius; + } + } + + if (draggingFormat != null){ + if (draggingFormat.radius != null){ + this.dragging.args.draggingFormat = draggingFormat.radius; + } + } +}; + + + +var Colors = new function() +{ + this.hashColor = []; + this.getColorByScoreArrayValue = function (arrayScore) + { + var array = new Array(); + + for (var i = 0; i< arrayScore.length; i++) + { + + var color = this.getColorByScoreValue(arrayScore[i]) + array.push( color); + + } + return array; + }; + + this.getHexStringByScoreArrayValue = function (arrayScore) + { + var arrayColor = this.getColorByScoreArrayValue(arrayScore); + var arrayHex = new Array(); + for (var i = 0; i< arrayColor.length; i++) + { + arrayHex.push( arrayColor[i].HexString()); + } + return arrayHex; + }; + + this.getColorByScoreValue = function (score) + { + + var truncate = score.toString().substr(0,4); + if (this.hashColor[truncate]!=null) + { + return this.hashColor[truncate]; + } + + + if(isNaN(score)) { + return Colors.ColorFromRGB(0,0,0); + } + var value; + + var from, to; + if(score < 0.5) { + from = Colors.ColorFromRGB(0,0,255); + to = Colors.ColorFromRGB(255,255,255); + value = (score * 2); + } else { + from = Colors.ColorFromRGB(255,255,255); + to = Colors.ColorFromRGB(255,0,0); + value = (score * 2) - 1; + } + + var x = value; + var y = 1.0 - value; + var color = Colors.ColorFromRGB(y * from.Red() + x * to.Red(), y * from.Green() + x * to.Green(), y * from.Blue() + x * to.Blue()); + + this.hashColor[truncate] = color; + + return color; + }; + + this.ColorFromHSV = function(hue, sat, val) + { + var color = new Color(); + color.SetHSV(hue,sat,val); + return color; + }; + + this.ColorFromRGB = function(r, g, b) + { + var color = new Color(); + color.SetRGB(r,g,b); + return color; + }; + + this.ColorFromHex = function(hexStr) + { + var color = new Color(); + color.SetHexString(hexStr); + return color; + }; + + function Color() { + //Stored as values between 0 and 1 + var red = 0; + var green = 0; + var blue = 0; + + //Stored as values between 0 and 360 + var hue = 0; + + //Strored as values between 0 and 1 + var saturation = 0; + var value = 0; + + this.SetRGB = function(r, g, b) + { + red = r/255.0; + green = g/255.0; + blue = b/255.0; + calculateHSV(); + }; + + this.Red = function() { return Math.round(red*255); }; + + this.Green = function() { return Math.round(green*255); }; + + this.Blue = function() { return Math.round(blue*255); }; + + this.SetHSV = function(h, s, v) + { + hue = h; + saturation = s; + value = v; + calculateRGB(); + }; + + this.Hue = function() + { return hue; }; + + this.Saturation = function() + { return saturation; }; + + this.Value = function() + { return value; }; + + this.SetHexString = function(hexString) + { + if(hexString == null || typeof(hexString) != "string") + { + this.SetRGB(0,0,0); + return; + } + + if (hexString.substr(0, 1) == '#') + hexString = hexString.substr(1); + + if(hexString.length != 6) + { + this.SetRGB(0,0,0); + return; + } + + var r = parseInt(hexString.substr(0, 2), 16); + var g = parseInt(hexString.substr(2, 2), 16); + var b = parseInt(hexString.substr(4, 2), 16); + if (isNaN(r) || isNaN(g) || isNaN(b)) + { + this.SetRGB(0,0,0); + return; + } + + this.SetRGB(r,g,b); + }; + + this.HexString = function() + { + + var rStr = this.Red().toString(16); + if (rStr.length == 1) + rStr = '0' + rStr; + var gStr = this.Green().toString(16); + if (gStr.length == 1) + gStr = '0' + gStr; + var bStr = this.Blue().toString(16); + if (bStr.length == 1) + bStr = '0' + bStr; + return ('#' + rStr + gStr + bStr).toUpperCase(); + }; + + this.Complement = function() + { + var newHue = (hue >= 180) ? hue - 180 : hue + 180; + var newVal = (value * (saturation - 1) + 1); + var newSat = (value*saturation) / newVal; + var newColor = new Color(); + newColor.SetHSV(newHue, newSat, newVal); + return newColor; + } ; + + function calculateHSV() + { + var max = Math.max(Math.max(red, green), blue); + var min = Math.min(Math.min(red, green), blue); + + value = max; + + saturation = 0; + if(max != 0) + saturation = 1 - min/max; + + hue = 0; + if(min == max) + return; + + var delta = (max - min); + if (red == max) + hue = (green - blue) / delta; + else if (green == max) + hue = 2 + ((blue - red) / delta); + else + hue = 4 + ((red - green) / delta); + hue = hue * 60; + if(hue < 0) + hue += 360; + } + + function calculateRGB() + { + red = value; + green = value; + blue = value; + + if(value == 0 || saturation == 0) + return; + + var tHue = (hue / 60); + var i = Math.floor(tHue); + var f = tHue - i; + var p = value * (1 - saturation); + var q = value * (1 - saturation * f); + var t = value * (1 - saturation * (1 - f)); + switch(i) + { + case 0: + red = value; green = t; blue = p; + break; + case 1: + red = q; green = value; blue = p; + break; + case 2: + red = p; green = value; blue = t; + break; + case 3: + red = p; green = q; blue = value; + break; + case 4: + red = t; green = p; blue = value; + break; + default: + red = value; green = p; blue = q; + break; + } + } + } +} +(); +/* + * Clase gestiona la insercción, eliminación de los elementos en el DOM + * + * Vital hacerla SIEMPRE compatible hacia atrás + * + * Last update: 28-10-2010 + * + */ + + +var DOM = {}; + +DOM.createNewElement = function(type, nodeParent, attributes) +{ + + var node = document.createElement(type); + for (var i=0; i0) + { + parent.removeChild(parent.childNodes[0]); + + } +}; + +DOM.select = function(targetID) +{ + return document.getElementById(targetID); +// return $("#"+targetID); +}; +var Geometry = +{ + + /** From tow points obtains the angles formed with the cartesian side **/ + getAngleBetweenTwoPoints : function(x1, y1, x2, y2){ + //var m = (y1 - y2)/ (x1 - x2); + return Math.atan2(y2-y1, x2-x1); + }, + + getAdjacentSideOfRectangleRight : function(angle, hypotenuse){ + return Math.abs(Math.cos(angle)*hypotenuse); + }, + + getOppositeSideOfRectangleRight : function(angle, hypotenuse){ + return Math.abs(Math.sin(angle)*hypotenuse); + }, + + toDegree: function(radian){ + return radian*180/Math.PI; + + } + + +}; + +var SVG = +{ + svgns : 'http://www.w3.org/2000/svg', + xlinkns : "http://www.w3.org/1999/xlink", + + createSVGCanvas: function(parentNode, attributes) + { + + attributes.push( ['xmlns', SVG.svgns], ['xmlns:xlink', 'http://www.w3.org/1999/xlink']); + var svg = document.createElementNS("http://www.w3.org/2000/svg", "svg"); + this._setProperties(svg, attributes); + parentNode.appendChild( svg); + return svg; + + }, + + createRectangle : function (x, y, width, height, attributes){ + var rect = document.createElementNS(this.svgns, "rect"); + rect.setAttribute("x",x); + rect.setAttribute("y",y); + rect.setAttribute("width",width); + rect.setAttribute("height",height); + SVG._setProperties(rect, attributes); + return rect; + }, + + drawImage64 : function (x, y, width, height, base64, svgNode, attributes) { + var node = SVG.createImage64(x, y, width, height, base64, attributes); + svgNode.appendChild(node); + return node; + }, + + createImage64 : function (x, y, width, height, base64, attributes) { + var img = document.createElementNS(this.svgns, "image"); + img.setAttribute("x",x); + img.setAttribute("y",y); + img.setAttribute("width",width); + img.setAttribute("height",height); + img.setAttribute("xlink:href",base64); + SVG._setProperties(img, attributes); + return img; + }, + + createLine: function (x1, y1, x2, y2, attributes){ + var line = document.createElementNS(this.svgns,"line"); + line.setAttribute("x1",x1); + line.setAttribute("y1",y1); + line.setAttribute("x2", x2); + line.setAttribute("y2", y2); + SVG._setProperties(line, attributes); + return line; + }, + + createClip: function (id, nodeToClip, attributes){ + var clip = document.createElementNS(this.svgns,"clipPath"); + clip.setAttribute("id",id); + clip.appendChild(nodeToClip); + return clip; + }, + + drawClip : function (id, nodeToClip, svgNode) { + var node = SVG.createClip(id, nodeToClip); + svgNode.appendChild(node); + return node; + }, + + drawRectangle : function (cx, cy, width, height, svgNode, attributes) { + try{ + var node = SVG.createRectangle(cx, cy, width, height, attributes); + svgNode.appendChild(node); + } + catch(e){ + + console.log("-------------------- "); + console.log("Error on drawRectangle " + e); + console.log(attributes); + console.log("-------------------- "); + } + return node; + }, + + createEllipse : function (x, y, rx, ry, attributes){ + var rect = document.createElementNS(this.svgns, "ellipse"); + rect.setAttribute("cx",x); + rect.setAttribute("cy",y); + rect.setAttribute("rx",rx); + rect.setAttribute("ry",ry); + SVG._setProperties(rect, attributes); + return rect; + }, + + drawEllipse : function (cx, cy, rx, ry, svgNode, attributes) { + var node = SVG.createEllipse(cx, cy, rx, ry, attributes); + svgNode.appendChild(node); + return node; + }, + + drawImage : function (x, y, canvasSVG, attributes) { + var image = document.createElementNS(this.svgns, "image"); + image.setAttribute("x",x); + image.setAttribute("y",y); + canvasSVG.appendChild(image); + SVG._setProperties(image, attributes); + }, + + drawLine : function (x1, y1, x2, y2, nodeSVG, attributes) { + try{ + var line = SVG.createLine(x1, y1, x2, y2, attributes); + nodeSVG.appendChild(line); + }catch(e){ + } + return line; + }, + + + drawPath: function (d, nodeSVG, attributes) { + var path = SVG.createPath(d, attributes); + nodeSVG.appendChild(path); + return path; + }, + + createPoligon : function (points, attributes){ + var poligon = document.createElementNS(this.svgns, "polygon"); + poligon.setAttribute("points",points); + SVG._setProperties(poligon, attributes); + return poligon; + }, + + drawPoligon : function (points, canvasSVG, attributes){ + var poligon = SVG.createPoligon(points, attributes); + canvasSVG.appendChild(poligon); + return poligon; + }, + // + + createPath : function (d, attributes){ + var path = document.createElementNS(this.svgns, "path"); + path.setAttribute("d",d); + SVG._setProperties(path, attributes); + return path; + }, + + drawCircle : function (x, y, radio, canvasSVG, attributes) { + + var newText = document.createElementNS(this.svgns,"circle"); + newText.setAttribute("cx",x); + newText.setAttribute("cy",y); + newText.setAttribute("r",radio); + + canvasSVG.appendChild(newText); + attributes["cursor"] = "pointer"; + this._setProperties(newText, attributes); + return newText; + }, + + + _setProperties: function(node, attributes) + { + if (attributes instanceof Array){ + for (var i=0; i< attributes.length; i++) + { + node.setAttribute(attributes[i][0], attributes[i][1]); + } + } + else{ + for ( var key in attributes){ + node.setAttribute(key, attributes[key]); + } + } + }, + +/* drawPath: function(pointsArray, canvasSVG, attributes){ + var path = document.createElementNS(this.svgns,"polyline"); + path.setAttribute ('id', id); + + var d= pointsArray[0].x+ " "+ pointsArray[0].y; + for (var i=1; i< pointsArray.length; i++) + { + d=d+" "+pointsArray[i].x+" "+pointsArray[i].y; + } + path.setAttribute ('points', d); + canvasSVG.appendChild(path); + },*/ + + createText : function (x, y, text, attributes) { + var node = document.createElementNS(this.svgns,"text"); + node.setAttributeNS(null , "x",x); + node.setAttributeNS(null, "y",y); + + var textNode = document.createTextNode(text); + node.appendChild(textNode); + + this._setProperties(node, attributes); + return node; + }, + + drawText : function (x, y, text, canvasSVG, attributes) { + var text = SVG.createText(x, y, text, attributes); + canvasSVG.appendChild(text); + return text; + }, + + + + drawGroup: function(svgNode, attributes) + { + var group = SVG.createGroup(attributes); + svgNode.appendChild(group); + return group; + }, + + createGroup: function(attributes){ + var group = document.createElementNS(this.svgns,"g"); + this._setProperties(group, attributes); + return group; + } + +}; + + + +var CanvasToSVG = { + + convert: function(sourceCanvas, targetSVG, x, y, id, attributes) { + + var img = this._convert(sourceCanvas, targetSVG, x, y, id); + + for (var i=0; i< attributes.length; i++) + { + img.setAttribute(attributes[i][0], attributes[i][1]); + } + }, + + _convert: function(sourceCanvas, targetSVG, x, y, id) { + var svgNS = "http://www.w3.org/2000/svg"; + var xlinkNS = "http://www.w3.org/1999/xlink"; + // get base64 encoded png from Canvas + var image = sourceCanvas.toDataURL(); + + // must be careful with the namespaces + var svgimg = document.createElementNS(svgNS, "image"); + + svgimg.setAttribute('id', id); + + //svgimg.setAttribute('class', class); + //svgimg.setAttribute('xlink:href', image); + svgimg.setAttributeNS(xlinkNS, 'xlink:href', image); + + + + + svgimg.setAttribute('x', x ? x : 0); + svgimg.setAttribute('y', y ? y : 0); + svgimg.setAttribute('width', sourceCanvas.width); + svgimg.setAttribute('height', sourceCanvas.height); + //svgimg.setAttribute('cursor', 'pointer'); + svgimg.imageData = image; + + targetSVG.appendChild(svgimg); + return svgimg; + }, + + importSVG: function(sourceSVG, targetCanvas) { + svg_xml = sourceSVG;//(new XMLSerializer()).serializeToString(sourceSVG); + var ctx = targetCanvas.getContext('2d'); + + var img = new Image(); + img.src = "data:image/svg+xml;base64," + btoa(svg_xml); +// img.onload = function() { + ctx.drawImage(img, 0, 0); +// }; + } + +}; +/* +Graph.prototype.importSVG = function(sourceSVG, targetCanvas) { + sourceSVG = this._svg; + targetCanvas = document.createElementNS('canvas'); + // https://developer.mozilla.org/en/XMLSerializer + svg_xml = (new XMLSerializer()).serializeToString(sourceSVG); + var ctx = targetCanvas.getContext('2d'); + // this is just a JavaScript (HTML) image + var img = new Image(); + // http://en.wikipedia.org/wiki/SVG#Native_support + // https://developer.mozilla.org/en/DOM/window.btoa + img.src = "data:image/svg+xml;base64," + btoa(svg_xml); + img.onload = function() { + // after this, Canvas’ origin-clean is DIRTY + ctx.drawImage(img, 0, 0); + } +}; +*/ + +/* + +Normalizacion de datos para dibujar colores +Issues: + No sé como debería llamarse esta libreria + No sé si ya existe una funciçon en javascript que lo haga + + +*/ + + +var Normalizer = new function() +{ + this.normalizeArray = function (arrayData) + { + + return this.standardizeArray(this.normal(arrayData)); + +// var result = this._getMaxAndMin(arrayData); +// var min =result[0]; +// var max = result[1]; +// +// +// //los hacemos todos positivos +// for (var i = 0; i< arrayData.length; i++) +// { +// arrayData[i]= Math.abs(min) + parseFloat(arrayData[i]); +// } +// +// var result = this._getMaxAndMin(arrayData); +// var min =result[0]; +// var max = result[1]; +// +// +// var resultArray = new Array(); +// for (var i = 0; i< arrayData.length; i++) +// { +// resultArray.push(arrayData[i]*1/max); +// } +// return resultArray; + }; + + this.normal = function(arrayData){ + var mean = this._getMean(arrayData); + var deviation = this._getStdDeviation(arrayData); + var result = this._getMaxAndMin(arrayData); + var min = result[0]; + var max = result[1]; + + var resultArray = new Array(); + for (var i = 0; i< arrayData.length; i++) { + if (deviation!=0){ + resultArray.push((arrayData[i]-mean)/deviation); + }else{ + resultArray.push(arrayData[i]); + } + } + return resultArray; + }; + + this.standardizeArray = function(arrayData) + { + var result = this._getMaxAndMin(arrayData); + var min = result[0]; + var max = result[1]; + + var offset = ( min <= 0 ) ? Math.abs(min) : (-1 * min); + var resultArray = new Array(); + for (var i = 0; i< arrayData.length; i++) { + if(max + offset!=0){ + resultArray.push((arrayData[i] + offset) / (max + offset)); + }else{ + resultArray.push(arrayData[i]+offset); + } + } + return resultArray; + }; + + + this._getMean = function(arrayData) { + var sum = 0; + for (var i = 0; i< arrayData.length; i++) { + sum = sum + parseFloat(arrayData[i]); + } + return sum/arrayData.length; + }; + + this._getStdDeviation = function(arrayData) { + var mean = this._getMean(arrayData); + var acum = 0.0; + for(var i=0; i max) max = parseFloat(arrayData[i]); + } + + return [min, max]; + }; +}; +function GraphCanvas(componentID, targetNode, args) { + this.args = {}; + /** target */ + this.targetID = targetNode.id; + + /** id manage */ + this.id = componentID; + this.args.idGraph = this.id + "main"; + this.args.idBackgroundNode = this.id + "background"; + + this.args.idEdgesGraph = this.id + "edges"; + this.args.idNodesGraph = this.id + "vertices"; + this.args.idLabelGraph = this.id + "label"; + this.args.idBackground = this.id + "background"; + + /** Objects Graph **/ + this.dataset = null; + this.formatter = null; + this.layout = null; + + /** Drawing **/ + this.circleDefaultRadius = 2; + this.squareDefaultSide = this.circleDefaultRadius * 1.5; + + /** Directed Arrow **/ + this.arrowDefaultSize = this.circleDefaultRadius; + + /** Groups **/ + this.GraphGroup = null; + this.GraphNodeGroup = null; + this.GraphLabelGroup = null; + this.GraphBackground = null; + + /** SETTINGS FLAGS **/ + this.args.draggingCanvasEnabled = false; //Flag to set if the canvas can be dragged + this.args.multipleSelectionEnabled = false; + this.args.interactive = false; + this.args.labeled = false; + this.args.linkEnabled = false; + + /** If numberEdge > maxNumberEdgesMoving then only it will move edges when mouse up **/ + this.args.maxNumberEdgesMoving = 3; + this.args.maxNumberEdgesFiringEvents = 50; + + /** Linking edges **/ + this.args.linking = false; + this.linkStartX = 0; + this.linkStartY = 0; + this.linkSVGNode = null; + this.linkNodeSource = null; + this.linkNodeTarget = null; + + /** Dragging Control **/ + this.draggingElement = null; + this.dragging = false; + this.nMouseOffsetX = 0; + this.nMouseOffsetY = 0; + this.dragStartX = 0; + this.dragStartY = 0; + this.desplazamientoX = 0; + this.desplazamientoY = 0; + + /** Selection Control **/ + this.selecting = false; + this.selectorX = null; + this.selectorY = null; + this.selectorSVGNode = null; + + /** Node status **/ + this.args.isVertexSelected = {}; + this.args.selectedVertices = []; + + /** Edges status **/ + this.args.isEdgeSelected = {}; + //this.args.selectedEdges = []; + + if (args != null) { + if (args.multipleSelectionEnabled != null) { + this.args.multipleSelectionEnabled = args.multipleSelectionEnabled; + this.args.draggingCanvasEnabled = !(this.args.multipleSelectionEnabled); + } + if (args.draggingCanvasEnabled != null) { + this.args.draggingCanvasEnabled = args.draggingCanvasEnabled; + this.args.multipleSelectionEnabled = !(this.args.draggingCanvasEnabled); + } + if (args.interactive != null) { + this.args.interactive = args.interactive; + } + if (args.labeled != null) { + this.args.labeled = args.labeled; + } + + } + + /** Hashmap with the svg node labels **/ + this.svgLabels = {}; + + /** EVENTS **/ + this.onVertexOut = new Event(this); + this.onVertexOver = new Event(this); + this.onVertexSelect = new Event(this); + this.onEdgeSelect = new Event(this); + this.onCanvasClicked = new Event(this); + this.onVertexUp = new Event(this); +} + +GraphCanvas.prototype.showLabels = function(value) { + this.args.labeled = value; + this.removeLabels(); + if (value) { + this.renderLabels(); + } +}; + +GraphCanvas.prototype.getSelectedVertices = function() { + return this.args.selectedVertices; +}; + +GraphCanvas.prototype.getSelectedEdges = function() { + var selected = []; + for ( var selectedEdge in this.args.isEdgeSelected) { + selected.push(selectedEdge); + } + return selected; +}; + +GraphCanvas.prototype.createSVGDom = function(targetID, id, width, height, backgroundColor) { + var container = document.getElementById(targetID); + this._svg = SVG.createSVGCanvas(container, [ + [ "style", "background-color:" + backgroundColor + ";" ], [ "id", id ], [ "dragx", 0 ], [ "dragy", 0 ], + [ "height", this.getFormatter().getHeight() ], [ "width", this.getFormatter().getWidth() ] ]); + return this._svg; +}; + +/** MULTIPLE SELECTION **/ +GraphCanvas.prototype.isMultipleSelectionEnabled = function() { + return this.args.multipleSelectionEnabled; +}; + +GraphCanvas.prototype.setMultipleSelection = function(value) { + this.args.multipleSelectionEnabled = value; + this.args.draggingCanvasEnabled = (!value); +}; + +GraphCanvas.prototype.setSelecting = function(value) { + this.selecting = value; +}; + +/** linking **/ +GraphCanvas.prototype.setLinking = function(value) { + this.args.linkEnabled = value; + this.selecting = !value; + this.dragging = !value; +}; + +/** CANVAS MOVING **/ +GraphCanvas.prototype.setDraggingCanvas = function(value) { + this.args.draggingCanvasEnabled = value; + this.args.multipleSelectionEnabled = !value; +}; + +GraphCanvas.prototype.isDraggingCanvasEnabled = function() { + return this.args.draggingCanvasEnabled; +}; +/** ZOOM **/ +GraphCanvas.prototype.getScale = function() { + return this.getFormatter().getZoomScale(); +}; + +GraphCanvas.prototype.setScale = function(scale) { + var graphNode = document.getElementById(this.args.idGraph); + graphNode.setAttribute("transform", graphNode.getAttribute("transform").replace("scale(" + this.getScale() + ")", "scale(" + scale + ")")); + this.getFormatter().setZoomScale(scale); +}; + +GraphCanvas.prototype.zoomIn = function() { + this.setScale(this.getScale() + this.getFormatter().getZoomScaleStepFactor()); +}; + +GraphCanvas.prototype.zoomOut = function() { + this.setScale(this.getScale() - this.getFormatter().getZoomScaleStepFactor()); + +}; + +/** SVG COORDENATES **/ +GraphCanvas.prototype.getSVGCoordenates = function(evt) { + var p = this._svg.createSVGPoint(); + p.x = evt.clientX; + p.y = evt.clientY; + + var m = this._svg.getScreenCTM(document.documentElement); + p = p.matrixTransform(m.inverse()); + return p; +}; + +/** SVG EVENTS **/ +GraphCanvas.prototype.mouseClick = function(event) { + if (event.button == 0) { + if (!this.args.interactive) { + return; + } + + if (this.isVertex(event.target)) { + this.clickNode(this.getVertexIdFromSVGId(event.target.id)); + } + /** Como el evento mouseClick viene despues del mouse up es aqui donde manejo el tema de deseccionar los elementos que estoy dragging **/ + if (this.dragging) { + this.dragging = false; + } + } +}; + +GraphCanvas.prototype.mouseMove = function(evt) { + if (this.selecting) { + this.clearLabels(); + + var width = (this.getSVGCoordenates(evt).x - this.selectorX); + var height = (this.getSVGCoordenates(evt).y - this.selectorY); + if ((width > 0) && (height > 0)) { + this.displaySelection(this.selectorX, this.selectorY, width, height); + } + if ((width > 0) && (height < 0)) { + this.displaySelection(this.selectorX, this.getSVGCoordenates(evt).y, width, Math.abs(height)); + } + if ((width < 0) && (height < 0)) { + this.displaySelection(this.getSVGCoordenates(evt).x, this.getSVGCoordenates(evt).y, Math.abs(width), Math.abs(height)); + } + if ((width < 0) && (height > 0)) { + this.displaySelection(this.selectorX + width, this.selectorY, Math.abs(width), Math.abs(height)); + } + + var x1 = (parseFloat(this.selectorSVGNode.getAttribute("x")) - DOM.select(this.id).getAttribute("dragx")) / this.getFormatter().getWidth(); + var y1 = (parseFloat(this.selectorSVGNode.getAttribute("y")) - DOM.select(this.id).getAttribute("dragy")) / this.getFormatter().getHeight(); + var x2 = (x1 + parseFloat(this.selectorSVGNode.getAttribute("width") / this.getFormatter().getWidth())); + var y2 = (y1 + parseFloat(this.selectorSVGNode.getAttribute("height") / this.getFormatter().getHeight())); + + this.deselectNodes(this.getLayout()); + var verticesSelected = this.getLayout().getVerticesByArea(x1 / this.getFormatter().getZoomScale(), y1 / this.getFormatter().getZoomScale(), + x2 / this.getFormatter().getZoomScale(), y2 / this.getFormatter().getZoomScale()); + for ( var i = 0; i < verticesSelected.length; i++) { + this.selectNode(verticesSelected[i].getId()); + this.renderLabel(verticesSelected[i].getId()); + } + + } + var p = null; + if (this.args.linking) { + p = this.getSVGCoordenates(evt); + if (this.linkSVGNode != null) { + this.linkSVGNode.setAttribute("x2", p.x - 2); + this.linkSVGNode.setAttribute("y2", p.y - 2); + } + } + + if (this.dragging) { + p = this.getSVGCoordenates(evt); + p.x -= this.nMouseOffsetX; + p.y -= this.nMouseOffsetY; + this.desplazamientoX = (this.getSVGCoordenates(evt).x - this.dragStartX);// + parseFloat(DOM.select(this.id).getAttribute("dragx")); + this.desplazamientoY = (this.getSVGCoordenates(evt).y - this.dragStartY);// + parseFloat(DOM.select(this.id).getAttribute("dragy")); + + if (this.draggingElement != null) { + /** Click sobre el recct del banground que provoca que mueva todo el canvas **/ + if (this.isNodeCanvas(this.draggingElement)) { + + p = this.getSVGCoordenates(evt); + p.x = this.desplazamientoX; + p.y = this.desplazamientoY; + + this.draggingElement.setAttribute("dragx", p.x); + this.draggingElement.setAttribute("dragy", p.y); + this.draggingElement = document.getElementById(this.args.idGraph); + this.draggingElement.setAttribute("transform", "translate(" + p.x + "," + p.y + "), scale(" + this.getScale() + ")"); + + DOM.select(this.id).setAttribute("dragx", p.x); + DOM.select(this.id).setAttribute("dragy", p.y); + + if (this.NodeSVGbackgroundImage != null) { + this.NodeSVGbackgroundImage.setAttribute("dragx", p.x); + this.NodeSVGbackgroundImage.setAttribute("dragy", p.y); + } + } else { + if (this.isVertex(this.draggingElement)) { + this.selectNode(this.getVertexIdFromSVGId(this.draggingElement.id)); + this.desplazamientoX = this.desplazamientoX / this.getFormatter().getZoomScale(); + this.desplazamientoY = this.desplazamientoY / this.getFormatter().getZoomScale(); + this.moveSelectedNodes(this.desplazamientoX, this.desplazamientoY); + + this.dragStartX = this.getSVGCoordenates(evt).x; + this.dragStartY = this.getSVGCoordenates(evt).y; + } else { + if (this.isNodeBackground(this.draggingElement)) { + + this.draggingElement.setAttribute("dragx", p.x); + this.draggingElement.setAttribute("dragy", p.y); + this.draggingElement = document.getElementById(this.args.idGraph); + this.draggingElement.setAttribute("transform", "translate(" + p.x + "," + p.y + "), scale(" + this.getScale() + ")"); + } else { + this.draggingElement.setAttribute("transform", "translate(" + p.x + "," + p.y + ")"); + } + } + } + } + } +}; + +GraphCanvas.prototype.moveSelectedNodes = function(offsetX, offsetY) { + for ( var i = 0; i < this.getSelectedVertices().length; i++) { + + var nodeId = this.getSelectedVertices()[i]; + var svgNodeId = this.getSVGNodeId(nodeId); + + var x = parseFloat(DOM.select(svgNodeId).getAttribute("dragx")) + parseFloat(offsetX);// - parseFloat(DOM.select(this.id).getAttribute("dragx")); + var y = parseFloat(DOM.select(svgNodeId).getAttribute("dragy")) + parseFloat(offsetY);// + parseFloat(DOM.select(this.id).getAttribute("dragy")); + + this._movingNode(DOM.select(svgNodeId), x, y); + } +}; + +GraphCanvas.prototype.mouseDown = function(evt) { + if (event.button == 0) { + + /** if !no interactive mouse events do anything **/ + if (!this.args.interactive) { + return; + } + + var p = this.getSVGCoordenates(evt); + + /** When click on canvas or background deselect all **/ + if (this.isNodeCanvas(evt.target) || this.isNodeBackground(evt.target)) { + this.deselectNodes(); + this.deselectEdges(); + this.onCanvasClicked.notify(); + } + + /** if I am linking vertices **/ + if (this.args.linkEnabled) { + + if (!this.args.linking) { + this.args.linking = true; + if (this.isVertex(evt.target)) { + this.linkStartX = p.x; + this.linkStartY = p.y; + this.linkSVGNode = SVG.drawLine(p.x, p.y, p.x, p.y, this._svg, { + "stroke" : "#FF0000" + }); + this.linkNodeSource = this.getVertexIdFromSVGId(evt.target.id); + } + } else { + this.linkNodeTarget = this.getVertexIdFromSVGId(evt.target.id); + this.args.linking = false; + this.args.linkEnabled = false; + if (this.isVertex(evt.target)) { + this.getDataset().addEdge(this.linkNodeSource + "_" + this.linkNodeTarget, this.linkNodeSource, this.linkNodeTarget, {}); + } + this.linkSVGNode.parentNode.removeChild(this.linkSVGNode); + } + return; + } + + /** Id is a vertex or the canvas **/ + if (this.isVertex(evt.target) || this.isNodeCanvas(evt.target) || this.isNodeBackground(evt.target)) { + this._startDragging(evt); + } + /** if i is edge **/ + if (this.isEdge(evt.target)) { + this.selectEdge(this.getEdgeIdFromSVGId(evt.target.getAttribute("id"))); + } + + if (this.args.multipleSelectionEnabled) { + if (!this.dragging) { + this.setSelecting(true); + this.selectorX = p.x; + this.selectorY = p.y; + this.displaySelection(p.x, p.y, 1, 1); + } + } + + } + if (event.button == 1) { + this.setLinking(false); + this.setMultipleSelection(false); + this.selecting = false; + + /** Id is a vertex or the canvas **/ + if (this.isVertex(evt.target) || this.isNodeCanvas(evt.target) || this.isNodeBackground(evt.target)) { + this._startDragging(evt); + } + } +}; + +GraphCanvas.prototype.mouseUp = function(event) { + if (!this.args.interactive) { + return; + } + + if (this.dragging) { + this._stopDragging(event); + if (this.isVertex(event.target)) { + var vertexId = this.getVertexIdFromSVGId(event.target.id); + if (this.getDataset().getVertexById(vertexId).getEdges().length >= this.args.maxNumberEdgesMoving) { + this.moveEdge(vertexId); + } + } + } + + if (this.selecting) { + this.setSelecting(false); + + var x1 = (parseFloat(this.selectorSVGNode.getAttribute("x")) - DOM.select(this.id).getAttribute("dragx")) / this.getFormatter().getWidth(); + var y1 = (parseFloat(this.selectorSVGNode.getAttribute("y")) - DOM.select(this.id).getAttribute("dragy")) / this.getFormatter().getHeight(); + var x2 = (x1 + parseFloat(this.selectorSVGNode.getAttribute("width") / this.formatter.getWidth())); + var y2 = (y1 + parseFloat(this.selectorSVGNode.getAttribute("height") / this.formatter.getHeight())); + + var verticesSelected = this.getLayout().getVerticesByArea(x1 / this.getFormatter().getZoomScale, y1 / this.getFormatter().getZoomScale, + x2 / this.getFormatter().getZoomScale, y2 / this.getFormatter().getZoomScale); + + for ( var i = 0; i < verticesSelected.length; i++) { + this.selectNode(verticesSelected[i].getId()); + } + + if (this.selectorSVGNode != null) { + this._svg.removeChild(this.selectorSVGNode); + } + + if (this.args.labeled) { + this.clearLabels(); + this.renderLabels(); + } + + this.selectorSVGNode = null; + // this.renderLabels(); + } +}; + +/** SELECTION **/ +GraphCanvas.prototype.displaySelection = function(x, y, width, height) { + if (this.selectorSVGNode != null) { + this.selectorSVGNode.setAttribute("x", x); + this.selectorSVGNode.setAttribute("y", y); + this.selectorSVGNode.setAttribute("width", width); + this.selectorSVGNode.setAttribute("height", height); + } else { + this.selectorSVGNode = SVG.drawRectangle(x, y, width, height, this._svg, { + "fill" : "red", + "stroke" : "black", + "opacity" : "0.2", + "stroke-opacity" : "1" + }); + } +}; + +/** DRAGGING **/ +GraphCanvas.prototype._startDragging = function(evt) { + if (!this.isDraggingCanvasEnabled()) { + if (this.isNodeCanvas(evt.target)) { + this.draggingElement = null; + } + } + + if (this.isVertex(evt.target) || (this.isNodeBackground(evt.target) && (this.isDraggingCanvasEnabled()))|| (this.isNodeCanvas(evt.target) && (this.isDraggingCanvasEnabled()))) { + this.clearLabels(); + this.draggingElement = evt.target; + this.dragging = true; + var p = this.getSVGCoordenates(evt); + + this.nMouseOffsetX = p.x - parseInt(evt.target.getAttribute("dragx")); + this.nMouseOffsetY = p.y - parseInt(evt.target.getAttribute("dragy")); + + if (this.isVertex(evt.target)) { + this.dragStartX = parseInt(this.draggingElement.getAttribute("dragx")) * this.getFormatter().getZoomScale() + + parseFloat(DOM.select(this.id).getAttribute("dragx")); + this.dragStartY = parseInt(this.draggingElement.getAttribute("dragy")) * this.getFormatter().getZoomScale() + + parseFloat(DOM.select(this.id).getAttribute("dragy")); + } else { + this.dragStartX = p.x - parseInt(this.draggingElement.getAttribute("dragx"));// + parseFloat(DOM.select(this.id).getAttribute("dragx")); + this.dragStartY = p.y - parseInt(this.draggingElement.getAttribute("dragy"));// + parseFloat(DOM.select(this.id).getAttribute("dragy")); + } + } +}; + +GraphCanvas.prototype._stopDragging = function(event) { + this.nMouseOffsetX = 0; + this.nMouseOffsetX = 0; + /** despues del evento up viene el evento click entonces acabo el dragging en el mouseclick **/ + this.dragging = false; + this.draggingElement = null; + this.renderLabels(); + + this.setLinking(false); + this.setMultipleSelection(true); + this.selecting = false; + +}; + +/** Move the edges of the vertex with the vertexId indicado **/ +GraphCanvas.prototype.moveEdge = function(vertexId) { + var x = this.getLayout().getNodeById(vertexId).x * this.getFormatter().getWidth(); + var y = this.getLayout().getNodeById(vertexId).y * this.getFormatter().getHeight(); + + /** Moving edges out **/ + for ( var i = 0; i < this.getDataset().getVertexById(vertexId).getEdgesOut().length; i++) { + var edgeId = this.getDataset().getVertexById(vertexId).getEdgesOut()[i].getId(); + var svgEdgeId = this.getSVGEdgeId(edgeId); + var edgeFormatter = this.getFormatter().getEdgeById(edgeId); + if (edgeFormatter instanceof LineEdgeGraphFormatter) { + DOM.select(svgEdgeId + "_shadow").setAttribute("x2", x); + DOM.select(svgEdgeId + "_shadow").setAttribute("y2", y); + DOM.select(svgEdgeId).setAttribute("x2", x); + DOM.select(svgEdgeId).setAttribute("y2", y); + } + + if ((edgeFormatter instanceof DirectedLineEdgeGraphFormatter) || (edgeFormatter instanceof OdirectedLineEdgeGraphFormatter) + || (edgeFormatter instanceof OdotDirectedLineEdgeGraphFormatter) || (edgeFormatter instanceof DotDirectedLineEdgeGraphFormatter) + || (edgeFormatter instanceof CutDirectedLineEdgeGraphFormatter)) { + this.removeEdge(edgeId); + this.renderEdge(edgeId); + } + } + + /** Moving edges in **/ + for ( var i = 0; i < this.getDataset().getVertexById(vertexId).getEdgesIn().length; i++) { + var edgeId = this.getDataset().getVertexById(vertexId).getEdgesIn()[i].getId(); + var svgEdgeId = this.getSVGEdgeId(edgeId); + var edgeFormatter = this.getFormatter().getEdgeById(edgeId); + if (edgeFormatter instanceof LineEdgeGraphFormatter) { + DOM.select(svgEdgeId).setAttribute("x1", x); + DOM.select(svgEdgeId).setAttribute("y1", y); + DOM.select(svgEdgeId + "_shadow").setAttribute("x1", x); + DOM.select(svgEdgeId + "_shadow").setAttribute("y1", y); + } + + if ((edgeFormatter instanceof DirectedLineEdgeGraphFormatter) || (edgeFormatter instanceof OdirectedLineEdgeGraphFormatter) + || (edgeFormatter instanceof OdotDirectedLineEdgeGraphFormatter) || (edgeFormatter instanceof DotDirectedLineEdgeGraphFormatter) + || (edgeFormatter instanceof CutDirectedLineEdgeGraphFormatter)) { + this.removeEdge(edgeId); + this.renderEdge(edgeId); + } + + if (edgeFormatter instanceof BezierEdgeGraphFormatter) { + var radius = this.getFormatter().getVertexById(vertexId).getDefault().getSize() * this.getFormatter().getNodesMaxSize(); + var d = this.calculateCoordenatesBezier(radius, x, y); + DOM.select(svgEdgeId).setAttribute("d", d); + } + } +}; + +GraphCanvas.prototype.moveNode = function(vertexId) { + var x = this.getLayout().getNodeById(vertexId).x * this.getFormatter().getWidth(); + var y = this.getLayout().getNodeById(vertexId).y * this.getFormatter().getHeight(); + var svgNodeElement = DOM.select(this.getSVGNodeId(vertexId)); + + svgNodeElement.setAttribute("dragx", x); + svgNodeElement.setAttribute("dragy", y); + svgNodeElement.setAttribute("transform", "translate(" + x + "," + y + ")"); + + if (this.getDataset().getVertexById(vertexId).getEdges().length < this.args.maxNumberEdgesMoving) { + this.moveEdge(vertexId); + } +}; + +GraphCanvas.prototype._movingNode = function(svgNodeElement, x, y) { + var vertexId = this.getVertexIdFromSVGId(svgNodeElement.getAttribute("id")); + this.getLayout().getNodeById(vertexId).setCoordinates(x / this.getFormatter().getWidth(), y / this.getFormatter().getHeight()); + this.desplazamientoX = 0; + this.desplazamientoY = 0; + this.removeLabel(vertexId); + this.renderLabel(vertexId); +}; + +/** INIT **/ +GraphCanvas.prototype.init = function() { + + this._svg = this.createSVGDom(this.targetID, this.id, this.getFormatter().getWidth(), this.getFormatter().getHeight(), this.getFormatter() + .getBackgroundColor()); + this.GraphGroup = SVG.drawGroup(this._svg, [ [ "id", this.args.idGraph ], [ "transform", "translate(0,0), scale(1)" ] ]); + this.GraphBackground = SVG.drawGroup(this.GraphGroup, [ [ "id", this.args.idBackground ] ]); + this.GraphEdgeGroup = SVG.drawGroup(this.GraphGroup, [ [ "id", this.args.idEdgesGraph ] ]); + this.GraphNodeGroup = SVG.drawGroup(this.GraphGroup, [ [ "id", this.args.idNodesGraph ] ]); + this.GraphLabelGroup = SVG.drawGroup(this.GraphGroup, [ [ "id", this.args.idLabelGraph ] ]); + + if ((this.getFormatter().getBackgroundImage() != null) && (this.getFormatter().getBackgroundImage() != "")) { + this.setBackgroundImage(this.getFormatter().getBackgroundImage()); + } + /** SVG Events listener */ + var _this = this; + this._svg.addEventListener("click", function(event) { + _this.mouseClick(event); + }, false); + this._svg.addEventListener("mousemove", function(event) { + _this.mouseMove(event, _this); + }, false); + this._svg.addEventListener("mousedown", function(event) { + _this.mouseDown(event, _this); + }, false); + this._svg.addEventListener("mouseup", function(event) { + _this.mouseUp(event, _this); + }, false); +}; + +/* + GraphCanvas.prototype.backgroungToSVG = function(){ + var _this = this; + var canvas = document.createElement('canvas'); + canvas.setAttribute("id", "canvas"); + canvas.width = this.formatter.getWidth(); + canvas.height = this.formatter.getHeight(); + + this._svg.parentNode.parentNode.appendChild(canvas); + var ctx = document.getElementById('canvas').getContext('2d'); + var img = new Image(); + + img.src = this.formatter.getBackgroundImage(); + ctx.drawImage(img,0,0 ,_this.formatter.getWidth(), _this.formatter.getHeight()); + + + img.onload = function() { + canvas.parentNode.removeChild(canvas); + } + + this.NodeSVGbackgroundImage.setAttribute("xlink:href", document.getElementById("canvas").toDataURL()); + this.NodeSVGbackgroundImage.removeAttribute("href"); + + // + + };*/ + +GraphCanvas.prototype.setBackgroundImage = function() { + if (this.NodeSVGbackgroundImage != null) { + this.NodeSVGbackgroundImage.parentNode.removeChild(this.NodeSVGbackgroundImage); + } + $('#' + this.targetID).svg(); + $('#' + this.targetID).svg("get"); + + $('#' + this.targetID).svg("get")._svg = document.getElementById(this.id); + + var svg = $('#' + this.targetID).svg("get"); + this.NodeSVGbackgroundImage = svg.image(0, 0, this.getFormatter().getWidth(), this.getFormatter().getHeight(), this.getFormatter() + .getBackgroundImage()); + this.NodeSVGbackgroundImage.setAttribute("id", this.args.idBackgroundNode); + + this.NodeSVGbackgroundImage.setAttribute("x", 0); + this.NodeSVGbackgroundImage.setAttribute("y", 0); + + this.NodeSVGbackgroundImage.setAttribute("dragx", 0); + this.NodeSVGbackgroundImage.setAttribute("dragy", 0); + + if (this.getFormatter().args.backgroundImageHeight != null) { + this.NodeSVGbackgroundImage.setAttribute("height", this.getFormatter().args.backgroundImageHeight); + } + if (this.getFormatter().args.backgroundImageWidth != null) { + this.NodeSVGbackgroundImage.setAttribute("width", this.getFormatter().args.backgroundImageWidth); + } + + if (this.getFormatter().args.backgroundImageX != null) { + this.NodeSVGbackgroundImage.setAttribute("x", this.getFormatter().args.backgroundImageX); + } + if (this.getFormatter().args.backgroundImageY != null) { + this.NodeSVGbackgroundImage.setAttribute("y", this.getFormatter().args.backgroundImageY); + } + + this.GraphBackground.appendChild(this.NodeSVGbackgroundImage); + this.NodeSVGbackgroundImage.removeAttribute("href"); + this.NodeSVGbackgroundImage.setAttribute("xlink:href", this.getFormatter().getBackgroundImage()); +}; + +GraphCanvas.prototype.removeBackgroundImage = function() { + if (this.NodeSVGbackgroundImage != null) { + this.NodeSVGbackgroundImage.parentNode.removeChild(this.NodeSVGbackgroundImage); + } +}; + +GraphCanvas.prototype._setBackgroundColor = function(color) { + var attributes = [ [ "fill", color ] ]; + SVG.drawRectangle(0, 0, this.getFormatter().getWidth(), this.getFormatter().getHeight(), this.GraphBackground, attributes); +}; + +/** Serialize **/ +GraphCanvas.prototype.toJSON = function() { + var json = {}; + json.dataset = {}; + json.formatter = {}; + json.layout = {}; + json.dataset = this.getDataset().toJSON(); + json.formatter = this.getFormatter().toJSON(); + json.layout = this.getLayout().toJSON(); + return json; +}; + +GraphCanvas.prototype.toHTML = function() { + //this.backgroungToSVG(); + var html = this._svg.parentElement.innerHTML; + + var start = html.indexOf("") + 6; + + return html.substr(start, end); +}; + +/** DRAW **/ +GraphCanvas.prototype.draw = function(graphdataset, graphformatter, graphlayout) { + this.setDataset(graphdataset); + this.setFormatter(graphformatter); + this.setLayout(graphlayout); + + var _this = this; + this.getFormatter().changed.attach(function(sender, item) { + _this.removeNode(item.getId()); + _this.renderNode(item.getId()); + if (_this.args.labeled) { + _this.removeLabel(item.getId()); + _this.renderLabel(item.getId()); + } + + }); + //TODO + this.getFormatter().edgeChanged.attach(function(sender, item) { + _this.removeEdge(item.getId()); + _this.renderEdge(item.getId()); + }); + + this.getFormatter().resized.attach(function(sender, item) { + _this.resize(_this.getFormatter().getWidth(), _this.getFormatter().getHeight()); + }); + + this.getFormatter().backgroundImageChanged.attach(function(sender, item) { + _this.setBackgroundImage(_this.getFormatter().getBackgroundImage()); + }); + + this.getFormatter().backgroundColorChanged.attach(function(sender, item) { + _this._setBackgroundColor(_this.getFormatter().getBackgroundColor()); + }); + + this.getLayout().changed.attach(function(sender, item) { + _this.moveNode(item.getId()); + _this.moveEdge(item.getId()); + if (_this.args.labeled) { + _this.removeLabel(item.getId()); + _this.renderLabel(item.getId()); + } + }); + + this.getDataset().newVertex.attach(function(sender, item) { + + _this.renderNode(item.getId()); + if (_this.args.labeled) { + _this.renderLabel(item.getId()); + } + }); + + this.getDataset().newEdge.attach(function(sender, item) { + _this.renderEdge(item.getId()); + }); + + this.getDataset().vertexDeleted.attach(function(sender, item) { + _this.removeNode(item.getId()); + if (_this.args.labeled) { + _this.removeLabel(item.getId()); + } + }); + + this.getDataset().edgeDeleted.attach(function(sender, item) { + _this.removeEdge(item.getId()); + }); + + this.getDataset().vertexNameChanged.attach(function(sender, args) { + if (_this.args.labeled) { + _this.removeLabel(args.item.getId()); + _this.removeLabel(args.item.getId()); + _this.renderLabel(args.item.getId()); + } + }); + this.init(); + this.render(); +}; + +GraphCanvas.prototype.render = function() { + for ( var id in this.getDataset().getVertices()) { + this.renderNode(id); + } + this.renderLabels(); + this.renderEdges(); +}; + +GraphCanvas.prototype.renderLabels = function() { + if (this.args.labeled) { + for ( var id in this.getDataset().getVertices()) { + this.renderLabel(id); + } + } +}; + +GraphCanvas.prototype.removeLabels = function() { + for ( var id in this.getDataset().getVertices()) { + this.removeLabel(id); + } +}; + +/** Utilities method for nodes **/ +GraphCanvas.prototype.isNodeCanvas = function(node) { + return ((node.id == this.args.idGraph) || (node.id == this.id)); +}; + +GraphCanvas.prototype.isNodeBackground = function(node) { + return ((node.id == this.args.idBackgroundNode)); +}; + +GraphCanvas.prototype.isVertex = function(node) { + if (node.getAttribute("id") != null) { + if (node.getAttribute("id").indexOf("_v_") != -1) { + return true; + } + } + return false; +}; + +GraphCanvas.prototype.isLabel = function(node) { + if (node.getAttribute("id") != null) { + if (node.getAttribute("id").indexOf("_l_") != -1) { + return true; + } + } + return false; +}; + +GraphCanvas.prototype.isEdge = function(node) { + if (node.getAttribute("id") != null) { + if (node.getAttribute("id").indexOf("_e_") != -1) { + return true; + } + } + return false; +}; + +/** Resize **/ +GraphCanvas.prototype.resize = function(width, height) { + // this._svg.setAttribute("width", width); + // this._svg.setAttribute("height", height); + if (this.NodeSVGbackgroundImage != null) { + this.NodeSVGbackgroundImage.setAttribute("width", width); + this.NodeSVGbackgroundImage.setAttribute("height", height); + } + + this._svg.setAttribute("width", width); + this._svg.setAttribute("height", height); + + this.clearCanvas(); + this.render(); +}; + +GraphCanvas.prototype.clearCanvas = function() { + DOM.removeChilds(this.GraphEdgeGroup.getAttribute("id")); + DOM.removeChilds(this.GraphNodeGroup.getAttribute("id")); + this.clearLabels(); +}; + +GraphCanvas.prototype.clearLabels = function() { + DOM.removeChilds(this.GraphLabelGroup.getAttribute("id")); +}; + +/** ID'S converter **/ +GraphCanvas.prototype.getSVGNodeId = function(nodeId) { + return this.id + "_v_" + nodeId; +}; + +GraphCanvas.prototype.getSVGEdgeId = function(edgeId) { + return this.id + "_e_" + edgeId; +}; + +GraphCanvas.prototype.getSVGArrowEdgeId = function(edgeId) { + return this.id + "_arrow_" + edgeId; +}; + +GraphCanvas.prototype.getSVGLabelId = function(edgeId) { + return this.id + "_l_" + edgeId; +}; + +GraphCanvas.prototype.blinkVertexById = function(vertexId) { + $("#" + this.getSVGNodeId(vertexId)).fadeIn().fadeOut().fadeIn().fadeOut().fadeIn().fadeOut(); +}; + +GraphCanvas.prototype.getVertexIdFromSVGId = function(svgVertexId) { + return svgVertexId.replace(this.id, "").replace("_v_", ""); +}; + +GraphCanvas.prototype.getEdgeIdFromSVGId = function(svgEdgeId) { + return svgEdgeId.replace(this.id, "").replace("_e_", ""); +}; + +/** VERTEX **/ +GraphCanvas.prototype.getVertexById = function(id) { + return document.getElementById(this.getSVGNodeId(id)); +}; + +GraphCanvas.prototype.renderNodes = function() { + for ( var id in this.getDataset().getVertices()) { + this.renderNode(id); + } +}; + +GraphCanvas.prototype.overNode = function(nodeId) { + if (!this.args.interactive) { + return; + } + /** If selected we don't change the format **/ + if (this.args.isVertexSelected[nodeId] == null) { + var args = this.getFormatter().getVertexById(nodeId).getOver(); + args.args["cursor"] = 'pointer'; + this.changeVertexFormat(nodeId, args); + } +}; + +GraphCanvas.prototype.outNode = function(nodeId) { + if (!this.args.interactive) { + return; + } + + /** If selected we don't change the format **/ + if (this.args.isVertexSelected[nodeId] == null) { + this.changeVertexFormat(nodeId, this.getFormatter().getVertexById(nodeId).getDefault()); + } +}; + +GraphCanvas.prototype.overLabel = function(nodeId) { + this.overNode(nodeId); + // this.svgLabels[nodeId].setAttribute("cursor", "pointer"); +}; + +GraphCanvas.prototype.outLabel = function(nodeId) { + this.outNode(nodeId); + // this.svgLabels[nodeId].setAttribute("cursor", ""); +}; + +GraphCanvas.prototype.clickNode = function(nodeId) { + if (!this.args.interactive) { + return; + } + + /** si el evento se dispara oprque estaba dragging entonces no activo nada **/ + if (this.args.isVertexSelected[nodeId] == null) { + this.selectNode(nodeId); + } else { + this.deselectNode(nodeId); + } +}; + +GraphCanvas.prototype.selectNode = function(nodeId) { + for ( var i = 0; i < this.args.selectedVertices.length; i++) { + var format = this.getFormatter().getVertexById(nodeId).getSelected(); + format.opacity = 0.2; + this.changeVertexFormat(nodeId, this.getFormatter().getVertexById(nodeId).getSelected()); + } + + if (this.args.isVertexSelected[nodeId] == null) { + var format = this.getFormatter().getVertexById(nodeId).getSelected(); + format.opacity = 1; + this.changeVertexFormat(nodeId, this.getFormatter().getVertexById(nodeId).getSelected()); + this.args.selectedVertices.push(nodeId); + this.args.isVertexSelected[nodeId] = this.args.selectedVertices.length - 1; + this.onVertexSelect.notify(nodeId); + } +}; + +GraphCanvas.prototype.selectAllEdges = function() { + this.deselectNodes(); + this.deselectEdges(); + + for ( var edgesId in this.getDataset().edges) { + this.selectEdge(edgesId); + } +}; + +GraphCanvas.prototype.selectAllNodes = function() { + this.deselectNodes(); + this.deselectEdges(); + + for ( var vertexId in this.getDataset().vertices) { + this.selectNode(vertexId); + } +}; + +GraphCanvas.prototype.selectAll = function() { + this.deselectNodes(); + this.deselectEdges(); + + for ( var vertexId in this.getDataset().vertices) { + this.selectNode(vertexId); + } + + for ( var edgesId in this.getDataset().edges) { + this.selectEdge(edgesId); + } +}; + +GraphCanvas.prototype.selectEdge = function(edgeId) { + if (this.args.isEdgeSelected[edgeId] == null) { + this.changeEdgeFormat(edgeId, this.getFormatter().getEdgeById(edgeId).getSelected()); + //this.args.selectedEdges.push(edgeId); + this.args.isEdgeSelected[edgeId] = true; //this.args.selectedEdges.length - 1; + this.onEdgeSelect.notify(edgeId); + } +}; + +GraphCanvas.prototype.selectEdges = function(edges) { + + for ( var i = 0; i < edges.length; i++) { + this.selectEdge(edges[i]); + } +}; + +GraphCanvas.prototype.deselectNode = function(nodeId) { + if (this.args.isVertexSelected[nodeId] != null) { + this.changeVertexFormat(nodeId, this.getFormatter().getVertexById(nodeId).getDefault()); + this.args.selectedVertices.splice(this.args.isVertexSelected[nodeId], 1); + var index = this.args.isVertexSelected[nodeId]; + delete this.args.isVertexSelected[nodeId]; + + for ( var vertex in this.args.isVertexSelected) { + if (this.args.isVertexSelected[vertex] > index) { + this.args.isVertexSelected[vertex] = this.args.isVertexSelected[vertex] - 1; + } + } + } +}; + +GraphCanvas.prototype.deselectNodes = function() { + var selected = JSON.parse(JSON.stringify(this.getSelectedVertices())); + for ( var i = 0; i < selected.length; i++) { + this.deselectNode(selected[i]); + } +}; +GraphCanvas.prototype.selectNodes = function(idNodes) { + + for ( var i = 0; i < idNodes.length; i++) { + this.selectNode(idNodes[i]); + } + + // for ( var vertex in this.args.isVertexSelected) { + // if (this.args.isVertexSelected[vertex] > index){ + // this.args.isVertexSelected[vertex] = this.args.isVertexSelected[vertex] - 1; + // } + // } + +}; + +GraphCanvas.prototype.changeVertexFormat = function(nodeId, format) { + var svgNode = DOM.select(this.getSVGNodeId(nodeId)); + if (svgNode != null) { + var properties = format.toJSON(); + for ( var item in properties) { + svgNode.setAttribute(item, properties[item]); + } + + if (this.getFormatter().getVertexById(nodeId) instanceof CircleVertexGraphFormatter) { + var transform = "translate(" + svgNode.getAttribute("dragx") + "," + svgNode.getAttribute("dragy") + "), scale(" + format.getSize() + ")"; + svgNode.setAttribute("transform", transform); + } + } +}; + +GraphCanvas.prototype.renderLabel = function(nodeId) { + var x = Math.ceil(this.getLayout().getNodeById(nodeId).x * this.getFormatter().getWidth()); + var y = Math.ceil(this.getLayout().getNodeById(nodeId).y * this.getFormatter().getHeight()); + + var svgAttributesNode = JSON.parse(JSON.stringify(this.getFormatter().getVertexById(nodeId).getDefault().toJSON().title)); + svgAttributesNode.id = this.getSVGLabelId(this.getDataset().getVertexById(nodeId).getId()); + svgAttributesNode.dx = (-1) * (this.getDataset().getVertexById(nodeId).getName().length * svgAttributesNode["font-size"]) / 4 - 4; + + svgAttributesNode.dy = parseFloat((this.getFormatter().getVertexById(nodeId).getDefault().getSize())) + + parseFloat(svgAttributesNode["font-size"]) + parseFloat(this.getFormatter().getVertexById(nodeId).getDefault().getStrokeWidth()) - 4; + svgAttributesNode.dragx = Math.ceil(this.getLayout().getNodeById(nodeId).x * this.getFormatter().getWidth()); + + var gragy = parseFloat(this.getFormatter().getVertexById(nodeId).getDefault().getSize()) + + Math.ceil(this.getLayout().getNodeById(nodeId).y * this.getFormatter().getHeight()); + svgAttributesNode.dragy = gragy; + svgAttributesNode.transform = "translate(" + svgAttributesNode.dragx + "," + svgAttributesNode.dragy + ")";//, scale("+this.formatter.getVertexById(nodeId).getDefault().getSize()+")"; + + var nodeSVG = SVG.drawText(0, 0, this.getDataset().getVertexById(nodeId).getName(), this.GraphLabelGroup, svgAttributesNode); + + this.svgLabels[nodeId] = nodeSVG; + + /** Events for the SVG node **/ + var _this = this; + if (nodeSVG != null) { + nodeSVG.addEventListener("mouseover", function() { + _this.overLabel(nodeId); + }, false); + nodeSVG.addEventListener("mouseout", function() { + _this.outLabel(nodeId); + }, false); + } + +}; + +GraphCanvas.prototype.removeLabel = function(labelId) { + if (DOM.select(this.getSVGLabelId(labelId)) != null) { + DOM.select(this.getSVGLabelId(labelId)).parentNode.removeChild(DOM.select(this.getSVGLabelId(labelId))); + } +}; + +GraphCanvas.prototype.renderNode = function(nodeId) { + var svgAttributesNode = JSON.parse(JSON.stringify(this.getFormatter().getVertexById(nodeId).getDefault().toJSON())); + svgAttributesNode.dragx = Math.ceil(this.getLayout().getNodeById(nodeId).x * this.getFormatter().getWidth()); + svgAttributesNode.dragy = Math.ceil(this.getLayout().getNodeById(nodeId).y * this.getFormatter().getHeight()); + svgAttributesNode.transform = "translate(" + svgAttributesNode.dragx + "," + svgAttributesNode.dragy + ")"; + svgAttributesNode.id = this.getSVGNodeId(nodeId); + /*svgAttributesNode["stroke-width"] = 3 ; + svgAttributesNode["stroke-opacity"] = 1 ; + svgAttributesNode["fill-opacity"] = svgAttributesNode["opacity"] ; + svgAttributesNode["opacity"] = 1 ;*/ + this.circleDefaultRadius = this.getFormatter().getVertexById(nodeId).getDefault().getSize(); + var nodeSVG; + + if (this.getFormatter().getVertexById(nodeId) instanceof CircleVertexGraphFormatter) { + nodeSVG = SVG.drawCircle(0, 0, this.circleDefaultRadius, this.GraphNodeGroup, svgAttributesNode); + } + + if (this.getFormatter().getVertexById(nodeId) instanceof SquareVertexGraphFormatter) { + //nodeSVG = SVG.drawRectangle(0 - (this.circleDefaultRadius) ,0 - (this.formatter.getVertexById(nodeId).getDefault().getSize()) , (this.getFormatter().getVertexById(nodeId).getDefault().getSize()*2), (this.getFormatter().getVertexById(nodeId).getDefault().getSize()*2), this.GraphNodeGroup, svgAttributesNode); + nodeSVG = SVG.drawRectangle(0 - (this.circleDefaultRadius), 0 - (this.circleDefaultRadius), (this.circleDefaultRadius * 2), + (this.circleDefaultRadius * 2), this.GraphNodeGroup, svgAttributesNode); + } + + if (this.getFormatter().getVertexById(nodeId) instanceof EllipseVertexGraphFormatter) { + nodeSVG = SVG.drawEllipse(0, 0, this.circleDefaultRadius * 1.5, this.circleDefaultRadius, this.GraphNodeGroup, svgAttributesNode); + } + + if (this.getFormatter().getVertexById(nodeId) instanceof RectangleVertexGraphFormatter) { + //nodeSVG = SVG.drawRectangle(0 - (this.circleDefaultRadius) ,0 - ((this.circleDefaultRadius*2)/2) , (this.circleDefaultRadius*2), (this.circleDefaultRadius), this.GraphNodeGroup, svgAttributesNode); + nodeSVG = SVG.drawRectangle(0 - (this.circleDefaultRadius * 1.5), 0 - (this.circleDefaultRadius), (this.circleDefaultRadius * 2 * 1.5), + (this.circleDefaultRadius * 2), this.GraphNodeGroup, svgAttributesNode); + + } + + if (this.getFormatter().getVertexById(nodeId) instanceof RoundedVertexGraphFormatter) { + svgAttributesNode.ry = 2;// this.formatter.getVertexById(nodeId).getDefault().getSize()/4; + svgAttributesNode.rx = 2;// this.formatter.getVertexById(nodeId).getDefault().getSize()/4; + nodeSVG = SVG.drawRectangle(0 - (this.circleDefaultRadius * 1.5), 0 - (this.circleDefaultRadius), (this.circleDefaultRadius * 2 * 1.5), + (this.circleDefaultRadius * 2), this.GraphNodeGroup, svgAttributesNode); + } + + // + + if (this.getFormatter().getVertexById(nodeId) instanceof OctagonVertexGraphFormatter) { + svgAttributesNode.ry = 2; + svgAttributesNode.rx = 2; + nodeSVG = SVG.drawRectangle(0 - (this.circleDefaultRadius * 1.5), 0 - (this.circleDefaultRadius), (this.circleDefaultRadius * 2 * 1.5), + (this.circleDefaultRadius * 2), this.GraphNodeGroup, svgAttributesNode); + } + + nodeSVG.internalId = nodeId; + // + var _this = this; + + /** Events for the SVG node **/ + if (nodeSVG != null) { + nodeSVG.addEventListener("mouseover", function() { + _this.onVertexOver.notify(nodeId); + _this.overNode(nodeId); + }, false); + nodeSVG.addEventListener("mouseout", function() { + _this.onVertexOut.notify(nodeId); + _this.outNode(nodeId); + }, false); + //nodeSVG.addEventListener("click", function(){_this.clickNode(nodeId);}, false); + // + nodeSVG.addEventListener("mouseup", function() { + _this.onVertexUp.notify(nodeId); + }, false); + } +}; + +GraphCanvas.prototype.removeNode = function(nodeId) { + DOM.select(this.getSVGNodeId(nodeId)).parentNode.removeChild(DOM.select(this.getSVGNodeId(nodeId))); + if (this.args.labeled) { + this.removeLabel(nodeId); + } +}; + +/** REMOVING **/ +GraphCanvas.prototype.removeSelected = function() { + /** El orden importa **/ + this.removeSelectedEdges(); + this.removeSelectedNode(); + +}; + +GraphCanvas.prototype.removeSelectedNode = function() { + var selected = JSON.parse(JSON.stringify(this.getSelectedVertices())); + this.deselectNodes(); + var sorted = selected.sort(function(a, b) { + return a - b + }); + for ( var i = 0; i < sorted.length; i++) { + if (this.getDataset().getVertexById(sorted[i]) != null) { + this.getDataset().getVertexById(sorted[i]).remove(); + } + } +}; + +/** EDGES **/ +GraphCanvas.prototype.removeEdge = function(edgeId) { + if (DOM.select(this.getSVGEdgeId(edgeId)) != null) { + DOM.select(this.getSVGEdgeId(edgeId)).parentNode.removeChild(DOM.select(this.getSVGEdgeId(edgeId))); + } + + if (DOM.select(this.getSVGEdgeId(edgeId) + "_shadow") != null) { + DOM.select(this.getSVGEdgeId(edgeId) + "_shadow").parentNode.removeChild(DOM.select(this.getSVGEdgeId(edgeId) + "_shadow")); + } + + if (DOM.select(this.getSVGArrowEdgeId(edgeId)) != null) { + DOM.select(this.getSVGArrowEdgeId(edgeId)).parentNode.removeChild(DOM.select(this.getSVGArrowEdgeId(edgeId))); + } +}; + +GraphCanvas.prototype.overEdge = function(edgeId) { + if ((!this.args.interactive) || this.dragging || this.selecting) { + return; + } + + /** If selected we don't change the format **/ + if (this.args.isEdgeSelected[edgeId] == null) { + var format = this.getFormatter().getEdgeById(edgeId).getOver(); + format.args["cursor"] = "pointer"; + this.changeEdgeFormat(edgeId, format); + } +}; + +GraphCanvas.prototype.outEdge = function(edgeId) { + if (!this.args.interactive) { + return; + } + + /** If selected we don't change the format **/ + if (this.args.isEdgeSelected[edgeId] == null) { + this.changeEdgeFormat(edgeId, this.getFormatter().getEdgeById(edgeId).getDefault()); + } +}; + +GraphCanvas.prototype.changeEdgeFormat = function(edgeId, format) { + var svgEdge = DOM.select(this.getSVGEdgeId(edgeId) + "_shadow"); + if (svgEdge != null) { + var properties = format.toJSON(); + for ( var item in properties) { + svgEdge.setAttribute(item, properties[item]); + } + } +}; + +GraphCanvas.prototype.deselectEdge = function(edgeID) { + if (this.args.isEdgeSelected[edgeID] != null) { + this.changeEdgeFormat(edgeID, this.getFormatter().getEdgeById(edgeID).getDefault()); + var index = this.args.isEdgeSelected[edgeID]; + delete this.args.isEdgeSelected[edgeID]; + } +}; + +GraphCanvas.prototype.deselectEdges = function() { + var selected = JSON.parse(JSON.stringify(this.getSelectedEdges())); + for ( var i = 0; i < selected.length; i++) { + this.deselectEdge(selected[i]); + } +}; + +GraphCanvas.prototype.removeSelectedEdges = function() { + var selected = JSON.parse(JSON.stringify(this.getSelectedEdges())); + this.deselectEdges(); + for ( var i = 0; i < selected.length; i++) { + if (this.getDataset().getEdgeById(selected[i]) != null) { + this.getDataset().getEdgeById(selected[i]).remove(); + } + } +}; + +GraphCanvas.prototype.renderEdge = function(edgeId) { + var svgAttributesEdge = this.getFormatter().getEdgeById(edgeId).getDefault().toJSON(); + var edge = this.getDataset().getEdgeById(edgeId); + + var svgNodeTarget = this.getVertexById(edge.getNodeTarget().getId()); + var svgNodeSource = this.getVertexById(edge.getNodeSource().getId()); + svgAttributesEdge.id = this.getSVGEdgeId(edge.getId()) + "_shadow"; + + var svgEdge = null; + + if (this.getFormatter().getEdgeById(edgeId) instanceof LineEdgeGraphFormatter) { + var coordenateSourceX = svgNodeSource.getAttribute("dragx"); + var coordenateSourceY = svgNodeSource.getAttribute("dragy"); + var coordenateTargetX = svgNodeTarget.getAttribute("dragx"); + var coordenateTargetY = svgNodeTarget.getAttribute("dragy"); + + SVG.drawLine(coordenateSourceX, coordenateSourceY, coordenateTargetX, coordenateTargetY, this.GraphEdgeGroup, svgAttributesEdge); + var attributesShadow = {}; + attributesShadow.id = this.getSVGEdgeId(edge.getId()); + attributesShadow["stroke-opacity"] = 0; + attributesShadow["stroke-width"] = 4; + attributesShadow["stroke"] = "black"; + svgEdge = SVG.drawLine(svgNodeSource.getAttribute("dragx"), svgNodeSource.getAttribute("dragy"), svgNodeTarget.getAttribute("dragx"), + svgNodeTarget.getAttribute("dragy"), this.GraphEdgeGroup, attributesShadow); + } + + if (this.getFormatter().getEdgeById(edgeId) instanceof BezierEdgeGraphFormatter) { + var nodeId = edge.getNodeTarget().getId(); + var nodeSize = this.formatter.getVertexById(nodeId).getDefault().getSize() * this.getFormatter().getNodesMaxSize(); + svgAttributesEdge.fill = "none"; + svgAttributesEdge.id = this.getSVGEdgeId(edgeId); + var d = this.calculateCoordenatesBezier(nodeSize, svgNodeSource.getAttribute("dragx"), svgNodeSource.getAttribute("dragy")); + svgEdge = SVG.drawPath(d, this.GraphEdgeGroup, svgAttributesEdge); + } + ; + + if ((this.getFormatter().getEdgeById(edgeId) instanceof DirectedLineEdgeGraphFormatter) + || (this.getFormatter().getEdgeById(edgeId) instanceof CutDirectedLineEdgeGraphFormatter) + || (this.getFormatter().getEdgeById(edgeId) instanceof DotDirectedLineEdgeGraphFormatter) + || (this.getFormatter().getEdgeById(edgeId) instanceof OdotDirectedLineEdgeGraphFormatter) + || (this.getFormatter().getEdgeById(edgeId) instanceof OdirectedLineEdgeGraphFormatter)) { + var coordenateSourceX = svgNodeSource.getAttribute("dragx"); + var coordenateSourceY = svgNodeSource.getAttribute("dragy"); + var coordenateTargetX = svgNodeTarget.getAttribute("dragx"); + var coordenateTargetY = svgNodeTarget.getAttribute("dragy"); + + var offset = parseFloat(this.getFormatter().getVertexById(this.getDataset().getEdgeById(edgeId).getNodeTarget().getId()).getDefault() + .getSize() + * this.circleDefaultRadius); + var point = this._calculateEdgePointerPosition(coordenateSourceX, coordenateSourceY, coordenateTargetX, coordenateTargetY, offset); + coordenateTargetX = point.x; + coordenateTargetY = point.y; + SVG.drawLine(coordenateSourceX, coordenateSourceY, coordenateTargetX, coordenateTargetY, this.GraphEdgeGroup, svgAttributesEdge); + + var attributesShadow = {}; + attributesShadow.id = this.getSVGEdgeId(edge.getId()); + attributesShadow["stroke-opacity"] = 0; + attributesShadow["stroke-width"] = 4; + attributesShadow["stroke"] = "black"; + svgEdge = SVG.drawLine(coordenateSourceX, coordenateSourceY, coordenateTargetX, coordenateTargetY, this.GraphEdgeGroup, attributesShadow); + } + + if (this.getFormatter().getEdgeById(edgeId) instanceof DirectedLineEdgeGraphFormatter + || (this.getFormatter().getEdgeById(edgeId) instanceof OdirectedLineEdgeGraphFormatter)) { + var coordenateSourceX = svgNodeSource.getAttribute("dragx"); + var coordenateSourceY = svgNodeSource.getAttribute("dragy"); + var coordenateTargetX = svgNodeTarget.getAttribute("dragx"); + var coordenateTargetY = svgNodeTarget.getAttribute("dragy"); + + var point = this._calculateEdgePointerPosition(coordenateSourceX, coordenateSourceY, coordenateTargetX, coordenateTargetY, offset); + coordenateTargetX = point.x; + coordenateTargetY = point.y; + + var angle = Geometry.toDegree(point.angle) + 90; + this.arrowDefaultSize = this.getFormatter().getEdgeById(edgeId).getArrowSize(); //getDefault().getArrowSize(); + var d = "-" + this.arrowDefaultSize + ",0 0,-" + parseFloat(this.arrowDefaultSize) * 2 + " " + this.arrowDefaultSize + ",0"; + + var attributes; + + if (this.getFormatter().getEdgeById(edgeId) instanceof DirectedLineEdgeGraphFormatter) { + attributes = [ + [ "fill", this.getFormatter().getEdgeById(edgeId).getDefault().getStroke() ], + [ "stroke", this.getFormatter().getEdgeById(edgeId).getDefault().getStroke() ], [ "id", this.getSVGArrowEdgeId(edgeId) ] ]; + } else { + attributes = [ + [ "fill", "#FFFFFF" ], [ "stroke", this.getFormatter().getEdgeById(edgeId).getDefault().getStroke() ], + [ "id", this.getSVGArrowEdgeId(edgeId) ] ]; + } + + var flechaSVGNode = SVG.drawPoligon(d, this.GraphEdgeGroup, attributes);//, ["transform", "rotate("+angle+"), translate(0,0)"]]); + flechaSVGNode.setAttribute("transform", " translate(" + coordenateTargetX + ", " + coordenateTargetY + "), rotate(" + angle + ")"); + } + ; + + if (this.getFormatter().getEdgeById(edgeId) instanceof CutDirectedLineEdgeGraphFormatter) { + var coordenateSourceX = svgNodeSource.getAttribute("dragx"); + var coordenateSourceY = svgNodeSource.getAttribute("dragy"); + var coordenateTargetX = svgNodeTarget.getAttribute("dragx"); + var coordenateTargetY = svgNodeTarget.getAttribute("dragy"); + + var point = this._calculateEdgePointerPosition(coordenateSourceX, coordenateSourceY, coordenateTargetX, coordenateTargetY, offset); + coordenateTargetX = point.x; + coordenateTargetY = point.y; + + var angle = Geometry.toDegree(point.angle) + 90; + + //this.arrowDefaultSize = 2; //getDefault().getArrowSize(); + var d = "-4,0 4,0 4,-2 -4,-2"; + + var flechaSVGNode = SVG.drawPoligon(d, this.GraphEdgeGroup, [ + [ "fill", this.getFormatter().getEdgeById(edgeId).getDefault().getStroke() ], + [ "stroke", this.getFormatter().getEdgeById(edgeId).getDefault().getStroke() ], [ "id", this.getSVGArrowEdgeId(edgeId) ] ]);//, ["transform", "rotate("+angle+"), translate(0,0)"]]); + flechaSVGNode.setAttribute("transform", " translate(" + coordenateTargetX + ", " + coordenateTargetY + "), rotate(" + angle + ")"); + } + ; + + if ((this.getFormatter().getEdgeById(edgeId) instanceof DotDirectedLineEdgeGraphFormatter) + || (this.getFormatter().getEdgeById(edgeId) instanceof OdotDirectedLineEdgeGraphFormatter)) { + var coordenateSourceX = svgNodeSource.getAttribute("dragx"); + var coordenateSourceY = svgNodeSource.getAttribute("dragy"); + var coordenateTargetX = svgNodeTarget.getAttribute("dragx"); + var coordenateTargetY = svgNodeTarget.getAttribute("dragy"); + var point = this._calculateEdgePointerPosition(coordenateSourceX, coordenateSourceY, coordenateTargetX, coordenateTargetY, offset); + coordenateTargetX = point.x; + coordenateTargetY = point.y; + var angle = Geometry.toDegree(point.angle) + 90; + // this.arrowDefaultSize = this.formatter.getEdgeById(edgeId).getArrowSize(); //getDefault().getArrowSize(); + var attributes = []; + if (this.getFormatter().getEdgeById(edgeId) instanceof OdotDirectedLineEdgeGraphFormatter) { + attributes = [ + [ "fill", "#FFFFFF" ], [ "stroke", this.getFormatter().getEdgeById(edgeId).getDefault().getStroke() ], + [ "id", this.getSVGArrowEdgeId(edgeId) ] ]; + } else { + attributes = [ + [ "fill", this.getFormatter().getEdgeById(edgeId).getDefault().getStroke() ], + [ "stroke", this.getFormatter().getEdgeById(edgeId).getDefault().getStroke() ], [ "id", this.getSVGArrowEdgeId(edgeId) ] ]; + } + var flechaSVGNode = SVG.drawCircle(0, 0, 4, this.GraphEdgeGroup, attributes); + flechaSVGNode.setAttribute("transform", " translate(" + coordenateTargetX + ", " + coordenateTargetY + "), rotate(" + angle + ")"); + } + ; + + var _this = this; + /** Events for the SVG edge **/ + if (svgEdge != null) { + if (this.getDataset().getEdgesCount() < this.args.maxNumberEdgesFiringEvents) { + svgEdge.addEventListener("mouseover", function() { + _this.overEdge(edgeId); + }, false); + svgEdge.addEventListener("mouseout", function() { + _this.outEdge(edgeId); + }, false); + } + } +}; + +GraphCanvas.prototype._calculateEdgePointerPosition = function(sourceX, sourceY, targetX, targetY, radius) { + var angle = Geometry.getAngleBetweenTwoPoints(sourceX, sourceY, targetX, targetY); + + /** Suponiendo el node source que este a la derecha **/ + if ((targetX - sourceX) < 0) { + var b = Geometry.getAdjacentSideOfRectangleRight(angle, radius); + targetX = parseFloat(targetX) + parseFloat(b); + arrowX = parseFloat(targetX) + parseFloat(b) + this.arrowDefaultSize / 2; + } else { + var b = Geometry.getAdjacentSideOfRectangleRight(angle, radius); + targetX = parseFloat(targetX) - parseFloat(b); + arrowX = parseFloat(targetX) - parseFloat(b) - this.arrowDefaultSize / 2; + } + + /** Suponiendo el node source que este a la arriba **/ + if ((targetY - sourceY) > 0) { + var a = Geometry.getOppositeSideOfRectangleRight(angle, radius); + targetY = parseFloat(targetY) - parseFloat(a); + arrowY = parseFloat(targetY) - parseFloat(a) - this.arrowDefaultSize / 2; + } else { + var a = Geometry.getOppositeSideOfRectangleRight(angle, radius); + targetY = parseFloat(targetY) + parseFloat(a); + arrowY = parseFloat(targetY) + parseFloat(a) - this.arrowDefaultSize / 2; + + } + + return { + "x" : arrowX, + "y" : arrowY, + "angle" : angle + }; +}; + +GraphCanvas.prototype.calculateCoordenatesBezier = function(nodeSize, x1, y1) { + var x11 = x1 - (nodeSize / 2); + var y11 = y1 - (nodeSize / 2); + + var x12 = parseFloat(x1) + parseFloat(nodeSize / 2); + var y12 = y1 - (nodeSize / 2); + + var curvePointX = (x12 - x11) / 2 + x11; + var curvePointY = y1 - (nodeSize * 2); + var d = "M" + x11 + "," + y11 + " T" + curvePointX + "," + curvePointY + " " + x12 + "," + y12; + return d; + +}; + +GraphCanvas.prototype.renderEdges = function() { + for ( var edge in this.getDataset().getEdges()) { + this.renderEdge(this.getDataset().getEdgeById(edge).getId()); + + } +}; + +GraphCanvas.prototype.getLastSelectedNode = function() { + var node = null; + if (this.getSelectedVertices().length > 0) { + var nodeId = this.getSelectedVertices()[this.getSelectedVertices().length - 1]; + node = this.getDataset().getVertexById(nodeId); + } + return node; +}; +/* + GraphCanvas.prototype.getNodeByNameAndIndex = function(node, index){ + var nodeId = this.getDataset().verticesIndex[node][index]; + var nodeItem = this.getDataset().getVertexById(nodeId); + return nodeItem; + }; + */ + +GraphCanvas.prototype.setDataset = function(dataset) { + this.dataset = dataset; +}; + +GraphCanvas.prototype.setFormatter = function(formatter) { + this.formatter = formatter; +}; + +GraphCanvas.prototype.setLayout = function(layout) { + this.layout = layout; +}; + +/** API **/ +GraphCanvas.prototype.getDataset = function() { + return this.dataset; +}; + +GraphCanvas.prototype.getFormatter = function() { + return this.formatter; +}; + +GraphCanvas.prototype.getLayout = function() { + return this.layout; +}; + +/** API DATASET **/ +GraphCanvas.prototype.addVertex = function(name, args) { + this.getDataset().addNode(name, args); +}; + +GraphCanvas.prototype.removeVertex = function(vertexId) { + this.getDataset().getVertexById(vertexId).remove(); +}; + +GraphCanvas.prototype.addEdge = function(edgeName, nodeSourceId, nodeTargetId, args) { + this.getDataset().addEdge(edgeName, nodeSourceId, nodeTargetId, args); +}; +/* + GraphCanvas.prototype.removeEdge = function(edgeId){ + this.getDataset().getEdgeById(edgeId).remove(); + }; + */ + +/** API FORMATTER **/ +GraphCanvas.prototype.getWidth = function() { + return this.getFormatter().getWidth(); +}; + +GraphCanvas.prototype.getHeight = function() { + return this.getFormatter().getHeight(); +}; + +GraphCanvas.prototype.getBackgroundImage = function() { + return this.getFormatter().getBackgroundImage(); +}; + +//GraphCanvas.prototype.setBackgroundImage = function(value){ +// this.getFormatter().setBackgroundImage(value); +//}; + +GraphCanvas.prototype.getBackgroundColor = function() { + return this.getFormatter().getBackgroundColor(); +}; + +GraphCanvas.prototype.setBackgroundColor = function() { + this.getFormatter().setBackgroundColor(value); +}; + +//GraphCanvas.prototype.setEdgeFill = function(edgeId, value){ +// this.getFormatter().getEdgeById(edgeId).getDefault().setFill(value); +//}; +// +//GraphCanvas.prototype.getEdgeFill = function(edgeId){ +// return this.getFormatter().getEdgeById(edgeId).getDefault().getFill(); +//}; + +/** VERTICES FORMATTER **/ +GraphCanvas.prototype.setVertexSize = function(vertexId, value) { + this.getFormatter().getVertexById(vertexId).getDefault().setSize(value); +}; + +GraphCanvas.prototype.getVertexSize = function(vertexId) { + return this.getFormatter().getVertexById(vertexId).getDefault().getSize(); +}; + +GraphCanvas.prototype.setVertexStroke = function(vertexId, value) { + this.getFormatter().getVertexById(vertexId).getDefault().setStroke(value); +}; + +GraphCanvas.prototype.getVertexStroke = function(vertexId) { + return this.getFormatter().getVertexById(vertexId).getDefault().getStroke(); +}; + +GraphCanvas.prototype.setVertexStrokeOpacity = function(vertexId, value) { + this.getFormatter().getVertexById(vertexId).getDefault().setStrokeOpacity(value); +}; + +GraphCanvas.prototype.getVertexStrokeOpacity = function(vertexId) { + return this.getFormatter().getVertexById(vertexId).getDefault().getStrokeOpacity(); +}; + +GraphCanvas.prototype.setVertexOpacity = function(vertexId, value) { + this.getFormatter().getVertexById(vertexId).getDefault().setOpacity(value); +}; + +GraphCanvas.prototype.getVertexOpacity = function(vertexId) { + return this.getFormatter().getVertexById(vertexId).getDefault().getOpacity(); +}; + +GraphCanvas.prototype.setVertexFill = function(vertexId, color) { + this.getFormatter().getVertexById(vertexId).getDefault().setFill(color); +}; + +GraphCanvas.prototype.getVertexFill = function(vertexId) { + return this.getFormatter().getVertexById(vertexId).getDefault().getFill(); +}; + +/** EDGES FORMATTER **/ +GraphCanvas.prototype.setEdgeSize = function(edgeId, value) { + this.getFormatter().getEdgeById(edgeId).getDefault().setSize(value); +}; + +GraphCanvas.prototype.getEdgeSize = function(edgeId) { + return this.getFormatter().getEdgeById(edgeId).getDefault().getSize(); +}; + +GraphCanvas.prototype.setEdgeStroke = function(edgeId, value) { + this.getFormatter().getEdgeById(edgeId).getDefault().setStroke(value); +}; + +GraphCanvas.prototype.getEdgeStroke = function(edgeId) { + return this.getFormatter().getEdgeById(edgeId).getDefault().getStroke(); +}; + +GraphCanvas.prototype.setEdgeStrokeOpacity = function(edgeId, value) { + this.getFormatter().getEdgeById(edgeId).getDefault().setStrokeOpacity(value); +}; + +GraphCanvas.prototype.getEdgeStrokeOpacity = function(edgeId) { + return this.getFormatter().getEdgeById(edgeId).getDefault().getStrokeOpacity(); +}; + +GraphCanvas.prototype.setEdgeFill = function(edgeId, color) { + this.getFormatter().getEdgeById(edgeId).getDefault().setFill(color); +}; + +GraphCanvas.prototype.getEdgeFill = function(edgeId) { + return this.getFormatter().getEdgeById(edgeId).getDefault().getFill(); +}; + +/** API LAYOUT **/ +GraphCanvas.prototype.setCoordinates = function(vertexId, x, y) { + return this.getLayout().getEdgeById(vertexId).setCoordinates(x, y); +}; + + + +function HPLCGraph(args) { + this.width = 600; + this.height = 600; + this.title = ''; + this.bbar = false; + this.plotInnerPanelPadding = 10; + this.plotPanelPadding = 5; + this.id = BUI.id(); - if (record.raw.measurementId != null) { - /** For testing * */ - grid.setLoading("ISPyB: Removing measurement"); - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, data) { - grid.setLoading(false); - /** - * We get and refresh experiment - * because specimens has changed * - */ - var adapter2 = new BiosaxsDataAdapter(); - adapter2.onSuccess.attach(function(sender, experiment) { - _this.onRemoved.notify(experiment); - _this._showStatusBarReady('Ready'); - }); - if (_this.experiments.experiments[0].experimentId != null) { - adapter2.getExperimentById(_this.experiments.experiments[0].experimentId, "MEDIUM"); - _this._showStatusBarBusy("ISPyB: Removing Unused Specimens"); - } - }); + this.hidePlots = null; + this.xlabel = ""; + this.scaled = false; + this.xParam = null; + this.showRangeSelector = true; + this.interactionModel = null; + + /** for each stat the max and minimum value when it is scaled in order to show correctly in the legend **/ + this.ranges = {}; + if (args != null) { + if (args.interactionModel != null) { + this.interactionModel = args.interactionModel; + } + if (args.width != null) { + this.width = args.width; + } + if (args.height != null) { + this.height = args.height; + } + if (args.bbar != null) { + this.bbar = args.bbar; + } + if (args.title != null) { + this.title = args.title; + } + if (args.plots != null) { + this.plots = args.plots; + } + + if (args.scaled != null) { + this.scaled = args.scaled; + } + if (args.xlabel != null) { + this.xlabel = args.xlabel; + } + if (args.xParam != null) { + this.xParam = args.xParam; + } + if (args.showRangeSelector != null) { + this.showRangeSelector = args.showRangeSelector; + } + } + + this.onZoomX = new Event(this); + this.onResetZoom = new Event(this); + this.dblclick = new Event(this); +} + +HPLCGraph.prototype.getMenu = function () { + var _this = this; + /** Actions buttons **/ + var actions = []; + + function toggle(item, pressed) { + if (pressed) { + _this.plots[item.param] = true; + } else { + delete _this.plots[item.param]; + } + _this.reloadData(this.hplcData); + } + + for (var i = 0; i < this.hplcData.length; i++) { + if (this.hplcData[i].showOnMenu != false) { + var param = this.hplcData[i].param; + var style = "style='padding:0 0px 0 5px;'"; + actions.push({ + text : "
" + BUI.getRectangleColorDIV(this.hplcData[i].color, 10, 10) + " " + this.hplcData[i].label + "
", + id : _this.id + param, + param : param, + enableToggle : true, + scope : this, + toggleHandler : toggle, + pressed : (_this.plots[param] != null) + }); + } + } + actions.push("-"); + + actions.push({ + text : "Scale", + enableToggle : true, + scope : this, + pressed : this.scaled, + icon : '../images/icon_graph.png', + toggleHandler : function (item, pressed) { + _this.scaled = pressed; + _this.reloadData(this.hplcData); + } + }); + + actions.push("->"); + actions.push({ + text : "Save", + scope : this, + icon : '../images/save.gif', + handler : function (item, pressed) { + var largeImage = document.createElement("img"); + largeImage.style.display = 'block'; + largeImage.style.width = 200 + "px"; + largeImage.style.height = 200 + "px"; + largeImage.setAttribute('src', Dygraph.Export.asCanvas(this.dygraphObject.dygraph).toDataURL()); + window.open(Dygraph.Export.asCanvas(this.dygraphObject.dygraph).toDataURL(), 'Image', ''); + } + }); + + return actions; +}; - adapter.onError.attach(function(sender, data) { - alert("Error: " + data); - grid.setLoading(false); - }); +/** Looks for the maximum value and then divide everything but that value **/ +HPLCGraph.prototype.scaledData = function (data) { + for (var i = 0; i < data.length; i++) { + var values = this.getMaxAndMinValue(data[i]); + data[i] = this.divideValuesByMax(data[i], values.max); + this.ranges[data[i].label] = values; + } + return data; +}; - adapter.removeMeasurement(record.raw); - } - } - } +/** Given a stat float[] and a max number it will divide each value by max **/ +HPLCGraph.prototype.divideValuesByMax = function (stat, max) { + for (var j = 0; j < stat.data.length; j++) { + if (max != 0) { + stat.data[j] = Number(stat.data[j]) / max; + stat.std[j] = Number(stat.std[j]) / max; + } + } + return stat; +}; - } - } - }); +/** returns max value of a stat **/ +HPLCGraph.prototype.getMaxAndMinValue = function (stat) { + var max = 0; + var min = stat.data[0]; + for (var j = 0; j < stat.data.length; j++) { + if (Number(stat.data[j]) > max) { + max = Number(stat.data[j]); + } + if (Number(stat.std[j]) > max) { + max = Number(stat.std[j]); + } + if (Number(stat.data[j]) < min) { + min = Number(stat.data[j]); + } + } + return { + max : Number(max), + min : Number(min) + }; +}; - this.grid.on("afterrender", function() { +HPLCGraph.prototype.getPoint = function (data, i) { + var point = [ 10, 10, 10 ]; + var y = parseFloat(data.data[i]); + var error = parseFloat(data.std[i]); + if (data.fdata == null) { + return [ y - error, y, y + error ]; + } else { + if (data.fstd != null) { + return [ data.fstd(y - error), data.fdata(y), data.fstd(y + error) ]; + } + return [ data.fdata(y) - error, data.fdata(y), data.fdata(y) + error ]; + } + return point; +}; - function updateTime() { - try { - _this.estimatedTime = _this.estimatedTime - 1; +HPLCGraph.prototype.reloadData = function(hplcData) { + this.panel.setLoading(false); + this.hplcData = hplcData; - _this.onUpdateTime.notify({ - hours : (Number(_this.estimatedTime / 3600).toFixed()), - minutes : (Number((_this.estimatedTime / 60) % 60).toFixed()), - seconds : (Number(_this.estimatedTime % 60).toFixed()) + var data = hplcData; + - }); + /** In case of having peaks **/ + if (this.peaks != null) { + for (var peak in this.peaks) { + var values = []; + var std = []; + for (var i = 0; i < this.peaks[peak].length; i++) { + values.push(this.peaks[peak][i][1]); + std.push(this.peaks[peak][i][2]); + } + data.push({ + param : peak, + data : values, + showOnMenu : false, + fdata : function (a) { + var value = (Math.log(parseFloat(a))); + if (isNumber(value)) { + return value; + } + }, + fstd : function (a) { + var value = Math.log(Math.abs(parseFloat(a))); + if (isNumber(value)) + return value; + }, + std : std, + color : this.colorPeak[peak], + label : peak + }); + + } + } + + + if (this.scaled) { + data = this.scaledData(JSON.parse(JSON.stringify(hplcData))); + } - if (Number(_this.estimatedTime) < 0) { - _this.timer = null; - grid.setTitle(_this.title); + var paramIndex = {}; + var parsed = []; + var j = 0; + for (var i = 0; i < data[0].data.length - 1; i++) { + var aux = []; + for (j = 0; j < data.length; j++) { + if (this.plots != null) { + if (this.plots[data[j].param] != null) { + aux.push(this.getPoint(data[j], i)); + paramIndex[data[j].param] = aux.length - 1; } + } else { + aux.push([ data[j].data[i] - data[j].std[i], data[j].data[i], data[j].data[i] + data[j].std[i] ]); + } + } + parsed.push([]); - } catch (e) { - console.log(e); - _this.timer = null; + var index = i; + if (this.xParam != null) { + index = parseFloat(data[this.xParam].data[i]); + } + + parsed[parsed.length - 1].push(index); + + for (j = 0; j < data.length; j++) { + if (this.plots != null) { + if (this.plots[data[j].param] != null) { + parsed[parsed.length - 1].push(aux[paramIndex[data[j].param]]); + } + } else { + parsed[parsed.length - 1].push(aux[j]); } } + } - if (_this.estimateTime) { - var experimentList = _this.experiments; - var collected = experimentList.getMeasurementsCollected(); - if (collected.length > 0) { - if (collected[0].run3VO != null) { - try { - var end = collected[0].run3VO.timeEnd; - var start = collected[0].run3VO.timeStart; - var dstart = moment(start); - var dend = moment(end); - var seconds = Number(dend.diff(dstart) / 1000).toFixed(); + var colors = []; + var labels = [ "" ]; + for (j = 0; j < data.length; j++) { + if (this.plots != null) { + if (this.plots[data[j].param] != null) { + colors.push(data[j].color); + labels.push(data[j].label); + } + } else { + parsed[parsed.length - 1].push(aux[j]); + } + } - _this.estimatedTime = (seconds * experimentList.getMeasurementsNotCollected().length); + this._renderDygraph(parsed, colors, labels); +}; - if (_this.estimatedTime > 0) { - updateTime(); - _this.timer = setInterval(function() { - updateTime(); - }, 1000); +HPLCGraph.prototype._renderDygraph = function (parsed, colors, labels) { + this.dygraphObject = new StdDevDyGraph(this.id, { + width : this.width, + height : this.height - 10, + xlabel : this.xlabel, + showRangeSelector : this.showRangeSelector, + interactionModel : this.interactionModel, + scaled : this.scaled, + ranges : this.ranges + }); + this.dygraphObject.draw(parsed, colors, labels); + + var _this = this; + this.dygraphObject.onZoomX.attach(function (sender, args) { + try { + _this.onZoomX.notify(args); + } catch (e) { + } + }); + + this.dygraphObject.onResetZoom.attach(function (sender, args) { + try { + _this.onResetZoom.notify(args); + } catch (e) { + } + }); + + this.dygraphObject.dblclick.attach(function (sender, args) { + try { + _this.dblclick.notify(args); + } catch (e) { + } + }); + +}; + +HPLCGraph.prototype.loadData = function (data) { + var _this = this; + this.reloadData(data); + this.panel.addDocked({ + xtype : 'toolbar', + items : this.getMenu() + }); + + + if (this.bbar == true){ + this.panel.addDocked({ + xtype : 'toolbar', + dock: 'bottom', + items : [ + { + xtype: 'numberfield', + id: 'main_field_start', + fieldLabel: 'Range from', + width: 170, + labelWidth : 70, + value: 0, + minValue: 0 + }, + { + xtype: 'numberfield', + id: 'main_field_end', + fieldLabel: 'to', + width: 130, + labelWidth : 30, + value: 0, + minValue: 0 + }, + { + xtype: 'button', + text: 'Go', + handler: function () { + var start = parseFloat(Ext.getCmp("main_field_start").getValue()); + var end = parseFloat(Ext.getCmp("main_field_end").getValue()); + + if (start < 0) { + start = 0; + } + if (end < 0) { + end = 0; + } + if (start > end) { + var aux = end; + end = start; + start = aux; + } + + _this.dygraphObject.dygraph.updateOptions({ isZoomedIgnoreProgrammaticZoom: true, dateWindow: [start, end] }); + } } + ] + }); + } +}; - } catch (e) { - } - } - } - } +HPLCGraph.prototype.getPanel = function () { + this.panel = Ext.create('Ext.panel.Panel', { + padding : this.plotPanelPadding, + width : this.width + 4 * this.plotInnerPanelPadding, + height : this.height + 4 * this.plotInnerPanelPadding, + items : [ { + html : "", + id : this.id, + width : this.width, + height : this.height + } ] }); - return this.grid; + return this.panel; }; -/** Method for testing * */ -MeasurementGrid.prototype.input = function() { - var experiment = DATADOC.getExperiment_10(); - var measurements = DATADOC.getMeasurements_10(); - var proposal = DATADOC.getProposal_10(); - return { - experiment : experiment, - measurements : measurements, - proposal : proposal - }; +HPLCGraph.prototype.input = function () { + return DATADOC.getHPLCData(); }; -MeasurementGrid.prototype.test = function(targetId) { - var measurementGrid = new MeasurementGrid({ - tbar : true - - }); - BIOSAXS.proposal = new Proposal(measurementGrid.input().proposal); - var panel = measurementGrid.getPanel(measurementGrid.input().measurements, new ExperimentList([ new Experiment(measurementGrid.input().experiment) ])); - panel.render(targetId); +HPLCGraph.prototype.getDataByFrameNumber = function (frameNumber) { + var data = {}; + data.frameNumber = frameNumber; + for (var key in this.hplcData){ + data[this.hplcData[key].label] = this.hplcData[key].data[frameNumber]; + } + console.log(data); + return data; }; - -/** - * A shipment may contains one or more cases where stock solutions and sample - * plates are stored - * - * @height - * @btnEditVisible - * @btnRemoveVisible - * - * #onEditButtonClicked - */ -function MolarityGrid(args) { - this.height = 100; - this.width = 100; - if (args != null) { - if (args.height != null) { - this.height = args.height; - } - if (args.width != null) { - this.width = args.width; + +HPLCGraph.prototype.test = function (targetId) { + var mainPlotPanel = new HPLCGraph({ + title : 'I0', + width : 800, + height : 400, + plots : { + "I0" : true, + "Rg" : true, + "Mass" : true + }, + xlabel : "HPLC Frames", + scaled : this.scaled, + interactionModel : { + 'dblclick' : function (event, g, context) { + } } - } + }); + mainPlotPanel.getPanel().render(targetId); + mainPlotPanel.loadData(mainPlotPanel.input()); - var _this = this; - - this.molarityForm = new MolarityForm({height : 180, width : 455}); +}; - this.molarityForm.onSave.attach(function(sender){ - _this.molarityWindow.destroy(); - _this.updateProposal(); - - }); - - this.molarityForm.onClose.attach(function(sender){ - _this.molarityWindow.destroy(); - - }); + +function MergesHPLCGraph(args) { + HPLCGraph.prototype.constructor.call(this, args); - /** Events * */ - this.onEditButtonClicked = new Event(this); +// this.peakColors = ["#00FB42", "#00BA31", "#007C21", "#003E10"]; + this.peakColors = ["#DEBD00", "#6D9100", "#872900", "#0092CC"]; } -MolarityGrid.prototype._getColumns = function() { - return [ { - text : 'Subunit', - columns : [ { - text : "Acronym", - width : 100, - hidden : false, - dataIndex : 'acronym', - sortable : true - }, { - text : "Name", - width : 125, - hidden : false, - dataIndex : 'name', - sortable : true - }, { - text : "MM Est.", - width : 100, - dataIndex : 'molecularMass', - sortable : true, - renderer : function(grid, cls, record){ - return BUI.formatValuesUnits(record.data.molecularMass , "Da", 10, 2); - - } - } ] - }, { -// text : "Number
in assymmetric units", - text : "Ratio", - width : 100, - dataIndex : 'ratio', - tooltip : 'Number of times the subunit is present in the macromolecule', - sortable : true - }, { - id : this.id + 'MOLARITY_REMOVE', - flex : 0.1, - sortable : false, - renderer : function(value, metaData, record, rowIndex, colIndex, store) { - return BUI.getRedButton('REMOVE'); - } - } ]; -}; -MolarityGrid.prototype._openMolarityWindow = function() { - this.molarityWindow = Ext.create('Ext.window.Window', { - title : 'Molarity', - height : 220, - width : 500, - modal : true, - items : [this.molarityForm.getPanel() ] - }).show(); -}; +MergesHPLCGraph.prototype.scaledData = HPLCGraph.prototype.scaledData; +MergesHPLCGraph.prototype.divideValuesByMax = HPLCGraph.prototype.divideValuesByMax; +MergesHPLCGraph.prototype.getMaxAndMinValue = HPLCGraph.prototype.getMaxAndMinValue; +MergesHPLCGraph.prototype.getPoint = HPLCGraph.prototype.getPoint; +MergesHPLCGraph.prototype.reloadData = HPLCGraph.prototype.reloadData; +MergesHPLCGraph.prototype._renderDygraph = HPLCGraph.prototype._renderDygraph; +MergesHPLCGraph.prototype.loadData = HPLCGraph.prototype.loadData; +MergesHPLCGraph.prototype.getPanel = HPLCGraph.prototype.getPanel; +MergesHPLCGraph.prototype.getDataByFrameNumber = HPLCGraph.prototype.getDataByFrameNumber; -MolarityGrid.prototype._getButtons = function() { - var _this = this; - return [ { - text : 'Add subunit', - icon : '../images/add.png', - handler : function() { - _this._openMolarityWindow(); + +MergesHPLCGraph.prototype.setPeaks = function (data) { + this.peaks = data; + /** get size of peaks **/ + this.peakKeys = []; + this.colorPeak = {}; + var colorCount = 1; + for (var key in this.peaks) { + if (this.peaks.hasOwnProperty(key)) { + var color = this.peakColors[colorCount % this.peakColors.length]; + colorCount = colorCount + 1; + this.peakKeys.push(key); + this.colorPeak[key] = color; } - }]; + } + this.peakKeys.sort(); }; -MolarityGrid.prototype.updateProposal = function() { +MergesHPLCGraph.prototype.getMenu = function () { var _this = this; - this.panel.setLoading(); - BIOSAXS.proposal.onInitialized.attach(function() { - if (BIOSAXS.proposal != null) { - var macromolecules = BIOSAXS.proposal.macromolecules; - for (var i = 0; i < macromolecules.length; i++) { - - if (macromolecules[i].macromoleculeId == _this.macromolecule.macromoleculeId) { - _this.refresh(macromolecules[i]); - _this.panel.setLoading(false); + /** Actions buttons **/ + var actions = []; + + function toggle(item, pressed) { + if (pressed) { + _this.plots[item.param] = true; + } else { + delete _this.plots[item.param]; + } + _this.reloadData(_this.hplcData); + } + + + /** Toolbar for peaks Average **/ + if (this.peaks != null) { + var items = []; + for (var i = 0; i < this.peakKeys.length; i++) { + var color = this.colorPeak[this.peakKeys[i]]; + items.push({ + text: "Peak #" + i + " " + this.peakKeys[i].replace("- ", " to #").replace(".0", "").replace(".0", "") + "", + peakid : this.peakKeys[i], + checked: false, + checkHandler: function (sender, pressed) { + var item = new Object(); + item.param = sender.peakid; + toggle(item, pressed); } - } + }); } - }); - BIOSAXS.proposal.init(); -}; - + + var menu = Ext.create('Ext.menu.Menu', { + id: 'mainMenu', + style: { + overflow: 'visible' + }, + items: items + }); + var tb = Ext.create('Ext.toolbar.Toolbar'); + tb.add({ + text: 'Peaks Avg.', + menu: menu + }); + actions.push(tb); + } -MolarityGrid.prototype.getPanel = function() { - var _this = this; + - this.molarityStore = Ext.create('Ext.data.Store', { - fields : [ 'acronym', 'ratio', 'comments', 'stoichiometryId', 'name', 'molecularMass' ], - sorters : { - property : 'ratio', - direction : 'DESC' + for (var i = 0; i < this.hplcData.length; i++) { + if (this.hplcData[i].showOnMenu != false) { + var param = this.hplcData[i].param; + var style = "style='padding:0 0px 0 5px;'"; + actions.push({ + text : "
" + BUI.getRectangleColorDIV(this.hplcData[i].color, 10, 10) + " " + this.hplcData[i].label + "
", + id : _this.id + param, + param : param, + enableToggle : true, + scope : this, + margin : 5, + toggleHandler : toggle, + pressed : (_this.plots[param] != null) + }); } - }); - - this.panel = Ext.create('Ext.grid.Panel', { - store : this.molarityStore, - height : this.height, - padding : 5, - columns : this._getColumns(), - listeners : { - cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { - - /** Remove entry * */ - if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'MOLARITY_REMOVE') { - var dataAdapter = new BiosaxsDataAdapter(); - dataAdapter.onSuccess.attach(function(sender) { - _this.updateProposal(); - - }); - dataAdapter.removeStoichiometry(record.data.stoichiometryId); - _this.panel.setLoading("Removing Structure"); - } - } - }, - tbar : this._getButtons() - }); - return this.panel; -}; + } -MolarityGrid.prototype._prepareData = function(macromolecule) { - /** Return an array of [{ratio,acronym, stoichiometryId, name}] **/ - var data = []; - if (macromolecule.stoichiometry != null) { - for (var i = 0; i < macromolecule.stoichiometry.length; i++) { - var hostMacromolecule = BIOSAXS.proposal.getMacromoleculeById(macromolecule.stoichiometry[i].macromoleculeId); - data.push({ - ratio : macromolecule.stoichiometry[i].ratio, - acronym : hostMacromolecule.acronym, - comments : hostMacromolecule.comments, - molecularMass : hostMacromolecule.molecularMass, - stoichiometryId : macromolecule.stoichiometry[i].stoichiometryId, - name : hostMacromolecule.name - }); - } + actions.push("->"); + actions.push({ + text : "Save", + scope : this, + icon : '../images/save.gif', + handler : function (item, pressed) { + var largeImage = document.createElement("img"); + largeImage.style.display = 'block'; + largeImage.style.width = 200 + "px"; + largeImage.style.height = 200 + "px"; + largeImage.setAttribute('src', Dygraph.Export.asCanvas(this.dygraphObject.dygraph).toDataURL()); + window.open(Dygraph.Export.asCanvas(this.dygraphObject.dygraph).toDataURL(), 'Image', ''); } - return data; -}; + }); -MolarityGrid.prototype.refresh = function(macromolecule) { - if (macromolecule != null){ - this.molarityStore.loadData(this._prepareData(macromolecule)); - this.molarityForm.refresh(macromolecule); - this.macromolecule = macromolecule; - } + return actions; }; -MolarityGrid.prototype.input = function() { - return { - proposal : DATADOC.getProposal_10(), - dewars : DATADOC.getDewars_10() - }; +MergesHPLCGraph.prototype.input = function () { + return DATADOC.getScatteringHPLCFrameData(); }; -MolarityGrid.prototype.test = function(targetId) { - var MolarityGrid = new MolarityGrid({ - height : 150 +MergesHPLCGraph.prototype.test = function (targetId) { + var mainPlotPanel = new MergesHPLCGraph({ + title : 'Scattering', + width : this.plotWidth, + height : 500, + showRangeSelector : false, + xParam : 0, + xlabel : "scattering_I", + plots : { + "scattering_I" : true, + "subtracted_I" : true, + "buffer_I" : true + } }); - BIOSAXS.proposal = new Proposal(MolarityGrid.input().proposal); - var panel = MolarityGrid.getPanel(MolarityGrid.input().dewars); - panel.render(targetId); - + mainPlotPanel.getPanel().render(targetId); + mainPlotPanel.loadData(mainPlotPanel.input()); }; + + +function NetworkWidget(args) { + this.id = "NetworkViewer_" + Math.random().toString().replace(".", "_"); + + this.label = true; + if (args != null) { + if (args.targetId != null) { + this.targetId = args.targetId; + } + if (args.label != null) { + this.label = args.label; + } + } + + this.onVertexOver = new Event(this); + this.onVertexOut = new Event(this); +} + +NetworkWidget.prototype.draw = function(dataset, formatter, layout) { + + this.graphCanvas = new GraphCanvas(this.id, document.getElementById(this.targetId), { + "labeled" : this.label, + "multipleSelectionEnabled" : false, + "draggingCanvasEnabled" : false + }); + this.graphCanvas.draw(dataset, formatter, layout); + + var _this = this; + this.graphCanvas.onVertexOver.attach(function(sender, nodeId) { + _this.onVertexOver.notify(nodeId); + }); + + this.graphCanvas.onVertexOut.attach(function(sender, nodeId) { + _this.onVertexOut.notify(nodeId); + }); +}; + +/** SELECT VERTICES BY NAME * */ +NetworkWidget.prototype.selectVertexByName = function(vertexName) { + var vertices = this.getDataset().getVertexByName(vertexName); + if (vertices != null) { + for ( var nodeId in vertices) { + if (vertices.hasOwnProperty(nodeId)) { + var vertexId = vertices[nodeId].getId(); + this.selectVertexById(vertexId); + } + } + } +}; + +NetworkWidget.prototype.selectVerticesByName = function(verticesName) { + for ( var i = 0; i < verticesName.length; i++) { + this.selectVertexByName(verticesName[i]); + } +}; + +/** SELECT VERTICES BY ID * */ +NetworkWidget.prototype.selectVertexById = function(vertexId) { + this.graphCanvas.selectNode(vertexId); + this.blinkVertexById(vertexId); +}; + +NetworkWidget.prototype.selectVerticesById = function(verticesId) { + for ( var i = 0; i < verticesId.length; i++) { + this.selectVertexById(verticesId[i]); + } +}; + +/** VECINDARIO * */ +NetworkWidget.prototype.selectNeighbourhood = function() { + this.selectEdgesFromVertices(); + this.selectAdjacent(); +}; +/** DESELECT * */ +NetworkWidget.prototype.deselectNodes = function() { + this.graphCanvas.deselectNodes(); +}; + +/** SELECT ALL NODES * */ +NetworkWidget.prototype.selectAllNodes = function() { + this.getGraphCanvas().selectAllNodes(); +}; + +/** SELECT EVERYTHING * */ +NetworkWidget.prototype.selectAll = function() { + this.getGraphCanvas().selectAll(); +}; + +/** SELECT ALL EDGES * */ +NetworkWidget.prototype.selectAllEdges = function() { + this.getGraphCanvas().selectAllEdges(); +}; + +/** ZOOM * */ +NetworkWidget.prototype.setScale = function(value) { + this.graphCanvas.setScale(value); +}; + +NetworkWidget.prototype.getScale = function() { + return this.graphCanvas.getScale(value); +}; + +/** SELECT ADJACENT VERTICES FROM SELECTED VERTICES * */ +NetworkWidget.prototype.selectAdjacent = function() { + var selectedVertices = this.getGraphCanvas().getSelectedVertices(); + var edges = []; + for ( var i = 0; i < selectedVertices.length; i++) { + edges = edges.concat(this.getGraphCanvas().getDataset().getVertexById(selectedVertices[i]).getEdges()); + } + var vertices = []; + for ( i = 0; i < edges.length; i++) { + vertices.push(edges[i].getNodeSource().getId()); + vertices.push(edges[i].getNodeTarget().getId()); + } + + this.selectVerticesById(vertices); +}; + +/** SELECT EDGES FROM SELECTED VERTICES * */ +NetworkWidget.prototype.selectEdgesFromVertices = function() { + var selectedVertices = this.getGraphCanvas().getSelectedVertices(); + var edges = []; + for ( var i = 0; i < selectedVertices.length; i++) { + edges = edges.concat(this.getGraphCanvas().getDataset().getVertexById(selectedVertices[i]).getEdges()); + } + var edgesId = []; + for ( i = 0; i < edges.length; i++) { + edgesId.push(edges[i].getId()); + } + this.getGraphCanvas().selectEdges(edgesId); +}; + +/** BLINKING * */ +NetworkWidget.prototype.blinkVertexById = function(vertexId) { + this.graphCanvas.blinkVertexById(vertexId); +}; + +/** COMPONENTE CONEXA DE LOS NODOS SELECCIONADOS * */ +NetworkWidget.prototype.selectConnectedComponent = function() { + var elements = this.getConnectedComponent(); + this.selectVerticesById(elements.nodes); + this.graphCanvas.selectEdges(elements.edges); +}; + +NetworkWidget.prototype.getConnectedComponent = function() { + var nodosVisitados = {}; + var aristasVisitadas = {}; + var arrNodos = []; + var arrAristas = []; + var selectedGraphNodesId = this.getGraphCanvas().getSelectedVertices(); + for ( var i = 0; i < selectedGraphNodesId.length; i++) { + this.visitNode(selectedGraphNodesId[i], nodosVisitados, aristasVisitadas, arrNodos, arrAristas); + } + return { + nodes : arrNodos, + edges : arrAristas + }; +}; + +NetworkWidget.prototype.visitNode = function(nodeId, nodosVisitados, aristasVisitadas, arrNodos, arrAristas) { + nodosVisitados[nodeId] = true; + arrNodos.push(nodeId); + var nodeEdges = this.getDataset().getVertexById(nodeId).getEdges(); + for ( var j = 0; j < nodeEdges.length; j++) { + var edge = nodeEdges[j]; + var edgeId = edge.getId(); + if (aristasVisitadas[edgeId] == null) { + aristasVisitadas[edgeId] = true; + arrAristas.push(edgeId); + var nodeTargetId = edge.getNodeTarget().getId(); + if (nodosVisitados[nodeTargetId] == null) { + this.visitNode(nodeTargetId, nodosVisitados, aristasVisitadas, arrNodos, arrAristas); + } + var nodeSourceId = edge.getNodeSource().getId(); + if (nodosVisitados[nodeSourceId] == null) { + this.visitNode(nodeSourceId, nodosVisitados, aristasVisitadas, arrNodos, arrAristas); + } + } + } +}; + +/** COLLAPSE SELECTED VERTICES * */ +NetworkWidget.prototype.collapse = function() { + var selectedVertices = this.getGraphCanvas().getSelectedVertices(); + var xMin = -Infinity; + var xMax = Infinity; + var yMin = -Infinity; + var yMax = Infinity; + + for ( var i = 0; i < selectedVertices.length; i++) { + var vertex = this.getGraphCanvas().getLayout().getNodeById(selectedVertices[i]); + if (xMin < vertex.x) { + xMin = vertex.x; + } + if (xMax > vertex.x) { + xMax = vertex.x; + } + if (yMin < vertex.y) { + yMin = vertex.y; + } + if (yMax > vertex.y) { + yMax = vertex.y; + } + } + + var centerX = xMin - xMax; + var centerY = yMin - yMax; + var radius = (xMax - xMin) / 4; + + var count = selectedVertices.length; + var verticesCoordinates = []; + + for ( i = 0; i < selectedVertices.length; i++) { + x = centerX + radius * Math.sin(i * 2 * Math.PI / count); + y = centerY + radius * Math.cos(i * 2 * Math.PI / count); + verticesCoordinates.push({ + 'x' : x, + 'y' : y + }); + } + + for ( i = 0; i < selectedVertices.length; i++) { + this.getGraphCanvas().getLayout().getNodeById(selectedVertices[i]).setCoordinates(verticesCoordinates[i].x, verticesCoordinates[i].y); + } +}; +/** SETTER FONT SIZE * */ +NetworkWidget.prototype.setVerticesFontSize = function(value) { + for ( var nodeId in this.getDataset().getVertices()) { + if (this.getDataset().getVertices().hasOwnProperty(nodeId)) { + this.getFormatter().getVertexById(nodeId).getDefault().setFontSize(value); + } + } +}; + +/** GETTERS * */ +NetworkWidget.prototype.getFormatter = function() { + return this.getGraphCanvas().getFormatter(); +}; +NetworkWidget.prototype.getLayout = function() { + return this.getGraphCanvas().getLayout(); +}; + +NetworkWidget.prototype.getDataset = function() { + return this.getGraphCanvas().getDataset(); +}; + +NetworkWidget.prototype.getGraphCanvas = function() { + return this.graphCanvas; +}; + +RangeWhiskerGraph.prototype.cleanArray = GenericGraph.prototype.cleanArray; +RangeWhiskerGraph.prototype.plotAxes = GenericGraph.prototype.plotAxes; +RangeWhiskerGraph.prototype.plotRuler = GenericGraph.prototype.plotRuler; +RangeWhiskerGraph.prototype.drawSVGVerticalText = GenericGraph.prototype.drawSVGVerticalText; +RangeWhiskerGraph.prototype.getClassColor = GenericGraph.prototype.getClassColor; +RangeWhiskerGraph.prototype.getDimensions = GenericGraph.prototype.getDimensions; +RangeWhiskerGraph.prototype.calculate = GenericGraph.prototype.calculate; +RangeWhiskerGraph.prototype.getMedian = GenericGraph.prototype.getMedian; +RangeWhiskerGraph.prototype.pointToPixel = GenericGraph.prototype.pointToPixel; +RangeWhiskerGraph.prototype.maxValueWhisker = GenericGraph.prototype.maxValueWhisker; +RangeWhiskerGraph.prototype.refresh = GenericGraph.prototype.refresh; + /** - * Given data analysis parsed with ResultsAssemblyWidget makes a selector grid with buffer/protein - **/ -function SpecimenSelectorResultGrid() { - this.id = BUI.id(); -} + * Subclass of GenericGraph + * + * @plotHorizontalByCluster + */ +function RangeWhiskerGraph(args) { + this.maxBoxWidth = 25; -SpecimenSelectorResultGrid.prototype._prepareData = function(data) { - var parsed = []; - for ( var i = 0; i < data.length; i++) { - var row = data[i]; - for ( var j = 0; j < row.conditions.length; j++) { - parsed.push({ - bufferId : row.conditions[j].bufferId, - macromoleculeId : row.macromoleculeId, - macromoleculeAcronym : BIOSAXS.proposal.getMacromoleculeById(row.macromoleculeId).acronym, - bufferAcronym : BIOSAXS.proposal.getBufferById(row.conditions[j].bufferId).acronym - }); - } + if (args == null) { + args = {}; } - return parsed; -}; - -SpecimenSelectorResultGrid.prototype.refresh = function(data) { - this.store.loadData(this._prepareData(data)); -}; - -SpecimenSelectorResultGrid.prototype.getPanel = function(data) { - var _this = this; - this.store = Ext.create('Ext.data.Store', { - fields : [ 'macromoleculeId', 'bufferId', 'macromoleculeAcronym', 'bufferAcronym', 'concentration' ], - data : this._prepareData(data), - groupField : 'macromoleculeAcronym' - }); + args.plotHorizontalByCluster = false; - this.store.sort('concentration'); - this.grid = Ext.create('Ext.grid.Panel', { - id : this.id, - store : this.store, - width : this.width, - height : this.height, - maxHeight : this.maxHeight, - border : 1, - features : [ { - ftype : 'grouping', - groupHeaderTpl : '{name}', - hideGroupedHeader : true, - startCollapsed : false - } ], - selModel : Ext.create('Ext.selection.CheckboxModel', { - allowDeselect : true, - mode : 'MULTI', - listeners : { - selectionchange : function(sm, selections) { - _this.selected = []; - for ( var i = 0; i < selections.length; i++) { - _this.selected.push(selections[i].raw); - } - } - } - }), - margin : 10, - sortableColumns : true, - columns : [ { - text : 'Macromolecule', - dataIndex : 'macromoleculeAcronym', - flex : 1 - }, { - text : '', - dataIndex : 'bufferId', - width : 20, - hidden : false, - renderer : function(val, y, sample) { - return BUI.getRectangleColorDIV(BIOSAXS.proposal.bufferColors[val], 10, 10); - } - }, { - text : 'Buffer', - dataIndex : 'bufferAcronym', - flex : 1 - }, { - text : 'Concentration', - dataIndex : 'concentration', - flex : 1, - renderer : function(val, y, sample) { - return val + " mg/ml"; - } - } ] - }); + GenericGraph.call(this, args); - /** Adding the tbar **/ - if (this.tbar) { - this.grid.addDocked({ - xtype : 'toolbar', - items : this.getTbar() - }); + if (args.maxBoxWidth != null) { + this.maxBoxWidth = args.maxBoxWidth; } - return this.grid; -}; +} -SpecimenSelectorResultGrid.prototype.input = function() { - return { - data : new ResultsAssemblyGrid()._prepareData(new ResultsAssemblyGrid().input().data), - proposal : new ResultsAssemblyGrid().input().proposal - }; +RangeWhiskerGraph.prototype.refresh = function(data) { + document.getElementById(this.targetId).innerHTML = ""; + this.draw(this.targetId, data); }; -SpecimenSelectorResultGrid.prototype.test = function(targetId) { - var specimenSelectorResultGrid = new SpecimenSelectorResultGrid(); - BIOSAXS.proposal = new Proposal(specimenSelectorResultGrid.input().proposal); - var panel = specimenSelectorResultGrid.getPanel(specimenSelectorResultGrid.input().data); - panel.render(targetId); -}; +RangeWhiskerGraph.prototype.isNumber = function(value) { + if (value == "") + return false; -/** - * Show all buffer conditions for each macromolecules pointing out the measurements quality - * - * @height - * @maxHeight - * @width - * @searchBar - * @tbar - * @btnResultVisible - * - * #onClick - */ -function ResultsAssemblyGrid(args) { - this.height = 500; - this.id = BUI.id(); - this.maxHeight = this.height; + var d = parseInt(value); + if (!isNaN(d)) + return true; + else + return false; - this.width = 900; - this.searchBar = false; - this.tbar = false; - this.btnResultVisible = false; +}; - /** For processing **/ - this.processed = {}; - this.renderedPlotIndex = 0; +/** + There are several different methods for calculating quartiles.[1] This calculator uses a method described by Moore and McCabe to find quartile values. + The same method also used by The TI-83 to calculate quartile values. + With this method, the first quartile is the median of the numbers below the median, the third quartile is the median of the numbers above the median. - this.plotWidth = 210; - this.plotHeight = 80; + http://www.miniwebtool.com/quartile-calculator/ + http://www.alcula.com/calculators/statistics/box-plot/ - /** Colors **/ - this.validColor = BUI.getValidColor(); - this.warningColor = BUI.getWarningColor(); - this.notValidColor = BUI.getErrorColor(); + **/ +RangeWhiskerGraph.prototype.getQ1 = function(array) { + array = array.slice(0, array.length / 2); + return this.getMedian(array); +}; - /** Show warning if guinier quality less than **/ - this.guinierQuality = BUI.getQualityThreshold(); - this.framePercentageThreshold = BUI.getRadiationDamageThreshold(); +RangeWhiskerGraph.prototype.getQ3 = function(array) { + array = array.slice((array.length + 1) / 2); + return this.getMedian(array); - if (args != null) { - if (args.height != null) { - this.height = args.height; - this.maxHeight = this.height; - } - if (args.maxHeight != null) { - this.maxHeight = args.maxHeight; - } - if (args.width != null) { - this.width = args.width; - } - if (args.searchBar != null) { - this.searchBar = args.searchBar; +}; + +RangeWhiskerGraph.prototype.getQ2 = function(array) { + return this.getMedian(array); +}; + +RangeWhiskerGraph.prototype.getBelowOutliers = function(belowLimit, array) { + var points = []; + for ( var i = 0; i < array.length; i++) { + if (array[i] <= belowLimit) { + points.push(array[i]); } + } + return points; +}; - if (args.tbar != null) { - this.tbar = args.tbar; +RangeWhiskerGraph.prototype.getAboveOutliers = function(aboveLimit, array) { + var points = []; + for ( var i = 0; i < array.length; i++) { + if (array[i] >= aboveLimit) { + points.push(array[i]); } - if (args.btnResultVisible != null) { - this.btnResultVisible = args.btnResultVisible; + } + return points; +}; + +RangeWhiskerGraph.prototype.minValueWhisker = function(belowLimit, q1, array) { + var points = []; + + for ( var i = 0; i < array.length; i++) { + if ((array[i] < q1) && (array[i]) > belowLimit) { + points.push(array[i]); } + } + if (points.length > 0) { + points.sort(function(a, b) { + return a - b; + }); + return points[0]; } + return null; +}; - this.onClick = new Event(); -} +//RangeWhiskerGraph.prototype.maxValueWhisker = function(aboveLimit, q3, array){ +// var points = []; +// for (var i = 0; i < array.length; i++){ +// if ((array[i] > q3) && (array[i]) <= aboveLimit){ +// points.push(array[i]); +// } +// } +// if (points.length > 0){ +// points.sort(function(a, b){return a - b;}); +// return points[points.length - 1]; +// } +// return null; +//}; -ResultsAssemblyGrid.prototype.edit = function(macromoleculeId) { - var _this = this; - var window = new MacromoleculeWindow(); - window.onSuccess.attach(function(sender, proposal) { - _this.store.loadData(_this._prepareData(BIOSAXS.proposal.getMacromolecules())); - }); - window.draw(BIOSAXS.proposal.getMacromoleculeById(macromoleculeId)); +RangeWhiskerGraph.prototype.drawPoints = function(boxProperties) { + if (this.plotPoints) { + for ( var i = 0; i < boxProperties.values.length; i++) { + var value = boxProperties.values[i]; + var toPixel = this.pointToPixel(value, boxProperties); + SVG.drawCircle(boxProperties.x, toPixel, this.pointRadius, this.svg, + [ + [ "fill", "green" ], [ "fill-opacity", this.fillOpacityPoint ], [ 'stroke-opacity', this.strokeOpacityPoint ], + [ "stroke", "black" ] ]); + } + } }; -ResultsAssemblyGrid.prototype.getTbar = function() { - var _this = this; - var actions = []; +RangeWhiskerGraph.prototype.plotRangeQ1Q3 = function(data, properties) { + var posX = this.left + this.rulerWidth; - actions.push(Ext.create('Ext.Action', { - icon : '../images/add.png', - text : 'Add Macromolecule', - disabled : false, - handler : function(widget, event) { - var window = new MacromoleculeWindow(); - window.onSuccess.attach(function(sender) { - _this.refresh(); - }); - window.draw({}); - } - })); + var boxBordersPointsQ1 = []; + var boxBordersPointsQ3 = []; + var Q2 = []; - actions.push(Ext.create('Ext.Action', { - icon : '../images/add.png', - text : 'Define an Assembly', - disabled : false, - handler : function(widget, event) { - var createAssemblywindow = new CreateAssemblyWindow(); - createAssemblywindow.onSaved.attach(function(evt, args) { - if (args.macromoleculeIds.length > 0) { - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, proposal) { - _this.refresh(); - }); - adapter.saveAssembly(args.assemblyId, args.macromoleculeIds); + for ( var i = 0; i < data.clusters.length; i++) { + var cluster = data.clusters[i]; + /** inter cluster space **/ + posX = posX + this.interClustersSpace; - } + for ( var j = 0; j < cluster.classes.length; j++) { + var ratio = properties.width / (properties.xValues.range); + var coorX = (cluster.x - properties.xValues.min) * ratio + this.rulerWidth; + var boxProperties = { + name : cluster.classes[j].name, + values : cluster.classes[j].values, + minPoint : properties.minPoint, + maxPoint : properties.maxPoint, + x : coorX + this.left, + y : this.top, + width : properties.classWidth, + height : this.height - this.top - this.bottom - this.clusterTitleHeight - this.rulerHeight + }; - }); - createAssemblywindow.draw(_this.experiment); - } - })); + var result = this.calculate(boxProperties.values); + var boxColor = this.getClassColor(boxProperties.name); - return actions; -}; + if (this.isNumber(result.Q1) && this.isNumber(result.Q3)) { + var x = boxProperties.x; + var y = this.pointToPixel(result.Q1, boxProperties); + if (boxBordersPointsQ1[cluster.classes[j].name] == null) { + boxBordersPointsQ1[cluster.classes[j].name] = []; + } + boxBordersPointsQ1[cluster.classes[j].name].push({ + x : x, + y : y + }); -ResultsAssemblyGrid.prototype.refresh = function() { - this.store.loadData(this._prepareData()); -}; + x = boxProperties.x; + y = this.pointToPixel(result.Q3, boxProperties); + if (boxBordersPointsQ3[cluster.classes[j].name] == null) { + boxBordersPointsQ3[cluster.classes[j].name] = []; + } + boxBordersPointsQ3[cluster.classes[j].name].push({ + x : x, + y : y + }); + } else { -ResultsAssemblyGrid.prototype.addCondition = function(record, data_parsed, i) { - function getCondition(record) { - return { - concentration : record.conc, - quality : record.quality, - bufferBeforeFramesMerged : record.bufferBeforeFramesMerged, - bufferAfterFramesMerged : record.bufferAfterFramesMerged, - framesCount : record.framesCount, - framesMerge : record.framesMerge - }; - } + if (this.isNumber(result.Q2)) { - if (data_parsed[i].conditions != null) { - var bufferFound = false; - for ( var index in data_parsed[i].conditions) { - condition = data_parsed[i].conditions[index]; + if (boxBordersPointsQ1[cluster.classes[j].name] == null) { + boxBordersPointsQ1[cluster.classes[j].name] = []; + } - if ((condition.macromoleculeId == record.macromoleculeId) && (condition.bufferId == record.bufferId)) { - data_parsed[i].conditions[index].concentrations.push(getCondition(record)); - bufferFound = true; + if (boxBordersPointsQ3[cluster.classes[j].name] == null) { + boxBordersPointsQ3[cluster.classes[j].name] = []; + } + + boxBordersPointsQ1[cluster.classes[j].name].push({ + x : boxProperties.x, + y : this.pointToPixel(result.Q2, boxProperties) + }); + boxBordersPointsQ3[cluster.classes[j].name].push({ + x : boxProperties.x, + y : this.pointToPixel(result.Q2, boxProperties) + }); + } } } - if (!bufferFound) { - data_parsed[i].conditions.push({ - macromoleculeId : record.macromoleculeId, - bufferId : record.bufferId, - concentrations : [ getCondition(record) ] - }); + } + for ( var classe in boxBordersPointsQ1) { + var points = boxBordersPointsQ1[classe]; + var pointsSVG = ""; + for (var k = 0; k < points.length; k++) { + pointsSVG = Number(points[k].x).toFixed(1) + "," + Number(points[k].y).toFixed(1) + " " + pointsSVG; + } + + points = boxBordersPointsQ3[classe]; + for (var z = points.length - 1; z >= 0; z--) { + pointsSVG = Number(points[z].x).toFixed(1) + "," + Number(points[z].y).toFixed(1) + " " + pointsSVG; } + + SVG.drawPoligon(pointsSVG, this.svg, [ + [ "fill", this.getClassColor(classe) ], [ "opacity", "0.3" ], [ "stroke", "black" ], [ "stroke-width", 1 ] ]); } }; -ResultsAssemblyGrid.prototype.process = function(record, data_parsed) { - if (this.processed[record.macromoleculeId] == null) { - this.processed[record.macromoleculeId] = true; - record.measurementCount = 1; - record.subtractionCount = 1; - record.averageCount = 1; - record.conditions = []; - data_parsed.push(record); - } +RangeWhiskerGraph.prototype.plotWhisters = function(data, properties) { + var colors = [ "yellow", "orange", "green" ]; - for ( var i = 0; i < data_parsed.length; i++) { - if (data_parsed[i].macromoleculeId == record.macromoleculeId) { - data_parsed[i].measurementCount = data_parsed[i].measurementCount + 1; + this.plotRangeQ1Q3(data, properties); + var Q2 = {}; - if (record.subtractionId != null) { - data_parsed[i].subtractionCount = data_parsed[i].subtractionCount + 1; - this.addCondition(record, data_parsed, i); - } + for ( var i = 0; i < data.clusters.length; i++) { + var cluster = data.clusters[i]; + for ( var j = 0; j < cluster.classes.length; j++) { + var ratio = properties.width / (properties.xValues.range); + var coorX = (cluster.x - properties.xValues.min) * ratio + this.left + this.rulerWidth - this.rulerStroke; + var boxProperties = { + name : cluster.classes[j].name, + values : cluster.classes[j].values, + minPoint : properties.minPoint, + maxPoint : properties.maxPoint, + x : coorX, + y : this.top, + width : properties.classWidth, + height : this.height - this.top - this.bottom - this.clusterTitleHeight - this.rulerHeight + }; - if (record.framesMerge != null) { - data_parsed[i].averageCount = data_parsed[i].averageCount + 1; - } + this.drawPoints(boxProperties); - if (record.timeStart != null) { - if (data_parsed[i].timeStart != null) { - if (moment(data_parsed[i].timeStart).format("X") > moment(record.timeStart).format("X")) { - data_parsed[i].timeStart = record.timeStart; - } + /** PLOTTING Q2 **/ + var result = this.calculate(boxProperties.values); + var boxColor = this.getClassColor(boxProperties.name); + if (this.isNumber(result.Q2)) { + if (Q2[boxProperties.name] != null) { + SVG.drawLine(boxProperties.x, this.pointToPixel(result.Q2, boxProperties), Q2[boxProperties.name].x, Q2[boxProperties.name].y, + this.svg, [ [ "stroke", boxColor ], [ "stroke-width", "2" ] ]); + } + Q2[boxProperties.name] = { + x : boxProperties.x, + y : this.pointToPixel(result.Q2, boxProperties) } } } } - return data_parsed; - }; -ResultsAssemblyGrid.prototype._prepareData = function(data) { - var data_parsed = []; - for ( var i = 0; i < data.length; i++) { - data_parsed = this.process(data[i], data_parsed); - } - this.data = data_parsed; - return data_parsed; +RangeWhiskerGraph.prototype.draw = function(targetId, data) { + //this.calculate(data); + this.targetId = targetId; + var properties = (this.getDimensions(data)); + this.svg = SVG.createSVGCanvas(document.getElementById(this.targetId), [ [ "width", this.width ], [ "height", this.height ] ]); + this.plotAxes(properties); + this.plotWhisters(data, properties); }; -/** Given an array of conditions it returns distinct(concentrations) order by concentration and hash map with number of ocurrences**/ -ResultsAssemblyGrid.prototype.parseConcentrations = function(val, conditions, differentConcentration, quality) { - var conditions = []; - var differentConcentration = {}; - var quality = []; - for ( var i = 0; i < val.length; i++) { - var conc = Number(val[i].concentration).toFixed(1); - var quality = Number(val[i].quality).toFixed(2); - if (differentConcentration[conc] == null) { - differentConcentration[conc] = 0; - conditions.push({ - concentration : conc, - quality : [ quality ], - frames : [ { - bufferAfterFramesMerged : val[i].bufferAfterFramesMerged, - bufferBeforeFramesMerged : val[i].bufferBeforeFramesMerged, - framesMerge : val[i].framesMerge, - framesCount : val[i].framesCount - } ] - }); - } else { - /** Add quality **/ - for ( var j = 0; j < conditions.length; j++) { - if (conditions[j].concentration == conc) { - conditions[j].quality.push(quality); - conditions[j].frames.push({ - bufferAfterFramesMerged : val[i].bufferAfterFramesMerged, - bufferBeforeFramesMerged : val[i].bufferBeforeFramesMerged, - framesMerge : val[i].framesMerge, - framesCount : val[i].framesCount - }); - } - } - } +RangeWhiskerGraph.prototype.input = function() { + return DATADOC.getBoxWhikerData(); +}; - differentConcentration[conc] = differentConcentration[conc] + 1; - } - /** sorting concentrations **/ - conditions.sort(function(a, b) { - return a.concentration - b.concentration; +RangeWhiskerGraph.prototype.test = function(targetId) { + var plot = new RangeWhiskerGraph({ + targetId : targetId, + height : 350, + width : 450, + maxBoxWidth : 25 }); - return { - concentrations : conditions, - differentConcentration : differentConcentration - }; + plot.refresh(this.input()); }; + +StdDevDyGraph.prototype.dblclick = DygraphWidget.prototype.dblclick; +StdDevDyGraph.prototype._createHTLMWrapper = DygraphWidget.prototype._createHTLMWrapper; +StdDevDyGraph.prototype.draw = DygraphWidget.prototype.draw; -ResultsAssemblyGrid.prototype.getConditionWarnings = function(condition) { - - var withWarnings = 0; - - for ( var i = 0; i < condition.frames.length; i++) { - if (condition.quality[i] == null) { - withWarnings = withWarnings + 1; - continue; - } else { - if (Number(condition.quality[i]) < this.guinierQuality) { - withWarnings = withWarnings + 1; - continue; - } - } - - if (condition.frames[i].bufferBeforeFramesMerged / condition.frames[i].framesCount < this.framePercentageThreshold|| condition.frames[i].framesMerged / condition.frames[i].framesCount < this.framePercentageThreshold|| condition.frames[i].bufferAfterFramesMerged / condition.frames[i].framesCount < this.framePercentageThreshold) { - withWarnings = withWarnings + 1; - continue; - } +function StdDevDyGraph(targetId, args) { + this.scaled = false; + if (args == null) { + args = {}; } + args.customBars = true; + DygraphWidget.prototype.constructor.call(this, targetId, args); +} +StdDevDyGraph.prototype.input = function () { return { - withWarnings : withWarnings, - withoutWarnings : condition.frames.length - withWarnings + data : [ [ 1, [ 2, 3, 3.5 ], [ 4, 4.2, 5 ] ], [ 2, [ 5, 5.5, 5.7 ], [ 4, 4.2, 5 ] ] ], + colors : [ "blue", "red" ], + labels : [ "", 'data1', 'data2' ] }; }; -ResultsAssemblyGrid.prototype.getFrameHTMLTable = function(warnings) { - var html = ""; - if (warnings.withWarnings > 0) { - html = html + "
" + warnings.withWarnings + - "x
"; - } - - if (warnings.withoutWarnings > 0) { - html = html + "
" + warnings.withoutWarnings + - "x
"; - } - return html; -}; +StdDevDyGraph.prototype.test = function (targetId) { + var dygraphObject = new StdDevDyGraph(targetId, { + width : 500, + height : 400, + xlabel : "xLabel", + showRangeSelector : false + }); -ResultsAssemblyGrid.prototype.createConcentrationRow = function(numberOcu, condition, warnings){ - var html = ""; - if (numberOcu > 1){ - html = html + "" +numberOcu + "x " + BUI.formatConcentration(condition.concentration); - } - else{ - html = html + BUI.formatConcentration(condition.concentration); - } - html = html + ""; - html = html + this.getFrameHTMLTable(warnings); + ""; - html = html + ""; - return html; + dygraphObject.draw(dygraphObject.input().data, dygraphObject.input().colors, dygraphObject.input().labels); }; + + + +function AbinitioGrid(args) { + this.height = null; + this.width = null; + this.id = BUI.id(); + + if (args != null) { + if (args.height != null) { + this.height = args.height; + + if (args.width != null) { + this.width = args.width; + } + } + } + + this.onSelected = new Event(this); +}; + + +AbinitioGrid.prototype.refresh = function(subtractions){ + this.store.loadData(this._prepareData(subtractions)); +}; + +AbinitioGrid.prototype._prepareData = function(subtractions){ + /** Parsing data * */ + var models = []; + for (var l = 0; l < subtractions.length; l++) { + var subtraction = subtractions[l]; + for (var k = 0; k < subtraction.substractionToAbInitioModel3VOs.length; k++) { + var data = subtraction.substractionToAbInitioModel3VOs[k].abinitiomodel3VO; + if (data.averagedModel != null) { + models.push(data.averagedModel); + models[models.length - 1].type = "Reference"; + } + + if (data.shapeDeterminationModel != null) { + models.push(data.shapeDeterminationModel); + models[models.length - 1].type = "Refined"; + } + + if (data.modelList3VO != null) { + if (data.modelList3VO.modeltolist3VOs != null) { + for (var i = 0; i < data.modelList3VO.modeltolist3VOs.length; i++) { + models.push(data.modelList3VO.modeltolist3VOs[i].model3VO); + models[models.length - 1].type = "Model"; + } + } + } + } + } + return models; +}; + +AbinitioGrid.prototype.getPanel = function(){ + var _this = this; + + + var modelFields = [ "modelId", "type", "chiSqrt", "dmax", "firFile", "logFile", "fitFile", "pdbFile", "rfactor", "rg", "volume" ]; + Ext.define('AbinitioModel', { + extend : 'Ext.data.Model', + fields : modelFields + + }); + + /** + * Store in Memory + */ + this.store = Ext.create('Ext.data.Store', { + model : 'AbinitioModel', + autoload : true, + groupField : 'type' + }); + + + var groupingFeature = Ext.create('Ext.grid.feature.Grouping',{ + groupHeaderTpl: '{name} ({rows.length} model{[values.rows.length > 1 ? "s" : ""]})', + startCollapsed: true, + collapsible : true + }); + + this.grid = Ext.create('Ext.grid.Panel', { + collapsible : false, + resizable : true, + features: [groupingFeature], + autoscroll : true, + multiSelect : true, + store : this.store, + height : this.height, + width : this.width, + margin : 10, + columns : [ { + text : "Type", + dataindex : "type", + hidden : true, + renderer : function(a, b, record) { + return record.data.type; + }, + flex : 1 + }, + { + text : "ModelId", + dataindex : "modelId", + hidden : true, + renderer : function(a, b, record) { + return record.data.modelId; + + }, + flex : 1 + }, + + { + text : "chiSqrt", + dataindex : "chiSqrt", + renderer : function(a, b, record) { + if (record.data.dmax != null) { + return BUI.formatValuesUnits(record.data.chiSqrt, "", 12, this.decimals); + } + + }, + flex : 1 + }, + { + text : "Dmax", + dataindex : "dmax", + renderer : function(a, b, record) { + if (record.data.dmax != null) { + return BUI.formatValuesUnits(record.data.dmax, "nm", 12, this.decimals); + } + + }, + flex : 1 + }, { + text : "rFactor", + dataindex : "rfactor", + hidden : true, + renderer : function(a, b, record) { + if (record.data.rfactor != null) { + return record.data.rfactor; + } + }, + flex : 1 + }, { + text : "Rg", + dataindex : "rg", + renderer : function(a, b, record) { + if (record.data.rg != null) { + return BUI.formatValuesUnits(record.data.rg, "nm", 12, this.decimals); + } + + }, + flex : 1 + }, + { + text : "Volume", + dataindex : "volume", + renderer : function(a, b, record) { + if (record.raw.volume != null){ + return BUI.formatValuesUnits(record.raw.volume, '') + " nm3"; + } + }, + flex : 1 + }, + { + text : "PDB", + dataindex : "pdbFile", + renderer : function(a, b, record) { + if (record.data.pdbFile != null){ + return record.data.pdbFile.split("/")[record.data.pdbFile.split("/").length - 1]; + } + }, + flex : 1 + }, { + text : "Fir", + dataindex : "firFile", + renderer : function(a, b, record) { + if (record.data.firFile != null){ + return record.data.firFile.split("/")[record.data.firFile.split("/").length - 1]; + } + }, + flex : 1 + }, { + text : "LOG", + dataindex : "logFile", + hidden : true, + renderer : function(a, b, record) { + if (record.data.logFile != null){ + return record.data.logFile.split("/")[record.data.logFile.split("/").length - 1]; + } + }, + flex : 1 + } + ], + viewConfig : { + enableTextSelection : true, + preserveScrollOnRefresh : true, + stripeRows : true, + listeners : { + 'celldblclick' : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + }, + 'cellclick' : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + var models = []; + for (var i = 0; i < grid.getSelectionModel().selected.items.length; i++) { + models.push(grid.getSelectionModel().selected.items[i].raw); + } + _this.onSelected.notify(models); + } + } + } + }); + return this.grid; + +}; + +function AdditiveGrid(args) { + this.onRemoveButtonClicked = new Event(this); +} -ResultsAssemblyGrid.prototype.getConditionHTMLTable = function(val, style, record) { - var maxNumberColumns = 2; - var html = "
"; - var nColumns = 0; - for ( var r = 0; r < val.length; r++) { - if (nColumns == maxNumberColumns) { - nColumns = 0; - html = html + ""; - } - html = html + ""; - nColumns = nColumns + 1; - } - html = html + "
"; - var value = val[r]; - - var bufferAcronym = (BIOSAXS.proposal.getBufferById(value.bufferId).acronym); - - var parsed = (this.parseConcentrations(value.concentrations)); - var conditions = parsed.concentrations; - var differentConcentration = parsed.differentConcentration; - - /** Checking warnings **/ - var warnings = []; - var concentrationValidPerconcentration = 0; - var measurements = 0; - for ( var i = 0; i < conditions.length; i++) { - measurements = measurements + conditions[i].frames.length; - var warning = this.getConditionWarnings(conditions[i]); - warnings.push(warning); - if (warning.withoutWarnings > 0) { - concentrationValidPerconcentration = concentrationValidPerconcentration + 1; - } - } - - this.validColor = '#E0F8E0'; - this.warningColor = '#F5DA81'; - this.notValidColor = '#F6CED8'; - - var color = this.warningColor; - if (concentrationValidPerconcentration > 2) { - color = this.validColor; - } - /** More measurement need to be done **/ - if (measurements < 3) { - color = this.notValidColor; - } - - html = html + ""; - html = html + ""; - for ( var i = 0; i < conditions.length; i++) { - html = html + this.createConcentrationRow(differentConcentration[conditions[i].concentration], conditions[i], warnings[i]); - } - - html = html + ""; - html = html + "
" + bufferAcronym.toUpperCase() + - "
"; - - html = html + "
"; - return html; +AdditiveGrid.prototype.getBuffer = function() { + return this.buffer; }; -ResultsAssemblyGrid.prototype._getTbar = function() { - function goTo(url) { - window.location = url; - } - +AdditiveGrid.prototype._getActions = function() { var _this = this; - return [ { - icon : '../images/application_view_list.png', - text : 'Multiple Select', - handler : function() { - var specimenSelectorResultGrid = new SpecimenSelectorResultGrid(); - var window = Ext.create('Ext.window.Window', { - title : 'Multiple select', - height : 600, - width : 600, - layout : 'fit', - items : [ specimenSelectorResultGrid.getPanel(_this.data) ], - buttons : [ { - text : 'Go', - handler : function() { - var array = []; - for ( var i = 0; i < specimenSelectorResultGrid.selected.length; i++) { - var row = specimenSelectorResultGrid.selected[i]; - array.push({ - macromoleculeId : row.macromoleculeId, - bufferId : row.bufferId - }); - } - goTo(BUI.getMacromoleculeResultsURLByMultipleSearch(array)); + /** Actions buttons **/ + var actions = []; - } - }, { - text : 'Cancel', - handler : function() { - window.close(); - } - } ] + actions.push(Ext.create('Ext.Action', { + icon : '../images/add.png', + text : 'Add', + disabled : true, + alwaysEnabled : true, + handler : function(widget, event) { + _this.buffer.bufferhasadditive3VOs.push(BIOSAXS_BEANS.getBufferhasAdditive3VO()); + _this.refresh(_this.buffer, _this.experiment); + } + })); - }).show(); + return actions; +}; +AdditiveGrid.prototype.refresh = function(buffer, experiment) { + this.buffer = buffer; + this.experiment = experiment; + if (buffer) { + if (buffer.bufferhasadditive3VOs) { + this.features = buffer.bufferhasadditive3VOs; } - } ]; + } + this.experiment = experiment; + this.store.loadData(this._prepareData(), false); }; -ResultsAssemblyGrid.prototype.getLegendPanel = function() { - return { - html : '
' + BUI.getRectangleColorDIV(this.validColor, 10, 10) + - 'Good quality measurements' + - BUI.getRectangleColorDIV(this.warningColor, 10, 10) + - 'Probably valid with manual processing' + - BUI.getRectangleColorDIV(this.notValidColor, 10, 10) + - 'More measurements need to be done
' - }; -}; -/** Returns the grid **/ -ResultsAssemblyGrid.prototype.getPanel = function(macromolecules) { - var _this = this; +AdditiveGrid.prototype.getAdditives = function() { + var additives = []; + for ( var i = 0; i < this.store.getCount(); i++) { + var bufferHasAdditive = BIOSAXS_BEANS.getBufferhasAdditive3VO(); + var data = this.store.getAt(i).getData(); - this.store = Ext.create('Ext.data.Store', { - fields : [ - 'macromoleculeId', 'macromoleculeAcronym', 'measurementCount', 'subtractionCount', 'averageCount', 'timeStart', 'concentrationArray', - 'bufferConditions', 'conditions' ], - data : this._prepareData(macromolecules) - }); + bufferHasAdditive.additive3VO.name = data.name; + bufferHasAdditive.additive3VO.comments = data.comments; + bufferHasAdditive.additive3VO.additiveType = data.additiveType; - this.store.sort('macromoleculeAcronym'); + bufferHasAdditive.bufferId = this.buffer.bufferId; + bufferHasAdditive.bufferHasAdditiveId = data.bufferHasAdditiveId; + bufferHasAdditive.quantity = data.quantity; + additives.push(bufferHasAdditive); + } - this.grid = Ext.create('Ext.grid.Panel', { - id : this.id, - title : 'Macromolecules', - store : this.store, - tbar : this._getTbar(), - bbar : [ this.getLegendPanel() ], - width : this.width, - height : this.height, - maxHeight : this.maxHeight, - sortableColumns : true, - columns : [ - { - text : '', - dataIndex : 'macromoleculeId', - width : 20, - hidden : false, - renderer : function(val, y, sample) { - return BUI.getRectangleColorDIV(BIOSAXS.proposal.macromoleculeColors[val], 10, 10); - } - }, - { - text : 'Macromolecule', - dataIndex : 'macromoleculeAcronym', - width : 200 + return additives; +}; + +AdditiveGrid.prototype._getFields = function(buffers) { + var columns = [ + + { + header : 'Name', + dataIndex : 'name', + type : 'string', + editor : { + allowBlank : true }, - { - text : 'Buffer Conditions', - dataIndex : 'conditions', - flex : 1, - renderer : function(val, style, record) { - return _this.getConditionHTMLTable(val, style, record); - } + flex : 1 + }, + { + header : 'Type', + name : 'additiveType', + dataIndex : 'additiveType', + editor : { + xtype : 'combobox', + typeAhead : true, + triggerAction : 'all', + selectOnTab : true, + store : BIOSAXS.proposal.getAdditiveTypes(), + lazyRender : true, + listClass : 'x-combo-list-small' }, - { - header : 'Average', - dataIndex : 'percentageCollected', - name : 'percentageCollected', - type : 'string', - hidden : true, - renderer : function(val, y, sample) { - return "
" + - BUI.getProgessBar((sample.raw.averageCount / sample.raw.measurementCount) * 100, sample.raw.averageCount + "/" + - sample.raw.measurementCount) + "
"; - }, - width : 100, - sorter : false + flex : 0.6 + }, + { + header : 'Quantity', + dataIndex : 'quantity', + name : 'quantity', + editor : { + allowBlank : true }, - { - header : 'Subtractions', - dataIndex : 'percentageCollected', - name : 'percentageCollected', - type : 'string', - hidden : true, - renderer : function(val, y, sample) { - return "
"+ BUI.getProgessBar((sample.raw.subtractionCount / sample.raw.measurementCount) * 100, sample.raw.subtractionCount + "/" + - sample.raw.measurementCount) + "
"; - }, - width : 100 - }, { - header : 'Date', - dataIndex : 'timeStart', - name : 'timeStart', - type : 'string', - renderer : function(value, metaData, record, rowIndex, colIndex, store) { - if (record.raw.timeStart != null) { - return moment(record.raw.timeStart).format("MMM Do YY"); + type : 'string', + flex : 1 + }, + { + xtype : 'actioncolumn', + items : [ { + icon : '../images/cancel.png', + tooltip : 'Delete additive', + scope : this, + handler : function(grid, rowIndex, colIndex) { + if ((grid.getStore().getAt(rowIndex).data.bufferHasAdditiveId == null)|| (grid.getStore().getAt(rowIndex).data.bufferHasAdditiveId == "")) { + grid.getStore().removeAt(rowIndex); + } else { + this.onRemoveButtonClicked.notify({ + 'bufferId' : this.buffer.bufferId, + 'bufferHasAdditiveId' : this.store.getAt(rowIndex).data.bufferHasAdditiveId + }); } } - }, { - header : 'Download', - dataIndex : 'percentageCollected', - name : 'percentageCollected', - type : 'string', - renderer : function(val, y, sample) { - return BUI.getZipHTMLByMacromoleculeId(sample.raw.macromoleculeId); - }, - width : 100 - }, + } ] + } ]; + + return columns; +}; + +AdditiveGrid.prototype._prepareData = function() { + var data = []; + if (this.features == null) { + this.features = []; + } + for (var i = 0; i < this.features.length; i++) { + var object = this.features[i]; + object.name = this.features[i].additive3VO.name; + object.additiveType = this.features[i].additive3VO.additiveType; + object.comments = this.features[i].additive3VO.comments; + object.additiveId = this.features[i].additive3VO.additiveId; + data.push(object); + } + return data; +}; - { - id : 'btnResultVisible', - width : 85, - sortable : false, - renderer : function(value, metaData, record, rowIndex, colIndex, store) { - return BUI.getGreenButton('GO'); - } - } ], +AdditiveGrid.prototype.getPanel = function(buffer, experiment) { + this.buffer = buffer; + this.features = buffer.bufferhasadditive3VOs; + this.experiment = experiment; + return this._renderGrid(); +}; + +AdditiveGrid.prototype.getStore = function() { + var _this = this; + var store = Ext.create('Ext.data.Store', { + fields : [ "name", "additiveType", "comments", "additiveId", "bufferHasAdditiveId", "quantity" ], + autoload : false, + data : this._prepareData(), + listeners : { + update : function(store, record) { + record.index = _this.grid.getSelectionModel().getCurrentPosition().row; + _this.buffer.bufferhasadditive3VOs[record.index].additive3VO.name = record.data.name; + _this.buffer.bufferhasadditive3VOs[record.index].additive3VO.additiveType = record.data.additiveType; + _this.buffer.bufferhasadditive3VOs[record.index].additive3VO.comments = record.data.comments; + _this.buffer.bufferhasadditive3VOs[record.index].quantity = record.data.quantity; + } + } + }); + + // store.sort('bufferHasAdditiveId', 'ASC'); + store.loadData(this._prepareData(), false); + return store; +}; + +AdditiveGrid.prototype._renderGrid = function() { + var _this = this; + this.store = this.getStore(); + + var cellEditing = Ext.create('Ext.grid.plugin.CellEditing', { + clicksToEdit : 1 + }); + + this.grid = Ext.create('Ext.grid.Panel', { + dockedItems : [ { + xtype : 'toolbar', + items : this._getActions() + } ], + store : this.store, + height : 230, + title : "Additives", + width : "100%", + columns : _this._getFields(), + plugins : [ cellEditing ], viewConfig : { stripeRows : true, listeners : { - afterrender : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { - }, - celldblclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { - _this.edit(record.data.macromoleculeId); - }, - cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { - if (grid.getGridColumns()[cellIndex].getId() == 'buttonEditMacromolecule') { - _this.edit(record.data.macromoleculeId); - } - if (grid.getGridColumns()[cellIndex].getId() == 'buttonRemoveMacromolecule') { - BUI.showBetaWarning(); - } - if (grid.getGridColumns()[cellIndex].getId() == 'btnResultVisible') { - window.location = BUI.getMacromoleculeResultsURL(record.data.macromoleculeId); - } + itemcontextmenu : function(view, rec, node, index, e) { + e.stopEvent(); + contextMenu.showAt(e.getXY()); + return false; } } + }, + selModel : { + mode : 'SINGLE' } }); - /** Adding the tbar **/ - if (this.tbar) { - this.grid.addDocked({ - xtype : 'toolbar', - items : this.getTbar() - }); - } return this.grid; }; -ResultsAssemblyGrid.prototype.input = function(targetId) { - return { - data : DATADOC.getData_3367(), - proposal : DATADOC.getProposal_3367() - }; +AdditiveGrid.prototype.input = function() { }; -ResultsAssemblyGrid.prototype.test = function(targetId) { - var grid = new ResultsAssemblyGrid({ - height : 600, - searchBar : false, - tbar : false, - btnResultVisible : true, - width : 1000 - }); - BIOSAXS.proposal = new Proposal(grid.input().proposal); - var panel = grid.getPanel(grid.input().data); +AdditiveGrid.prototype.test = function(targetId) { + var grid = new AdditiveGrid(); + var panel = grid.getPanel([]); panel.render(targetId); }; +/** AnalysisGrid **/ +function AnalysisGrid(args) { + var _this = this; -function RigidModelGrid(args) { - this.height = null; - this.width = null; this.id = BUI.id(); + if (Ext.get("mainPanel")){ + this.width = Ext.get("mainPanel").getWidth(); + } + else{ + this.width = this.maxWidth; + } + this.maxWidth = 1500; + + /** Visibles **/ + this.isGuinierTabVisible = true; + this.isGnomTabVisible = true; + this.isPorodTabVisible = true; + this.isScatteringPlotVisible = true; + this.isAbinitioTabVisible = true; + this.showButtonsVisible = true; + this.isI0Visible = true; + + this.margin = null; + this.grouped = true; + + this.hideNulls = false; + this.decimals = 4; + this.height = null; + this.sorters = [ { + property : 'conc', + direction : 'ASC' + } ]; if (args != null) { + if (args.grouped != null) { + this.grouped = args.grouped; + } + + if (args.showButtonsVisible != null) { + this.showButtonsVisible = args.showButtonsVisible; + } + if (args.isI0Visible != null) { + this.isI0Visible = args.isI0Visible; + } + if (args.sorters != null) { + this.sorters = args.sorters; + } + if (args.isScatteringPlotVisible != null) { + this.isScatteringPlotVisible = args.isScatteringPlotVisible; + } + if (args.isGuinierTabVisible != null) { + this.isGuinierTabVisible = args.isGuinierTabVisible; + } + if (args.isGnomTabVisible != null) { + this.isGnomTabVisible = args.isGnomTabVisible; + } + if (args.isPorodTabVisible != null) { + this.isPorodTabVisible = args.isPorodTabVisible; + } + if (args.hideNulls != null) { + this.hideNulls = args.hideNulls; + } if (args.height != null) { this.height = args.height; + } + if (args.width != null) { + this.width = args.width; + } + if (args.maxWidth != null) { + this.maxWidth = args.maxWidth; + } + } +} - if (args.width != null) { - this.width = args.width; +AnalysisGrid.prototype.refresh = function(data, args) { + this.store.loadData(this._prepareData(data), false); + if (args != null){ + if (args.experiment != null){ + this.experiment = args.experiment; + } + } +}; + +AnalysisGrid.prototype._getPorod = function() { + return { + text : 'Porod', + name : 'Porod', + columns : [ + { + text : 'Volume', + dataIndex : 'volumeEdna', + width : 75, + sortable : true, + hidden : !this.isPorodTabVisible, + renderer : function(val, y, sample) { + if (sample.raw.volume != null) + return BUI.formatValuesUnits(sample.raw.volume, '') + " nm3"; + } + }, + { + text : 'MM Vol. est.', + dataIndex : 'volumeEdna', + tooltip : '[Volume/2 - Volume/1.5] (Guinier)', + sortable : true, + width : 95, + hidden : !this.isPorodTabVisible, + renderer : function(val, y, sample) { + if (sample.raw.volume != null) + return Number(sample.raw.volume / 2).toFixed(1) + " - " + Number(sample.raw.volume / 1.5).toFixed(1)+ "kD"; + } + } ] + }; +}; + + +AnalysisGrid.prototype._getFramesColumn = function() { + var _this = this; + return { + text : 'Frames (Averaged/Total)', + dataIndex : 'datacollection', + name : 'datacollection', + sortable : true, + width : 150, + renderer : function(dataCollections, y, data) { + /** Bug of Webservices: frames count were swapped with frames averages **/ + if (data.raw.bufferAfterFramesCount){ + if (data.raw.bufferAfterFramesMerged){ + if (parseInt(data.raw.bufferAfterFramesMerged) > parseInt(data.raw.bufferAfterFramesCount)){ + var aux = parseInt(data.raw.bufferAfterFramesCount); + data.raw.bufferAfterFramesCount= data.raw.bufferAfterFramesMerged; + data.raw.bufferAfterFramesMerged = aux; + } + } + } + + if (data.raw.bufferBeforeFramesCount){ + if (data.raw.bufferBeforeFramesMerged){ + if (parseInt(data.raw.bufferBeforeFramesMerged) > parseInt(data.raw.bufferBeforeFramesCount)){ + var aux = parseInt(data.raw.bufferBeforeFramesCount); + data.raw.bufferBeforeFramesCount= data.raw.bufferBeforeFramesMerged; + data.raw.bufferBeforeFramesMerged = aux; + } + } + + } + + var bufferAcronym = data.raw.bufferAcronym; + var macromoleculeAcronym = data.raw.macromoleculeAcronym; + var bbmerges = data.raw.bufferBeforeFramesMerged; + var molmerges = data.raw.framesMerge; + var bamerges = data.raw.bufferAfterFramesMerged; + var totalframes = data.raw.framesCount; + var bufferId = data.raw.bufferId; + var macromoleculeId = data.raw.macromoleculeId; + var macromoleculeColor = null; + if (_this.experiment != null){ + macromoleculeColor = _this.experiment.macromoleculeColors[data.raw.macromoleculeId]; } - } - } - - this.onSelected = new Event(this); -} - - -RigidModelGrid.prototype.refresh = function(subtractions) { - var data = []; - if (subtractions != null){ - if (subtractions.length > 0){ - for (j in subtractions){ - var subtraction = subtractions[j]; - if (subtraction.rigidBodyModeling3VOs != null){ - for (i in subtraction.rigidBodyModeling3VOs){ - data.push(subtraction.rigidBodyModeling3VOs[i]); + + /** BUG in the database to be fixed **/ + try{ + if (totalframes != null){ + if(molmerges != null){ + if (parseFloat(totalframes) < parseFloat(molmerges)){ + var aux = totalframes; + totalframes = molmerges; + molmerges = aux; + } } } } + catch(e){ + + } + + return BUI.getHTMLTableForFrameAveraged(bufferAcronym, + macromoleculeAcronym, + bbmerges, + molmerges, + bamerges, + totalframes, + bufferId, + macromoleculeId, + macromoleculeColor); } - } - this.store.loadData(data); + }; }; -RigidModelGrid.prototype.getPanel = function() { +AnalysisGrid.prototype._getColumns = function() { var _this = this; - this.store = Ext.create('Ext.data.Store', { - fields : [ 'symmetry', 'subtractionId', 'subUnitConfigFilePath', 'rigidBodyModelingId', 'rigidBodyModelFilePath', 'logFilePath', 'fitFilePath', 'curveConfigFilePath', - 'crossCorrConfigFilePath', 'contactDescriptionFilePath' ], - data : [] - }); - - this.store.sort([ { - property : 'name', - direction : 'ASC' - } ]); + return [ + { + name : 'groupeField', + dataIndex : 'groupeField', + hidden : true - this.panel = Ext.create('Ext.grid.Panel', { - store : this.store, - width : this.width, - height : this.height, - margin : 10, - deferredRender : false, - columns : [ { - text : 'RBM', - dataIndex : 'rigidBodyModelFilePath', - hidden : false, - flex : 1, - renderer : function(val){ - return BUI.getFileName(val); - } - }, { - text : 'Sub Unit Conf.', - dataIndex : 'subUnitConfigFilePath', + }, + { + "header" : "subtractionId", hidden : true, - flex : 1, - renderer : function(val){ - return BUI.getFileName(val); + "dataIndex" : "subtractionId", + "name" : "subtractionId" + }, + { + header : "Macromolecule", + dataIndex : "macromoleculeAcronym", + name : "macromoleculeAcronym", + renderer : function(val, y, sample) { + return '
' + val + '
' + + BUI.formatValuesUnits(sample.raw.conc, "mg/ml", 10, this.decimals) + '
' + + BUI.formatValuesUnits(sample.raw.exposureTemperature, "C", 10, this.decimals) + '
' } - }, { - text : 'Log', - dataIndex : 'logFilePath', + }, + { + header : "Order", + dataIndex : "priorityLevelId", + name : "priorityLevelId", + hidden : true + }, + { + header : "Code", + dataIndex : "code", + name : "code", + hidden : true + }, + { + header : "Conc.", + dataIndex : "conc", + width : 80, + name : "conc", + type : "number", hidden : true, - flex : 1, - renderer : function(val){ - return BUI.getFileName(val); - } - }, { - text : 'Fit', - dataIndex : 'fitFilePath', - hidden : false, - flex : 1, - renderer : function(val){ - return BUI.getFileName(val); - } - }, { - text : 'Curve Conf.', - dataIndex : 'curveConfigFilePath', - hidden : false, - flex : 1, - renderer : function(val){ - return BUI.getFileName(val); + renderer : function(val, y, sample) { + return BUI.formatValuesUnits(val, "mg/ml", 10, this.decimals); } - }, { - text : 'Cross Corr.', - dataIndex : 'crossCorrConfigFilePath', - hidden : false, - flex : 1, - renderer : function(val){ - return BUI.getFileName(val); + }, + { + text : 'Scattering', + width : 100, + dataIndex : 'subtractionId', + name : 'subtractionId', + hidden : !this.isScatteringPlotVisible, + renderer : function(val, y, sample) { + var url = BUI.getURL() + '&type=scattering&subtractionId=' + sample.raw.subtractionId; + var event = "OnClick= window.open('" + url + "')"; + return ''; } - }, { - text : 'Contact Desc.', - dataIndex : 'contactDescriptionFilePath', + }, + { + text : 'Kratky', + width : 100, + dataIndex : 'subtractionId', hidden : true, - flex : 1, - renderer : function(val){ - return BUI.getFileName(val); + name : 'subtractionId', + renderer : function(val, y, sample) { + var url = BUI.getURL() + '&type=kratky&subtractionId=' + sample.raw.subtractionId; + var event = "OnClick= window.open('" + url + "')"; + return ''; } - } ], - - viewConfig : { - enableTextSelection : true, - preserveScrollOnRefresh : true }, - listeners : { - cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { - - }, - afterrender : function() { + this._getFramesColumn(), + { + text : 'File Name', + dataIndex : 'averageFilePath', + name : 'averageFilePath', + sortable : true, + width : 150, + hidden : true, + renderer : function(dataCollections, y, data) { + return BUI.getHTMLTableForPrefixes(data.raw.bufferBeforeAverageFilePath, data.raw.averageFilePath, + data.raw.bufferAfterAverageFilePath); } - } - }); - return this.panel; -}; + }, - -/** - * shows shipments - * - * @height - * @width - * @minHeight - * @btnEditVisible - */ -function ShipmentGrid(args) { - this.id = BUI.id(); - this.height = 100; - this.width = null; - this.minHeight = null; - this.btnEditVisible = true; + { + text : 'Guinier', + name : 'Guinier', + columns : [ + { + text : 'Rg', + dataIndex : 'rgGuinier', + name : 'rgGuinier', + hidden : !this.isGuinierTabVisible, + width : 75, + tooltip : 'In polymer physics, the radius of gyration is used to describe the dimensions of a polymer chain.', + sortable : true, + renderer : function(val, y, sample) { + if (sample.raw.rgGuinier != null) { + /** Show warning if rgGuinier and rgGnom differ more than 10% **/ + if (Math.abs(sample.raw.rgGuinier - sample.raw.rgGnom) > (sample.raw.rgGuinier * 0.1)) { + return "" + BUI.formatValuesUnits(sample.raw.rgGuinier, "") + ""; - if (args != null) { - if (args.height != null) { - this.height = args.height; - } - if (args.width != null) { - this.width = args.width; - } - if (args.minHeight != null) { - this.minHeight = args.minHeight; - } - if (args.btnEditVisible != null) { - this.btnEditVisible = args.btnEditVisible; - } - } -} + } + return BUI.formatValuesUnits(sample.raw.rgGuinier, "nm", 12, this.decimals); + } + } + }, + { + text : 'Points', + dataIndex : 'points', + sortable : true, + width : 100, + type : 'string', + hidden : !this.isGuinierTabVisible, + renderer : function(val, y, sample) { + if ((sample.raw.firstPointUsed == "") || (sample.raw.firstPointUsed == null)) + return; + return "" + sample.raw.firstPointUsed + " - " + sample.raw.lastPointUsed + " (" + + (sample.raw.lastPointUsed - sample.raw.firstPointUsed) + " )"; + } + }, + { + text : 'Quality', + dataIndex : 'quality', + hidden : !this.isGuinierTabVisible, + tooltip : 'Estimated data quality. 1.0 - means ideal quality, 0.0 - unusable data. In table format it is given in percent (100% - ideal quality, 0% - unusable data). Please note that this estimation is based only on the Guinier interval (very low angles).', + width : 60, + sortable : true, + renderer : function(val, y, sample) { + if (sample.raw.quality != null) { + val = sample.raw.quality; + if ((val != null) && (val != "")) { + return "" + (Number(val) * 100).toFixed(2) + " %"; + } + } + } + }, { + text : 'I(0)', + dataIndex : 'I0', + sortable : true, + hidden : !this.isI0Visible, + tooltip : 'Extrapolated scattering intensity at zero angle I(0) (forward scattering)', + width : 75, + type : 'string', + renderer : function(val, y, sample) { + if (sample.raw.I0 != null) { + return BUI.formatValuesErrorUnitsScientificFormat(sample.raw.I0, sample.raw.i0stdev, ""); + } + } + }, { + text : 'Aggregated', + tooltip : "If aggregation was detected from the slope of the data curve at low angles the value is '1', otherwise '0'.", + dataIndex : 'isagregated', + hidden : true, + width : 75, + renderer : function(val, y, sample) { + if ((sample.raw.isagregated != null)) { + if (val == true) { + return "Yes"; + } else { + return "No"; + } + } + } + }, { + text : 'Guinier', + sortable : true, + dataIndex : 'subtractionId', + type : 'string', + width : 100, + hidden : true, + renderer : function(val, y, sample) { -ShipmentGrid.prototype._getColumns = function() { - var _this = this; - var columns = [ - { - text : 'Name', - dataIndex : 'shippingName', - flex : 1 + if (sample.raw.subtractionId != null) { + var url = BUI.getURL() + '&type=guinier&subtractionId=' + sample.raw.subtractionId; + var event = "OnClick= window.open('" + url + "')"; + return ''; + } + } + } ] }, { - header : 'Type', - dataIndex : 'shippingType', - flex : 1, - hidden : true, - renderer : function(val, comp, record) { - if (val != null) { - return val.toUpperCase(); - } + text : 'Gnom', + name : 'Gnom', - } - }, - { - header : 'Status', - type : 'string', - flex : 1, - hidden : false, - renderer : function(comp, val, record) { - if (record.raw.shippingStatus != null) { - if (new String(record.raw.shippingStatus).toUpperCase() == 'OPENED') { - return "" + new String(record.raw.shippingStatus).toUpperCase() + ""; + columns : [ { + text : 'Rg', + dataIndex : 'rgGnom', + type : 'string', + width : 65, + hidden : !this.isGnomTabVisible, + sortable : true, + renderer : function(val, y, sample) { + /** Show warning if rgGuinier and rgGnom differ more than 10% **/ + if (sample.raw.rgGnom != null) { + if (Math.abs(sample.raw.rgGuinier - sample.raw.rgGnom) > (sample.raw.rgGuinier * 0.1)) { + return "" + BUI.formatValuesUnits(sample.raw.rgGnom, "") + ""; + + } + return BUI.formatValuesUnits(sample.raw.rgGnom, "nm"); } - return "" + new String(record.raw.shippingStatus).toUpperCase() + ""; } - } + }, { + text : 'Total', + dataIndex : 'total', + width : 65, + hidden : !this.isGnomTabVisible, + sortable : true, + renderer : function(val, y, sample) { + if (sample.raw.total != null) + return BUI.formatValuesUnits(sample.raw.total, ''); + } + }, { + text : 'Dmax', + dataIndex : 'dmax', + sortable : true, + width : 75, + hidden : !this.isGnomTabVisible, + renderer : function(val, y, sample) { + if (sample.raw.dmax != null) + return BUI.formatValuesUnits(sample.raw.dmax, "") + " nm"; + } + }, { + text : 'P(r)', + sortable : true, + hidden : true, + width : 100, + dataIndex : 'subtractionId', + type : 'string', + renderer : function(val, y, sample) { + var url = BUI.getURL() + '&type=gnom&subtractionId=' + sample.raw.subtractionId; + var event = "OnClick= window.open('" + url + "')"; + return ''; + } + } ] }, + this._getPorod(), { - text : 'Cases', - flex : 1, - hidden : false, - renderer : function(comp, val, record) { - var shipment = BIOSAXS.proposal.getShipmentById(record.data.shippingId); - var container = ""; - if (shipment.dewarVOs.length > 0) { - container = container + ""; - } else { - return "Empty"; - } - return container + "
" + shipment.dewarVOs.length + "x
"; - } - }, { - header : 'Comments', - dataIndex : 'comments', - flex : 1, - hidden : false - }, { - header : 'Creation Date', - dataIndex : 'creationDate', - hidden : true - } ]; - - if (this.btnEditVisible) { - columns.push({ - id : _this.id + 'buttonEdit', - text : '', - hidden : !_this.btnEditVisible, - width : 100, - sortable : false, - renderer : function(value, metaData, record, rowIndex, colIndex, store) { - return BUI.getGreenButton('EDIT'); - } - }); - } - - columns.push({ - id : _this.id + 'buttonRemove', - text : '', - width : 100, - sortable : false, - renderer : function(value, metaData, record, rowIndex, colIndex, store) { - var shipment = BIOSAXS.proposal.getShipmentById(record.data.shippingId); - if (shipment.dewarVOs.length == 0) { - return BUI.getRedButton('REMOVE'); - } - - } - }); - - return columns; -}; - -ShipmentGrid.prototype._getTopButtons = function() { - var _this = this; - /** Actions buttons **/ - var actions = []; - actions.push(Ext.create('Ext.Action', { - icon : '../images/add.png', - text : 'Add Shipment', - handler : function(widget, event) { - //window.location = BUI.getCreateShipmentURL(); - var _this = this; - - var shipmentForm = new ShipmentForm({ - creationMode : true, - showTitle : false - }); - shipmentForm.onSaved.attach(function(sender, shipment) { - _this.showShipmentTabs(shipment, targetId); - }); - - var window = Ext.create('Ext.window.Window', { - title : 'New Shipment', - height : 600, - width : 800, - layout : 'fit', - items : [ shipmentForm.getPanel() ] - }).show(); - - } - })); - return actions; -}; - -ShipmentGrid.prototype.refresh = function(shippings) { - this.features = shippings; - this.store.loadData(this._prepareData(), false); -}; - -ShipmentGrid.prototype._prepareData = function() { - var data = []; - for ( var i = 0; i < this.features.length; i++) { - data.push(this.features[i]); - } - return data; -}; - -ShipmentGrid.prototype.getPanel = function(shipments) { - this.features = shipments; - return this._renderGrid(); -}; - -ShipmentGrid.prototype.edit = function(shippingId) { - window.location = BUI.getShippingURL(shippingId); -}; - -ShipmentGrid.prototype._getStoreFields = function(data) { - var _this = this; - return [ { - name : 'shippingId' - }, { - name : 'shippingName' - }, { - name : 'shippingStatus' - }, { - name : 'shippingType' - }, { - name : 'creationDate' - }, { - name : 'comments' - } ]; -}; - -ShipmentGrid.prototype._renderGrid = function() { - var _this = this; - - /** Store **/ - var data = this._prepareData(); - this.store = Ext.create('Ext.data.Store', { - fields : this._getStoreFields(data), - autoload : true, - data : data - }); - - this.store.loadData(data, false); + text : 'AbInitio Modeling', + name : 'AbInitio_modeling', - this.grid = Ext.create('Ext.grid.Panel', { - title : "Shipping", - icon : '/ispyb/images/plane.gif', - width : this.width, - minWidth : this.minWidth, - height : this.height, - store : this.store, - columns : this._getColumns(), - viewConfig : { - stripeRows : true, - listeners : { - itemdblclick : function(dataview, record, item, e) { - _this.edit(record.raw.shippingId); + columns : [ + { + text : 'NSD', + dataIndex : 'volumeEdna', + width : 100, + sortable : true, + hidden : true, + renderer : function(val, y, sample) { + var url = BUI.getNSDImageURL(sample.raw.modelListId); + var event = "OnClick= window.open('" + url + "')"; + return ''; + } }, - cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { - if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonEdit') { - _this.edit(record.data.shippingId); + { + text : 'Chi2', + dataIndex : 'volumeEdna', + sortable : true, + hidden : true, + width : 100, + renderer : function(val, y, sample) { + var url = BUI.getCHI2ImageURL(sample.raw.modelListId); + var event = "OnClick= window.open('" + url + "')"; + return ''; } + }, + { + text : 'Pdb', + dataIndex : 'volumeEdna', + tooltip : 'Damaver: The program suite DAMAVER is a set of programs to align ab initio models, select the most typical one and build an averaged model. Dammif : Rapid ab initio shape determination by simulated annealing using a single phase dummy atom model. Dammin :Ab initio shape determination by simulated annealing using a single phase dummy atom model', + sortable : true, + width : 125, + hidden : !this.isAbinitioTabVisible, + renderer : function(val, y, sample) { + var html = new String(); + var split = null; + var url = null; + var file = sample.raw.averagedModel; + if (file != null) { + split = file.split("/"); + if (split.length > 0) { + url = BUI.getPdbURL() + '&type=AVERAGED&abInitioModelId=' + sample.raw.abInitioModelId; + html = html + ''+ split[split.length - 1] + '

'; - if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonRemove') { - var shipment = BIOSAXS.proposal.getShipmentById(record.data.shippingId); - if (shipment.dewarVOs.length == 0) { + } + } - var adapter = new BiosaxsDataAdapter(); - _this.grid.setLoading("ISPyB: Removing shipment"); - adapter.onSuccess.attach(function(sender) { - BIOSAXS.proposal.onInitialized.attach(function(sender) { - _this.refresh(BIOSAXS.proposal.getShipments()); - _this.grid.setLoading(false); - }); - BIOSAXS.proposal.init(); - }); + file = sample.raw.rapidShapeDeterminationModel; + if (file != null) { + split = file.split("/"); + if (split.length > 0) { + url = BUI.getPdbURL() + '&type=RAPIDSHAPEDETERMINATIONMODEL&abInitioModelId=' + sample.raw.abInitioModelId; + html = html + '' + split[split.length - 1] + '

'; + } + } - adapter.onError.attach(function(sender) { - alert("Error"); - _this.grid.setLoading(false); - }); - adapter.removeShipment(record.data.shippingId); + file = sample.raw.shapeDeterminationModel; + if (file != null) { + split = file.split("/"); + if (split.length > 0) { + url = BUI.getPdbURL() + '&type=SHAPEDETERMINATIONMODEL&abInitioModelId=' + sample.raw.abInitioModelId; + html = html + ''+ split[split.length - 1] + ''; + } } + return html; } - } - } - }, - selModel : { - mode : 'SINGLE' - } - }); - - var actions = _this._getTopButtons(); - this.grid.addDocked({ - xtype : 'toolbar', - items : actions - }); - - this.grid.getSelectionModel().on({ - selectionchange : function(sm, selections) { - if (selections.length) { - for ( var i = 0; i < actions.length; i++) { - if (actions[i].enable) { - actions[i].enable(); + }, + { + text : 'Damaver', + dataIndex : 'volumeEdna', + hidden : true, + tooltip : 'Damaver: The program suite DAMAVER is a set of programs to align ab initio models, select the most typical one and build an averaged model. ', + sortable : true, + width : 125, + renderer : function(val, y, sample) { + var file = sample.raw.averagedModel; + if (file != null) { + var split = file.split("/"); + if (split.length > 0) { + var url = BUI.getPdbURL() + '&type=AVERAGED&abInitioModelId=' + sample.raw.abInitioModelId; + return '' + split[split.length - 1]+ ''; + } + } + return sample.raw.averagedModel; } - } - } else { - for ( var i = 0; i < actions.length; i++) { - if (actions[i].alwaysEnabled == false) { - if (actions[i].disable) { - actions[i].disable(); + }, + { + text : 'Dammif', + dataIndex : 'volumeEdna', + hidden : true, + tooltip : 'Dammif : Rapid ab initio shape determination by simulated annealing using a single phase dummy atom model', + sortable : true, + width : 125, + renderer : function(val, y, sample) { + var file = sample.raw.rapidShapeDeterminationModel; + if (file != null) { + var split = file.split("/"); + if (split.length > 0) { + var url = BUI.getPdbURL() + '&type=RAPIDSHAPEDETERMINATIONMODEL&abInitioModelId=' + sample.raw.abInitioModelId; + return '' + split[split.length - 1] + ''; + } } + return sample.raw.averagedModel; } - } - } - } - }); - return this.grid; -}; - -ShipmentGrid.prototype.input = function() { - return { - proposal : new MeasurementGrid().input().proposal, - shippings : DATADOC.getShippings_10() - }; -}; - -ShipmentGrid.prototype.test = function(targetId) { - var shipmentGrid = new ShipmentGrid({ - minHeight : 300, - height : 440 - }); - BIOSAXS.proposal = new Proposal(shipmentGrid.input().proposal); - var panel = shipmentGrid.getPanel(targetId); - panel.render(targetId); - shipmentGrid.refresh(shipmentGrid.input().shippings); -}; - -function SpecimenGrid(args) { - this.id = BUI.id(); - this.height = 500; - this.unitsFontSize = 9; - this.editEnabled = false; - this.isPositionColumnHidden = false; - this.removeBtnEnabled = false; - - this.selectionMode = "MULTI"; - this.updateRowEnabled = false; - this.grouped = true; - this.width = 900; - this.title = 'Specimens'; - - this.margin = "0 0 0 0"; -// this.experimentColorBased = false; - - if (args != null) { - if (args.height != null) { - this.height = args.height; - } - - if (args.showTitle == false) { - this.title = null; - } - - if (args.margin == false) { - this.margin = args.margin; - } - - if (args.grouped == false) { - this.grouped = null; - } - - if (args.width != null) { - this.width = args.width; - } - - - if (args.editEnabled != null) { - this.editEnabled = args.editEnabled; - } - if (args.removeBtnEnabled != null) { - this.removeBtnEnabled = args.removeBtnEnabled; - } - if (args.isPositionColumnHidden != null) { - this.isPositionColumnHidden = args.isPositionColumnHidden; - } - if (args.selectionMode != null) { - this.selectionMode = args.selectionMode; - } - if (args.updateRowEnabled != null) { - this.updateRowEnabled = args.updateRowEnabled; - } - - } - this.onClick = new Event(this); - this.onSelected = new Event(this); - this.onRemoved = new Event(this); - this.onSpecimenChanged = new Event(); -} - -SpecimenGrid.prototype._prepareData = function(experiment) { - var data = []; - - var samples = experiment.getSamples(); - for ( var i = 0; i < samples.length; i++) { - var sample = samples[i]; - if (sample.macromolecule3VO != null) { - sample.macromolecule = sample.macromolecule3VO.acronym; - sample.exposureTemperature = []; - sample.macromoleculeId = sample.macromolecule3VO.macromoleculeId; - } + }, + { + text : 'Dammin', + dataIndex : 'volumeEdna', + hidden : true, + tooltip : 'Dammin :Ab initio shape determination by simulated annealing using a single phase dummy atom model', + sortable : true, + width : 125, + renderer : function(val, y, sample) { + var file = sample.raw.shapeDeterminationModel; + if (file != null) { + var split = file.split("/"); + if (split.length > 0) { + var url = BUI.getPdbURL() + '&type=SHAPEDETERMINATIONMODEL&abInitioModelId=' + sample.raw.abInitioModelId; + return '' + split[split.length - 1]+ ''; + } + } + return sample.raw.averagedModel; + } + } ] + }, { + text : 'Time', + dataIndex : 'substractionCreationTime', + name : 'substractionCreationTime', + hidden : true, + width : 80, + sortable : true, + renderer : function(value, metaData, record, rowIndex, colIndex, store) { + try { + if (record.raw.substractionCreationTime != null) { + return moment(record.raw.substractionCreationTime).format('h:mm:ss a'); + } + } catch (e) { + return "NA"; + } + } + }, { + text : 'Date', + dataIndex : 'substractionCreationTime', + name : 'substractionCreationTime', + hidden : true, + width : 80, + sortable : true, + renderer : function(value, metaData, record, rowIndex, colIndex, store) { + try { + if (record.raw.substractionCreationTime != null) { + return moment(record.raw.substractionCreationTime).format("LL"); + } + } catch (e) { + return "NA"; + } + } + }, + this._getButtons()]; +}; - if (sample.sampleplateposition3VO != null) { - if (sample.sampleplateposition3VO.samplePlateId != null) { - sample.samplePlateId = sample.sampleplateposition3VO.samplePlateId; - sample.rowNumber = sample.sampleplateposition3VO.rowNumber; - sample.columnNumber = sample.sampleplateposition3VO.columnNumber; - if (experiment.getSamplePlateById(sample.sampleplateposition3VO.samplePlateId).plategroup3VO != null) { - sample.plateGroupName = experiment.getSamplePlateById(sample.sampleplateposition3VO.samplePlateId).plategroup3VO.name; - sample.samplePlateName = experiment.getSamplePlateById(sample.sampleplateposition3VO.samplePlateId).name + " [" + sample.plateGroupName + "]"; - sample.slotPositionColumn = experiment.getSamplePlateById(sample.sampleplateposition3VO.samplePlateId).slotPositionColumn; +AnalysisGrid.prototype._getButtons = function() { + return { + id : this.id + 'buttonPlot', + name : 'buttonPlot', + hidden : !this.showButtonsVisible, + width : 110, + sortable : false, + renderer : function(value, metaData, sample, rowIndex, colIndex, store) { + var html = ""; + + if (sample.raw.subtractionId) { + html = html + ""; + if (sample.raw.rapidShapeDeterminationModelId != null) { + html = html + ""; } } - } else { - sample.samplePlateName = "Unallocated Specimens"; + return html + "
" + BUI.getGreenButton('Calibration', { + height : 20, + width : 100 + }) + "
" + BUI.getGreenButton('Primary Data Proc.', { + height : 20, + width : 100 + }) + "
" + BUI.getGreenButton('AbInitio Modeling', { + height : 20, + width : 100 + }) + "
"; } - - /** For grouping, because sencha has not option for multiple grouping I add a field to your store with a convert function that concatenates these two fields and then group by that field.**/ - sample.groupIndex = sample.bufferId + sample.macromoleculeId; - var macromolecule = BIOSAXS.proposal.getMacromoleculeById(sample.macromoleculeId); - - sample.acronym = "Buffers"; - if (macromolecule != null) { - sample.acronym = BIOSAXS.proposal.getMacromoleculeById(sample.macromoleculeId).acronym; + }; +}; +AnalysisGrid.prototype._prepareData = function(data) { + if (this.hideNulls) { + var result = []; + for ( var i = 0; i < data.length; i++) { + if (data[i].subtractionId != null) { + data[i].groupeField = data[i].macromoleculeAcronym + " " + data[i].bufferAcronym; + result.push(data[i]); + } } - - sample.buffer = experiment.getBufferById(sample.bufferId); - - sample.volumeToLoad = experiment.getVolumeToLoadBySampleId(sample.sampleId); - data.push(sample); + return result; + } else { + return data; } - return data; }; -SpecimenGrid.prototype.deselectAll = function() { - this.grid.getSelectionModel().deselectAll(); -}; +AnalysisGrid.prototype.getPanel = function(data) { + var _this = this; + var columns = this._getColumns(); -SpecimenGrid.prototype.selectById = function(specimenId) { - this.grid.getSelectionModel().deselectAll(); - for ( var i = 0; i < this.grid.getStore().data.items.length; i++) { - var item = this.grid.getStore().data.items[i].raw; - if (item.specimenId == specimenId) { - this.grid.getSelectionModel().select(i); - } - } -}; + var fields = JSON.parse(JSON.stringify(columns)); + fields.push({ + name : 'experimentId', + dataIndex : 'experimentId' -SpecimenGrid.prototype.getStore = function() { - return this.store; -}; + }); + this.store = Ext.create('Ext.data.Store', { + fields : fields, + autoload : true, + data : this._prepareData(data), + groupField : 'groupeField' + }); -SpecimenGrid.prototype.getPlugins = function() { - var _this = this; + this.store.sort(this.sorters); - var plugins = []; + var features = []; - if (this.updateRowEnabled) { - plugins.push(Ext.create('Ext.grid.plugin.RowEditing', { - clicksToEdit : 1, + if (this.grouped) { + features.push({ + ftype : 'grouping', + groupHeaderTpl : '{name}', + hideGroupedHeader : false, + startCollapsed : false + }); + } + + this.grid = Ext.create('Ext.grid.Panel', { + style : { + padding : 5 + }, + maxWidth : this.maxWidth, + width : this.width, + height : this.height, + store : this.store, + columns : columns, + resizable : true, + features : features, + viewConfig : { + preserveScrollOnRefresh : true, + stripeRows : true, listeners : { - validateedit : function(grid, e) { - var measurements = []; + cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { - if (e.newValues.bufferId != e.record.raw.bufferId) { - /** If buffer has changed we have to change all the specimens sharing same datacollection **/ - var dataCollections = []; - if (e.record.raw.macromoleculeId == null) { - dataCollections = dataCollections.concat(_this.experiment.getDataCollectionsBySpecimenId(e.record.raw.specimenId)); - } else { - var sampleDataCollections = _this.experiment.getDataCollectionsBySpecimenId(e.record.raw.specimenId); - for ( var i = 0; i < sampleDataCollections.length; i++) { - var sampleDataCollection = sampleDataCollections[i]; - if (sampleDataCollection != null) { - for ( var j = 0; j < sampleDataCollection.measurementtodatacollection3VOs.length; j++) { - var measurementTODc = sampleDataCollection.measurementtodatacollection3VOs[j]; - if (measurementTODc.dataCollectionOrder == 1) { - dataCollections = dataCollections.concat(_this.experiment.getDataCollectionsBySpecimenId(_this.experiment - .getMeasurementById(measurementTODc.measurementId).specimenId)); - } - } - } - } - } - var i = null; - for ( i = 0; i < dataCollections.length; i++) { - var dataCollection = dataCollections[i]; - var specimens = _this.experiment.getSpecimenByDataCollectionId(dataCollection.dataCollectionId); - measurements = measurements.concat(specimens); + if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonPlot') { + if (e.target.defaultValue == 'AbInitio Modeling') { + var url = BUI.getPDBVisualizerURL(record.raw.averagedModelId, record.raw.subtractionId, record.raw.experimentId); + window.open(url, "_blank"); } - for ( i = 0; i < measurements.length; i++) { - var measurement = measurements[i]; - var specimen = _this.experiment.getSpecimenById(measurement.specimenId); - specimen.bufferId = e.newValues.bufferId; - new BiosaxsDataAdapter().saveSpecimen(specimen, _this.experiment); + if (e.target.defaultValue == 'Primary Data Proc.') { + _this._edit(record.raw); } - } - - /** Setting values **/ - e.record.raw.concentration = e.newValues.concentration; - e.record.raw.volume = e.newValues.volume; - /** Position **/ - if (e.record.raw.sampleplateposition3VO != null) { - var samplePlate = _this.experiment.getSamplePlateBySlotPositionColumn(e.newValues.slotPositionColumn); - if (samplePlate != null) { - e.record.raw.sampleplateposition3VO = { - columnNumber : e.newValues.columnNumber, - rowNumber : e.newValues.rowNumber, - samplePlateId : samplePlate.samplePlateId - }; - } - } else { - if (e.newValues.slotPositionColumn != null) { - var samplePlate = _this.experiment.getSamplePlateBySlotPositionColumn(e.newValues.slotPositionColumn); - if (samplePlate != null) { - e.record.raw.sampleplateposition3VO = { - columnNumber : e.newValues.columnNumber, - rowNumber : e.newValues.rowNumber, - samplePlateId : samplePlate.samplePlateId - }; - } + if (e.target.defaultValue == 'Calibration') { + _this._showCalibration(record.raw); } } - - var macromoleculeId = e.record.data.macromoleculeId; - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, specimen) { - /** Because macromolecule3VO is fecthed LAZY **/ - if (macromoleculeId != null) { - specimen.macromolecule3VO = BIOSAXS.proposal.getMacromoleculeById(macromoleculeId); - } - _this.onSpecimenChanged.notify(specimen); - _this.grid.setLoading(false); - }); - adapter.onError.attach(function() { - alert("Error"); - _this.grid.setLoading(false); - }); - _this.grid.setLoading(); - adapter.saveSpecimen(e.record.raw, _this.experiment); } } - })); - } - return plugins; + }, + selModel : { + mode : 'SINGLE' + } + }); + + return this.grid; + +}; + +AnalysisGrid.prototype._openVisualizarBySubstractionId = function(record) { + //experimentId, subtractionId) { + var experimentId = record.experimentId; + var subtractionId = record.subtractionId; + + var _this = this; + var adapter = new BiosaxsDataAdapter(); + this.subtractionId = subtractionId; + + adapter.onSuccess.attach(function(sender, data) { + var experiment = new Experiment(data); + var experimentList = new ExperimentList([ experiment ]); + var subtraction = experiment.getSubtractionById(_this.subtractionId); + _this.grid.setLoading(false); + _this._openVisualizer(experimentList, subtraction, record); + }); + this.grid.setLoading("ISPyB: Fetching experiment"); + adapter.getExperimentById(experimentId, "MEDIUM"); }; -SpecimenGrid.prototype._getRowCombo = function() { - var data = []; - for ( var i = 1; i <= 8; i++) { - data.push({ - rowNumber : i, - name : BUI.getSamplePlateLetters()[i - 1] +AnalysisGrid.prototype._edit = function(record) { + var experimentId = record.experimentId; + var _this = this; + + if (BIOSAXS.proposal.macromolecules == null) { + this.grid.setLoading("ISPyB: Fetching proposal"); + BIOSAXS.proposal.onInitialized.attach(function(sender, data) { + _this._openVisualizarBySubstractionId(record); }); + BIOSAXS.proposal.init(); + } else { + this._openVisualizarBySubstractionId(record); } +}; - var positionsStore = Ext.create('Ext.data.Store', { - fields : [ 'rowNumber', 'name' ], - data : data - }); - return Ext.create('Ext.form.ComboBox', { - store : positionsStore, - queryMode : 'local', - displayField : 'name', - valueField : 'rowNumber' +AnalysisGrid.prototype._showCalibration = function(row) { + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, data) { + Ext.create('Ext.window.Window', { + title : 'Calibration', + width : 900, + resizable : true, + height : 400, + modal : true, + frame : false, + draggable : true, + closable : true, + autoscroll : true, + paddin : 5, + layout : { + type : 'vbox', + align : 'stretch' + }, + items : [ new AnalysisGrid({ + isGuinierTabVisible : false, + isGnomTabVisible : false, + isPorodTabVisible : false, + isAbinitioTabVisible : false, + isI0Visible : true, + showButtonsVisible : false, + height : 350, + sorters : [ { + property : 'experimentId', + direction : 'DESC' + } ] + }).getPanel(data) ] + }).show(); }); + adapter.getAnalysisCalibrationByProposalId(); }; -SpecimenGrid.prototype._getColumnCombo = function() { - var data = []; - for ( var i = 1; i <= 12; i++) { - data.push({ - columnNumber : i - }); +AnalysisGrid.prototype._openVisualizer = function(experimentList, subtraction, record) { + var merges = experimentList.getMergesByDataCollectionId(subtraction.dataCollectionId); + var mergeIdList = []; + for ( var i = 0; i < merges.length; i++) { + mergeIdList.push(merges[i].mergeId); } - var positionsStore = Ext.create('Ext.data.Store', { - fields : [ 'columnNumber' ], - data : data + var dataAdapter = new BiosaxsDataAdapter(); + dataAdapter.onSuccess.attach(function(sender, merges) { + var dataCollectionCurveVisualizer = new DataCollectionCurveVisualizer(); + dataCollectionCurveVisualizer.draw(); + dataCollectionCurveVisualizer.refresh(merges, experimentList, subtraction.dataCollectionId, record); }); - return Ext.create('Ext.form.ComboBox', { - store : positionsStore, - queryMode : 'local', - displayField : 'columnNumber', - valueField : 'columnNumber' + dataAdapter.onError.attach(function(sender, error) { }); + + dataAdapter.getMergesByIdsList(mergeIdList); }; -SpecimenGrid.prototype._getSlotColumBombo = function() { - if (this.experiment){ - var length = this.experiment.getSamplePlates().length; - - var data = []; - for ( var i = 1; i <= length; i++) { - data.push({ - slotPositionColumn : i - }); - } +AnalysisGrid.prototype.input = function() { + return { + data : DATADOC.getData_3367().slice(20, 30),//[{"total":"0.447505552082","bufferBeforeMeasurementId":22150,"sampleMergeId":12373,"averagedModelId":43636,"I0":"32.50776","proposalCode":"OPD","averagedModel":"/data/pyarch/bm29/opd29/1137/1d/damaver.pdb","bufferAfterMergeId":12374,"framesCount":"10","bufferBeforeMergeId":12372,"averageFilePath":" /data/pyarch/bm29/opd29/1137/1d/MG386_016_ave.dat","bufferAfterMeasurementId":22152,"rgGuinier":"2.96883","subtractedFilePath":" /data/pyarch/bm29/opd29/1137/1d/MG386_016_sub.dat","firstPointUsed":"30","chi2RgFilePath":"/data/pyarch/bm29/opd29/1137/1d/chi2_R.png","abInitioModelId":272,"macromoleculeId":112,"code":"_20.0_1.25","transmission":"100.0","timeStart":"2013-07-17 17:10:25.743734","bufferAcronym":"MES","quality":"0.87091","shapeDeterminationModelId":43638,"expermientComments":"[BsxCube] Generated from BsxCube","bufferId":707,"isagregated":"False","dmax":"10.390905","bufferBeforeAverageFilePath":" /data/pyarch/bm29/opd29/1137/1d/MG386_015_ave.dat","exposureTemperature":"20.0","subtractionId":5165,"conc":"1.25","rapidShapeDeterminationModel":"/data/pyarch/bm29/opd29/1137/1d/damfilt.pdb","experimentCreationDate":"Jul 17, 2013 5:08:37 PM","lastPointUsed":"92","modelListId":272,"framesMerge":"10","rapidShapeDeterminationModelId":43637,"bufferAfterAverageFilePath":" /data/pyarch/bm29/opd29/1137/1d/MG386_017_ave.dat","nsdFilePath":"/data/pyarch/bm29/opd29/1137/1d/nsd.png","bufferAfterFramesMerged":"10","experimentId":1137,"macromoleculeAcronym":"MG386","i0stdev":"0.05726128","sampleMeasurementId":22151,"measurementComments":"[1] MES","priorityLevelId":2,"bufferBeforeFramesMerged":"10","substractionCreationTime":"Jul 17, 2013 5:12:32 PM","proposalNumber":"29","rgGnom":"3.05242714339","volume":"44.1406","shapeDeterminationModel":"/data/pyarch/bm29/opd29/1137/1d/dammin.pdb","comments":null},{"total":"0.582641941147","bufferBeforeMeasurementId":22152,"sampleMergeId":12375,"averagedModelId":43809,"I0":"31.4366153846","proposalCode":"OPD","averagedModel":"/data/pyarch/bm29/opd29/1137/1d/damaver.pdb","bufferAfterMergeId":12376,"framesCount":"10","bufferBeforeMergeId":12374,"averageFilePath":" /data/pyarch/bm29/opd29/1137/1d/MG386_018_ave.dat","bufferAfterMeasurementId":22155,"rgGuinier":"2.95541","subtractedFilePath":" /data/pyarch/bm29/opd29/1137/1d/MG386_018_sub.dat","firstPointUsed":"21","chi2RgFilePath":"/data/pyarch/bm29/opd29/1137/1d/chi2_R.png","abInitioModelId":273,"macromoleculeId":112,"code":"_20.0_0.65000000000000002","transmission":"100.0","timeStart":"2013-07-17 17:12:55.837462","bufferAcronym":"MES","quality":"0.870164","shapeDeterminationModelId":43811,"expermientComments":"[BsxCube] Generated from BsxCube","bufferId":707,"isagregated":"False","dmax":"10.343935","bufferBeforeAverageFilePath":" /data/pyarch/bm29/opd29/1137/1d/MG386_017_ave.dat","exposureTemperature":"20.0","subtractionId":5166,"conc":"0.65000000000000002","rapidShapeDeterminationModel":"/data/pyarch/bm29/opd29/1137/1d/damfilt.pdb","experimentCreationDate":"Jul 17, 2013 5:08:37 PM","lastPointUsed":"80","modelListId":273,"framesMerge":"10","rapidShapeDeterminationModelId":43810,"bufferAfterAverageFilePath":" /data/pyarch/bm29/opd29/1137/1d/MG386_019_ave.dat","nsdFilePath":"/data/pyarch/bm29/opd29/1137/1d/nsd.png","bufferAfterFramesMerged":"10","experimentId":1137,"macromoleculeAcronym":"MG386","i0stdev":"0.100250461538","sampleMeasurementId":22154,"measurementComments":"[2] MES","priorityLevelId":4,"bufferBeforeFramesMerged":"10","substractionCreationTime":"Jul 17, 2013 5:15:02 PM","proposalNumber":"29","rgGnom":"2.9963404542","volume":"42.2255","shapeDeterminationModel":"/data/pyarch/bm29/opd29/1137/1d/dammin.pdb","comments":null}], + proposal : new ResultsAssemblyGrid().input().proposal + }; +}; + +AnalysisGrid.prototype.test = function(targetId) { + var analysisGrid = new AnalysisGrid(); + BIOSAXS.proposal = new Proposal(analysisGrid.input().proposal); + var panel = analysisGrid.getPanel(analysisGrid.input().data); + panel.render(targetId); +}; + + +function HPLCAnalysisGrid(args) { + AnalysisGrid.prototype.constructor.call(this, args); +} - var positionsStore = Ext.create('Ext.data.Store', { - fields : [ 'slotPositionColumn' ], - data : data - }); +HPLCAnalysisGrid.prototype._edit = AnalysisGrid.prototype._edit; +HPLCAnalysisGrid.prototype._getColumns = AnalysisGrid.prototype._getColumns; +HPLCAnalysisGrid.prototype._openVisualizarBySubstractionId = AnalysisGrid.prototype._openVisualizarBySubstractionId; +HPLCAnalysisGrid.prototype._openVisualizer = AnalysisGrid.prototype._openVisualizer; +HPLCAnalysisGrid.prototype._prepareData = AnalysisGrid.prototype._prepareData; +HPLCAnalysisGrid.prototype._showCalibration = AnalysisGrid.prototype._showCalibration; +HPLCAnalysisGrid.prototype.getPanel = AnalysisGrid.prototype.getPanel; +HPLCAnalysisGrid.prototype.input = AnalysisGrid.prototype.input; +HPLCAnalysisGrid.prototype.test = AnalysisGrid.prototype.test; +HPLCAnalysisGrid.prototype.refresh = AnalysisGrid.prototype.refresh; +HPLCAnalysisGrid.prototype._getPorod = AnalysisGrid.prototype._getPorod; + +HPLCAnalysisGrid.prototype._getButtons = function() { + return { + id : this.id + 'buttonPlot', + name : 'buttonPlot', + hidden : !this.showButtonsVisible, + width : 110, + sortable : false, + renderer : function(value, metaData, sample, rowIndex, colIndex, store) { + var html = ""; - return Ext.create('Ext.form.ComboBox', { - store : positionsStore, - queryMode : 'local', - displayField : 'slotPositionColumn', - valueField : 'slotPositionColumn' - }); + if (sample.raw.subtractionId) { + if (sample.raw.rapidShapeDeterminationModelId != null) { + html = html + ""; + } + } + return html + "
" + BUI.getGreenButton('AbInitio Modeling', { + height : 20, + width : 100 + }) + "
"; + } + }; +}; + +HPLCAnalysisGrid.prototype._getFramesColumn = function() { + return { + text : 'Frames', + dataIndex : 'datacollection', + name : 'datacollection', + sortable : true, + width : 150, + renderer : function(dataCollections, y, data) { + molmerges = data.raw.framesMerge; + totalframes = data.raw.framesCount; + return molmerges +" - "+ totalframes; + } + }; +}; + + + + +/** + * Rigid body grid to show PDB, symmetry and multiplicity + * + * + * #onUploadFile click on upload file + */ +function AprioriRigidBodyGrid(args) { + + this.height = 250; + this.btnEditVisible = true; + this.btnRemoveVisible = true; + + if (args != null) { + if (args.height != null) { + this.height = args.height; + } + if (args.btnEditVisible != null) { + this.btnEditVisible = args.btnEditVisible; + } + if (args.btnRemoveVisible != null) { + this.btnRemoveVisible = args.btnRemoveVisible; + } + } + + /** Events **/ + this.onUploadFile = new Event(this); + this.onRemove = new Event(this); +} + +AprioriRigidBodyGrid.prototype._getColumns = function() { +}; + +AprioriRigidBodyGrid.prototype._getTopButtons = function() { + var _this = this; + /** Actions buttons **/ + var actions = []; + + /** ADD BUTTON **/ + actions.push(Ext.create('Ext.Action', { + icon : '../images/add.png', + text : 'Add', + disabled : false, + handler : function(widget, event) { + _this.onAddButtonClicked.notify(); + } + })); + + return actions; +}; + +AprioriRigidBodyGrid.prototype.refresh = function(macromolecule) { + this.macromolecule = macromolecule; + if (macromolecule != null){ + this.pdbStore.loadData(macromolecule.structure3VOs); + } +}; + +AprioriRigidBodyGrid.prototype._prepareData = function() { + var data = []; + for ( var i = 0; i < this.features.length; i++) { + data.push(this.features[i]); + } + return data; +}; + +AprioriRigidBodyGrid.prototype._getPlugins = function() { + var _this = this; + var plugins = []; + plugins.push(Ext.create('Ext.grid.plugin.RowEditing', { + clicksToEdit : 1, + listeners : { + validateedit : function(grid, e) { + /** Comments are always updatable* */ + e.record.raw.symmetry = e.newValues.symmetry; + e.record.raw.multiplicity = e.newValues.multiplicity; + + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, proposal) { + BIOSAXS.proposal.setItems(proposal); + _this.refresh(BIOSAXS.proposal.getMacromoleculeById(_this.macromolecule.macromoleculeId)); + _this.panel.setLoading(false); + }); + adapter.onError.attach(function() { + alert("Error"); + }); + + _this.panel.setLoading(); + adapter.saveStructure(e.record.raw); + } + } + })); + return plugins; +}; + +AprioriRigidBodyGrid.prototype.getPanel = function() { + var _this = this; + + this.pdbStore = Ext.create('Ext.data.Store', { + fields : [ 'filePath', 'structureId', 'structureType', 'symmetry', 'structureId', 'name', 'multiplicity' ], + groupField : 'structureType', + sorters : { + property : 'structureId', + direction : 'DESC' + } + }); + + var groupingFeature = Ext.create('Ext.grid.feature.Grouping', { + groupHeaderTpl : Ext.create('Ext.XTemplate', + "
{name:this.formatName}
", { + formatName : function(name) { + return name; + } + }), + hideGroupedHeader : true, + startCollapsed : false + }); + + this.panel = Ext.create('Ext.grid.Panel', { + margin : "15 10 0 10", + height : this.height, + store : this.pdbStore, + plugins : _this._getPlugins(), + tbar : [ { + text : 'Add Modeling Option (PDB)', + icon : '../images/add.png', + handler : function() { + _this.onUploadFile.notify('PDB', 'Upload PDB File'); + } + } + + ], + columns : [ + { + text : "structureId", + flex : 0.2, + hidden : true, + dataIndex : 'structureId', + sortable : true + }, + { + text : "File", + flex : 0.5, + dataIndex : 'filePath', + sortable : true, + hidden : true + }, + { + text : "PDB", + flex : 0.4, + dataIndex : 'name', + sortable : true + }, + { + text : "Symmetry", + flex : 0.2, + dataIndex : 'symmetry', + sortable : true, + editor : { + xtype : 'combobox', + typeAhead : true, + triggerAction : 'all', + selectOnTab : true, + store : [ [ "P1", "P1" ], [ "P2", "P2" ], [ "P3", "P3" ], [ "P4", "P4" ], [ "P5", "P5" ], [ "P6", "P6" ], [ "P32", "P32" ], [ "P42", "P42" ], + [ "P52", "P52" ], [ "P62", "P62" ], [ "P222", "P222" ] ], + } + }, { + text : "Multiplicity", + flex : 0.2, + dataIndex : 'multiplicity', + sortable : true, + editor : { + xtype : 'textfield' + } + + }, { + text : "Subunit", + flex : 0.2, + dataIndex : 'isSubunit', + sortable : true, + hidden : true + }, { + text : "Type", + flex : 0.2, + dataIndex : 'structureType', + sortable : true, + hidden : true + }, + + { + id : this.id + 'REMOVE', + flex : 0.2, + sortable : false, + renderer : function(value, metaData, record, rowIndex, colIndex, store) { + return BUI.getRedButton('REMOVE'); + } + }, ], + + listeners : { + itemdblclick : function(dataview, record, item, e) { + _this._editExperiment(record.raw.experimentId); + }, + cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + + if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'REMOVE') { + var dataAdapter = new BiosaxsDataAdapter(); + dataAdapter.onSuccess.attach(function() { + _this.panel.setLoading(false); + _this.onRemove.notify(); + }); + _this.panel.setLoading("Removing PDB file"); + dataAdapter.removeStructure(record.data.structureId); + } + + } + }, + viewConfig : { + getRowClass : function(record, rowIdx, params, store) { + if (record.raw.isSubunit != null) { + return "blue-row"; + } + } + } + }); + + return this.panel; +}; + + + + +AprioriRigidBodyGrid.prototype.input = function() { + return { + proposal : DATADOC.getProposal_10(), + dewars : DATADOC.getDewars_10() + + }; +}; + +AprioriRigidBodyGrid.prototype.test = function(targetId) { + var AprioriRigidBodyGrid = new AprioriRigidBodyGrid({ + height : 150 + }); + BIOSAXS.proposal = new Proposal(AprioriRigidBodyGrid.input().proposal); + var panel = AprioriRigidBodyGrid.getPanel(AprioriRigidBodyGrid.input().dewars); + panel.render(targetId); + +}; + +/** + * It shows buffer grid with a top bar with "Add" button + * + * @height + * @searchBar + * @collapsed + * @width + */ +function BufferGrid(args) { + this.height = 500; + this.searchBar = false; + this.tbar = false; + this.collapsed = false; + this.id = BUI.id(); + + if (args != null) { + if (args.height != null) { + this.height = args.height; + } + if (args.searchBar != null) { + this.searchBar = args.searchBar; + } + + if (args.tbar != null) { + this.tbar = args.tbar; + } + + if (args.collapsed != null) { + this.collapsed = args.collapsed; + } + + if (args.width != null) { + this.width = args.width; + } } +} + +BufferGrid.prototype._edit = function(bufferId) { + var _this = this; + var window = new BufferWindow(); + window.onSuccess.attach(function(sender, proposal) { + _this.store.loadData(BIOSAXS.proposal.getBuffers()); + }); + window.draw(BIOSAXS.proposal.getBufferById(bufferId)); }; -SpecimenGrid.prototype.getPanelByExperiment = function(experiment) { - this.experiment = experiment; - var data = this._prepareData(experiment); - return this.getPanel(data); +BufferGrid.prototype.refresh = function(buffers) { + this.store.loadData(this._prepareData(buffers), false); }; -SpecimenGrid.prototype.refresh = function(experiment) { - this.experiment = experiment; - var data = this._prepareData(experiment); - this.store.loadData(data); +BufferGrid.prototype._prepareData = function(buffers) { + return buffers; }; -SpecimenGrid.prototype.getPanel = function() { +BufferGrid.prototype._getTbar = function() { var _this = this; - this.store = Ext.create('Ext.data.Store', { - fields : [ - 'buffer', 'bufferId', 'code', 'macromolecule', 'acronym', 'macromoleculeId', 'concentration', 'volume', 'samplePlateId', - 'slotPositionColumn', 'rowNumber', 'columnNumber', 'groupIndex' ], - data : [], - groupField : 'acronym' - }); - this.store.sort([ { - property : 'concentration', - direction : 'ASC' - }, { - property : 'buffer', - direction : 'ASC' - } ]); + var actions = []; - var selModel = Ext.create('Ext.selection.RowModel', { - allowDeselect : true, - mode : this.selectionMode, - listeners : { - selectionchange : function(sm, selections) { - var selected = []; - for ( var i = 0; i < selections.length; i++) { - selected.push(selections[i].raw); - } - _this.onSelected.notify(selected); - } + actions.push(Ext.create('Ext.Action', { + icon : '../images/add.png', + text : 'Add buffer', + disabled : false, + handler : function(widget, event) { + var window = new BufferWindow(); + window.onSuccess.attach(function(sender) { + _this.store.loadData(BIOSAXS.proposal.getBuffers()); + }); + window.draw({}); } - }); - - var features = []; - - if (this.grouped) { - features.push({ - ftype : 'grouping', - groupHeaderTpl : '{name}', - hideGroupedHeader : false, - startCollapsed : false, - id : 'myGroupedStore' - }); - } + })); + return actions; +}; - this.grid = Ext.create( - 'Ext.grid.Panel', - { - title : this.title, - height : this.height, - width : this.width, - selModel : selModel, - store : this.store, - features : features, - margin : this.margin, - plugins : this.getPlugins(), - columns : [ - { - text : '', - dataIndex : 'macromolecule', - width : 20, - renderer : function(val, y, sample) { - var macromoleculeId = null; - if (sample.raw.macromolecule3VO != null) { - macromoleculeId = sample.raw.macromolecule3VO.macromoleculeId; - } - else{ - macromoleculeId = sample.raw.macromoleculeId; - } - - if (macromoleculeId == null) return; - return BUI.getRectangleColorDIV(_this.experiment.macromoleculeColors[macromoleculeId], 10, 10); - } - }, - { - text : 'Macromolecule', - dataIndex : 'macromolecule', - width : 100 - }, - { - text : '', - dataIndex : 'buffer', - width : 20, - renderer : function(val, y, sample) { - var color = "black"; - if (sample.raw.bufferId != null) { - if (_this.experiment.getDataCollectionsBySpecimenId(sample.raw.specimenId)[0] != null){ - color = _this.experiment.getSpecimenColorByBufferId(_this.experiment.getMeasurementById(_this.experiment.getDataCollectionsBySpecimenId(sample.raw.specimenId)[0].measurementtodatacollection3VOs[0].measurementId).specimenId); - } - return BUI.getRectangleColorDIV(color, 10, 10); - } - } - }, { - text : 'Buffer', - dataIndex : 'bufferId', - width : 140, - renderer : function(val, y, sample) { - if (sample.raw.bufferId != null) { - return BIOSAXS.proposal.getBufferById(val).acronym; - } - }, - editor : BIOSAXS_COMBOMANAGER.getComboBuffers(BIOSAXS.proposal.getBuffers(), { - noLabel : true, - width : 300 - }) - }, { - text : 'Conc.', - dataIndex : 'concentration', - width : 100, - editor : { - allowBlank : false - }, - renderer : function(val, meta, sample) { - if (isNaN(val)) { - meta.tdCls = 'yellow-cell'; - return val; - } else { - if (val != 0) { - return BUI.formatValuesUnits(val, 'mg/ml', { - fontSize : 16, - decimals : 3, - unitsFontSize : this.unitsFontSize - }); - } else { - return; - } - } - } - }, { - text : 'Vol. Well', - dataIndex : 'volume', - width : 70, - editor : { - allowBlank : true - }, - renderer : function(val, y, sample) { - return BUI.formatValuesUnits(sample.data.volume, 'µl', { - fontSize : 12, - decimals : 2, - unitsFontSize : this.unitsFontSize - }); - } - }, { - text : 'Position', - hidden : true, - flex : 1, - renderer : function(val, y, sample) { - return BUI.getSamplePositionHTML(sample.raw, _this.experiment); - } - }, { - text : 'samplePlateId', - dataIndex : 'samplePlateId', - hidden : true - }, { - text : 'Plate', - hidden : this.isPositionColumnHidden, - dataIndex : 'slotPositionColumn', - editor : _this._getSlotColumBombo(), - flex : 1, - renderer : function(val, meta, sample) { - if ((val != null) & (val != "")) { - return val; - } else { - meta.tdCls = 'yellow-cell'; - } - } - }, { - text : 'Row', - hidden : this.isPositionColumnHidden, - dataIndex : 'rowNumber', - editor : this._getRowCombo(), - flex : 1, - renderer : function(val, meta, sample) { - if ((val != null) && (val != "")) { - return BUI.getSamplePlateLetters()[val - 1]; - } else { - meta.tdCls = 'yellow-cell'; - } - } - }, { - text : 'Well', - hidden : this.isPositionColumnHidden, - dataIndex : 'columnNumber', - editor : this._getColumnCombo(), - flex : 1, - renderer : function(val, meta, sample) { - if ((val != null) && (val != "")) { - return val; - } else { - meta.tdCls = 'yellow-cell'; - } - } - }, { - id : _this.id + 'buttonEditSample', - text : 'Edit', - width : 80, - sortable : false, - hidden : !_this.editEnabled, - renderer : function(value, metaData, record, rowIndex, colIndex, store) { - if (_this.editEnabled) { - return BUI.getGreenButton('EDIT'); - } - } - }, { - id : _this.id + 'buttonRemoveSample', - text : '', - hidden : !_this.removeBtnEnabled, - width : 100, - sortable : false, - renderer : function(value, metaData, record, rowIndex, colIndex, store) { - if (_this.removeBtnEnabled) { - return BUI.getRedButton('REMOVE'); - } - } - } +BufferGrid.prototype.getPanel = function(buffers) { + var _this = this; - ], - viewConfig : { - preserveScrollOnRefresh : true, - stripeRows : true, - getRowClass : function(record) { - var specimens = _this.experiment.getSampleByPosition(record.data.samplePlateId, record.data.rowNumber, - record.data.columnNumber); - if (specimens.length > 1) { - return 'red-row'; + this.store = Ext.create('Ext.data.Store', { + fields : [ 'bufferId', 'acronym', 'name', 'composition' ], + data : buffers + }); - } - }, - listeners : { - selectionchange : function(grid, selected) { - _this.onClick.notify(record.raw); - }, - cellclick : function(grid, td, cellIndex, record, tr) { - if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonEditSample') { - } - if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonRemoveSample') { - grid.getStore().removeAt(rowIndex); - _this.onRemoved.notify(); - } + this.store.sort('acronym'); - } + var type = 'Ext.grid.Panel'; + if (this.searchBar == true) { + type = 'Ext.ux.LiveSearchGridPanel'; + } - } - } - }); + this.grid = Ext.create(type, { + title : 'Buffers', + collapsible : true, + collapsed : this.collapsed, + store : this.store, + height : this.height, + width : this.width, + columns : [ + /*{ + text : '', + dataIndex : 'bufferId', + width : 20, + renderer : function(val, y, sample) { + return BUI.getRectangleColorDIV(BIOSAXS.proposal.bufferColors[val], 10, 10); + } + },*/ + { + text : 'Acronym', + dataIndex : 'acronym', + flex : 1 + }, { + text : 'Name', + dataIndex : 'name', + flex : 1, + hidden : true + }, { + text : 'Composition', + dataIndex : 'composition', + flex : 1, + hidden : true + }, { + id : _this.id + 'buttonEditBuffer', + width : 80, + sortable : false, + renderer : function(value, metaData, record, rowIndex, colIndex, store) { + return BUI.getGreenButton('EDIT'); + } + }, { + id : _this.id + 'buttonRemoveBuffer', + width : 85, + hidden : true, + sortable : false, + renderer : function(value, metaData, record, rowIndex, colIndex, store) { + return BUI.getRedButton('REMOVE'); + } + } ], + flex : 1, + viewConfig : { + stripeRows : true, + listeners : { + 'celldblclick' : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + _this._edit(record.data.bufferId); + }, + 'cellclick' : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonEditBuffer') { + _this._edit(record.data.bufferId); + } + + if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonRemoveBuffer') { + BUI.showBetaWarning(); + } + } + + } + } + }); + + /** Adding the tbar **/ + if (this.tbar) { + this.grid.addDocked({ + xtype : 'toolbar', + items : this._getTbar() + }); + } return this.grid; }; -SpecimenGrid.prototype.input = function() { - return { - experiment : DATADOC.getExperiment_10(), - proposal : DATADOC.getProposal_10() - }; +BufferGrid.prototype.input = function() { + return new MacromoleculeGrid().input(); }; -SpecimenGrid.prototype.test = function(targetId) { - var specimenGrid = new SpecimenGrid({ - height : 400, - maxHeight : 400, - width : 1000 +BufferGrid.prototype.test = function(targetId) { + var bufferGrid = new BufferGrid({ + width : 800, + height : 350, + collapsed : false, + tbar : true }); - BIOSAXS.proposal = new Proposal(specimenGrid.input().proposal); - var experiment = new Experiment(specimenGrid.input().experiment); - var panel = specimenGrid.getPanelByExperiment(experiment); + BIOSAXS.proposal = new Proposal(bufferGrid.input().proposal); + var panel = bufferGrid.getPanel(BIOSAXS.proposal.macromolecules); panel.render(targetId); - }; /** - * Shows a list of stock solutions with macromolecule, buffer, storage temperature, concentration, shipment and comments + * A shipment may contains one or more cases where stock solutions and sample plates are stored * - * @multiselect allows multiple selection - * @height - * @minHeight - * @width - * @tbar - * @showTitle - * @isPackedVisible shows is stock solution is in a box - * @btnEditVisible shows edit button - * @btnAddVisible - * @btnAddExisting - * @btnUnpackVisible allows to unpack a stock solution - * @btnRemoveVisible allow to remove a stock solution + * @height + * @btnEditVisible + * @btnRemoveVisible + * + * #onEditButtonClicked + * #onAddButtonClicked + * #onRemoveButtonClicked + * #onDuplicateButtonClicked */ +function CaseGrid(args) { -function StockSolutionGrid(args) { - this.id = BUI.id(); this.height = 100; - this.width = null; - this.minHeight = null; - this.tbar = true; - - this.title = "Stock Solutions"; - - /** Visible buttons and actions **/ this.btnEditVisible = true; this.btnRemoveVisible = true; - this.btnAddVisible = true; - this.btnAddExisting = false; - this.isPackedVisible = true; - this.btnUnpackVisible = false; - - /** Selectors **/ - this.multiselect = false; - this.selectedStockSolutions = []; if (args != null) { - if (args.btnUnpackVisible != null) { - this.btnUnpackVisible = args.btnUnpackVisible; - } - if (args.multiselect != null) { - this.multiselect = args.multiselect; - } if (args.height != null) { this.height = args.height; } if (args.btnEditVisible != null) { this.btnEditVisible = args.btnEditVisible; } - if (args.btnAddVisible != null) { - this.btnAddVisible = args.btnAddVisible; - } - if (args.btnAddExisting != null) { - this.btnAddExisting = args.btnAddExisting; - } - if (args.width != null) { - this.width = args.width; - } - if (args.minHeight != null) { - this.minHeight = args.minHeight; - } - if (args.tbar != null) { - this.tbar = args.tbar; - } if (args.btnRemoveVisible != null) { this.btnRemoveVisible = args.btnRemoveVisible; } - if (args.isPackedVisible != null) { - this.isPackedVisible = args.isPackedVisible; - } - if (args.showTitle != null) { - this.showTitle = args.showTitle; - if (this.showTitle == false) { - this.title = null; + } + + /** Events **/ + this.onEditButtonClicked = new Event(this); + this.onAddButtonClicked = new Event(this); + this.onRemoveButtonClicked = new Event(this); + this.onDuplicateButtonClicked = new Event(this); +} + +CaseGrid.prototype._getColumns = function() { + var _this = this; + + function actionItemRenderer(value, meta, record, rowIx, ColIx, store) { + /*if (record.raw.specimen3VOs.length > 0){ + return 'x-hide-display'; + }*/ + } + + var columns = [ + { + header : 'Code', + dataIndex : 'code', + name : 'code', + type : 'string', + flex : 1 + }, + { + header : 'Bar Code', + dataIndex : 'barCode', + name : 'barCode', + type : 'string', + flex : 1, + hidden : true + }, + { + header : 'BeamLine', + dataIndex : 'barCode', + type : 'string', + flex : 1, + renderer : function(comp, val, record) { + if (record.raw.sessionVO != null) { + return "" + record.raw.sessionVO.beamlineName + ""; + } + } + }, + { + header : 'Session', + dataIndex : 'barCode', + type : 'string', + flex : 1, + renderer : function(comp, val, record) { + if (record.raw.sessionVO != null) { + return "" + moment(record.raw.sessionVO.startDate).format("MMM Do YY") + ""; + } } - } - - } - - /** Events **/ - this.onProposalChanged = new Event(this); - this.onStockSolutionSelected = new Event(this); -} - -StockSolutionGrid.prototype._getColumns = function() { - var _this = this; - var columns = [ - - { - header : 'Macromolecule', - dataIndex : 'macromolecule', - id : _this.id + 'macromolecule', - type : 'string', - renderer : function(val, y, specimen) { - return '' + val + ''; }, - hidden : false, - flex : 1 - }, { - header : 'Buffer', - dataIndex : 'buffer', - name : 'buffer', - hidden : false, - renderer : function(val, y, specimen) { - return '' + val + ''; + { + header : 'Status', + dataIndex : 'dewarStatus', + name : 'dewarStatus', + type : 'string', + flex : 1, + renderer : function(comp, val, record) { + if (new String(record.raw.dewarStatus).toUpperCase() == 'OPENED') { + return "" + new String(record.raw.dewarStatus).toUpperCase() + ""; + } + return "" + new String(record.raw.dewarStatus).toUpperCase() + ""; + } }, - type : 'string', - flex : 1 - }, { - header : 'Acronym', - dataIndex : 'name', - name : 'name', - type : 'string', - flex : 1, - hidden : true - }, { - header : 'Storage Temp.', - dataIndex : 'storageTemperature', - name : 'storageTemperature', - type : 'string', - flex : 1, - hidden : false, - renderer : function(val) { - return BUI.formatValuesUnits(val, 'C', { - fontSize : 12, - decimals : 2, - unitsFontSize : 10 - }); - } - }, { - header : 'Volume', - dataIndex : 'volume', - type : 'string', - flex : 1, - hidden : false, - renderer : function(val) { - return BUI.formatValuesUnits(val, 'µl', { - fontSize : 12, - decimals : 2, - unitsFontSize : 10 - }); - } - }, { - header : 'Concentration', - dataIndex : 'concentration', - name : 'concentration', - type : 'string', - flex : 1, - renderer : function(val) { - return BUI.formatValuesUnits(val, 'mg/ml', { - fontSize : 12, - decimals : 2, - unitsFontSize : 10 - }); - } - }, { - header : 'Packed', - dataIndex : 'comments', - id : _this.id + "box", - type : 'string', - width : 50, - hidden : !this.isPackedVisible, - renderer : function(val, cmp, a) { - if (a.raw.boxId != null) { - return "
"; + { + header : 'Type', + dataIndex : 'type', + name : 'type', + type : 'string', + flex : 1, + hidden : true + }, + { + header : 'Sample Plates', + dataIndex : 'plates', + name : 'plates', + type : 'string', + flex : 1, + hidden : true + }, + { + header : 'Stock Solutions', + dataIndex : 'plates', + name : 'plates', + type : 'string', + width : 100, + renderer : function(comp, val, record) { + var html = "
"; + var stockSolutions = BIOSAXS.proposal.getStockSolutionsByDewarId(record.raw.dewarId); + html = html + "" + stockSolutions.length + " x"; + return html + "
"; } - + }, { + header : 'isStorageDewar', + dataIndex : 'isStorageDewar', + name : 'isStorageDewar', + type : 'string', + flex : 1, + hidden : true + }, { + header : 'Tracking Number From Synchrotron', + dataIndex : 'trackingNumberFromSynchrotron', + name : 'trackingNumberFromSynchrotron', + type : 'string', + flex : 1, + hidden : true + }, { + header : 'Tracking Number To Synchrotron', + dataIndex : 'trackingNumberToSynchrotron', + name : 'trackingNumberToSynchrotron', + type : 'string', + flex : 1, + hidden : true + }, { + header : 'Storage Location', + dataIndex : 'storageLocation', + name : 'storageLocation', + type : 'string', + flex : 1, + hidden : false + }, { + header : 'Comments', + dataIndex : 'comments', + name : 'comments', + type : 'string', + flex : 1, + hidden : false } - }, { - header : 'Comments', - dataIndex : 'comments', - type : 'string', - flex : 1 - } ]; + ]; if (this.btnEditVisible) { columns.push({ id : _this.id + 'buttonEdit', + text : '', + hidden : !_this.btnEditVisible, width : 85, sortable : false, renderer : function(value, metaData, record, rowIndex, colIndex, store) { - if (_this.btnEditVisible) { - return BUI.getGreenButton('EDIT'); - } + return BUI.getGreenButton('EDIT'); } }); } @@ -22014,4646 +16857,9803 @@ StockSolutionGrid.prototype._getColumns = function() { }); } - if (this.btnUnpackVisible) { - columns.push({ - id : _this.id + 'buttonUnpack', - width : 85, - sortable : false, - renderer : function(value, metaData, record, rowIndex, colIndex, store) { - if (_this.btnUnpackVisible) { - return BUI.getBlueButton('UNPACK'); - } - } - }); - } + columns.push({ + dataIndex : 'comments', + type : 'string', + width : 85, + hidden : false, + renderer : function(comp, val, record) { + return BUI.getBlueButton("LABELS", { + href : BUI.getPrintcomponentURL(record.raw.dewarId) + }); + } + }); + return columns; }; -StockSolutionGrid.prototype._getTopButtons = function() { - var _this = this; - /** Actions buttons **/ - var actions = []; - - /** ADD BUTTON **/ - if (this.btnAddVisible) { - actions.push(Ext.create('Ext.Action', { - icon : '../images/add.png', - text : 'Add Stock Solution', - tooltip : 'Will create a new stock solution', - disabled : false, - alwaysEnabled : true, - handler : function(widget, event) { - _this.edit(); - } - })); - } - - if (this.btnAddExisting) { - actions.push(Ext.create('Ext.Action', { - icon : '../images/add.png', - text : 'Add Existing', - tooltip : 'Allows to select upacked stock solutions', - disabled : false, - alwaysEnabled : true, - handler : function(widget, event) { - var stockSolutionGrid = new StockSolutionGrid({ - btnAddVisible : false, - btnEditVisible : false, - btnRemoveVisible : false, - btnAddExisting : false, - isPackedVisible : true, - multiselect : true - }); - - var window = Ext.create('Ext.window.Window', { - title : 'Select', - height : 400, - width : 800, - layout : 'fit', - items : [ stockSolutionGrid.getPanel() ], - buttons : [ { - text : 'Pack', - handler : function() { - _this.onStockSolutionSelected.notify(stockSolutionGrid.selectedStockSolutions); - window.close(); - } - }, { - text : 'Cancel', - handler : function() { - window.close(); - } - } ] - - }).show(); +CaseGrid.prototype._getTopButtons = function() { + var _this = this; + /** Actions buttons **/ + var actions = []; - stockSolutionGrid.refresh(BIOSAXS.proposal.getUnpackedStockSolutions()); - } - })); - } + /** ADD BUTTON **/ + actions.push(Ext.create('Ext.Action', { + icon : '../images/add.png', + text : 'Add', + disabled : false, + handler : function(widget, event) { + _this.onAddButtonClicked.notify(); + } + })); return actions; }; -StockSolutionGrid.prototype.refresh = function(stockSolutions) { - this.features = stockSolutions; - this.store.loadData(this._prepareData(), false); +CaseGrid.prototype.refresh = function(dewars) { + this.features = dewars; + this.store.loadData(this._prepareData(dewars), false); }; -StockSolutionGrid.prototype._prepareData = function() { +CaseGrid.prototype._sort = function(store) { + store.sort('dewarId', 'DESC'); +}; + +CaseGrid.prototype._prepareData = function() { var data = []; for ( var i = 0; i < this.features.length; i++) { - var stockSolution = this.features[i]; - stockSolution.buffer = BIOSAXS.proposal.getBufferById(stockSolution.bufferId).acronym; - if (stockSolution.macromoleculeId != null) { - stockSolution.macromolecule = BIOSAXS.proposal.getMacromoleculeById(stockSolution.macromoleculeId).acronym; - } - data.push(stockSolution); + data.push(this.features[i]); } return data; }; -StockSolutionGrid.prototype.getPanel = function() { +CaseGrid.prototype.getPanel = function(dewars, plates) { + this.features = dewars; + this.plates = plates; return this._renderGrid(); }; -StockSolutionGrid.prototype.edit = function(stockSolutionId) { +CaseGrid.prototype._edit = function(dewar) { var _this = this; - var stockSolutionWindow = new StockSolutionWindow(); - /** On stock solution SAVED **/ - stockSolutionWindow.onSaved.attach(function(sender, stockSolution) { - _this.onProposalChanged.notify(stockSolution); + var caseWindow = new CaseWindow(); + /**SAVED **/ + caseWindow.onSaved.attach(function(sender, dewar) { + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, shipment) { + _this.refresh(shipment.dewarVOs); + }); + adapter.saveCase(BIOSAXS.proposal.getShipmentByDewarId(dewar.dewarId).shippingId, dewar); }); - stockSolutionWindow.draw(BIOSAXS.proposal.getStockSolutionById(stockSolutionId)); + caseWindow.draw(dewar); }; -StockSolutionGrid.prototype._getStoreFields = function() { +CaseGrid.prototype._getStoreFields = function(data) { return [ { - name : 'name', + name : 'dewarId', type : 'string' }, { - name : 'stockSolutionId', + name : 'barCode', type : 'string' }, { - name : 'macromolecule', + name : 'code', type : 'string' }, { - name : 'buffer', + name : 'comments', type : 'string' }, { - name : 'storageTemperature', - type : 'numeric' + name : 'dewarStatus', + type : 'string' }, { - name : 'volume', + name : 'isStorageDewar', type : 'string' }, { - name : 'concentration', + name : 'plates', type : 'string' }, { - name : 'buffer', + name : 'transportValue', type : 'string' }, { - name : 'comments', + name : 'trackingNumberFromSynchrotron', type : 'string' - } ]; -}; + }, { + name : 'trackingNumberToSynchrotron', + type : 'string' + }, { + name : 'timeStamp', + type : 'string' + }, { + name : 'storageLocation', + type : 'string' + }, { + name : 'type', + type : 'string' + } -//StockSolutionGrid.prototype.refresh = function() { -// this.proposal.onInitialized.attach(function(sender){ -// -// }); -// this.proposal.init(Ext.urlDecode(window.location.href).sessionId); -//}; + ]; +}; -StockSolutionGrid.prototype._renderGrid = function() { +CaseGrid.prototype._renderGrid = function() { var _this = this; /** Store **/ + var data = this._prepareData(); this.store = Ext.create('Ext.data.Store', { - fields : this._getStoreFields(), //columns, - autoload : true + fields : this._getStoreFields(data), + autoload : true, + data : data }); + this._sort(this.store); - var filters = { - ftype : 'filters', - local : true, - filters : this.filters - }; - - var selModel = null; - - if (this.multiselect) { - selModel = Ext.create('Ext.selection.CheckboxModel', { - //multiSelect : false,//this.multiselect, - mode : 'SINGLE', - listeners : { - selectionchange : function(sm, selections) { - _this.selectedStockSolutions = []; - for ( var i = 0; i < selections.length; i++) { - _this.selectedStockSolutions.push(selections[i].raw); - } - } - } - }); - } else { - selModel = { - mode : 'SINGLE' - }; - } - - this.store.sort("stockSolutionId", "desc"); + this.store.loadData(data, false); this.grid = Ext.create('Ext.grid.Panel', { style : { padding : 5 }, - icon : '/ispyb/images/SampleHolder_24x24_01.png', - title : this.title, height : this.height, - width : this.width, - minWidth : this.minWidth, - selModel : selModel, store : this.store, columns : this._getColumns(), viewConfig : { stripeRows : true, listeners : { itemdblclick : function(dataview, record, item, e) { - _this.edit(record.raw.stockSolutionId); - }, - cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { - var adapter = null; - if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonUnpack') { - _this.grid.setLoading("ISPyB: Unpacking stock solution"); - adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender) { - _this.onProposalChanged.notify(); - _this.grid.setLoading(false); - }); - adapter.onError.attach(function(sender) { - _this.onProposalChanged.notify(); - _this.grid.setLoading(false); - }); - record.raw.boxId = null; - adapter.saveStockSolution(record.raw); - } - - if (grid.getGridColumns()[cellIndex].getId() == _this.id + "box") { - window.location = BUI.getShippingURL(BIOSAXS.proposal.getShipmentByDewarId(record.raw.boxId).shippingId); - } - if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonEdit') { - _this.edit(record.data.stockSolutionId); - } - - if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonRemove') { - _this.grid.setLoading("ISPyB: Removing stock solution"); - adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender) { - _this.onProposalChanged.notify(); - _this.grid.setLoading(false); - }); - adapter.onError.attach(function(sender) { - _this.onProposalChanged.notify(); - _this.grid.setLoading(false); - }); - adapter.removeStockSolution(record.data.stockSolutionId); - } - } - } - } - - }); - - var actions = _this._getTopButtons(); - this.grid.addDocked({ - xtype : 'toolbar', - items : actions - }); - - var i = null; - this.grid.getSelectionModel().on({ - selectionchange : function(sm, selections) { - if (selections.length) { - for ( i = 0; i < actions.length; i++) { - if (actions[i].enable) { - actions[i].enable(); - } - } - } else { - for ( i = 0; i < actions.length; i++) { - if (actions[i].alwaysEnabled == false) { - if (actions[i].disable) { - actions[i].disable(); - } - } - } - } - } - }); - return this.grid; -}; - -StockSolutionGrid.prototype.input = function() { - return { - proposal : DATADOC.getProposal_10() - }; -}; - -StockSolutionGrid.prototype.test = function(targetId) { - var stockSolutionGrid = new StockSolutionGrid({ - height : 300, - width : 900 - }); - BIOSAXS.proposal = new Proposal(stockSolutionGrid.input().proposal); - var panel = stockSolutionGrid.getPanel(); - stockSolutionGrid.refresh(BIOSAXS.proposal.getStockSolutions()); - panel.render(targetId); -}; - - -function SuperpositionGrid(args) { - this.height = null; - this.width = null; - this.id = BUI.id(); - - if (args != null) { - if (args.height != null) { - this.height = args.height; - - if (args.width != null) { - this.width = args.width; - } - } - } - - this.onSelected = new Event(this); -} - -SuperpositionGrid.prototype._prepareData = function(data) { - return data; -}; + _this._edit(record.raw); + }, + cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonEdit') { + _this._edit(record.raw); + } -SuperpositionGrid.prototype.refresh = function(subtractions) { - var data = []; - if (subtractions != null){ - if (subtractions.length > 0){ - for (j in subtractions){ - var subtraction = subtractions[j]; - if (subtraction.superposition3VOs != null){ - for (i in subtraction.superposition3VOs){ - data.push(subtraction.superposition3VOs[i]); + if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonRemove') { + _this.onRemoveButtonClicked.notify(_this.store.getAt(rowIndex).raw.dewarId); } } } + }, + selModel : { + mode : 'SINGLE' } - } - this.store.loadData(data); -}; + }); -SuperpositionGrid.prototype.getPanel = function() { - var _this = this; - this.store = Ext.create('Ext.data.Store', { - fields : [ 'abinitioModelPdbFilePath', 'aprioriPdbFilePath', 'alignedPdbFilePath' ], - data : [] + var actions = _this._getTopButtons(); + this.grid.addDocked({ + xtype : 'toolbar', + items : actions }); - this.selModel = Ext.create('Ext.selection.RowModel', { - allowDeselect : true, - mode : 'MULTI', - listeners : { - selectionchange : function(sm, selections) { - var selected = []; - for (var i = 0; i < selections.length; i++) { - selected.push(selections[i].raw); + this.grid.getSelectionModel().on({ + selectionchange : function(sm, selections) { + if (selections.length) { + for ( var i = 0; i < actions.length; i++) { + if (actions[i].enable) { + actions[i].enable(); + } + } + } else { + for ( var i = 0; i < actions.length; i++) { + if (actions[i].alwaysEnabled == false) { + if (actions[i].disable) { + actions[i].disable(); + } + } } - _this.onSelected.notify(selected); } } }); - - this.store.sort([ { - property : 'name', - direction : 'ASC' - } ]); + return this.grid; +}; - this.panel = Ext.create('Ext.grid.Panel', { - store : this.store, - selModel : this.selModel, - width : this.width, - height : this.height, - margin : 10, - deferredRender : false, - columns : [ { - text : 'Abinitio', - dataIndex : 'abinitioModelPdbFilePath', - hidden : false, - flex : 1, - renderer : function(val){ - return BUI.getFileName(val); - } - }, { - text : 'Apriori', - dataIndex : 'aprioriPdbFilePath', - hidden : false, - flex : 1, - renderer : function(val){ - return BUI.getFileName(val); - } - }, { - text : 'Aligned', - dataIndex : 'alignedPdbFilePath', - hidden : false, - flex : 1, - renderer : function(val){ - return BUI.getFileName(val); - } - } ], +CaseGrid.prototype.input = function() { + return { + proposal : DATADOC.getProposal_10(), + dewars : DATADOC.getDewars_10() - viewConfig : { - enableTextSelection : true, - preserveScrollOnRefresh : true - }, - listeners : { - cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + }; +}; - }, - afterrender : function() { - } - } +CaseGrid.prototype.test = function(targetId) { + var CaseGrid = new CaseGrid({ + height : 150 }); - return this.panel; + BIOSAXS.proposal = new Proposal(CaseGrid.input().proposal); + var panel = CaseGrid.getPanel(CaseGrid.input().dewars); + panel.render(targetId); + }; +/** + * A shipment may contains one or more cases where stock solutions and sample plates are stored + * + * @height + * @btnEditVisible + * @btnRemoveVisible + * + * #onEditButtonClicked + * #onAddButtonClicked + * #onRemoveButtonClicked + * #onDuplicateButtonClicked + */ +function ExampleGrid(args) { + + this.height = 100; + this.btnEditVisible = true; + this.btnRemoveVisible = true; + + if (args != null) { + if (args.height != null) { + this.height = args.height; + } + if (args.btnEditVisible != null) { + this.btnEditVisible = args.btnEditVisible; + } + if (args.btnRemoveVisible != null) { + this.btnRemoveVisible = args.btnRemoveVisible; + } + } + + /** Events **/ + this.onEditButtonClicked = new Event(this); + this.onAddButtonClicked = new Event(this); + this.onRemoveButtonClicked = new Event(this); + this.onDuplicateButtonClicked = new Event(this); +} + +ExampleGrid.prototype._getColumns = function() { + var _this = this; + + function actionItemRenderer(value, meta, record, rowIx, ColIx, store) { + /*if (record.raw.specimen3VOs.length > 0){ + return 'x-hide-display'; + }*/ + } + + var columns = [ + { + header : 'Code', + dataIndex : 'code', + name : 'code', + type : 'string', + flex : 1 + }, + { + header : 'Bar Code', + dataIndex : 'barCode', + name : 'barCode', + type : 'string', + flex : 1, + hidden : true + }, + { + header : 'BeamLine', + dataIndex : 'barCode', + type : 'string', + flex : 1, + renderer : function(comp, val, record) { + if (record.raw.sessionVO != null) { + return "" + record.raw.sessionVO.beamlineName + ""; + } + } + }, + { + header : 'Session', + dataIndex : 'barCode', + type : 'string', + flex : 1, + renderer : function(comp, val, record) { + if (record.raw.sessionVO != null) { + return "" + moment(record.raw.sessionVO.startDate).format("MMM Do YY") + ""; + } + } + }, + { + header : 'Status', + dataIndex : 'dewarStatus', + name : 'dewarStatus', + type : 'string', + flex : 1, + renderer : function(comp, val, record) { + if (new String(record.raw.dewarStatus).toUpperCase() == 'OPENED') { + return "" + new String(record.raw.dewarStatus).toUpperCase() + ""; + } + return "" + new String(record.raw.dewarStatus).toUpperCase() + ""; + } + }, + { + header : 'Type', + dataIndex : 'type', + name : 'type', + type : 'string', + flex : 1, + hidden : true + }, + { + header : 'Sample Plates', + dataIndex : 'plates', + name : 'plates', + type : 'string', + flex : 1, + hidden : true + }, + { + header : 'Stock Solutions', + dataIndex : 'plates', + name : 'plates', + type : 'string', + width : 100, + renderer : function(comp, val, record) { + var html = "
"; + var stockSolutions = BIOSAXS.proposal.getStockSolutionsByDewarId(record.raw.dewarId); + html = html + "" + stockSolutions.length + " x"; + return html + "
"; + } + }, { + header : 'isStorageDewar', + dataIndex : 'isStorageDewar', + name : 'isStorageDewar', + type : 'string', + flex : 1, + hidden : true + }, { + header : 'Tracking Number From Synchrotron', + dataIndex : 'trackingNumberFromSynchrotron', + name : 'trackingNumberFromSynchrotron', + type : 'string', + flex : 1, + hidden : true + }, { + header : 'Tracking Number To Synchrotron', + dataIndex : 'trackingNumberToSynchrotron', + name : 'trackingNumberToSynchrotron', + type : 'string', + flex : 1, + hidden : true + }, { + header : 'Storage Location', + dataIndex : 'storageLocation', + name : 'storageLocation', + type : 'string', + flex : 1, + hidden : false + }, { + header : 'Comments', + dataIndex : 'comments', + name : 'comments', + type : 'string', + flex : 1, + hidden : false + } + ]; + + if (this.btnEditVisible) { + columns.push({ + id : _this.id + 'buttonEdit', + text : '', + hidden : !_this.btnEditVisible, + width : 85, + sortable : false, + renderer : function(value, metaData, record, rowIndex, colIndex, store) { + return BUI.getGreenButton('EDIT'); + } + }); + } + + if (this.btnRemoveVisible) { + columns.push({ + id : _this.id + 'buttonRemove', + width : 85, + sortable : false, + renderer : function(value, metaData, record, rowIndex, colIndex, store) { + if (_this.btnRemoveVisible) { + return BUI.getRedButton('REMOVE'); + } + } + }); + } + + columns.push({ + dataIndex : 'comments', + type : 'string', + width : 85, + hidden : false, + renderer : function(comp, val, record) { + return BUI.getBlueButton("LABELS", { + href : BUI.getPrintcomponentURL(record.raw.dewarId) + }); + } + }); + + return columns; +}; + +ExampleGrid.prototype._getTopButtons = function() { + var _this = this; + /** Actions buttons **/ + var actions = []; + + /** ADD BUTTON **/ + actions.push(Ext.create('Ext.Action', { + icon : '../images/add.png', + text : 'Add', + disabled : false, + handler : function(widget, event) { + _this.onAddButtonClicked.notify(); + } + })); + + return actions; +}; + +ExampleGrid.prototype.refresh = function(dewars) { + this.features = dewars; + this.store.loadData(this._prepareData(dewars), false); +}; + +ExampleGrid.prototype._sort = function(store) { + store.sort('dewarId', 'DESC'); +}; + +ExampleGrid.prototype._prepareData = function() { + var data = []; + for ( var i = 0; i < this.features.length; i++) { + data.push(this.features[i]); + } + return data; +}; + +ExampleGrid.prototype.getPanel = function() { + this.grid = Ext.create('Ext.grid.Panel', { + style : { + padding : 5 + }, + height : this.height, + store : this.store, + columns : this._getColumns(), + viewConfig : { + stripeRows : true, + listeners : { + itemdblclick : function(dataview, record, item, e) { + _this._edit(record.raw); + }, + cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonEdit') { + _this._edit(record.raw); + } + + if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonRemove') { + _this.onRemoveButtonClicked.notify(_this.store.getAt(rowIndex).raw.dewarId); + } + } + } + }, + selModel : { + mode : 'SINGLE' + } + }); + + var actions = _this._getTopButtons(); + this.grid.addDocked({ + xtype : 'toolbar', + items : actions + }); + + this.grid.getSelectionModel().on({ + selectionchange : function(sm, selections) { + if (selections.length) { + for ( var i = 0; i < actions.length; i++) { + if (actions[i].enable) { + actions[i].enable(); + } + } + } else { + for ( var i = 0; i < actions.length; i++) { + if (actions[i].alwaysEnabled == false) { + if (actions[i].disable) { + actions[i].disable(); + } + } + } + } + } + }); + return this.grid; +}; + + +ExampleGrid.prototype._getStoreFields = function(data) { + return [ { + name : 'dewarId', + type : 'string' + }, { + name : 'barCode', + type : 'string' + }, { + name : 'code', + type : 'string' + }, { + name : 'comments', + type : 'string' + }, { + name : 'dewarStatus', + type : 'string' + }, { + name : 'isStorageDewar', + type : 'string' + }, { + name : 'plates', + type : 'string' + }, { + name : 'transportValue', + type : 'string' + }, { + name : 'trackingNumberFromSynchrotron', + type : 'string' + }, { + name : 'trackingNumberToSynchrotron', + type : 'string' + }, { + name : 'timeStamp', + type : 'string' + }, { + name : 'storageLocation', + type : 'string' + }, { + name : 'type', + type : 'string' + } + + ]; +}; + +ExampleGrid.prototype._renderGrid = function() { + var _this = this; + + /** Store **/ + var data = this._prepareData(); + this.store = Ext.create('Ext.data.Store', { + fields : this._getStoreFields(data), + autoload : true, + data : data + }); + this._sort(this.store); + + this.store.loadData(data, false); + + this.grid = Ext.create('Ext.grid.Panel', { + style : { + padding : 5 + }, + height : this.height, + store : this.store, + columns : this._getColumns(), + viewConfig : { + stripeRows : true, + listeners : { + itemdblclick : function(dataview, record, item, e) { + _this._edit(record.raw); + }, + cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonEdit') { + _this._edit(record.raw); + } + + if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonRemove') { + _this.onRemoveButtonClicked.notify(_this.store.getAt(rowIndex).raw.dewarId); + } + } + } + }, + selModel : { + mode : 'SINGLE' + } + }); + + var actions = _this._getTopButtons(); + this.grid.addDocked({ + xtype : 'toolbar', + items : actions + }); + + this.grid.getSelectionModel().on({ + selectionchange : function(sm, selections) { + if (selections.length) { + for ( var i = 0; i < actions.length; i++) { + if (actions[i].enable) { + actions[i].enable(); + } + } + } else { + for ( var i = 0; i < actions.length; i++) { + if (actions[i].alwaysEnabled == false) { + if (actions[i].disable) { + actions[i].disable(); + } + } + } + } + } + }); + return this.grid; +}; + +ExampleGrid.prototype.input = function() { + return { + proposal : DATADOC.getProposal_10(), + dewars : DATADOC.getDewars_10() + + }; +}; + +ExampleGrid.prototype.test = function(targetId) { + var ExampleGrid = new ExampleGrid({ + height : 150 + }); + BIOSAXS.proposal = new Proposal(ExampleGrid.input().proposal); + var panel = ExampleGrid.getPanel(ExampleGrid.input().dewars); + panel.render(targetId); + +}; + + /** - * See ExperimentGrid - * + * Shows a list of experiment AKA data acquisitions + * @height + * @sorters + * @minHeight + * @gridType: Ext.ux.LiveSearchGridPanel or Ext.grid.Panel + * @tbar true or false + * @grouping true or false + * @width + * @title + * #onEditButtonClicked */ -function TemplateGrid(args) { - - if (args == null) { - args = {}; - } - args.sorters = [ { - property : 'experimentId', - direction : 'DESC' - } ]; - - ExperimentGrid.prototype.constructor.call(this, args); -} - -TemplateGrid.prototype._getFilterTypes = ExperimentGrid.prototype._getFilterTypes; -TemplateGrid.prototype._prettyPrintMacromolecules = ExperimentGrid.prototype._prettyPrintMacromolecules; -TemplateGrid.prototype._getPercentage = ExperimentGrid.prototype._getPercentage; -TemplateGrid.prototype._getPercentageCollected = ExperimentGrid.prototype._getPercentageCollected; -TemplateGrid.prototype.getPercentageMerged = ExperimentGrid.prototype.getPercentageMerged; -TemplateGrid.prototype._prepareData = ExperimentGrid.prototype._prepareData; -TemplateGrid.prototype.getPanel = ExperimentGrid.prototype.getPanel; -TemplateGrid.prototype._renderGrid = ExperimentGrid.prototype._renderGrid; -TemplateGrid.prototype._editExperiment = ExperimentGrid.prototype._editExperiment; -TemplateGrid.prototype._removeExperimentById = ExperimentGrid.prototype._removeExperimentById; - -TemplateGrid.prototype._getTopButtons = function() { - /** Actions buttons * */ - var actions = []; - - actions.push(Ext.create('Ext.Action', { - icon : '../images/add.png', - text : 'Add experiment', - handler : function(widget, event) { - var wizardWidget = new WizardWidget({ - windowMode : true - }); - - wizardWidget.onFinished.attach(function(sender, result) { - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, data) { - var experiment = new Experiment(data); - BIOSAXS.openExperimentByExperiment(experiment); - wizardWidget.window.close(); - }); - wizardWidget.current.setLoading("ISPyB: Creating experiment"); - adapter.createTemplate(result.name, "comments", result.data); - }); - -// wizardWidget.draw(this.targetId, new ExperimentTypeWizardForm()); - wizardWidget.draw(this.targetId, new MeasurementCreatorStepWizardForm(BIOSAXS.proposal.getMacromolecules())); - } - })); - return actions; -}; - -TemplateGrid.prototype.refresh = function(data) { - var filtered = []; - for ( var i = 0; i < data.length; i++) { - if (data[i].experimentType == "TEMPLATE") { - filtered.push(data[i]); - } - } - this.store.loadData(this._prepareData(filtered), false); -}; - -TemplateGrid.prototype._getColumns = function() { - var _this = this; - function actionItemRenderer(value, meta, record, rowIx, ColIx, store) { - if (record.raw.buffer3VOs != null) { - if (record.raw.buffer3VOs.length > 0) { - return 'x-hide-display'; - } - } +function ExperimentGrid(args) { + this.width = "100%"; + this.height = 700; + this.minHeight = 500; - if (record.data.platesCount > 0) { - return 'x-hide-display'; - } - } + this.id = BUI.id(); + this.gridType = 'Ext.grid.Panel'; //'Ext.ux.LiveSearchGridPanel'; + this.tbar = false; + this.hideHeaders = false; + this.grouping = true; + this.title = null; - return [ { - xtype : 'rownumberer', - width : 40 - }, { - text : 'experimentId', - dataIndex : 'experimentId', - name : 'experimentId', - type : 'string', - hidden : true + this.filtered = null; + + /** maximum row count **/ + this.limit = 100; + + this.sessionIdFilter = null; + + /** if not null filtered by date **/ + this.date = null; + + this.sorters = [ { + property : 'date', + direction : 'DESC' }, { - text : 'Name', - dataIndex : 'name', - name : 'name', - type : 'string', - flex : 1 - }, + property : 'time', + direction : 'DESC' + } ]; - { - text : 'Type', - dataIndex : 'type', - name : 'type', - type : 'string', - hidden : true, - flex : 1, - renderer : function(val) { - return val; + this.dates = {}; + if (args != null) { + if (args.height != null) { + this.height = args.height; } - }, { - text : 'Macromolecules', - name : 'macromolecules_names', - dataIndex : 'macromolecules_names', - flex : 1, - renderer : function(val) { - return " " + val + ""; + if (args.limit != null) { + this.limit = args.limit; } - }, { - id : _this.id + 'GO', - width : 80, - sortable : false, - renderer : function(value, metaData, record, rowIndex, colIndex, store) { - return BUI.getGreenButton('EDIT'); + if (args.sorters != null) { + this.sorters = args.sorters; } - }, { - id : _this.id + 'REMOVE', - width : 100, - sortable : false, - renderer : function(value, metaData, record, rowIndex, colIndex, store) { - return BUI.getRedButton('REMOVE'); + if (args.sessionId != null){ + this.sessionIdFilter = args.sessionId; + } + if (args.filtered != null) { + this.filtered = args.filtered; + } + if (args.minHeight != null) { + this.minHeight = args.minHeight; + } + if (args.gridType != null) { + this.gridType = args.gridType; + } + if (args.tbar != null) { + this.tbar = args.tbar; + } + if (args.grouping != null) { + this.grouping = args.grouping; + } + if (args.width != null) { + this.width = args.width; + } + if (args.title != null) { + this.title = args.title; } - } ]; -}; - -TemplateGrid.prototype.input = function() { - var experiments = DATADOC.getExperimentList_10(); - return { - experiments : experiments, - proposal : new MeasurementGrid().input().proposal - - }; -}; - -TemplateGrid.prototype.test = function(targetId) { - var experimentGrid = new TemplateGrid({ - height : 350, - minHeight : 350, - width : 1000, - gridType : 'Ext.grid.Panel', - title : 'Experiments', - grouping : false, - tbar : true - }); - BIOSAXS.proposal = new Proposal(experimentGrid.input().proposal); - var panel = experimentGrid.getPanel(experimentGrid.input().experiments); - experimentGrid.refresh(experimentGrid.input().experiments); - panel.render(targetId); -}; - -function VolumeGrid() { - this.id = BUI.id(); + } + /** Events **/ + this.onEditButtonClicked = new Event(this); } -VolumeGrid.prototype.getPanel = function(experiment) { - this.experiment = experiment; - return this.render(); +ExperimentGrid.prototype._getFilterTypes = function() { + return []; }; -VolumeGrid.prototype.getVolumesPanel = function(data, title) { - var _this = this; - var store = Ext.create('Ext.data.Store', { - fields : [ 'name', 'volume', 'macromoleculeId', 'bufferId' ], - data : data - }); - store.sort([ { - property : 'name', - direction : 'ASC' - } ]); - - var grid = Ext.create('Ext.grid.Panel', { - title : title, - height : 400, - maxHeight : 400, - width : 900, - store : store, - margin : '10 0 50 10', - tbar : [ { - text : 'Go to Shipment', - icon : '../images/plane-small.gif', - handler : function() { - window.location = BUI.getCreateShipmentList(); - } - } ], - viewConfig : { - stripeRows : true, - listeners : { - cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { - if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonCreate') { - var stockSolutionWindow = new StockSolutionWindow(); - /** On stock solution SAVED **/ - stockSolutionWindow.onSaved.attach(function(sender, stockSolution) { - BIOSAXS.proposal.onInitialized.attach(function(sender, data) { - _this.refresh(_this.experiment); - }); - BIOSAXS.proposal.init(); - }); - var acronym = "ST"; - if (record.raw.macromoleculeId != null) { - acronym = acronym + "_" + BIOSAXS.proposal.getMacromoleculeById(record.raw.macromoleculeId).acronym; - } - if (record.raw.bufferId != null) { - acronym = acronym + "_" + BIOSAXS.proposal.getBufferById(record.raw.bufferId).acronym; - } - stockSolutionWindow.draw({ - concentration : record.raw.concentration, - macromoleculeId : record.raw.macromoleculeId, - bufferId : record.raw.bufferId, - name : acronym, - volume : record.raw.volume - }); - } - - if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonStockSolutions') { - var stockSolutionGrid = new StockSolutionGrid({ - btnAddVisible : false, - btnEditVisible : false, - btnRemoveVisible : false, - btnAddExisting : false, - isPackedVisible : true, - multiselect : false - }); - - var window = Ext.create('Ext.window.Window', { - title : 'Stock solutions by specimen', - height : 400, - width : 800, - layout : 'fit', - items : [ stockSolutionGrid.getPanel() ], - buttons : [ { - text : 'Close', - handler : function() { - window.close(); - } - } ] - - }).show(); - stockSolutionGrid.refresh(BIOSAXS.proposal.getStockSolutionsBySpecimen(record.raw.macromoleculeId, record.raw.bufferId)); - } - } - } - }, - columns : [ -// { -// text : '', -// dataIndex : 'macromoleculeId', -// width : 20, -// renderer : function(val, y, sample) { -// if (val != null) { -//// return BUI.getRectangleColorDIV(BIOSAXS.proposal.macromoleculeColors[val], 10, 10); -// return BUI.getRectangleColorDIV(_this.experiment.macromoleculeColors[val], 10, 10); -// } -// } -// }, -// { -// text : '', -// dataIndex : 'bufferId', -// width : 20, -// renderer : function(val, y, sample) { -// if (val != null) { -// return BUI.getRectangleColorDIV(_this.experiment.getSpecimenColorByBufferId(val), 10, 10); -// } -// } -// }, - { - text : 'Specimen', - dataIndex : 'name', - flex : 0.5 - }, - { - text : 'Estimated Volume', - dataIndex : 'volume', - tooltip : 'Estimation of the maximum volume needed for making this experiment', - flex : 0.5, - editor : { - allowBlank : true - }, - renderer : function(val, y, sample) { - return BUI.formatValuesUnits(sample.data.volume, 'µl', { - fontSize : 16, - decimals : 2, - unitsFontSize : this.unitsFontSize - }); - } - }, - { - text : 'Stock Solution', - id : _this.id + 'buttonStockSolutions', - dataIndex : 'name', - flex : 0.5, - tooltip : 'Stock Solutions containing this specimen in this proposal', - renderer : function(value, metaData, record, rowIndex, colIndex, store) { - var macromoleculeId = record.raw.macromoleculeId; - var bufferId = record.raw.bufferId; - var stockSolutions = BIOSAXS.proposal.getStockSolutionsBySpecimen(macromoleculeId, bufferId); - if (stockSolutions.length > 0) { - return "
" + stockSolutions.length + " x
"; - } +ExperimentGrid.prototype._prepareData = function(rows) { + var data = []; + var count = 0; + + rows.sort(function(a,b){return b.experimentId - a.experimentId;}); + for ( var i = 0; i < rows.length; i++) { + var row = rows[i]; + this.dates[moment(row.creationDate).format("YYYYMMDD")] = moment(row.creationDate).format("MMM Do YY"); + if ( + ( this.filtered == null || row.experimentType == this.filtered ) && (count < this.limit || this.limit == null ) && (this.sessionIdFilter == null || this.date != null || (this.date == null && this.sessionIdFilter == row.sessionId)) + ){ - } - }, { - id : _this.id + 'buttonCreate', - text : '', - tooltip : 'Create a new stock solution for shipping', - width : 170, - sortable : false, - renderer : function(value, metaData, record, rowIndex, colIndex, store) { - return BUI.getGreenButton('NEW STOCK SOLUTION', { - width : 160 + data.push({ + experimentId : row.experimentId, + status : row.status, + dataAcquisitionFilePath : row.dataAcquisitionFilePath, + type : row.experimentType, + name : row.name, + macromolecules_names : row.macromolecules, + percentageAnalysed : { + value : (row.dataCollectionDoneCount / row.dataCollectionCount) * 100, + text : row.dataCollectionDoneCount + " of " + row.dataCollectionCount + }, + percentageCollected : { + value : (row.measurementDoneCount / row.measurementCount) * 100, + text : row.measurementDoneCount + " of " + row.measurementCount + }, + percentageMerged : { + value : (row.measurementAveragedCount / row.measurementCount) * 100, + text : row.measurementAveragedCount + " of " + row.measurementCount + }, + date : moment(row.creationDate).format("YYYYMMDD"), + time : moment(row.creationDate).format("YYYYMMDDHHmmss"), + creationDate : row.creationDate }); - } - } ] - }); - return grid; + count ++; + } + } + return data; +}; +ExperimentGrid.prototype.getPanel = function(experiments) { + this.features = experiments; + return this._renderGrid(experiments); }; -VolumeGrid.prototype._prepareData = function(experiment) { - var keys = {}; - for ( var i = 0; i < experiment.getSamples().length; i++) { - var sample = experiment.getSamples()[i]; - var key = ""; - if (sample.macromoleculeId == null) { - key = experiment.getBufferById(sample.bufferId).acronym; - if (keys[key] == null) { - keys[key] = { - macromoleculeId : sample.macromoleculeId, - name : key, - bufferId : sample.bufferId, - volume : 0 - }; - } - keys[key].volume = Number(sample.volume) + Number(keys[key].volume); +ExperimentGrid.prototype.refresh = function(experiments) { + this.experiments = experiments; + var filtered = []; + for ( var i = 0; i < experiments.length; i++) { + if (experiments[i].experimentType != "TEMPLATE") { + filtered.push(experiments[i]); } + } - if ((sample.macromolecule3VO != null) || (sample.macromoleculeId != null)) { - macromoleculeId = sample.macromoleculeId; - if (sample.macromoleculeId == null) { - sample.macromoleculeId = sample.macromolecule3VO.macromoleculeId; - } - key = BIOSAXS.proposal.getMacromoleculeById(sample.macromoleculeId).acronym + " + " + experiment.getBufferById(sample.bufferId).acronym; - if (keys[key] == null) { - keys[key] = { - macromoleculeId : sample.macromoleculeId, - name : key, - bufferId : sample.bufferId, - volume : 0 - }; - } - keys[key].volume = Number(sample.volume) + Number(keys[key].volume); + this.parsedData = this._prepareData(filtered); + + var day = {}; + var dates = []; + for ( var i = 0; i < this.experiments.length; i++) { + var date = moment(this.experiments[i].creationDate).format("MMM Do YYYY"); + if (day[date] == null){ + dates.push({ + date : date, + value : moment(this.experiments[i].creationDate) + }); + day[date] = true; } } - var data = []; - for (var keyId in keys) { - data.push(keys[keyId]); + + this.storeDate.loadData(dates, false); + this.store.loadData(this.parsedData, false); + + /** If it has already been filtered by date we keep the filter **/ + if (this.date != null){ + this._filterByDate(this.date); } - - return data; }; -VolumeGrid.prototype.refresh = function(experiment) { - this.experiment = experiment; - this.macromoleculeGrid.getStore().loadData(this._prepareData(this.experiment), false); -}; +ExperimentGrid.prototype._getTopButtons = function() { + var _this = this; + /** Actions buttons **/ + var actions = []; -VolumeGrid.prototype.render = function() { - this.macromoleculeGrid = this.getVolumesPanel(this._prepareData(this.experiment), "Estimation of required Volume"); + actions.push(Ext.create('Ext.Action', { + icon : '../images/calendar_icon.png', + text : 'Show Calendar', + disabled : false, + handler : function(widget, event) { + var window = Ext.create('Ext.window.Window', { + title : 'Calendar', + width : 600, + height : 600, + modal : true, + closable : true, + layout : { + type : 'vbox', + align : 'stretch' + }, + items : [ { + xtype : 'label', + html : 'Click on a data acquisition to select:', + margin : '5 5 5 5' + }, { + html : '
', + margin : '5 5 5 5' + } - return { - xtype : 'container', - layout : 'vbox', - margin : "0, 0, 0, 5", - items : [ { - xtype : 'container', - layout : 'hbox', - margin : "0, 0, 0, 0", - items : [ this.macromoleculeGrid ] - } ] - }; + ] + }).show(); + + var calendarWidget = new CalendarWidget({ + height : 450 + }); + var aux = _this.limit; + /** we remove the limit temporarily **/ + _this.limit = null; + _this.sessionIdFilter = null; + calendarWidget.loadData(_this._prepareData(_this.experiments)); + _this.limit = aux; + calendarWidget.draw('calendar'); + calendarWidget.onClick.attach(function(sender, date) { + date = moment(date, "YYYY-MM-DD"); + _this._filterByDate(date); + window.close(); + + }); + + } + })); + this.storeDate = Ext.create('Ext.data.ArrayStore', { + fields: ['date', 'value'], + data : [] + }); + + this.dateMenu = Ext.create('Ext.form.field.ComboBox', { + hideLabel: true, + store: this.storeDate, + displayField: 'date', + typeAhead: true, + queryMode: 'local', + margin : '0 0 0 30', + triggerAction: 'all', + emptyText:'Select a date...', + selectOnFocus:true, + width:135, + listeners:{ + scope: this, + 'select': function (a,b,c){ + _this.limit = null; + _this._filterByDate(moment(b[0].raw.value, "YYYY-MM-DD")); + } + } + }); + + actions.push(this.dateMenu); + + actions.push("->"); + if (_this.filtered != null){ + actions.push({ + html : "Experiment Type: " + _this.filtered +"" + }); + } + else{ + actions.push({ + html : "Experiment Type: ALL" + }); + } + return actions; }; -VolumeGrid.prototype.input = function(experiment) { - return { - experiment : DATADOC.getExperiment_10() - }; +/** + * Date format: "YYYY-MM-DD" + */ +ExperimentGrid.prototype._filterByDate = function(date) { + var experimentsFiltered = []; + /** Getting all the experiments of date**/ + for ( var i = 0; i < this.experiments.length; i++) { + var experiment = this.experiments[i]; + if (experiment.creationDate != null) { + var experimentDate = moment(experiment.creationDate); + if (experimentDate.year() == date.year()) { + if (experimentDate.month() == date.month()) { + if (experimentDate.date() == date.date()) { + experimentsFiltered.push(experiment); + } + } + } + } + } + var parsedData = this._prepareData(experimentsFiltered); + this.store.loadData(parsedData); + this.date = date; }; -VolumeGrid.prototype.test = function(targetId) { - var volumeGrid = new VolumeGrid(); - BIOSAXS.proposal = new Proposal(new MeasurementGrid().input().proposal); - var panel = volumeGrid.getPanel(new Experiment(new VolumeGrid().input().experiment)); - Ext.create('Ext.panel.Panel', { - height : 500, - width : 1000, - renderTo : targetId, - items : [ panel ] +/** Only for templates **/ +ExperimentGrid.prototype._removeExperimentById = function(experimentId) { + var _this = this; + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(evt, args) { + _this.grid.setLoading(false); + document.getElementById(BIOSAXS.targetId).innerHTML = ""; + BIOSAXS.start(BIOSAXS.targetId); }); + this.grid.setLoading("Removing experiment "); + adapter.removeExperimentById(experimentId); }; - -/** - * Example of a tab panel to be populated with widgets - * - * @width width in pixels - * @height height in pixels - * - * #myEvent event that this class is supposed to throw - * - **/ -function ExampleTabs(args) { - this.width = 500; - this.height = 500; + +ExperimentGrid.prototype._renderGrid = function() { + var _this = this; + + /** Store **/ + this.store = Ext.create('Ext.data.Store', { + fields : this._getColumns(), + groupField : 'date', + autoload : true, + data : [], + remoteSort: false, + sorters : this.sorters + }); + - if (args != null){ - if (args.width != null){ - this.width= args.width; - } - if (args.height != null){ - this.height= args.height; - } - } - /** Events **/ - this.myEvent = new Event(); -} - -/** Populate the widget **/ -ExampleTabs.prototype.refresh = function(macromolecule) { -}; + var groupingFeature = Ext.create('Ext.grid.feature.Grouping', { + groupHeaderTpl : Ext.create('Ext.XTemplate', + "
{name:this.formatName}
", { + formatName : function(name) { + return _this.dates[name]; + } + }), + hideGroupedHeader : true, + startCollapsed : false + }); -/** It creates a tab panel with the specified with and height **/ -ExampleTabs.prototype.getPanel = function() { - this.panel = Ext.createWidget('tabpanel', { - height : this.height, - width : this.width, - style : { - padding : 2 + this.features = []; + if (this.grouping) { + this.features.push(groupingFeature); + } + + /** Grid **/ + this.grid = Ext.create(this.gridType, { + hideHeaders : this.hideHeaders, + resizable : true, + title : this.title, + width : this.width, + minHeight : this.minHeight, + height : this.height, + features : this.features, + store : this.store, + columns : this._getColumns(), + selModel : { + mode : 'SINGLE' }, - items : [ { - tabConfig : { - title : 'Test', - icon : '/ispyb/images/plane-small.gif' + viewConfig : { + stripeRows : true, + getRowClass : function(record, rowIdx, params, store) { + if (record.raw.type == "TEMPLATE") { + return "template-color-row"; + } + if ((record.raw.type == "CALIBRATION") && (record.raw.status == "FINISHED")) { + return "blue-row"; + } }, - items : [ { - xtype : 'container', - margin : '5 5 5 5', - items : [ ] - } ] - } ] + listeners : { + itemdblclick : function(dataview, record, item, e) { + _this._editExperiment(record.raw.experimentId); + }, + cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'GO') { + _this._editExperiment(record.raw.experimentId); + } + + if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'REMOVE') { + _this._removeExperimentById(record.raw.experimentId); + } + + } + } + } }); - return this.panel; -}; - -/** - * Shows an experiments with the specimens, measurements, analysis tabs where - * results are shown and the frames widget - * - * @targetId - */ -function ExperimentTabs(targetId) { - this.height = 900; - this.targetId = targetId; + var actions = _this._getTopButtons(); - this.id = BUI.id(); - var _this = this; + if (this.tbar) { + this.grid.addDocked({ + xtype : 'toolbar', + items : actions + }); + this.grid.getSelectionModel().on({ + selectionchange : function(sm, selections) { + if (selections.length) { + for ( var i = 0; i < actions.length; i++) { + if (actions[i].enable) { + actions[i].enable(); + } + } + } else { + for ( var i = 0; i < actions.length; i++) { + if (actions[i].alwaysEnabled == false) { + if (actions[i].disable) { + actions[i].disable(); + } + } + } + } + } + }); + } - this.INTERVAL_UPDATE = BUI.getUpdateInterval(); + return this.grid; +}; - this.gridHeight = 1000; - /** data * */ - this.experiment = null; +ExperimentGrid.prototype._getColumns = function() { + var _this = this; + function actionItemRenderer(value, meta, record, rowIx, ColIx, store) { + if (record.raw.buffer3VOs != null) { + if (record.raw.buffer3VOs.length > 0) { + return 'x-hide-display'; + } + } - - /** Specimen Widget contains a specimenGrid and a sampleChangerWidget than can be displayed with are vertical or horizontal layout **/ - this.specimenWidget = new SpecimenWidget({ - height : 600 - }); - - - /** For Overview * */ - /*this.samplePlateGroupWidget = new SamplePlateGroupWidget({ - showTitle : false, - height : 250, - margin : 5, - border : 0 + if (record.data.platesCount > 0) { + return 'x-hide-display'; + } + } - });*/ + return [ + { + text : 'experimentId', + dataIndex : 'experimentId', + name : 'experimentId', + type : 'string', + hidden : true + }, + { + xtype : 'rownumberer', + width : 40 + }, + { + text : 'Name', + dataIndex : 'name', + name : 'name', + type : 'string', + flex : 1 + }, - /** For Measurements * */ - /*this.measurementSamplePlateGroupWidget = new SamplePlateGroupWidget({ - showTitle : false, - height : 250, - margin : '5 0 0 0', - border : 0 - });*/ + { + text : 'Type', + dataIndex : 'type', + name : 'type', + type : 'string', + flex : 1, + renderer : function(val) { + if (val == "CALIBRATION") { + return "" + val + ""; + } + + return val; + } + }, + { + text : 'Macromolecules', + name : 'macromolecules_names', + dataIndex : 'macromolecules_names', + flex : 1, + renderer : function(val) { + if (val != null) { + return " " + val + ""; + } + return " Information not available"; + } + }, + { + text : 'Buffers', + dataIndex : 'buffer_names', + name : 'buffer_names', + flex : 1, + hidden : true, + renderer : function(val) { + return "Buffer/s: " + val + ""; + } + }, + { + text : 'Status', + dataIndex : 'status', + name : 'status', + type : 'string', + flex : 1, + renderer : function(val, x, sample) { + if (sample.raw.type == "TEMPLATE") { + return "READY"; + } + if (sample.raw.status == "ABORTED") { + return "" + val + ""; + } + return "" + val + ""; + } + }, + { + text : 'Download', + dataIndex : 'creationDate', + name : 'creationDate', + renderer : function(val, x, sample) { + if (sample != null) { + if (sample.raw.type == "HPLC") { + return; + } + return BUI.getZipHTMLByExperimentId(sample.raw.experimentId, sample.raw.name); + } + }, + width : 100 - this.measurementGridDone = new MeasurementGrid({ - height : 600, - minHeight : 400, - maxHeight : 800, - positionColumnsHidden : true, - showTitle : false, - estimateTime : true, - width : 900, - maxWidth : 1500, - addBtnEnable : false, - markDone : true, - removeBtnEnabled : false - }); - this.measurementGridDone.onSelected.attach(function(sender, measurements) { - var specimens = []; - for ( var i = 0; i < measurements.length; i++) { - specimens.push(_this.experiment.getSampleById(measurements[i].specimenId)); - } - //_this.measurementSamplePlateGroupWidget.selectSpecimens(specimens); - }); + }, + { + header : 'Measurements', + dataIndex : 'percentageCollected', + name : 'percentageCollected', + type : 'string', + renderer : function(val, y, sample) { + if ((sample.raw.type == "TEMPLATE") || (sample.raw.type == "HPLC")) { + return; + } + return "
" + BUI.getProgessBar(sample.raw.percentageCollected.value, sample.raw.percentageCollected.text) + "
"; + }, + width : 100 + }, + { + header : 'Averaged', + dataIndex : 'percentageMerged', + name : 'percentageMerged', + type : 'string', + renderer : function(val, y, sample) { + if ((sample.raw.type == "TEMPLATE") || (sample.raw.type == "HPLC")) { + return; + } + return "
" + BUI.getProgessBar(sample.raw.percentageMerged.value, sample.raw.percentageMerged.text) + "
"; + }, + width : 100 + }, + { + header : 'Subtractions', + dataIndex : 'percentageAnalysed', + name : 'percentageAnalysed', + type : 'string', + renderer : function(val, y, sample) { + if ((sample.raw.type == "TEMPLATE") || (sample.raw.type == "HPLC")) { + return; + } + return "
" + BUI.getProgessBar(sample.raw.percentageAnalysed.value, sample.raw.percentageAnalysed.text) + "
"; + }, + width : 100 + }, - /** AnalysisGrid * */ - this.analysisGrid = new AnalysisGrid({ - height : Ext.getBody().getViewSize().height * 0.9 - 300, - positionColumnsHidden : true, - sorters : [ { - property : 'priorityLevelId', - direction : 'ASC' - } ] - }); - - - /** Queue * */ - this.queueGrid = new QueueGrid({ - url : BUI.getQueueUrlByExperiment(), - height : Ext.getBody().getViewSize().height * 0.9 - 300, - width : Ext.getBody().getViewSize().width * 0.9 - 300, - isGrouped : true, - tbar : false, - bbar : false, - sorter : [{ - property: 'measurementId', - direction: 'DESC' - }] - }); + { + text : 'time', + dataIndex : 'time', + name : 'time', + hidden : true, + renderer : function(val) { + return val; + }, + width : 100 -} + }, { + text : 'Date', + dataIndex : 'date', + name : 'date', + renderer : function(val) { + return val; + }, + width : 100 -ExperimentTabs.prototype.error = function(error) { - var e = JSON.parse(error); - showError(e); - this.panel.setLoading(false); -}; + }, -ExperimentTabs.prototype.draw = function(experiment) { - this.renderDataAcquisition(experiment); -}; + { + text : 'Time', + dataIndex : 'creationDate', + name : 'creationDate', + renderer : function(val) { + return moment(val).format(" HH:mm:ss"); + }, + width : 100 -ExperimentTabs.prototype.refreshAnalysisData = function() { - var _this = this; - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, data) { - _this.analysisGrid.refresh(data, {experiment : _this.experiment}); - }); - adapter.getAnalysisInformationByExperimentId(this.experiment.experimentId); - - /** Auto load **/ - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, data) { - _this.queueGrid.refresh(data); - }); - adapter.getCompactAnalysisByExperimentId(this.experiment.experimentId); - this.queueGrid.store.proxy.url = BUI.getQueueUrlByExperiment(this.experiment.experimentId); - this.queueGrid.refresh(); + }, { + id : _this.id + 'GO', + width : 80, + sortable : false, + renderer : function(value, metaData, record, rowIndex, colIndex, store) { + return BUI.getGreenButton('GO'); + } + } ]; }; -ExperimentTabs.prototype.getSpecimenContainerHeight = function(experiment) { - var maxItems = 0; - if (maxItems < experiment.getSamples().length + 1) { - maxItems = experiment.getSamples().length + 1; - } - - var height = (maxItems + 1) * 40 + 40; - if (height > 400) { - height = 400; +/** Changes location.href in order to edit the experiment **/ +ExperimentGrid.prototype._editExperiment = function(experimentId) { + if (Ext.urlDecode(window.location.href).sessionId != null) { + location.href = 'viewProjectList.do?reqCode=display&experimentId=' + experimentId + '&sessionId='+ Ext.urlDecode(window.location.href).sessionId; + } else { + location.href = 'viewProjectList.do?reqCode=display&experimentId=' + experimentId; } - return height; -}; - -ExperimentTabs.prototype.getExperimentTitle = function() { - var experimentHeaderForm = new ExperimentHeaderForm(); - return experimentHeaderForm.getPanel(this.experiment); }; -ExperimentTabs.prototype.renderDataAcquisition = function(experiment) { - var _this = this; - this.experiment = experiment; +ExperimentGrid.prototype.input = function() { + var experiments = DATADOC.getExperimentList_10(); + return { + experiments : experiments, + proposal : new MeasurementGrid().input().proposal - var specimenGrid = new SpecimenGrid({ - height : 400, - maxHeight : 500, - width : 890 - }); + }; +}; - specimenGrid.onClick.attach(function(sender, args) { - }); +ExperimentGrid.prototype.test = function(targetId) { + var experimentGrid = new ExperimentGrid({ + height : 350, + minHeight : 350, + width : 1000 - specimenGrid.onSelected.attach(function(sender, specimens) { - //_this.samplePlateGroupWidget.selectSpecimens(specimens); - }); - var specimenContainer = this.specimenWidget.getPanel(); - this.specimenWidget.refresh(experiment); - /* - var specimenContainer = Ext.create('Ext.container.Container', { - layout : 'hbox', - width : 900, - padding : '10 0 0 2', - items : [ specimenGrid.getPanel() ] }); + BIOSAXS.proposal = new Proposal(experimentGrid.input().proposal); + var panel = experimentGrid.getPanel(experimentGrid.input().experiments); + experimentGrid.refresh(experimentGrid.input().experiments); + panel.render(targetId); +}; + + +function FitStructureToExperimentDataGrid(args) { + this.height = null; + this.width = null; + this.id = BUI.id(); + + if (args != null) { + if (args.height != null) { + this.height = args.height; + + if (args.width != null) { + this.width = args.width; + } + } + } + + this.onSelected = new Event(this); +} + +FitStructureToExperimentDataGrid.prototype.getStructuresByMacromolecule = function(macromolecule) { + var structures = []; + if (macromolecule.structure3VOs != null) { + if (macromolecule.structure3VOs.length > 0) { + for (var i = 0; i < macromolecule.structure3VOs.length; i++) { + structures.push(macromolecule.structure3VOs[i]); + } + } + } + return structures; +}; + +FitStructureToExperimentDataGrid.prototype._prepareData = function(subtractions) { + var data = []; + for (var i = 0; i < subtractions.length; i++) { + + for (var j = 0; j < subtractions[i].fitStructureToExperimentalData3VOs.length; j++) { + var fit = subtractions[i].fitStructureToExperimentalData3VOs[j]; + data.push({ + fit : fit.fitFilePath, + fitStructureToExperimentalDataId : fit.fitStructureToExperimentalDataId, + mixtureToStructure3VOs : fit.mixtureToStructure3VOs, + subtractedFile : subtractions[i].substractedFilePath + }); + } + } + return data; + +}; + +FitStructureToExperimentDataGrid.prototype.refresh = function(subtractions) { + this.store.loadData(this._prepareData(subtractions), false); +}; + +FitStructureToExperimentDataGrid.prototype.getPanel = function() { + var _this = this; + this.store = Ext.create('Ext.data.Store', { + fields : [ 'fit', 'subtractedFile', 'structureId', 'fitFilePath', 'mixtureToStructure3VOs' ], + data : [] + }); + + this.store.sort([ { + property : 'name', + direction : 'ASC' + } ]); + + this.selModel = Ext.create('Ext.selection.RowModel', { + allowDeselect : true, + mode : 'MULTI', + listeners : { + selectionchange : function(sm, selections) { + var selected = []; + for (var i = 0; i < selections.length; i++) { + selected.push(selections[i].raw); + } + _this.onSelected.notify(selected); + } + } + }); + + this.panel = Ext.create('Ext.grid.Panel', { + store : this.store, + deferredRender : false, + width : this.width, + height : this.height, + margin : 10, + selModel : this.selModel, + columns : [ { + text : 'Name', + dataIndex : 'fit', + flex : 1, + renderer : function(val, b, record) { + return BUI.getFileName(val); + } + }, { + text : 'PDB', + dataIndex : 'mixtureToStructure3VOs', + flex : 1, + renderer : function(values, b, record) { + var html = ""; + for (var i = 0; i < values.length; i++) { + var structure = BIOSAXS.proposal.getStructuresById(values[i].structureId); + html = html + ""; + if (structure != null){ + html = html + ""; + } + html = html + ""; + } + return html + "
" + structure.name + "
"; + } + }, { + text : 'Volume Fraction', + dataIndex : 'mixtureToStructure3VOs', + flex : 1, + renderer : function(values, b, record) { + var html = ""; + for (var i = 0; i < values.length; i++) { + html = html + ""; + html = html + ""; + html = html + ""; + } + + return html + "
" + values[i].volumeFraction + "
"; + } + },{ + text : 'Subtraction', + dataIndex : 'subtractedFile', + flex : 1, + renderer : function(values, b, record) { + return values.split("/")[values.split("/").length - 1]; + } + },], + + viewConfig : { + enableTextSelection : true, + preserveScrollOnRefresh : true + }, + listeners : { + cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + }, + afterrender : function() { + } + } + }); + return this.panel; +}; + +/** Static method **/ +FitStructureToExperimentDataGrid.prototype.RUNFitScattering = function(subtractionId, structureId) { + var _this = this; + /** Add to Workflow **/ + var adapter = new BiosaxsDataAdapter(); + var workflow = { + 'workflowTitle' : 'FitExperimentalDatatoStructure', + 'comments' : 'FitExperimentalDatatoStructure run from ISPyB for subtractionId: ' + subtractionId + ' and structureId ' + structureId + } + + var inputs = [ { + name : 'subtractionId', + value : subtractionId + }, { + name : 'structureId', + value : structureId + } ]; + + adapter.onSuccess.attach(function(sender, data) { + /** Add to Fit **/ + var adapter2 = new BiosaxsDataAdapter(); + var fit = { + 'workflowId' : data.workflowId, + 'subtractionId' : subtractionId, + 'structureId' : structureId, + 'comments' : 'Sent to workflow engine' + } + adapter2.onSuccess.attach(function(sender, fit) { + _this.refresh(_this.subtractionId, _this.macromolecule); + }); + adapter2.addFitStructureData(fit); + + }); + adapter.addWorkflow(workflow, inputs); + +}; + +/** + * It shows buffer grid with a top bar with "Add" button + * + * @height + * @searchBar + * @collapsed + * @width + */ +function FrameGrid(args) { + this.height = 500; + this.width = 500; + this.searchBar = false; + this.tbar = false; + this.collapsed = false; - specimenGrid.refresh(experiment);*/ - var experimentList = new ExperimentList([ _this.experiment ]); - var measurementContainer = Ext.create('Ext.container.Container', { - layout : 'vbox', - padding : '5px 0px 0px 10px', - items : [] - }); - measurementContainer.insert(0, _this.measurementGridDone.getPanel(this.experiment.getMeasurements(), experimentList)); -// measurementContainer.insert(1, _this.measurementSamplePlateGroupWidget.getPanel(experiment)); + if (args != null) { + if (args.height != null) { + this.height = args.height; + } + + if (args.searchBar != null) { + this.searchBar = args.searchBar; + } - // this.dataCollectionCurveVisualizer = new DataCollectionCurveVisualizer(); - // this.dataCollectionCurveVisualizer.experiments = [experiment]; - // this.dataCollectionCurveVisualizer.dataCollectionFrameTree.experiments = - // [experiment]; - // this.dataCollectionCurveVisualizer.dataCollections = - // experiment.getDataCollections(); + if (args.tbar != null) { + this.tbar = args.tbar; + } - this.panel = Ext.createWidget('tabpanel', { - plain : true, - style : { - padding : 2 - }, - items : [ { - tabConfig : { - id : 'genralTabl', - title : "Overview" - }, - items : [ specimenContainer ] - }, { - tabConfig : { - title : 'Measurements' - }, - items : [ measurementContainer] - }, { - tabConfig : { - id : 'SpecimenTab', - title : 'Analysis', - hidden : this.isTemplate() - }, - items : [ { - xtype : 'container', - layout : 'vbox', - padding : '5px 0px 10px 10px', - items : [ _this.analysisGrid.getPanel([]) ] - } ] - }, - { - tabConfig : { - id : 'newAnalysisTab', - title : 'Analysis BETA', - hidden : this.isTemplate() - }, - items : [ { - xtype : 'container', - layout : 'vbox', - padding : '5px 0px 10px 10px', - items : [ _this.queueGrid.getPanel([]) ] - } ] + if (args.collapsed != null) { + this.collapsed = args.collapsed; } - ] + + if (args.width != null) { + this.width = args.width; + } + } +} + +FrameGrid.prototype._edit = function(bufferId) { + var _this = this; + var window = new BufferWindow(); + window.onSuccess.attach(function(sender, proposal) { + _this.store.loadData(BIOSAXS.proposal.getBuffers()); }); + window.draw(BIOSAXS.proposal.getBufferById(bufferId)); +}; - return this.getPanel(this.panel); +FrameGrid.prototype.refresh = function(buffers, experimentId) { + this.experimentId = experimentId; + this.store.loadData(this._prepareData(buffers), false); }; -ExperimentTabs.prototype.isTemplate = function() { - if (this.experiment.json.type == "TEMPLATE") { - return true; - } - return false; +FrameGrid.prototype._prepareData = function(buffers) { + return buffers; }; -ExperimentTabs.prototype.update = function() { +FrameGrid.prototype._getTbar = function() { var _this = this; - var inter; - if (!_this.isTemplate()) { - function updateExperiments() { - _this.refreshAnalysisData(); - window.clearInterval(inter); - inter = setInterval(function() { - updateExperiments(); - }, _this.INTERVAL_UPDATE); - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, data) { - var experiment = new Experiment(data); - _this.measurementGridDone.refresh(experiment.getMeasurements(), new ExperimentList([ experiment ])); + var actions = []; + + actions.push(Ext.create('Ext.Action', { + icon : '../images/add.png', + text : 'Add Buffer', + disabled : false, + handler : function(widget, event) { + var window = new BufferWindow(); + window.onSuccess.attach(function(sender) { + _this.store.loadData(BIOSAXS.proposal.getBuffers()); }); - adapter.getExperimentById(_this.experiment.json.experimentId, "MEDIUM"); + window.draw({}); } - inter = setInterval(function() { - updateExperiments(); - }, _this.INTERVAL_UPDATE); - } + })); + return actions; }; -ExperimentTabs.prototype.getPanel = function(panel) { +FrameGrid.prototype.getPanel = function(buffers) { var _this = this; - if (this.experimentPanel == null) { - this.experimentPanel = Ext.create('Ext.container.Container', { - bodyPadding : 2, - width : Ext.getBody().getViewSize().width * 0.9, - renderTo : this.targetId, - style : { - padding : 2 - }, - items : [ this.getExperimentTitle(), this.panel ], - listeners : { - afterrender : function() { - _this.refreshAnalysisData(); - _this.update(); - } - } - }); - } - - return this.experimentPanel; -}; - -ExperimentTabs.prototype.input = function() { - return { - experiment : DATADOC.getExperiment_10(), - proposal : DATADOC.getProposal_10() - }; -}; - -ExperimentTabs.prototype.test = function(targetId) { - var experimentTabs = new ExperimentTabs(targetId); - BIOSAXS.proposal = new Proposal(experimentTabs.input().proposal); - experimentTabs.draw(new Experiment(experimentTabs.input().experiment)); -}; + this.store = Ext.create('Ext.data.Store', { + fields : [ 'frameNumber', 'I0', 'Rg', 'Mass', 'Vc', 'Qr', 'quality'], + data : buffers + }); - -/** - * Shows an experiments with the specimens, measurements, analysis tabs where results are shown and the frames widget - * - * @targetId - */ -function HPLCTabs(targetId) { - this.height = 1400; - this.targetId = targetId; + this.store.sort('frameNumber'); - this.id = BUI.id(); + var type = 'Ext.grid.Panel'; + if (this.searchBar == true) { + type = 'Ext.ux.LiveSearchGridPanel'; + } - this.gridHeight = 600; - this.pointsCount = 1036; - this.plotHeight = 350; - this.plotWidth = 800; - this.plotPanelPadding = 5; + this.grid = Ext.create(type, { + store : this.store, + height : this.height, + width : this.width, + margin : 5, + columns : [{ + text : 'Frame', + dataIndex : 'frameNumber', + flex : 1 + },{ + text : 'I0', + dataIndex : 'I0', + flex : 1, + renderer : function(val, y, sample) { + return BUI.formatValuesUnits(sample.data.I0, "", 12, 3); + } + },{ + text : 'Rg', + dataIndex : 'Rg', + flex : 1, + renderer : function(val, y, sample) { + return BUI.formatValuesUnits(sample.data.Rg, "nm", 12, 3); + } + },{ + text : 'Mass', + dataIndex : 'Mass', + flex : 1, + renderer : function(val, y, sample) { + return BUI.formatValuesUnits(sample.data.Mass, "", 12, 3); + } + },{ + text : 'Vc', + dataIndex : 'Vc', + flex : 1, + renderer : function(val, y, sample) { + return BUI.formatValuesUnits(sample.data.Vc, "", 12, 3); + } + },{ + text : 'Qr', + dataIndex : 'Qr', + flex : 1, + renderer : function(val, y, sample) { + return BUI.formatValuesUnits(sample.data.Qr, "", 12, 3); + } + },{ + text : 'Quality', + dataIndex : 'quality', + flex : 1, + renderer : function(val, y, sample) { + return (Number(sample.data.quality) * 100).toFixed(2) + "%"; + } + }, + { + text : '', + renderer : function(val, x, sample) { + if (sample != null) { + return BUI.getZipHTMLByFrameRangeId(_this.experimentId, sample.raw.frameNumber, sample.raw.frameNumber); + } + }, + width : 100 + }], + flex : 1, + viewConfig : { + stripeRows : true + } + }); - var _this = this; + /** Adding the tbar **/ + if (this.tbar) { + this.grid.addDocked({ + xtype : 'toolbar', + items : this._getTbar() + }); + } + return this.grid; +}; - this.analysisGrid = new HPLCAnalysisGrid({ - height : Ext.getBody().getViewSize().height * 0.9 - 300, - positionColumnsHidden : true, - sorters : [ { - property : 'priorityLevelId', - direction : 'ASC' - } ] - }); +FrameGrid.prototype.input = function() { + return []; +}; - this.frameGrid = new FrameGrid({ - width : 600, - height : 60, - collapsed : false, - tbar : false - }); - - this.peakGrid = new PeakGrid({ - width : 550, - height : 500, - collapsed : false, - tbar : false - }); - - this.peakGrid2 = new PeakGrid({ - width : 102, - height : this.plotHeight, +FrameGrid.prototype.test = function(targetId) { + var frameGrid = new FrameGrid({ + width : 800, + height : 350, collapsed : false, - tbar : false, - showExtendedColumns : true + tbar : true }); - - this.mainPlotPanel = new HPLCGraph({ - title : 'I0', - width : this.plotWidth - 110, - height : this.plotHeight, - bbar : true, - plots : { - "I0" : true, - "Rg" : true - }, - xlabel : "HPLC Frames", - scaled : true, - interactionModel : { - 'dblclick' : function(event, g, context) { - _this._selectFrame(g.lastx_); - var annotations = []; - annotations.push({ - series : g.selPoints_[0].name, - x : g.lastx_, - width : 100, - height : 23, - tickHeight : 4, - shortText : g.lastx_, - text : g.lastx_, - attachAtBottom : true - }); - g.setAnnotations(annotations); - } + var panel = frameGrid.getPanel([]); + panel.render(targetId); +}; + + +function PeakGrid(args) { + this.height = 500; + this.searchBar = false; + this.tbar = false; + this.collapsed = false; + this.width = 500; + this.showExtendedColumns = false; + + if (args != null) { + if (args.showExtendedColumns != null) { + this.showExtendedColumns = args.showExtendedColumns; + } + if (args.height != null) { + this.height = args.height; + } + if (args.searchBar != null) { + this.searchBar = args.searchBar; } - }); - this.intensityPlotPanel = new MergesHPLCGraph({ - title : 'Scattering', - width : this.plotWidth, - height : 500, - showRangeSelector : false, - xParam : 0, - xlabel : "scattering_I", - plots : { - "scattering_I" : true, - "subtracted_I" : true, - "buffer_I" : true + if (args.tbar != null) { + this.tbar = args.tbar; } - }); -} + if (args.collapsed != null) { + this.collapsed = args.collapsed; + } -HPLCTabs.prototype._selectFrame = function(frameNumber) { - try{ - this._renderScatteringCurve(frameNumber); - this.frameGrid.refresh([this.mainPlotPanel.getDataByFrameNumber(frameNumber)], this.experiment.experimentId); - } - catch(e){ - console.log(e); + if (args.width != null) { + this.width = args.width; + } } -}; +} -HPLCTabs.prototype.error = function(error) { - var e = JSON.parse(error); - showError(e); - this.panel.setLoading(false); -}; -HPLCTabs.prototype.loadDataMainPlot = function(data) { - var zeroArray = []; - for ( var i = 0; i < data.I0.length; i++) { - zeroArray.push(0); - } - data = [ { - param : "I0", - data : data.I0, - std : data.I0_Stdev, - color : '#0066CC', - label : "I0" - }, - { - param : "sum_I", - label : "sum_I", - color : "#00FF00", - data : data.sum_I, - std : zeroArray - }, - { - param : "Rg", - label : "Rg", - color : "#21610B", - data : data.Rg, - std : data.Rg_Stdev - }, { - param : "Mass", - data : data.mass, - std : data.mass_Stdev, - color : '#FF9900', - label : "Mass" - }, { - param : "Vc", - data : data.Vc, - std : data.Vc_Stdev, - color : '#990099', - label : "Vc" - }, { - param : "Qr", - data : data.Qr, - std : data.Qr_Stdev, - color : '#FF0066', - label : "Qr" - }, { - param : "quality", - label : "quality", - color : "#FF00FF", - data : data.quality, - std : zeroArray - } ]; - this.data = data; - this.mainPlotPanel.loadData(data); +PeakGrid.prototype.refresh = function(buffers) { + this.store.loadData(this._prepareData(buffers), false); }; -HPLCTabs.prototype._loadIntensityPlotByFrameNumber = function(frameNumber) { - var _this = this; +PeakGrid.prototype._prepareData = function(buffers) { - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, data) { - var array = []; - data = [ { - param : "q", - data : data[frameNumber].q, - std : data[frameNumber].q, - fstd : function(a) { - return parseFloat(a); - }, - color : '#green', - label : "q", - showOnMenu : false - }, - { - param : "scattering_I", - data : data[frameNumber].scattering_I, - fdata : function(a) { - return Math.log(parseFloat(a)); - }, - std : data[frameNumber].scattering_Stdev, - fstd : function(a) { - return Math.log(Math.abs(parseFloat(a))); - }, - color : 'green', - label : "Log(I) Sample" - }, - { - param : "buffer_I", - data : data[frameNumber].buffer_I, - fdata : function(a) { - return Math.log(parseFloat(a)); - }, - fstd : function(a) { - return Math.log(Math.abs(parseFloat(a))); - }, - std : data[frameNumber].subtracted_Stdev, - color : '#0000FF', - label : "Log(I) Avg Buf." - }, - { - param : "subtracted_I", - data : data[frameNumber].subtracted_I, - fdata : function(a) { - var value = (Math.log(parseFloat(a))); - if (isNumber(value)) - return value; - }, - fstd : function(a) { - var value = Math.log(Math.abs(parseFloat(a))); - if (isNumber(value)) - return value; - }, - std : data[frameNumber].subtracted_Stdev, - color : 'red', - label : "Log(I) Sub." - } - ]; +// for ( var i = 0; i < buffers.length; i++) { +// buffers[i].name = "Peak #" + (i+1); +// } + /** Adding information of buffer **/ + if (buffers.length > 0){ + buffers.unshift({ + name : "BUFFER", + start : 0, + experimentId : buffers[0].experimentId, + end : buffers[0].start - 1 + }); + } + + + return buffers; +}; - _this.intensityPlotPanel.xlabel = "Frame " + frameNumber + " (q, nm-1)"; +PeakGrid.prototype._getTbar = function() { + var _this = this; + var actions = []; - if (_this.intensityPlotPanel.hplcData == null) { - /** It creates also top bar **/ - _this.intensityPlotPanel.loadData(data); - } else { - _this.intensityPlotPanel.reloadData(data); - + actions.push(Ext.create('Ext.Action', { + icon : '../images/add.png', + text : 'Add Buffer', + disabled : false, + handler : function(widget, event) { + var window = new BufferWindow(); + window.onSuccess.attach(function(sender) { + _this.store.loadData(BIOSAXS.proposal.getBuffers()); + }); + window.draw({}); } - _this.intensityPlotPanel.panel.setLoading(false); - }); - adapter.onError.attach(function(sender, data) { - _this.intensityPlotPanel.panel.setLoading(false); - }); - this.intensityPlotPanel.panel.setLoading("Retrieving Frame " + frameNumber); - adapter.getH5FrameScattering(this.experiment.json.experimentId, frameNumber); + })); + return actions; }; -HPLCTabs.prototype._renderScatteringCurve = function(frameNumber) { +PeakGrid.prototype.getPanel = function(buffers) { var _this = this; + this.peakCount = 0; + this.store = Ext.create('Ext.data.Store', { + fields : ['name', {name:'start', type : 'int'}, {name:'end', type : 'int'}], + data : buffers + + }); - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, data) { - _this.intensityPlotPanel.setPeaks(data); - _this._loadIntensityPlotByFrameNumber(frameNumber); - try{ - var peaks = []; - for(key in data){ - peaks.push({ - start : key.split("-")[0], - end : key.split("-")[1], - experimentId : _this.experiment.json.experimentId - }); + this.store.sort('start'); + + var type = 'Ext.grid.Panel'; + if (this.searchBar == true) { + type = 'Ext.ux.LiveSearchGridPanel'; + } + this.grid = Ext.create(type, { + store : this.store, + height : this.height, + width : this.width, + margin : 5, + columns : [ + { + text : '', + dataIndex : 'name', + width : 100, + hidden : _this.showExtendedColumns, + renderer : function(val, y, sample) { + if (sample.data.name == "BUFFER") + return "BUFFER"; + _this.peakCount++; + return "Peak #" + _this.peakCount; + } + + + }, + { + text : 'Peaks', + dataIndex : 'start', + sortable : true, + width : 100, + hidden: !_this.showExtendedColumns, + renderer : function(val, y, sample) { + return "From " + Number(sample.raw.start) + " to " + Number(sample.raw.end); + } + }, + { + text : 'Frames', + hidden : _this.showExtendedColumns, + columns : [ + { + text : 'Start', + dataIndex : 'start', + sortable : true, + hidden : _this.showExtendedColumns, + width : 100, + renderer : function(val, y, sample) { + return Number(val); + } + },{ + text : 'End', + dataIndex : 'end', + width : 100, + hidden : _this.showExtendedColumns, + renderer : function(val, y, sample) { + return Number(val); + } + },{ + text : 'Total', + dataIndex : 'end', + width : 100, + hidden : _this.showExtendedColumns, + renderer : function(val, y, sample) { + return "" + (sample.raw.end - sample.raw.start) + " frames"; + } + } + ] + } +// , +// { +// text : '', +// hidden : _this.showExtendedColumns, +// renderer : function(val, x, sample) { +// if (sample != null) { +// return BUI.getZipHTMLByFrameRangeId(sample.raw.experimentId, sample.raw.start, sample.raw.end); +// } +// }, +// width : 100 +// } + ], + flex : 1, + viewConfig : { + stripeRows : true, + getRowClass : function(record, rowIdx, params, store) { + if (record.raw.name == "BUFFER") { + return "blue-row"; + } } - _this.peakGrid2.refresh(JSON.parse(JSON.stringify(peaks))); - _this.peakGrid.refresh(peaks); -// console.log(peaks); - } - catch(e){ - showError(e); } }); - adapter.onError.attach(function(sender, data) { - _this.intensityPlotPanel.setLoading(false); + + /** Adding the tbar **/ + if (this.tbar) { + this.grid.addDocked({ + xtype : 'toolbar', + items : this._getTbar() + }); + } + return this.grid; +}; + +PeakGrid.prototype.input = function() { + return []; +}; + +PeakGrid.prototype.test = function(targetId) { + var PeakGrid = new PeakGrid({ + width : 800, + height : 350, + collapsed : false, + tbar : true }); - this.intensityPlotPanel.panel.setLoading("Reading HDF5 File "); - adapter.getH5FramesMerge(this.experiment.json.experimentId); + + var panel = PeakGrid.getPanel([]); + panel.render(targetId); }; + +/** + * Macromolecule Grid showing macromolecules and adding anb updating buttons + * + * @height + * @maxHeight + * @width + * @cssFontStyle + * @searchBar makes this grid as Ext.ux.LiveSearchGridPanel + * @tbar top bar containing "Add" and "Update From SMIS" button + * @collapsed + * @collapsible + * @btnEditVisible + * @btnRemoveVisible + * @multiselect makes it multiselect using Ext.selection.CheckboxModel + * + * #onSelected + * #onMacromoleculesChanged + */ +function MacromoleculeGrid(args) { + this.height = 500; + this.width = 500; + this.id = BUI.id(); + this.maxHeight = this.height; -HPLCTabs.prototype._postRenderOverviewPanel = function() { - var _this = this; - var adapter = new BiosaxsDataAdapter(); - adapter.onError.attach(function(sender, data) { - data = data.replace(/NaN/g, '0'); - _this.loadDataMainPlot(JSON.parse(data)); - + this.searchBar = false; + this.tbar = false; + + this.collapsible = true; + this.collapsed = true; + + this.btnEditVisible = true; + this.btnRemoveVisible = false; + this.multiselect = false; + + /** Font style applied to the acronym column **/ + this.cssFontStyle = null; + + if (args != null) { + if (args.height != null) { + this.height = args.height; + this.maxHeight = this.height; + } + if (args.maxHeight != null) { + this.maxHeight = args.maxHeight; + } + if (args.width != null) { + this.width = args.width; + } + if (args.cssFontStyle != null) { + this.cssFontStyle = args.cssFontStyle; + } + + if (args.searchBar != null) { + this.searchBar = args.searchBar; + } + if (args.tbar != null) { + this.tbar = args.tbar; + } + if (args.collapsible != null) { + this.collapsible = args.collapsible; + } + if (args.collapsed != null) { + this.collapsed = args.collapsed; + } + if (args.btnEditVisible != null) { + this.btnEditVisible = args.btnEditVisible; + } + if (args.btnRemoveVisible != null) { + this.btnRemoveVisible = args.btnRemoveVisible; + } + if (args.multiselect != null) { + this.multiselect = args.multiselect; + } + } - }); - adapter.onSuccess.attach(function(sender, data) { - _this.loadDataMainPlot(data); - _this._renderScatteringCurve(0); - }); - adapter.getH5fileParameters(this.experiment.json.experimentId, [ - "I0", "I0_Stdev", "sum_I","Rg", "Rg_Stdev", "Vc", "Vc_Stdev", "Qr", "Qr_Stdev", "mass", "mass_Stdev", "quality" ]); + this.onSelected = new Event(); -}; + this.onMacromoleculesChanged = new Event(); +} -HPLCTabs.prototype._postRenderDetailsPanel = function() { - if (this.data != null) { - this.I0PlotPanel.loadData(this.data); - this.RgPlotPanel.loadData(this.data); - this.MassPlotPanel.loadData(this.data); - this.VcPlotPanel.loadData(this.data); - } -}; -HPLCTabs.prototype.draw = function(experiment) { - var _this = this; - this.renderDataAcquisition(experiment); -}; -HPLCTabs.prototype.getExperimentTitle = function() { - var experimentHeaderForm = new ExperimentHeaderForm(); - return experimentHeaderForm.getPanel(this.experiment); -}; -HPLCTabs.prototype.renderDataAcquisition = function(experiment) { +MacromoleculeGrid.prototype.edit = function(macromolecule) { var _this = this; - this.experiment = experiment; - if (this.experimentPanel == null) { - this.experimentPanel = Ext.create('Ext.container.Container', { - bodyPadding : 2, - height : this.height, - width : Ext.getBody().getViewSize().width * 0.9, - renderTo : this.targetId, - style : { - padding : 2 - }, - items : [ this.getExperimentTitle(), this.getPanel() ], - listeners : { - afterrender : function() { - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, data) { - _this.analysisGrid.refresh(data); - }); - adapter.getAnalysisInformationByExperimentId(_this.experiment.experimentId); - } - } - }); - } - return this.experimentPanel; + var window = new MacromoleculeWindow(); + window.onSave.attach(function(sender) { + _this.store.loadData(BIOSAXS.proposal.getMacromolecules()); + _this.onMacromoleculesChanged.notify(); + }); + window.draw(macromolecule); }; -HPLCTabs.prototype.getPanel = function() { +MacromoleculeGrid.prototype.getTbar = function() { var _this = this; - this.panel = Ext.createWidget('tabpanel', - { - plain : true, - height : this.height, -// width : 900, - style : { - padding : 2 - }, - items : [ - { - tabConfig : { - title : "Overview" + var actions = []; - }, - items : [ { - xtype : 'container', - layout : 'hbox', - border : 1, - flex : 1, - items : [ { - xtype : 'container', - layout : 'vbox', - border : 1, - items : [ { - xtype : 'container', - margin : '10px', - border : 1, - items : [ - { - xtype : 'container', - margin : '0px', - layout : 'hbox', - border : 1, - items : [ - this.peakGrid2.getPanel([]), - this.mainPlotPanel.getPanel() - ] - }, - { - xtype : 'container', - layout : 'vbox', - border : 1, - items : [ - { - html: '
Select a frame by double-clicking on the HPLC Frames plot
', - margin: 5, - border : 0 - }, - this.frameGrid.getPanel([]), - this.intensityPlotPanel.getPanel() - ] - } - - ] - }], - listeners : { - afterrender : function() { - _this._postRenderOverviewPanel(); - } - } - } ] - } ] - }, -// { -// tabConfig : { -// title : "Details" -// }, -// items : [ { -// xtype : 'container', -// layout : 'hbox', -// border : 1, -// flex : 1, -// items : [ { -// xtype : 'container', -// layout : 'vbox', -// border : 1, -// items : [ { -// xtype : 'container', -// padding : '1px', -// items : [ -// this.I0PlotPanel.getPanel(), this.RgPlotPanel.getPanel(), this.MassPlotPanel.getPanel(), -// this.VcPlotPanel.getPanel() ] -// } ], -// listeners : { -// afterrender : function() { -// _this._postRenderDetailsPanel(); -// } -// } -// } ] -// } ] -// }, - { - tabConfig : { - title : "Analysis" - }, - items : [ { - xtype : 'container', - layout : 'vbox', - padding : '5px 0px 10px 10px', - items : [ _this.analysisGrid.getPanel([]) ] - } ] - }, - { - tabConfig : { - title : "File Manager" - }, - items : [ { - xtype : 'container', - layout : 'vbox', - padding : '5px 0px 10px 10px', - items : [ - { - html: '
Custom Download
', - margin : '10 0 10 5', - border : 0 - }, - { - html: '
Download has been temporary disabled. Contact your labcontact if you want to extract the frames from the HDF5 file
', - margin : '10 0 10 5', - hidden : _this.experiment.experimentId == 2602, - border : 0 - }, - { - xtype : 'container', - layout : 'hbox', - margin : '0 0 0 20', - flex : 1, - items :[ - { - xtype: 'numberfield', - id: 'field_start', - fieldLabel: 'Frames from', - value: 0, - minValue: 0 - }, - { - xtype: 'numberfield', - id: 'field_end', - fieldLabel: 'to', - labelWidth : 20, - margin : '0 0 0 10', - minValue: 0 - }, - { - xtype: 'button', - /** allowing test account to download frames **/ - disabled : _this.experiment.experimentId != 2602, - text : 'Download', - margin : '0 0 0 10', - handler : function() { - var experimentId = _this.experiment.experimentId; - var start = Ext.getCmp("field_start").getValue(); - var end = Ext.getCmp("field_end").getValue(); - /** Checking if start is bigger than end **/ - if (start > end){ - var aux = end; - end = start; - start = aux; - } - var url = BUI.getZipFrameHPLCUrl(experimentId, start, end); - window.open(url) - } - } - ] - }, - _this.peakGrid.getPanel([]) - - ] - } - ] - } - ] + actions.push(Ext.create('Ext.Action', { + icon : '../images/add.png', + text : 'Add macromolecule', + disabled : false, + handler : function(widget, event) { + _this.edit(); + } + })); + actions.push("->"); + actions.push(Ext.create('Ext.Action', { + icon : '../images/folder_go.png', + text : 'Update From SMIS', + tooltip : "Retrieve all the macromolecules of your proposal from SMIS database", + disabled : false, + handler : function(widget, event) { + _this.grid.setLoading("Connecting to SMIS"); + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, data) { + BIOSAXS.proposal.setMacromolecules(data.macromolecules); + _this.refresh(BIOSAXS.proposal.macromolecules); + _this.grid.setLoading(false); + }); + adapter.onError.attach(function(sender, data) { + _this.grid.setLoading(false); }); + adapter.updateDataBaseFromSMIS(); + } + })); + return actions; +}; - return this.panel; +MacromoleculeGrid.prototype.deselectAll = function() { + this.grid.getSelectionModel().deselectAll(); +}; + +MacromoleculeGrid.prototype.selectById = function(macromoleculeId) { + this.grid.getSelectionModel().deselectAll(); + for ( var i = 0; i < this.grid.getStore().data.items.length; i++) { + var item = this.grid.getStore().data.items[i].raw; + if (item.macromoleculeId == macromoleculeId) { + this.grid.getSelectionModel().select(i); + } + } +}; + +MacromoleculeGrid.prototype.refresh = function(macromolecules) { + this.store.loadData(macromolecules, false); +}; + +MacromoleculeGrid.prototype.getColumns = function() { + var _this = this; + var columns = [ + { + text : 'Acronym', + dataIndex : 'acronym', + id : this.id + "acronym", + flex : 1, + renderer : function(value, metaData, record, rowIndex, colIndex, store) { + if (_this.cssFontStyle != null) { + return "" + value + ""; + } + return value; + } + }, { + text : 'Name', + dataIndex : 'name', + id : this.id + "name", + flex : 1, + hidden : true + } ]; + + if (this.btnEditVisible) { + columns.push({ + id : _this.id + 'buttonEditMacromolecule', + width : 85, + sortable : false, + renderer : function(value, metaData, record, rowIndex, colIndex, store) { + if (_this.btnEditVisible) { + return BUI.getGreenButton('EDIT'); + } + return null; + } + }); + } + if (this.btnRemoveVisible) { + columns.push({ + id : _this.id + 'buttonRemoveMacromolecule', + width : 85, + sortable : false, + renderer : function(value, metaData, record, rowIndex, colIndex, store) { + if (_this.btnRemoveVisible) { + return BUI.getRedButton('REMOVE'); + } + return null; + } + }); + } + + return columns; +}; + + +/** Returns the grid **/ +MacromoleculeGrid.prototype.getPanel = function() { + var _this = this; + + this.store = Ext.create('Ext.data.Store', { + fields : [ 'macromoleculeId', 'name', 'acronym' ] + }); + + var type = 'Ext.grid.Panel'; + if (this.searchBar == true) { + type = 'Ext.ux.LiveSearchGridPanel'; + } + + this.selModel = Ext.create('Ext.selection.RowModel', { +// allowDeselect : true, +// mode : 'MULTI', + listeners : { + selectionchange : function(sm, selections) { + var selected = []; + for (var i = 0; i < selections.length; i++) { + selected.push(selections[i].raw); + } + _this.onSelected.notify(selected); + } + } + }); + + this.grid = Ext.create(type, { + id : this.id, + title : 'Macromolecules', + collapsible : this.collapsible, + collapsed : this.collapsed, + store : this.store, + height : this.height, + maxHeight : this.maxHeight, + selModel : this.selModel, + columns : this.getColumns(), + width : this.width, + viewConfig : { + stripeRows : true, + listeners : { + 'celldblclick' : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + _this.edit(BIOSAXS.proposal.getMacromoleculeById(record.data.macromoleculeId)); + }, + 'cellclick' : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonEditMacromolecule') { + _this.edit(BIOSAXS.proposal.getMacromoleculeById(record.data.macromoleculeId)); + } + if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonRemoveMacromolecule') { + BUI.showBetaWarning(); + } + } + + } + } + }); + + /** Adding the tbar **/ + if (this.tbar) { + this.grid.addDocked({ + xtype : 'toolbar', + items : this.getTbar() + }); + } + return this.grid; +}; + +MacromoleculeGrid.prototype.input = function() { + return { + proposal : DATADOC.getProposal_10() + }; +}; + +MacromoleculeGrid.prototype.test = function(targetId) { + var macromoleculeGrid = new MacromoleculeGrid({ + width : 800, + height : 350, + collapsed : false, + tbar : true + }); + + BIOSAXS.proposal = new Proposal(macromoleculeGrid.input().proposal); + var panel = macromoleculeGrid.getPanel(BIOSAXS.proposal.macromolecules); + panel.render(targetId); }; /** - * Main form tab for macromolecule + * Shows measurements with their attributes as specimen, exposure temperature, + * volume to load, transmission etc... * - * @width + * @addBtnMultipleEdit: if true add a button for changing measurements' + * parameters by choosing multiple measurements. It opens + * MultipleEditMeasurementGridWindow + * @collapsed: if true it doesn't show buffer's measurements + * @editor: hashmap containing columns to be edited. It is an array of a json + * like editor of Ext + * @selModel + * @collapseBtnEnable + * @removeBtnEnabled Ext.selection.RowModel + * @addBtnEnable + * @updateRowEnabled true/false if update plugin is set to the grid + * @showTitle + * @title + * @isStatusColumnHidden + * @isPriorityColumnHidden + * @isTimeColumnHidden + * @estimateTime + * @margin + * @tbar * @height + * @maxHeight + * @minHeight + * @width + * @maxWidth + * @resizable + * @experimentColorBased when colors for buffers and macromolecules are not + * selected by the proposal but by experiment, so the + * number of colors is smaller * - * #onClose when user clicks on close button in the MacromoleculeForm - * #onSave when macromole is saved by macromoleculeForm + * #onClick #onSelected #onRemoved #onUpdateTime #onMeasurementChanged + * #onExperimentChanged */ -function MacromoleculeTabs(args) { - this.width = 500; +function MeasurementGrid(args) { + this.id = BUI.id(); + this.height = 500; + this.width = 900; - if (args != null) { - if (args.width != null) { - this.width = args.width; + this.maxWidth = 1200; + this.maxHeight = 600; + this.minHeight = 500; + + this.unitsFontSize = 9; + this.title = "Measurements"; + this.estimateTime = false; + this.collapsed = true; + this.tbar = true; + + this.showTitle = true; + this.resizable = true; + this.updateRowEnabled = true; + + /** + * Hash map containing the keys of the editable columns. Ex: + * 'exposureTemperature' * + */ + this.editor = { + comments : { + xtype : 'textfield', + allowBlank : true } - if (args.height != null) { - this.height = args.height; + }; + + this.isTimeColumnHidden = false; + this.isStatusColumnHidden = false; + this.isPriorityColumnHidden = true; + this.margin = "0 0 0 0"; + + this.addBtnEnable = true; + this.sorter = [ { + property : 'priority', + direction : 'ASC' + } ]; + + this.removeBtnEnabled = false; + this.collapseBtnEnable = true; + this.addBtnMultipleEdit = false; + this.sortingBtnEnable = false; + + var _this = this; + this.selModel = Ext.create('Ext.selection.RowModel', { + allowDeselect : true, + mode : 'MULTI', + listeners : { + selectionchange : function(sm, selections) { + var selected = []; + for (var i = 0; i < selections.length; i++) { + selected.push(selections[i].raw); + } + _this.onSelected.notify(selected); + } } - } + }); - var _this = this; + if (args != null) { - /** Widgets **/ + if (args.selModel != null) { + this.selModel = args.selModel; + } - /** Macromolecule Form **/ - this.macromoleculeForm = new MacromoleculeForm({ - width : this.width - 30, - height : this.height - 50, - }); + if (args.removeBtnEnabled != null) { + this.removeBtnEnabled = args.removeBtnEnabled; + } - this.macromoleculeForm.onClose.attach(function(sender) { - _this.onClose.notify(); - }); + if (args.addBtnMultipleEdit != null) { + this.addBtnMultipleEdit = args.addBtnMultipleEdit; + } - this.macromoleculeForm.onSave.attach(function(sender, macromolecule) { - _this.onSave.notify(macromolecule); - }); + // if (args.experimentColorBased != null) { + // this.experimentColorBased = args.experimentColorBased; + // } - this.assemblyForm = new AssemblyForm({ - width : this.width - 30, - height : this.height - 50, - }); - - this.rigibBodyModelingForm = new RigibBodyModelingForm({ - width : this.width - 30, - height : this.height - 50, - }); - - this.rigibBodyModelingForm.onSave.attach(function(sender, macromolecule) { - _this.onSave.notify(macromolecule); - }); - + if (args.collapsed != null) { + this.collapsed = args.collapsed; + } + if (args.resizable != null) { + this.resizable = args.resizable; + } - /** Events **/ - this.onClose = new Event(this); - this.onSave = new Event(this); -} + if (args.editor != null) { + this.editor = args.editor; + } -/** Populate the widget **/ -MacromoleculeTabs.prototype.refresh = function(macromolecule) { - this.macromoleculeForm.refresh(macromolecule); - this.assemblyForm.refresh(macromolecule); - this.rigibBodyModelingForm.refresh(macromolecule); + if (args.collapseBtnEnable != null) { + this.collapseBtnEnable = args.collapseBtnEnable; + } - if (macromolecule != null){ - if (macromolecule.macromoleculeId == null){ - Ext.getCmp(this.id + "_advancedTab").disable(); - Ext.getCmp(this.id + "_assemblyTab").disable(); + if (args.addBtnEnable != null) { + this.addBtnEnable = args.addBtnEnable; } - else{ - Ext.getCmp(this.id + "_advancedTab").enable(); - Ext.getCmp(this.id + "_assemblyTab").enable(); + if (args.sortingBtnEnable != null) { + this.sortingBtnEnable = args.sortingBtnEnable; } - } - else{ - Ext.getCmp(this.id + "_advancedTab").disable(); - Ext.getCmp(this.id + "_assemblyTab").disable(); - } -}; -MacromoleculeTabs.prototype.getItems = function() { - return [ { - tabConfig : { - title : 'General' - }, - items : [ { - xtype : 'container', - items : [ this.macromoleculeForm.getPanel() ] - } ] - }, { - id : this.id + "_assemblyTab", - tabConfig : { - title : 'Assembly' - }, - items : [ { - xtype : 'container', - items : [ this.assemblyForm.getPanel() ] - } ] - },{ - id : this.id + "_advancedTab", - tabConfig : { - title : 'Advanced' - }, - items : [ this.rigibBodyModelingForm.getPanel() ] - } ]; -}; + if (args.isPriorityColumnHidden != null) { + this.isPriorityColumnHidden = args.isPriorityColumnHidden; + } -MacromoleculeTabs.prototype.getPanel = function() { - this.panel = Ext.createWidget('tabpanel', { - height : this.height, - width : this.width, - plain : true, - margin : 5, - border : 0, - items : this.getItems() - }); - return this.panel; -}; - -function ResultTabs() { -} + if (args.width != null) { + this.width = args.width; + } -ResultTabs.prototype.draw = function(targetId, data, macromoleculeId) { - var panel = this.getPanel(targetId, data, macromoleculeId); - return Ext.create('Ext.container.Container', { - renderTo : targetId, - items : [ BUI.getMacromoleculeHeader(macromoleculeId), panel ] - }); + if (args.updateRowEnabled != null) { + this.updateRowEnabled = args.updateRowEnabled; + } -}; + if (args.showTitle != null) { + this.showTitle = args.showTitle; + if (this.showTitle == false) { + this.title = null; + } + } -ResultTabs.prototype._splitBySpecimen = function(data) { - var splitted = {}; + if (args.height != null) { + this.height = args.height; + } - for ( var i = 0; i < data.length; i++) { - var row = data[i]; - if (splitted[row.macromoleculeId + "_" + row.bufferId] == null) { - splitted[row.macromoleculeId + "_" + row.bufferId] = []; + if (args.maxHeight != null) { + this.maxHeight = args.maxHeight; } - splitted[row.macromoleculeId + "_" + row.bufferId].push(row); - } - return splitted; -}; + if (args.minHeight != null) { + this.minHeight = args.minHeight; + } -ResultTabs.prototype._getTabTitle = function(key, data) { - var macromoleculeId = key.split("_")[0]; - var bufferId = key.split("_")[1]; + if (args.maxWidth != null) { + this.maxWidth = args.maxWidth; + } - return BIOSAXS.proposal.getMacromoleculeById(macromoleculeId).acronym + " + " + BIOSAXS.proposal.getBufferById(bufferId).acronym + "(" + data.length + ")"; -}; + if (args.isStatusColumnHidden != null) { + this.isStatusColumnHidden = args.isStatusColumnHidden; + } + if (args.isTimeColumnHidden != null) { + this.isTimeColumnHidden = args.isTimeColumnHidden; + } -ResultTabs.prototype.getPanel = function(targetId, data, macromoleculeId) { + if (args.title != null) { + this.title = args.title; + } + if (args.estimateTime != null) { + this.estimateTime = args.estimateTime; + } - var dataFiltered = new AnalysisGrid({ - hideNulls : true, - grouped : true, - sorters : [ { - property : 'quality', - direction : 'DESC' - } ] - })._prepareData(data); + if (args.margin != null) { + this.margin = args.margin; + } - var items = [ { - tabConfig : { - title : 'Analysis (' + dataFiltered.length + ')' - }, - items : [ { - xtype : 'container', - layout : 'vbox', - padding : 10, - items : [ new AnalysisGrid({ - isScatteringPlotVisible : false - }).getPanel(dataFiltered) ] - } ] - }, { - tabConfig : { - title : 'Concentration Effects' - }, - items : [ new ResultSummaryForm().getPanel(data) ] - } ]; + if (args.tbar != null) { + this.tbar = args.tbar; + } + + if (args.sorter != null) { + this.sorter = args.sorter; + } - var splitted = this._splitBySpecimen(dataFiltered); - for ( var key in splitted) { - items.push({ - tabConfig : { - title : this._getTabTitle(key, splitted[key]) - }, - items : [ new ResultSummaryForm().getPanel(splitted[key]) ] - }); } + this.onClick = new Event(this); + this.onSelected = new Event(this); + this.onRemoved = new Event(this); + this.onUpdateTime = new Event(this); + this.onMeasurementChanged = new Event(this); + this.onExperimentChanged = new Event(this); +} - this.panel = Ext.createWidget('tabpanel', { - style : { - padding : 2 - }, - items : items +MeasurementGrid.prototype._sortBy = function(sort) { + var _this = this; + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, data) { + _this.onExperimentChanged.notify(data); + _this.grid.setLoading(false); + // wizardWidget.window.close(); }); - return this.panel; - + adapter.onError.attach(function(sender, data) { + _this.grid.setLoading(false); + alert("Oops, there was a problem"); + }); + _this.grid.setLoading("Sorting"); + adapter.sortMeasurements(this.experiments.experiments[0].experimentId, sort); }; - - -function ShipmentTabs(targetId) { - this.targetId = targetId; +MeasurementGrid.prototype._getMenu = function() { var _this = this; - this.gridHeight = 500; - /** data **/ - this.shipment = null; - - /** Shipment Form **/ - this.shipmentForm = new ShipmentForm({ - creationMode : false, - showTitle : false - }); - this.shipmentForm.onSaved.attach(function(sender, data) { - _this.refresh(data); + if (this.tbar) { - }); + var items = []; + if (_this.addBtnEnable) { + items.push({ + icon : '../images/add.png', + text : 'Add measurements', + handler : function() { + _this._openAddMeasurementWindow(); + } + }); + } + if (_this.addBtnMultipleEdit) { + items.push({ + icon : '../images/Edit_16x16_01.png', + text : 'Multiple Edit', + handler : function() { + var multipleEditMeasurementGridWindow = new MultipleEditMeasurementGridWindow(); + multipleEditMeasurementGridWindow.onExperimentChanged.attach(function(sender, data) { + _this.onExperimentChanged.notify(data); + }); - /** Cases grid **/ - this.caseGrid = new CaseGrid({ - height : this.gridHeight - }); + multipleEditMeasurementGridWindow.draw(_this.measurements, _this.experiments); - this.caseGrid.onAddButtonClicked.attach(function(sender, dewar) { - _this.caseGrid.grid.setLoading("ISPyB: Creating a new case"); - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, shipment) { - /** updateing shipment on proposal **/ - for ( var i = 0; i < BIOSAXS.proposal.shippings.length; i++) { - if (BIOSAXS.proposal.shippings[i].shippingId == shipment.shippingId) { - BIOSAXS.proposal.shippings[i] = shipment; } - } - _this.refresh(shipment); - }); - adapter.onError.attach(function(sender, shipment) { - _this.caseGrid.grid.setLoading(false); - }); - adapter.addCase(_this.shipment.json.shippingId); - }); + }); + } - this.caseGrid.onRemoveButtonClicked.attach(function(sender, dewarId) { - _this.panel.setLoading("ISPyB: removing case"); - _this.shipment.onSaved.attach(function(sender, shipment) { - _this.refresh(shipment); + items.push("->"); - }); - _this.shipment.removeCase(dewarId); - }); -} + if (_this.sortingBtnEnable) { + var split = Ext.create('Ext.button.Split', { + text : 'Sort by', + // handle a click on the button itself + handler : function() { + // alert("The button was clicked"); + }, + menu : new Ext.menu.Menu({ + items : [ + { + text : 'First Created First Measured', + handler : function() { + _this._sortBy("FIFO"); + } + }, "-", { + text : 'Default', + handler : function() { + _this._sortBy("DEFAULT"); + } + } ] + }) + }); + items.push(split); + } -ShipmentTabs.prototype.refresh = function(shipment) { + if (_this.collapseBtnEnable) { + items.push({ + text : 'Collapse buffers', + enableToggle : true, + scope : this, + toggleHandler : function(item, pressed) { + this.collapsed = pressed; + this.grid.getStore().loadData(this._prepareData(this.measurements, this.experiments), false); + }, + pressed : this.collapsed + }); + } + var tb = Ext.create('Ext.toolbar.Toolbar', { + items : items + }); + return tb; + } + return null; +}; +/** Opens WizardWidget for adding new measurements * */ +MeasurementGrid.prototype._openAddMeasurementWindow = function(measurements, experiments) { var _this = this; - this.shipment = shipment; - var proposal = new Proposal(); - proposal.onDataRetrieved.attach(function(sender, plates) { - - _this.refreshWithPlates(shipment, plates); - _this.caseGrid.grid.setLoading(false); + var wizardWidget = new WizardWidget(); + wizardWidget.onFinished.attach(function(sender, result) { + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, data) { + _this.onExperimentChanged.notify(data); + _this.grid.setLoading(false); + wizardWidget.window.close(); + }); + wizardWidget.current.setLoading("ISPyB: Adding measurements"); + adapter.addMeasurements(result.name, "comments", result.data, _this.experiments.experiments[0].experimentId); }); - proposal.getPlatesByProposal(); + + wizardWidget.draw(null, new MeasurementCreatorStepWizardForm(BIOSAXS.proposal.getMacromolecules(), { + noNext : true + })); }; -ShipmentTabs.prototype.refreshWithPlates = function(shipment, plates) { - this.shipment = new Shipment(shipment); +/******************************************************************************* + * Opens WizardWidget for adding new measurements + * + * @Measurements + * @Experiments experimentList Object + ******************************************************************************/ +MeasurementGrid.prototype._prepareData = function(measurements, experiments) { + var data = []; + for (var i = 0; i < measurements.length; i++) { + var measurement = measurements[i]; + var specimen = experiments.getSampleById(measurement.specimenId); + var buffer = experiments.getBufferById(specimen.bufferId); + measurement.buffer_acronym = buffer.acronym; + measurement.bufferId = buffer.bufferId; + measurement.volume = specimen.volume; + if (specimen.macromolecule3VO != null) { + measurement.acronym = specimen.macromolecule3VO.acronym; + measurement.macromoleculeId = specimen.macromolecule3VO.macromoleculeId; + } + measurement.concentration = specimen.concentration; + if (measurement.run3VO != null) { + measurement.energy = measurement.run3VO.energy; + measurement.expExposureTemperature = measurement.run3VO.exposureTemperature; + measurement.storageTemperature = measurement.run3VO.storageTemperature; + measurement.timePerFrame = measurement.run3VO.timePerFrame; + measurement.radiationAbsolute = measurement.run3VO.radiationAbsolute; + measurement.radiationRelative = measurement.run3VO.radiationRelative; + measurement.status = "DONE"; - this.caseGrid.refresh(this.shipment.getDewars(), plates); - this.panel.setLoading(false); + try { + if (measurement.run3VO.timeStart != null) { + if (measurement.run3VO.timeStart != "") { + measurement.miliseconds = moment(measurement.run3VO.timeStart).format("X"); + } + } + } catch (E) { + console.log(E); + } + } + + if (experiments.getDataCollectionByMeasurementId(measurement.measurementId).length > 0) { + var measurementtodatacollection3VOs = experiments.getDataCollectionByMeasurementId(measurement.measurementId)[0].measurementtodatacollection3VOs; + for (var k = 0; k < measurementtodatacollection3VOs.length; k++) { + if (measurementtodatacollection3VOs[k].dataCollectionOrder == 1) { + var specimenBuffer = experiments.getSampleById(experiments.getMeasurementById(measurementtodatacollection3VOs[k].measurementId).specimenId); + if (specimenBuffer.sampleplateposition3VO != null) { + measurement.bufferSampleplateposition3VO = specimenBuffer.sampleplateposition3VO; + measurement.bufferSampleplate = (experiments.getSamplePlateById(specimenBuffer.sampleplateposition3VO.samplePlateId)); + } + } + } + } + + if (this.collapsed) { + /** If collapsed only the samples * */ + if (specimen.macromolecule3VO != null) { + data.push(measurement); + } + } else { + data.push(measurement); + } + + } + return data; }; -ShipmentTabs.prototype.error = function(error) { - var e = JSON.parse(error); - showError(e); - this.panel.setLoading(false); +/** + * Refresh data grid with the measurements and the experiments + * + * @measurements array with measurement3VO objects + * @experiments array with experiments objects + */ +MeasurementGrid.prototype.refresh = function(measurements, experiments) { + this.experiments = experiments; + this.measurements = measurements; + this.store.loadData(this._prepareData(measurements, experiments), false); }; -//ShipmentTabs.prototype.refreshTabTitles = function() { -/*Ext.getCmp("MacromoleculeTab").setText(this.getMacromoleculeTitle()); -Ext.getCmp("BufferTab").setText(this.getBuffersTitle()); -Ext.getCmp("SpecimenTab").setText(this.getSpecimenTitle()); -Ext.getCmp("PlatesTab").setText(this.getPlatesTitle()); -Ext.getCmp("AssembliesTab").setText(this.getShipmentTitle()); -Ext.getCmp("PlateGroupsTab").setText(this.getPlateGroupsTitle());*/ -//}; -ShipmentTabs.prototype.draw = function(shipment) { - var _this = this; - _this.plates = []; - _this.shipment = shipment; - _this.render(shipment); +/** + * Set status bar to busy (refreshing icon) + * + * @msg message to be displayed on the bar + */ +MeasurementGrid.prototype._showStatusBarBusy = function(msg) { + var statusBar = Ext.getCmp(this.id + 'basic-statusbar'); + statusBar.setStatus({ + text : msg, + iconCls : 'x-status-busy', + clear : false + }); +}; - // var proposal = new Proposal(); - // proposal.onDataRetrieved.attach(function(sender, plates){ - // _this.plates = plates; - // _this.shipment = shipment; - // _this.render(shipment); - // - // }); - // proposal.getPlatesByProposal(); +/** + * Set status bar to ready (ok icon) + * + * @msg message to be displayed on the bar + */ +MeasurementGrid.prototype._showStatusBarReady = function(msg) { + var statusBar = Ext.getCmp(this.id + 'basic-statusbar'); + statusBar.setStatus({ + text : msg, + iconCls : 'x-status-valid', + clear : false + }); }; -//ShipmentTabs.prototype.getShipmentTitle = function() { -// return 'Shipment'; -//}; +/** + * If updateRowEnabled returns an array with Ext.grid.plugin.RowEditing + */ +MeasurementGrid.prototype._getPlugins = function() { + var _this = this; + var plugins = []; + if (this.updateRowEnabled) { + plugins.push(Ext.create('Ext.grid.plugin.RowEditing', { + clicksToEdit : 1, + listeners : { + validateedit : function(grid, e) { + /** Setting values * */ + for ( var key in _this.editor) { + e.record.raw[key] = e.newValues[key]; + } + /** Comments are always updatable* */ + e.record.raw.comments = e.newValues.comments; -//ShipmentTabs.prototype.getMacromoleculeTitle = function() { -// return 'Macromolecules (' + this.experiment.getMacromolecules().length + ')'; -//}; + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, measurement) { + _this.onMeasurementChanged.notify(measurement); + _this.grid.setLoading(false); + }); + adapter.onError.attach(function() { + alert("Error"); + _this.grid.setLoading(false); + }); -//ShipmentTabs.prototype.getBuffersTitle = function() { -// return 'Buffers (' + this.experiment.getBuffers().length + ')'; -//}; + _this.grid.setLoading(); + adapter.saveMeasurement(e.record.raw, _this.experiments.experiments[0]); + } + } + })); + } + return plugins; +}; -//ShipmentTabs.prototype.getPlateGroupsTitle = function() { -// return 'Plate Groups (' + this.experiment.getPlateGroups().length + ')'; -//}; +/** + * @key name of the columns mathing the this.editor[key] + */ +MeasurementGrid.prototype._getEditor = function(key) { + if (this.editor[key] != null) { + return this.editor[key]; + } + return null; +}; -//ShipmentTabs.prototype.getSampleChangerTitle = function() { -// return 'Sample Changer'; -//}; +MeasurementGrid.prototype.getPanel = function(measurements, experiments) { + this.experiments = experiments; + this.measurements = measurements; + var _this = this; + this.store = Ext.create('Ext.data.Store', { + fields : [ { + name : 'miliseconds', + type : 'int' + }, 'priority', 'bufferId', { + name : 'exposureTemperature', + type : 'numeric' + }, 'volumeToLoad','code', 'transmission', 'viscosity', 'waitTime', 'flow', 'buffer_acronym', 'macromoleculeId', 'acronym', 'concentration', 'extraFlowTime', 'volume', 'energy', + 'expExposureTemperature', 'storageTemperature', 'timePerFrame', 'radiationAbsolute', 'radiationRelative', 'status', 'comments' ], + data : this._prepareData(measurements, experiments) + }); + this.store.sort(this.sorter); + var bbar = {}; + try { + bbar = Ext.create('Ext.ux.StatusBar', { + id : _this.id + 'basic-statusbar', + defaultText : 'Ready', + text : 'Ready', + iconCls : 'x-status-valid', + items : [] + }); + } catch (exp) { + console.log("bbar error"); + } -//ShipmentTabs.prototype.getSpecimenTitle = function() { -// return 'Specimens(' + this.experiment.getSpecimenCount() + ')'; -//}; + this.grid = Ext + .create( + 'Ext.grid.Panel', + { + id : this.id, + title : this.title, + store : this.store, + selModel : this.selModel, + plugins : this._getPlugins(), + resizable : this.resizable, + margin : this.margin, + maxHeight : this.maxHeight, + minHeight : this.minHeight, + maxWidth : this.maxWidth, + width : this.width, + tbar : this._getMenu(), + columns : [ + { + text : 'Order', + dataIndex : 'priority', + width : 50, + hidden : _this.isPriorityColumnHidden, + sortable : true -//ShipmentTabs.prototype.getPlatesTitle = function() { -// return 'Plates(' + this.experiment.getSamplePlates().length + ')'; -//}; + }, + { + text : 'Run Number', + dataIndex : 'code', + width : 50, + hidden : true, + sortable : true -//ShipmentTabs.prototype.getBuffersTip = function() { -/*if (this.experiment.getBuffers().length == 0){ - return BUI.getWarningHTML("There are no buffers. Click on add to create new ones. Click on edit button or double click to edit them"); - -} -else{ - return BUI.getTipHTML("Click on edit button or double click to edit them. Click on duplicate to create an identical buffer including its additives") -}*/ -//}; + }, -//ShipmentTabs.prototype.refreshTips = function() { -/*Ext.getCmp("BufferTabTip").update(this.getBuffersTip()); -Ext.getCmp("SpecimenTabTip").update(this.getSpecimensTip());*/ -//}; + { + text : 'Specimen', + columns : [ -ShipmentTabs.prototype.render = function(shipment) { - return this.getPanel(shipment); -}; + { + text : '', + dataIndex : 'macromoleculeId', + width : 30, + renderer : function(val, y, sample) { + if (val != null) { + if (_this.experiments == null) { + return BUI.getRectangleColorDIV(BIOSAXS.proposal.macromoleculeColors[val], 10, 10); + } else { + return BUI.getRectangleColorDIV(_this.experiments.macromoleculeColors[val], 10, 10); + } + } + }, + sortable : true + }, + { + text : 'Macromo.', + dataIndex : 'acronym', + width : 80, + renderer : function(val, y, sample) { + return val; + }, + sortable : true + }, + { + text : 'Conc. ', + dataIndex : 'concentration', + width : 80, + renderer : function(val, y, sample) { + if (sample.raw.macromoleculeId == null) { + return ""; + } + if (isNaN(val)) + return val; -ShipmentTabs.prototype.getShipmentHeader = function(shipment) { - var _this = this; - function getHTMLSource() { - var name = shipment.json.shippingName; - var status = shipment.json.shippingStatus; - var creationDate = shipment.json.creationDate; - var html = BUI.createFormLabel("Name :", name, 75, 400); - html = html + BUI.createFormLabel("Status :", status, 75, 400); - html = html + BUI.createFormLabel("Date :", creationDate, 75, 400); - return html; - } + if (val != 0) { + return BUI.formatValuesUnits(val, '', { + fontSize : 16, + decimals : 3, + unitsFontSize : this.unitsFontSize + }); + } else { + return; + } - return Ext.create('Ext.container.Container', { - frame : false, - layout : 'hbox', - title : 'General', - padding : 5, - bodyPadding : '5 5 0 0', - width : 890, - margin : '0 0 10 0', - height : 100, - style : { - borderColor : '#BDBDBD', - borderStyle : 'solid', - borderWidth : '1px' - }, - fieldDefaults : { - msgTarget : 'side', - labelWidth : 100 - }, - items : [ { - margin : "0 0 0 0", - width : 475, - border : 0, - html : getHTMLSource() - } - ] - }); -}; + }, + sortable : true + }, + { + text : '', + dataIndex : 'bufferId', + width : 30, + hidden : false, + renderer : function(val, y, sample) { + if (val != null) { + var color = '#FFCCFF'; + if (_this.experiments != null) { + var dc = _this.experiments.getDataCollectionByMeasurementId(sample.raw.measurementId); + if (dc != null) { + if (dc.length > 0) { + color = _this.experiments.getSpecimenColorByBufferId(_this.experiments + .getMeasurementById(dc[0].measurementtodatacollection3VOs[0].measurementId).specimenId); + } + } + } else { + color = BIOSAXS.proposal.bufferColors[val]; + } + return BUI.getRectangleColorDIV(color, 10, 10); + } + }, + sortable : true + }, + { + text : 'Buffer', + dataIndex : 'buffer_acronym', + width : 120, + renderer : function(val, y, sample) { + if (sample.raw.bufferSampleplateposition3VO != null) { + return BIOSAXS.proposal.getBufferById(sample.raw.bufferId).acronym + " Plate: [" + + sample.raw.bufferSampleplate.slotPositionColumn + ", " + + BUI.getSamplePlateLetters()[sample.raw.bufferSampleplateposition3VO.rowNumber - 1] + "-" + + sample.raw.bufferSampleplateposition3VO.columnNumber + "]"; + } + return val; + }, + sortable : true + }, { + text : 'Position', + width : 100, + hidden : true, + renderer : function(val, y, sample) { + if (_this.experiments != null) { + return BUI.getSamplePositionHTML(_this.experiments.getSampleById(sample.raw.specimenId), _this.experiments.experiments[0]); + } + } + } ] + }, + { + text : 'Parameters', + columns : [ + { + text : 'Ex. Flow. time (s)', + dataIndex : 'extraFlowTime', + width : 100, + hidden : true, + renderer : function(val, y, sample) { + return BUI.formatValuesUnits(val, 's', { + fontSize : 12, + decimals : 0, + unitsFontSize : this.unitsFontSize + }); + } + }, + { + text : 'Exp. Temp.', + dataIndex : 'exposureTemperature', + width : 70, + renderer : function(val, y, sample) { + if (Number(val)) { + return BUI.formatValuesUnits(val, 'C', { + fontSize : 12, + decimals : 2, + unitsFontSize : this.unitsFontSize + }); + } + return null; + }, + sortable : true, + editor : this._getEditor("exposureTemperature") + }, + { + text : 'Vol. Load', + dataIndex : 'volumeToLoad', + width : 60, + hidden : false, + editor : this._getEditor("volumeToLoad"), + renderer : function(val, y, sample) { + // return val+ " µl"; + return BUI.formatValuesUnits(val, 'µl', { + fontSize : 12, + decimals : 2, + unitsFontSize : this.unitsFontSize + }); + } + }, + { + text : 'Volume in Well', + dataIndex : 'volume', + hidden : true, + editor : this._getEditor("volume"), + width : 80, + renderer : function(val, y, sample) { + // return val + "(µl)"; + return BUI.formatValuesUnits(val, 'µl', { + fontSize : 12, + decimals : 2, + unitsFontSize : this.unitsFontSize + }); + } + }, + { + text : 'Trans.', + dataIndex : 'transmission', + width : 60, + editor : this._getEditor("transmission"), + renderer : function(val, y, sample) { + return BUI.formatValuesUnits(val, '%', { + fontSize : 12, + decimals : 0, + unitsFontSize : this.unitsFontSize + }); + } + }, + { + text : 'Wait T.', + dataIndex : 'waitTime', + editor : this._getEditor("waitTime"), + width : 50, + renderer : function(val, y, sample) { + // if (val != 0) { + return BUI.formatValuesUnits(val, 's', { + fontSize : 12, + decimals : 0, + unitsFontSize : this.unitsFontSize + }); + // } + } + }, + { + text : 'Flow', + dataIndex : 'flow', + editor : this._getEditor("flow"), + width : 50, + renderer : function(val, y, sample) { + if (val == true) { + return "yes"; + } + return null; + } + }, + { + text : 'Viscosity', + dataIndex : 'viscosity', + tooltip : 'The viscosity of a fluid is a measure of its resistance to gradual deformation by shear stress or tensile stress. For liquids, it corresponds to the informal notion of "thickness"', + editor : this._getEditor("viscosity"), + width : 50, + renderer : function(val, y, sample) { + return val; + } + } ] + }, { + text : 'Status', + dataIndex : 'status', + width : 50, + hidden : _this.isStatusColumnHidden, + renderer : function(val, y, sample) { + if (val != null) { + if (val == 'DONE') { + return "" + val + " "; + } + } + } + }, { + text : 'Time', + dataIndex : 'time', + width : 80, + hidden : _this.isTimeColumnHidden, + renderer : function(val, y, sample) { + if (sample.raw.run3VO != null) { + if (sample.raw.run3VO.timeStart != null) { + if (sample.raw.run3VO.timeStart != "") { + var m = moment(sample.raw.run3VO.timeStart); + return m.format("hh:mm:ss a"); + } + } + } + } + }, { + text : 'Energy', + dataIndex : 'energy', + width : 100, + hidden : true + }, { + text : 'Real Exp. Temp.(C)', + width : 100, + dataIndex : 'expExposureTemperature', + hidden : true + }, { + text : 'Storage Temp.(C)', + width : 100, + dataIndex : 'storageTemperature', + hidden : true + }, { + text : 'Time/Frame (s)', + width : 100, + dataIndex : 'timePerFrame', + hidden : true + }, { + text : 'Radiation Relative', + dataIndex : 'radiationRelative', + width : 100, + hidden : true + }, { + text : 'Radiation Absolute', + dataIndex : 'radiationAbsolute', + width : 100, + hidden : true + }, { + text : 'Comments', + dataIndex : 'comments', + flex : 1, + hidden : false, + editor : this._getEditor("comments") -ShipmentTabs.prototype.getTabPanel = function(shipment) { - this.panel = Ext.createWidget('tabpanel', { - height : 600, - style : { - padding : 2 - }, - items : [ { - tabConfig : { - id : 'Shipment', - title : 'Shipment', - icon : '/ispyb/images/plane-small.gif' - }, - items : [ { - xtype : 'container', - margin : '5 5 5 5', - items : [ this.shipmentForm.getPanel(shipment) ] - } ] - }, { - tabConfig : { - id : 'Cases', - title : 'Cases', - icon : '../images/box-icon-very-small.png' - }, - items : [ { - xtype : 'container', - margin : '5 5 5 5', - items : [ this.caseGrid.getPanel(shipment.getDewars(), this.plates) ] - } ] - } ] - }); - return this.panel; -}; + }, { + id : _this.id + 'buttonRemoveSample', + text : '', + hidden : !_this.removeBtnEnabled, + width : 100, + sortable : false, + renderer : function(value, metaData, record, rowIndex, colIndex, store) { + if (record.raw.macromoleculeId != null) { + if (_this.removeBtnEnabled) { + return BUI.getRedButton('REMOVE'); + } + } + } + } ], + bbar : bbar, + viewConfig : { + stripeRows : true, + getRowClass : function(record, index, rowParams, store) { + if (record.data.status == "DONE") { + return 'green-row'; + } -ShipmentTabs.prototype.getPanel = function(shipment) { - var _this = this; - this.shipment = shipment; - if (this.plates == null) { - this.plates = []; - } + }, + listeners : { + 'itemclick' : function(grid, record, item, index, e, eOpts) { + _this.onClick.notify({ + specimen : record.raw + }); + }, + 'cellclick' : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonRemoveSample') { + grid.getStore().removeAt(rowIndex); - if (this.shipPanel == null) { - this.shipPanel = Ext.create('Ext.container.Container', { - bodyPadding : 2, - width : Ext.getBody().getViewSize().width * 0.9, - renderTo : this.targetId, - style : { - padding : 2 - }, - items : [ this.getShipmentHeader(shipment), this.getTabPanel(shipment) ] - }); - } + if (record.raw.measurementId != null) { + /** For testing * */ + grid.setLoading("ISPyB: Removing measurement"); + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, data) { + grid.setLoading(false); + /** + * We get and refresh experiment + * because specimens has changed * + */ + var adapter2 = new BiosaxsDataAdapter(); + adapter2.onSuccess.attach(function(sender, experiment) { + _this.onRemoved.notify(experiment); + _this._showStatusBarReady('Ready'); + }); + if (_this.experiments.experiments[0].experimentId != null) { + adapter2.getExperimentById(_this.experiments.experiments[0].experimentId, "MEDIUM"); + _this._showStatusBarBusy("ISPyB: Removing Unused Specimens"); + } + }); - return this.shipPanel; -}; - -/** - * Shows an template with the specimens, measurements and the experiment's - * requirement - * - * @targetId - */ -function TemplateTabs(targetId) { - this.height = 600; - this.targetId = targetId; + adapter.onError.attach(function(sender, data) { + alert("Error: " + data); + grid.setLoading(false); + }); - this.id = BUI.id(); - var _this = this; + adapter.removeMeasurement(record.raw); + } + } - this.gridHeight = 1000; - /** data * */ - this.experiment = null; + } - this.specimenSelected = null; + } + } + }); - /** Specimen Widget contains a specimenGrid and a sampleChangerWidget than can be displayed with are vertical or horizontal layout **/ - this.specimenWidget = new SpecimenWidget({ - height : this.height - }); - - this.samplePlateGroupWidget = new SamplePlateGroupWidget({ - showTitle : false, - height : 250, - margin : 5, - bbar : true - }); - - this.samplePlateGroupWidget.onExperimentChanged.attach(function(sender, experiment) { - _this.refresh(experiment); - }); + this.grid.on("afterrender", function() { - this.samplePlateGroupWidget.onClick.attach(function(sender, args) { - }); + function updateTime() { + try { + _this.estimatedTime = _this.estimatedTime - 1; - this.volumePlanificator = new VolumeGrid(); + _this.onUpdateTime.notify({ + hours : (Number(_this.estimatedTime / 3600).toFixed()), + minutes : (Number((_this.estimatedTime / 60) % 60).toFixed()), + seconds : (Number(_this.estimatedTime % 60).toFixed()) - /** For Measurements * */ - var storeViscosity = Ext.create('Ext.data.Store', { - fields : [ 'name' ], - data : [ { - "name" : "low" - }, { - "name" : "medium" - }, { - "name" : "high" - } ] - }); + }); - // Create the combo box, attached to the states data store - var viscosityEditor = Ext.create('Ext.form.ComboBox', { - fieldLabel : '', - store : storeViscosity, - queryMode : 'local', - displayField : 'name', - valueField : 'name' - }); + if (Number(_this.estimatedTime) < 0) { + _this.timer = null; + grid.setTitle(_this.title); + } - this.measurementGrid = new MeasurementGrid({ - maxWidth : 1500, - estimateTime : false, - positionColumnsHidden : true, - isPriorityColumnHidden : true, - isStatusColumnHidden : true, - isTimeColumnHidden : true, - updateRowEnabled : true, - collapsed : true, - removeBtnEnabled : true, - showTitle : false, - collapseBtnEnable : false, - addBtnMultipleEdit : true, - sortingBtnEnable : true, - editor : { - exposureTemperature : { - xtype : 'textfield', - allowBlank : true - }, - comments : { - xtype : 'textfield', - allowBlank : true - }, - volumeToLoad : { - xtype : 'numberfield', - allowBlank : true - }, - transmission : { - xtype : 'numberfield', - allowBlank : true - }, - viscosity : viscosityEditor, - waitTime : { - xtype : 'numberfield', - allowBlank : true - }, - flow : { - xtype : 'checkbox', - allowBlank : true + } catch (e) { + console.log(e); + _this.timer = null; } } - }); - - this.measurementGrid.onSelected.attach(function(sender, measurements) { - var specimens = []; - for ( var i = 0; i < measurements.length; i++) { - specimens.push(_this.experiment.getSampleById(measurements[i].specimenId)); - } - }); - this.measurementGrid.onMeasurementChanged.attach(function(sender, measurement) { - _this.experiment.setMeasurement(measurement); - _this.refresh(_this.experiment); - }); + if (_this.estimateTime) { + var experimentList = _this.experiments; + var collected = experimentList.getMeasurementsCollected(); + if (collected.length > 0) { + if (collected[0].run3VO != null) { + try { + var end = collected[0].run3VO.timeEnd; + var start = collected[0].run3VO.timeStart; + var dstart = moment(start); + var dend = moment(end); + var seconds = Number(dend.diff(dstart) / 1000).toFixed(); - this.measurementGrid.onExperimentChanged.attach(function(sender, json) { - _this.refresh(new Experiment(json)); - }); + _this.estimatedTime = (seconds * experimentList.getMeasurementsNotCollected().length); - this.measurementGrid.onRemoved.attach(function(sender, experiment) { - _this.refreshSpecimen(new Experiment(experiment)); - }); + if (_this.estimatedTime > 0) { + updateTime(); + _this.timer = setInterval(function() { + updateTime(); + }, 1000); + } - this.measurementGrid.onUpdateTime.attach(function(sender, args) { - document.getElementById(_this.id + "_counter").innerHTML = args.hours + 'h, ' + args.minutes + 'min and ' + args.seconds + ' seconds'; + } catch (e) { + } + } + } + } }); - -} - -TemplateTabs.prototype.refreshMeasurement = function(experiment) { - this.experiment = experiment; - this.experiment.onSaved = new Event(this); - this.experiment.onSpecimenSaved = new Event(this); - var experimentList = new ExperimentList([ this.experiment ]); - this.measurementGrid.refresh(experimentList.getMeasurementsNotCollected(), experimentList); -}; -TemplateTabs.prototype.refreshSpecimen = function(experiment) { - this.experiment = experiment; - this.experiment.onSaved = new Event(this); - this.experiment.onSpecimenSaved = new Event(this); - this.samplePlateGroupWidget.refresh(this.experiment); - this.specimenWidget.refresh(this.experiment); - this.volumePlanificator.refresh(this.experiment); + return this.grid; }; -TemplateTabs.prototype.refresh = function(experiment) { - // var start = new Date().getTime(); - this.experiment = experiment; - this.experiment.onSaved = new Event(this); - this.experiment.onSpecimenSaved = new Event(this); - - // var experimentList = new ExperimentList([this.experiment]); - this.refreshMeasurement(experiment); - this.refreshSpecimen(experiment); - /** Refreshing grids * */ - this.panel.setLoading(false); - +/** Method for testing * */ +MeasurementGrid.prototype.input = function() { + var experiment = DATADOC.getExperiment_10(); + var measurements = DATADOC.getMeasurements_10(); + var proposal = DATADOC.getProposal_10(); + return { + experiment : experiment, + measurements : measurements, + proposal : proposal + }; }; -TemplateTabs.prototype.error = function(error) { - var e = JSON.parse(error); - showError(e); - this.panel.setLoading(false); +MeasurementGrid.prototype.test = function(targetId) { + var measurementGrid = new MeasurementGrid({ + tbar : true + }); + BIOSAXS.proposal = new Proposal(measurementGrid.input().proposal); + var panel = measurementGrid.getPanel(measurementGrid.input().measurements, new ExperimentList([ new Experiment(measurementGrid.input().experiment) ])); + panel.render(targetId); }; + +/** + * A shipment may contains one or more cases where stock solutions and sample + * plates are stored + * + * @height + * @btnEditVisible + * @btnRemoveVisible + * + * #onEditButtonClicked + */ +function MolarityGrid(args) { + this.height = 100; + this.width = 100; + + if (args != null) { + if (args.height != null) { + this.height = args.height; + } + if (args.width != null) { + this.width = args.width; + } + } + + var _this = this; + + this.molarityForm = new MolarityForm({height : 180, width : 455}); + + this.molarityForm.onSave.attach(function(sender){ + _this.molarityWindow.destroy(); + _this.updateProposal(); + + }); + + this.molarityForm.onClose.attach(function(sender){ + _this.molarityWindow.destroy(); + + }); + + /** Events * */ + this.onEditButtonClicked = new Event(this); +} + +MolarityGrid.prototype._getColumns = function() { + return [ { + text : 'Subunit', + columns : [ { + text : "Acronym", + width : 100, + hidden : false, + dataIndex : 'acronym', + sortable : true + }, { + text : "Name", + width : 125, + hidden : false, + dataIndex : 'name', + sortable : true + }, { + text : "MM Est.", + width : 100, + dataIndex : 'molecularMass', + sortable : true, + renderer : function(grid, cls, record){ + return BUI.formatValuesUnits(record.data.molecularMass , "Da", 10, 2); + + } + } ] + }, { +// text : "Number
in assymmetric units", + text : "Ratio", + width : 100, + dataIndex : 'ratio', + tooltip : 'Number of times the subunit is present in the macromolecule', + sortable : true + }, { + id : this.id + 'MOLARITY_REMOVE', + flex : 0.1, + sortable : false, + renderer : function(value, metaData, record, rowIndex, colIndex, store) { + return BUI.getRedButton('REMOVE'); + } + } ]; +}; + +MolarityGrid.prototype._openMolarityWindow = function() { + this.molarityWindow = Ext.create('Ext.window.Window', { + title : 'Molarity', + height : 220, + width : 500, + modal : true, + items : [this.molarityForm.getPanel() ] + }).show(); +}; + +MolarityGrid.prototype._getButtons = function() { + var _this = this; + return [ { + text : 'Add subunit', + icon : '../images/add.png', + handler : function() { + _this._openMolarityWindow(); + } + }]; +}; + + +MolarityGrid.prototype.updateProposal = function() { + var _this = this; + this.panel.setLoading(); + BIOSAXS.proposal.onInitialized.attach(function() { + if (BIOSAXS.proposal != null) { + var macromolecules = BIOSAXS.proposal.macromolecules; + for (var i = 0; i < macromolecules.length; i++) { + + if (macromolecules[i].macromoleculeId == _this.macromolecule.macromoleculeId) { + _this.refresh(macromolecules[i]); + _this.panel.setLoading(false); + } + } + } + }); + BIOSAXS.proposal.init(); +}; + + +MolarityGrid.prototype.getPanel = function() { + var _this = this; + + this.molarityStore = Ext.create('Ext.data.Store', { + fields : [ 'acronym', 'ratio', 'comments', 'stoichiometryId', 'name', 'molecularMass' ], + sorters : { + property : 'ratio', + direction : 'DESC' + } + }); + + this.panel = Ext.create('Ext.grid.Panel', { + store : this.molarityStore, + height : this.height, + padding : 5, + columns : this._getColumns(), + listeners : { + cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + + /** Remove entry * */ + if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'MOLARITY_REMOVE') { + var dataAdapter = new BiosaxsDataAdapter(); + dataAdapter.onSuccess.attach(function(sender) { + _this.updateProposal(); + + }); + dataAdapter.removeStoichiometry(record.data.stoichiometryId); + _this.panel.setLoading("Removing Structure"); + } + } + }, + tbar : this._getButtons() + }); + return this.panel; +}; + +MolarityGrid.prototype._prepareData = function(macromolecule) { + /** Return an array of [{ratio,acronym, stoichiometryId, name}] **/ + var data = []; + if (macromolecule.stoichiometry != null) { + for (var i = 0; i < macromolecule.stoichiometry.length; i++) { + var hostMacromolecule = BIOSAXS.proposal.getMacromoleculeById(macromolecule.stoichiometry[i].macromoleculeId); + data.push({ + ratio : macromolecule.stoichiometry[i].ratio, + acronym : hostMacromolecule.acronym, + comments : hostMacromolecule.comments, + molecularMass : hostMacromolecule.molecularMass, + stoichiometryId : macromolecule.stoichiometry[i].stoichiometryId, + name : hostMacromolecule.name + }); + } + } + return data; +}; + +MolarityGrid.prototype.refresh = function(macromolecule) { + if (macromolecule != null){ + this.molarityStore.loadData(this._prepareData(macromolecule)); + this.molarityForm.refresh(macromolecule); + this.macromolecule = macromolecule; + } +}; + +MolarityGrid.prototype.input = function() { + return { + proposal : DATADOC.getProposal_10(), + dewars : DATADOC.getDewars_10() + + }; +}; + +MolarityGrid.prototype.test = function(targetId) { + var MolarityGrid = new MolarityGrid({ + height : 150 + }); + BIOSAXS.proposal = new Proposal(MolarityGrid.input().proposal); + var panel = MolarityGrid.getPanel(MolarityGrid.input().dewars); + panel.render(targetId); + +}; + + +/** + * Given data analysis parsed with ResultsAssemblyWidget makes a selector grid with buffer/protein + **/ +function SpecimenSelectorResultGrid() { + this.id = BUI.id(); +} -TemplateTabs.prototype.draw = function(experiment) { - this.render(experiment); +SpecimenSelectorResultGrid.prototype._prepareData = function(data) { + var parsed = []; + for ( var i = 0; i < data.length; i++) { + var row = data[i]; + for ( var j = 0; j < row.conditions.length; j++) { + parsed.push({ + bufferId : row.conditions[j].bufferId, + macromoleculeId : row.macromoleculeId, + macromoleculeAcronym : BIOSAXS.proposal.getMacromoleculeById(row.macromoleculeId).acronym, + bufferAcronym : BIOSAXS.proposal.getBufferById(row.conditions[j].bufferId).acronym + }); + } + } + return parsed; }; - -TemplateTabs.prototype.getExperimentTitle = function() { - var _this = this; - var experimentHeaderForm = new ExperimentHeaderForm(); - return experimentHeaderForm.getPanel(this.experiment); +SpecimenSelectorResultGrid.prototype.refresh = function(data) { + this.store.loadData(this._prepareData(data)); }; -TemplateTabs.prototype.render = function(experiment) { +SpecimenSelectorResultGrid.prototype.getPanel = function(data) { var _this = this; - this.experiment = experiment; + this.store = Ext.create('Ext.data.Store', { + fields : [ 'macromoleculeId', 'bufferId', 'macromoleculeAcronym', 'bufferAcronym', 'concentration' ], + data : this._prepareData(data), + groupField : 'macromoleculeAcronym' + }); - /** - * - * Depending on the sample Changer configuration we want to display the plates vertically or horizontally - * Default is vertical - * - * */ - - var specimenContainer = this.specimenWidget.getPanel(); - this.specimenWidget.refresh(experiment); - - var experimentList = new ExperimentList([ _this.experiment ]); - var measurementContainer = Ext.create('Ext.container.Container', { - layout : { - type : 'vbox' - }, - defaults : { - style : { - padding : '5px 0px 0px 10px' + this.store.sort('concentration'); + this.grid = Ext.create('Ext.grid.Panel', { + id : this.id, + store : this.store, + width : this.width, + height : this.height, + maxHeight : this.maxHeight, + border : 1, + features : [ { + ftype : 'grouping', + groupHeaderTpl : '{name}', + hideGroupedHeader : true, + startCollapsed : false + } ], + selModel : Ext.create('Ext.selection.CheckboxModel', { + allowDeselect : true, + mode : 'MULTI', + listeners : { + selectionchange : function(sm, selections) { + _this.selected = []; + for ( var i = 0; i < selections.length; i++) { + _this.selected.push(selections[i].raw); + } + } } - }, - items : [ _this.measurementGrid.getPanel(experimentList.getMeasurementsNotCollected(), experimentList) ] + }), + margin : 10, + sortableColumns : true, + columns : [ { + text : 'Macromolecule', + dataIndex : 'macromoleculeAcronym', + flex : 1 + }, { + text : '', + dataIndex : 'bufferId', + width : 20, + hidden : false, + renderer : function(val, y, sample) { + return BUI.getRectangleColorDIV(BIOSAXS.proposal.bufferColors[val], 10, 10); + } + }, { + text : 'Buffer', + dataIndex : 'bufferAcronym', + flex : 1 + }, { + text : 'Concentration', + dataIndex : 'concentration', + flex : 1, + renderer : function(val, y, sample) { + return val + " mg/ml"; + } + } ] }); - this.panel = Ext - .createWidget( - 'tabpanel', - { - plain : true, - items : [ - { - tabConfig : { - title : 'Measurements' - }, - items : [ { - xtype : 'container', - layout : 'vbox', - border : 1, -// height : _this.gridHeight, - margin : "0 0 10 0", - items : [ measurementContainer ] - } - - ] - }, - { - tabConfig : { - id : 'genralTabl', - title : "Specimens" - // width : 900, - // border : 3 - - }, - items : [ specimenContainer ] - }, - { - tabConfig : { - title : "Requirements" + /** Adding the tbar **/ + if (this.tbar) { + this.grid.addDocked({ + xtype : 'toolbar', + items : this.getTbar() + }); + } + return this.grid; +}; - }, - items : [ - { - html : BUI.getTipHTML("Estimated volume is the maximum volume required. Depending on the order of your measurements you may use less. Click on create stock solutions if you plan to ship these stock solutions"), - margin : "10 10 10 10", - border : 0 - }, this.volumePlanificator.getPanel(experiment) ] - } ] - }); - // ); - return this.getPanel(this.panel); +SpecimenSelectorResultGrid.prototype.input = function() { + return { + data : new ResultsAssemblyGrid()._prepareData(new ResultsAssemblyGrid().input().data), + proposal : new ResultsAssemblyGrid().input().proposal + }; }; -TemplateTabs.prototype.isTemplate = function() { - if (this.experiment.json.type == "TEMPLATE") { - return true; - } - return false; +SpecimenSelectorResultGrid.prototype.test = function(targetId) { + var specimenSelectorResultGrid = new SpecimenSelectorResultGrid(); + BIOSAXS.proposal = new Proposal(specimenSelectorResultGrid.input().proposal); + var panel = specimenSelectorResultGrid.getPanel(specimenSelectorResultGrid.input().data); + panel.render(targetId); }; +/** + * Show all buffer conditions for each macromolecules pointing out the measurements quality + * + * @height + * @maxHeight + * @width + * @searchBar + * @tbar + * @btnResultVisible + * + * #onClick + */ +function ResultsAssemblyGrid(args) { + this.height = 500; + this.id = BUI.id(); + this.maxHeight = this.height; -TemplateTabs.prototype.getPanel = function(panel) { - var _this = this; - if (this.experimentPanel == null) { - this.experimentPanel = Ext.create('Ext.container.Container', { - bodyPadding : 2, - width : 1000,//Ext.getBody().getViewSize().width * 0.9, - renderTo : this.targetId, - height : 500, -// style : { -// padding : 2 -// }, - items : [ - this.getExperimentTitle(), - this.panel - ], - listeners : { - afterrender : function(thisCmp) { - $("#SchemeReport" + _this.experiment.experimentId).click(function() { - $(this).target = "_blank"; - window.open('viewProjectList.do?reqCode=display&menu=platescheme&experimentId=' + _this.experiment.experimentId); - return false; - }); + this.width = 900; + this.searchBar = false; + this.tbar = false; + this.btnResultVisible = false; - } - } - }); - } - return this.experimentPanel; -}; + /** For processing **/ + this.processed = {}; + this.renderedPlotIndex = 0; -TemplateTabs.prototype.input = function(targetId) { - return new ExperimentTabs().input(); -}; + this.plotWidth = 210; + this.plotHeight = 80; -TemplateTabs.prototype.test = function(targetId) { - var experimentTabs = new TemplateTabs(targetId); - BIOSAXS.proposal = new Proposal(experimentTabs.input().proposal); - experimentTabs.draw(new Experiment(experimentTabs.input().experiment)); -}; - -var BUI = { - //interval : 60000, - interval : 40000, - rainbow : function(numOfSteps, step) { - // This function generates vibrant, "evenly spaced" colours (i.e. no clustering). This is ideal for creating easily distinguishable vibrant markers in Google Maps and other apps. - // Adam Cole, 2011-Sept-14 - // HSV to RBG adapted from: http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript - var r, g, b; - var h = step / numOfSteps; - var i = ~~(h * 6); - var f = h * 6 - i; - var q = 1 - f; - switch (i % 6) { - case 0: - r = 1, g = f, b = 0; - break; - case 1: - r = q, g = 1, b = 0; - break; - case 2: - r = 0, g = 1, b = f; - break; - case 3: - r = 0, g = q, b = 1; - break; - case 4: - r = f, g = 0, b = 1; - break; - case 5: - r = 1, g = 0, b = q; - break; + /** Colors **/ + this.validColor = BUI.getValidColor(); + this.warningColor = BUI.getWarningColor(); + this.notValidColor = BUI.getErrorColor(); + + /** Show warning if guinier quality less than **/ + this.guinierQuality = BUI.getQualityThreshold(); + this.framePercentageThreshold = BUI.getRadiationDamageThreshold(); + + if (args != null) { + if (args.height != null) { + this.height = args.height; + this.maxHeight = this.height; } - var c = "#" + ("00" + (~~(r * 255)).toString(16)).slice(-2) + ("00" + (~~(g * 255)).toString(16)).slice(-2) - + ("00" + (~~(b * 255)).toString(16)).slice(-2); - return (c); - }, - getFileNameByPath : function(filePath) { - if (filePath != null){ - var split = filePath.split("/"); - if (split.length > 0){ - return split[split.length - 1]; - } + if (args.maxHeight != null) { + this.maxHeight = args.maxHeight; + } + if (args.width != null) { + this.width = args.width; + } + if (args.searchBar != null) { + this.searchBar = args.searchBar; } - return "Not file"; - }, - getUpdateInterval : function() { - this.interval = this.interval + 2000; - return this.interval; - }, - getRadiationDamageThreshold : function() { - return 0.7; - }, - getQualityThreshold : function() { - return 0.7; - }, - getCreateShipmentURL : function() { - return '/ispyb/user/viewProjectList.do?reqCode=display&menu=create_shipment'; - }, - getCreateShipmentList : function() { - return '/ispyb/user/viewProjectList.do?reqCode=display&menu=list_shipment'; - }, - getShippingURL : function(shippingId) { - return '/ispyb/user/viewProjectList.do?reqCode=display&menu=shipment&shippingId=' + shippingId; - }, - getMacromoleculeResultsURLByMultipleSearch : function(array) { - return '/ispyb/user/viewProjectList.do?reqCode=display&menu=macromolecule&search=' - + JSON.stringify(array).replace(new RegExp("\"", 'g'), "'"); - }, - getMacromoleculeResultsURL : function(macromoleculeId) { - return '/ispyb/user/viewProjectList.do?reqCode=display&menu=macromolecule¯omoleculeId=' + macromoleculeId; - }, - getMacromoleculeBufferResultsURL : function(macromoleculeId, bufferId) { - return '/ispyb/user/viewProjectList.do?reqCode=display&menu=macromolecule&bufferId=' + bufferId + '¯omoleculeId=' + macromoleculeId; - }, + if (args.tbar != null) { + this.tbar = args.tbar; + } + if (args.btnResultVisible != null) { + this.btnResultVisible = args.btnResultVisible; + } - getMacromoleculeHeader : function(macromoleculeId) { + } - function getHTMLSource(macromoleculeId) { - if (macromoleculeId != null) { - var html = BUI.createFormLabel("Name :", BIOSAXS.proposal.getMacromoleculeById(macromoleculeId).name, 75, 400); - html = html + BUI.createFormLabel("Acronym :", BIOSAXS.proposal.getMacromoleculeById(macromoleculeId).acronym, 75, 400); - if (BIOSAXS.proposal.getMacromoleculeById(macromoleculeId).comments != null) { - html = html + BUI.createFormLabel("Comments :", BIOSAXS.proposal.getMacromoleculeById(macromoleculeId).comments, 75, 400); - } - return html; - } - } + this.onClick = new Event(); +} - return Ext.create('Ext.container.Container', { - frame : false, - layout : 'hbox', - title : 'Macromolecule', - bodyPadding : '5', - width : 890, - margin : '0 0 10 0', - height : 100, - style : { - borderColor : '#BDBDBD', - borderStyle : 'solid', - borderWidth : '1px' - }, - fieldDefaults : { - msgTarget : 'side', - labelWidth : 100 - }, - items : [ { - margin : "10 0 0 10", - width : 475, - border : 0, - html : getHTMLSource(macromoleculeId) - }, { - margin : "10 0 0 10", - width : 475, - border : 0, - html : BUI.getZipHTMLByMacromoleculeId(macromoleculeId) - } - ] - }); - }, +ResultsAssemblyGrid.prototype.edit = function(macromoleculeId) { + var _this = this; + var window = new MacromoleculeWindow(); + window.onSuccess.attach(function(sender, proposal) { + _this.store.loadData(_this._prepareData(BIOSAXS.proposal.getMacromolecules())); + }); + window.draw(BIOSAXS.proposal.getMacromoleculeById(macromoleculeId)); +}; - getZipHTMLByMacromoleculeId : function(macromoleculeId) { - if (macromoleculeId != null) { - var fileName = BIOSAXS.proposal.getMacromoleculeById(macromoleculeId).acronym; - return ""; - } - }, - getZipHTMLByExperimentId : function(experimentId, filename) { - if (filename == null){ - filename = "experiment"; - } - return ""; - }, - - getZipURLByAverageId : function(averageId, filename) { - if (filename == null){ - filename = "experiment"; - } - return "/ispyb/user/dataadapter.do?reqCode=getZipFileByAverageListId&f&mergeIdsList=" + averageId + "&fileName=" + filename; - }, - getZipURLBySubtractionId : function(subtractionId, filename) { - if (filename == null){ - filename = "experiment"; +ResultsAssemblyGrid.prototype.getTbar = function() { + var _this = this; + var actions = []; + + actions.push(Ext.create('Ext.Action', { + icon : '../images/add.png', + text : 'Add Macromolecule', + disabled : false, + handler : function(widget, event) { + var window = new MacromoleculeWindow(); + window.onSuccess.attach(function(sender) { + _this.refresh(); + }); + window.draw({}); } - return "/ispyb/user/dataadapter.do?reqCode=getZipFileByAverageListId&f&subtractionIdList=" + subtractionId + "&fileName=" + filename; - }, - - - getZipHTMLByFrameRangeId : function(experimentId, start, end) { - var fileName = "experiment"; - return ""; - }, - getZipFrameHPLCUrl : function(experimentId, start, end) { - return "/ispyb/user/dataadapter.do?reqCode=getZipFileH5ByFramesRange&f&experimentId=" + experimentId + "&start=" + Number(start) +"&end="+ Number(end); - }, - - getQueueUrl : function() { - return "/ispyb/user/dataadapter.do?reqCode=getPagingCompactAnalysisByProposalId"; - }, - - getQueueUrlByExperiment: function(experimentId) { - return "/ispyb/user/dataadapter.do?reqCode=getPagingCompactAnalysisByExperimentId&f&experimentId=" + experimentId; - }, - getStandardDeviation : function(values) { - var sum = 0; - var count = 0; - var avg = null; + })); + + actions.push(Ext.create('Ext.Action', { + icon : '../images/add.png', + text : 'Define an Assembly', + disabled : false, + handler : function(widget, event) { + var createAssemblywindow = new CreateAssemblyWindow(); + createAssemblywindow.onSaved.attach(function(evt, args) { + if (args.macromoleculeIds.length > 0) { + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, proposal) { + _this.refresh(); + }); + adapter.saveAssembly(args.assemblyId, args.macromoleculeIds); - var curatedValues = new Array(); - for ( var i = 0; i < values.length; i++) { - var value = values[i]; - if (value != null) { - if (!isNaN(value)) { - count = count + 1; - sum = sum + Number(value); - curatedValues.push(Number(value)); } - } - } - if (count > 0) { - avg = sum / count; - } else { - avg = sum; - } - var aux = 0; - for ( var i = 0; i < curatedValues.length; i++) { - var value = curatedValues[i]; - aux = aux + Math.pow(value - avg, 2); + }); + createAssemblywindow.draw(_this.experiment); } - /** std **/ - var std = Math.sqrt(aux / count); + })); + + return actions; +}; + +ResultsAssemblyGrid.prototype.refresh = function() { + this.store.loadData(this._prepareData()); +}; + +ResultsAssemblyGrid.prototype.addCondition = function(record, data_parsed, i) { + function getCondition(record) { return { - std : (std), - sum : (sum), - avg : (avg), - validNumber : count, - totalNumber : values.length, - values : values + concentration : record.conc, + quality : record.quality, + bufferBeforeFramesMerged : record.bufferBeforeFramesMerged, + bufferAfterFramesMerged : record.bufferAfterFramesMerged, + framesCount : record.framesCount, + framesMerge : record.framesMerge }; - }, - - getHTMLTableForFrameAveraged : function(bufferAcronym, macromoleculeAcronym, bbmerges, molmerges, bamerges, totalframes, bufferId,macromoleculeId, macromoleculeColor) { - - function getFrameSpan(framesMerged, total) { - return "(" + framesMerged + " of " + total + ")"; - } + } - function getColorFrame(framesMerged, total) { - if (framesMerged / total < 0.5) { - return "#FA5858"; - } - if ((framesMerged / total >= 0.5) && (framesMerged / total < 0.8)) { - return "#FF9900"; + if (data_parsed[i].conditions != null) { + var bufferFound = false; + for ( var index in data_parsed[i].conditions) { + condition = data_parsed[i].conditions[index]; + + if ((condition.macromoleculeId == record.macromoleculeId) && (condition.bufferId == record.bufferId)) { + data_parsed[i].conditions[index].concentrations.push(getCondition(record)); + bufferFound = true; } - return "white"; } - - function getRow(color, acroynm, framesMerged, totalframes) { - return " " - + BUI.getRectangleColorDIV(color, 10, 10) - + " " + acroynm + "" - + getFrameSpan(framesMerged, totalframes) + ""; + if (!bufferFound) { + data_parsed[i].conditions.push({ + macromoleculeId : record.macromoleculeId, + bufferId : record.bufferId, + concentrations : [ getCondition(record) ] + }); } + } +}; - var html = ""; +ResultsAssemblyGrid.prototype.process = function(record, data_parsed) { + if (this.processed[record.macromoleculeId] == null) { + this.processed[record.macromoleculeId] = true; + record.measurementCount = 1; + record.subtractionCount = 1; + record.averageCount = 1; + record.conditions = []; + data_parsed.push(record); + } - /** Buffer Before **/ - if (bufferAcronym != null) { - if (bbmerges != null) { - color = BIOSAXS.proposal.bufferColors[bufferId]; - html = html + getRow(color, bufferAcronym, bbmerges, totalframes); + for ( var i = 0; i < data_parsed.length; i++) { + if (data_parsed[i].macromoleculeId == record.macromoleculeId) { + data_parsed[i].measurementCount = data_parsed[i].measurementCount + 1; + + if (record.subtractionId != null) { + data_parsed[i].subtractionCount = data_parsed[i].subtractionCount + 1; + this.addCondition(record, data_parsed, i); } - } - /** Molecule **/ - if (macromoleculeAcronym != null) { - if (molmerges != null) { - if (macromoleculeColor == null){ - color = BIOSAXS.proposal.macromoleculeColors[macromoleculeId]; - } - else{ - color = macromoleculeColor; //BIOSAXS.proposal.macromoleculeColors[macromoleculeId]; + if (record.framesMerge != null) { + data_parsed[i].averageCount = data_parsed[i].averageCount + 1; + } + + if (record.timeStart != null) { + if (data_parsed[i].timeStart != null) { + if (moment(data_parsed[i].timeStart).format("X") > moment(record.timeStart).format("X")) { + data_parsed[i].timeStart = record.timeStart; + } } - html = html + getRow(color, macromoleculeAcronym, molmerges, totalframes); } } + } - /** Buffer After **/ - if (bufferAcronym != null) { - if (bamerges != null) { - color = BIOSAXS.proposal.bufferColors[bufferId]; - html = html + getRow(color, bufferAcronym, bamerges, totalframes); + return data_parsed; + +}; + +ResultsAssemblyGrid.prototype._prepareData = function(data) { + var data_parsed = []; + for ( var i = 0; i < data.length; i++) { + data_parsed = this.process(data[i], data_parsed); + } + this.data = data_parsed; + return data_parsed; +}; + +/** Given an array of conditions it returns distinct(concentrations) order by concentration and hash map with number of ocurrences**/ +ResultsAssemblyGrid.prototype.parseConcentrations = function(val, conditions, differentConcentration, quality) { + var conditions = []; + var differentConcentration = {}; + var quality = []; + for ( var i = 0; i < val.length; i++) { + var conc = Number(val[i].concentration).toFixed(1); + var quality = Number(val[i].quality).toFixed(2); + if (differentConcentration[conc] == null) { + differentConcentration[conc] = 0; + conditions.push({ + concentration : conc, + quality : [ quality ], + frames : [ { + bufferAfterFramesMerged : val[i].bufferAfterFramesMerged, + bufferBeforeFramesMerged : val[i].bufferBeforeFramesMerged, + framesMerge : val[i].framesMerge, + framesCount : val[i].framesCount + } ] + }); + } else { + /** Add quality **/ + for ( var j = 0; j < conditions.length; j++) { + if (conditions[j].concentration == conc) { + conditions[j].quality.push(quality); + conditions[j].frames.push({ + bufferAfterFramesMerged : val[i].bufferAfterFramesMerged, + bufferBeforeFramesMerged : val[i].bufferBeforeFramesMerged, + framesMerge : val[i].framesMerge, + framesCount : val[i].framesCount + }); + } } } - return html + "
"; - }, - isWebGLEnabled : function(return_context) { - if (!!window.WebGLRenderingContext) { - var canvas = document.createElement("canvas"); - names = [ "webgl", "experimental-webgl", "moz-webgl", "webkit-3d" ]; - context = false; - for ( var i = 0; i < 4; i++) { - try { - context = canvas.getContext(names[i]); - if (context && typeof context.getParameter == "function") { - // WebGL is enabled - if (return_context) { - // return WebGL object if the function's argument is present - return { - name : names[i], - gl : context - }; - } - // else, return just true - return true; - } - } catch (e) { - } + + differentConcentration[conc] = differentConcentration[conc] + 1; + } + /** sorting concentrations **/ + conditions.sort(function(a, b) { + return a.concentration - b.concentration; + }); + return { + concentrations : conditions, + differentConcentration : differentConcentration + }; +}; + +ResultsAssemblyGrid.prototype.getConditionWarnings = function(condition) { + + var withWarnings = 0; + + for ( var i = 0; i < condition.frames.length; i++) { + if (condition.quality[i] == null) { + withWarnings = withWarnings + 1; + continue; + } else { + if (Number(condition.quality[i]) < this.guinierQuality) { + withWarnings = withWarnings + 1; + continue; } - // WebGL is supported, but disabled - return false; } - // WebGL not supported28. - return false; - }, - getHTMLTableForPrefixes : function(bufferBeforeaverageFilePath, averageFilePath, bufferAfterAverageFilePath) { - function getRow(bufferBeforeaverageFilePath) { - file = bufferBeforeaverageFilePath; - try { - file = bufferBeforeaverageFilePath.split("/")[bufferBeforeaverageFilePath.split("/").length - 1]; - } catch (e) { - file = "NA"; + if (condition.frames[i].bufferBeforeFramesMerged / condition.frames[i].framesCount < this.framePercentageThreshold|| condition.frames[i].framesMerged / condition.frames[i].framesCount < this.framePercentageThreshold|| condition.frames[i].bufferAfterFramesMerged / condition.frames[i].framesCount < this.framePercentageThreshold) { + withWarnings = withWarnings + 1; + continue; + } + } + + return { + withWarnings : withWarnings, + withoutWarnings : condition.frames.length - withWarnings + }; +}; + +ResultsAssemblyGrid.prototype.getFrameHTMLTable = function(warnings) { + var html = ""; + if (warnings.withWarnings > 0) { + html = html + "
" + warnings.withWarnings + + "x
"; + } + + if (warnings.withoutWarnings > 0) { + html = html + "
" + warnings.withoutWarnings + + "x
"; + } + return html; +}; + +ResultsAssemblyGrid.prototype.createConcentrationRow = function(numberOcu, condition, warnings){ + var html = ""; + if (numberOcu > 1){ + html = html + "" +numberOcu + "x " + BUI.formatConcentration(condition.concentration); + } + else{ + html = html + BUI.formatConcentration(condition.concentration); + } + html = html + ""; + html = html + this.getFrameHTMLTable(warnings); + ""; + html = html + ""; + return html; +}; + + +ResultsAssemblyGrid.prototype.getConditionHTMLTable = function(val, style, record) { + var maxNumberColumns = 2; + var html = "
"; + var nColumns = 0; + for ( var r = 0; r < val.length; r++) { + if (nColumns == maxNumberColumns) { + nColumns = 0; + html = html + ""; + } + html = html + ""; } - var html = "
"; + var value = val[r]; + + var bufferAcronym = (BIOSAXS.proposal.getBufferById(value.bufferId).acronym); + + var parsed = (this.parseConcentrations(value.concentrations)); + var conditions = parsed.concentrations; + var differentConcentration = parsed.differentConcentration; + + /** Checking warnings **/ + var warnings = []; + var concentrationValidPerconcentration = 0; + var measurements = 0; + for ( var i = 0; i < conditions.length; i++) { + measurements = measurements + conditions[i].frames.length; + var warning = this.getConditionWarnings(conditions[i]); + warnings.push(warning); + if (warning.withoutWarnings > 0) { + concentrationValidPerconcentration = concentrationValidPerconcentration + 1; } - return "
" + file + "
"; - /** Buffer Before **/ - if (bufferBeforeaverageFilePath != null) { - html = html + getRow(bufferBeforeaverageFilePath); - } + this.validColor = '#E0F8E0'; + this.warningColor = '#F5DA81'; + this.notValidColor = '#F6CED8'; - /** Molecule **/ - if (averageFilePath != null) { - html = html + getRow(averageFilePath); + var color = this.warningColor; + if (concentrationValidPerconcentration > 2) { + color = this.validColor; + } + /** More measurement need to be done **/ + if (measurements < 3) { + color = this.notValidColor; } - /** Buffer After **/ - if (bufferAfterAverageFilePath != null) { - html = html + getRow(bufferAfterAverageFilePath); + html = html + "
"; + html = html + ""; + for ( var i = 0; i < conditions.length; i++) { + html = html + this.createConcentrationRow(differentConcentration[conditions[i].concentration], conditions[i], warnings[i]); } - return html + "
" + bufferAcronym.toUpperCase() + + "
"; - }, - getBaseURL : function() { - return '/ispyb/user/dataadapter.do'; - }, + html = html + ""; + html = html + ""; - getPrintcomponentURL : function(dewarId) { - return '/ispyb/user/viewDewarAction.do?reqCode=generateLabels&dewarId=' + dewarId; + html = html + ""; + nColumns = nColumns + 1; + } + html = html + "
"; + return html; +}; - }, - getPDBVisualizerURL : function(modelId, subtractionId, experimentId) { - return '/ispyb/user/viewProjectList.do?reqCode=display&menu=PDBVisualizer&modelId=' + modelId + '&experimentId=' + experimentId - + '&subtractionId=' + subtractionId; - }, +ResultsAssemblyGrid.prototype._getTbar = function() { + function goTo(url) { + window.location = url; + } - getURL : function() { - return this.getBaseURL() + '?reqCode=getImage'; + var _this = this; + return [ { + icon : '../images/application_view_list.png', + text : 'Multiple Select', + handler : function() { + var specimenSelectorResultGrid = new SpecimenSelectorResultGrid(); + var window = Ext.create('Ext.window.Window', { + title : 'Multiple select', + height : 600, + width : 600, + layout : 'fit', + items : [ specimenSelectorResultGrid.getPanel(_this.data) ], + buttons : [ { + text : 'Go', + handler : function() { + var array = []; + for ( var i = 0; i < specimenSelectorResultGrid.selected.length; i++) { + var row = specimenSelectorResultGrid.selected[i]; + array.push({ + macromoleculeId : row.macromoleculeId, + bufferId : row.bufferId + }); + } + goTo(BUI.getMacromoleculeResultsURLByMultipleSearch(array)); + + } + }, { + text : 'Cancel', + handler : function() { + window.close(); + } + } ] + + }).show(); - }, - getAbinitioImageURL : function() { - return this.getBaseURL() + '?reqCode=getAbinitioImage'; - }, - getNSDImageURL : function(modelListId) { - return BUI.getAbinitioImageURL() + '&type=NSD&modelListId=' + modelListId; - }, - getCHI2ImageURL : function(modelListId) { - return BUI.getAbinitioImageURL() + '&type=CHI2&modelListId=' + modelListId; - }, - getModelFile : function(type, modelId, format) { - return this.getBaseURL() + '?reqCode=getModelFile' + "&type=" + type + "&modelId=" + modelId + "&format=" + format; - }, - getPdbURL : function() { - return this.getBaseURL() + '?reqCode=getPdbFiles'; - }, - getStvArray : function(value, error) { - value = Number(value); - error = Number(error); - return [ value - error, value, value + error ]; - }, - getPointArrayForDygraph : function(x, y, error) { - return [ x, BUI.getStvArray(y, error) ]; - }, - createDIV : function(text, width, className, backgroundColor) { - var nameContainer = document.createElement("div"); - var nameSpan = document.createElement("span"); - if (className != null) { - nameSpan.setAttribute("class", className); - } - if (backgroundColor != null) { - nameSpan.setAttribute("style", "float:left;width:" + width + "px;height:18px;background-color:" + backgroundColor); - } else { - nameSpan.setAttribute("style", "float:left;width:" + width + "px;height:18px;"); } - nameSpan.appendChild(document.createTextNode(text)); - nameContainer.appendChild(nameSpan); - return nameContainer; - }, + } ]; +}; - createFormLabel : function(labelText, text, labelWidth, textWidth, backgroundColor) { - var div = document.createElement("div"); +ResultsAssemblyGrid.prototype.getLegendPanel = function() { + return { + html : '
' + BUI.getRectangleColorDIV(this.validColor, 10, 10) + + 'Good quality measurements' + + BUI.getRectangleColorDIV(this.warningColor, 10, 10) + + 'Probably valid with manual processing' + + BUI.getRectangleColorDIV(this.notValidColor, 10, 10) + + 'More measurements need to be done
' + }; +}; +/** Returns the grid **/ +ResultsAssemblyGrid.prototype.getPanel = function(macromolecules) { + var _this = this; - div.appendChild(BUI.createDIV(labelText, labelWidth, "bLabel", backgroundColor)); - div.appendChild(BUI.createDIV(text, textWidth, "btext", backgroundColor)); - return div.innerHTML; - }, + this.store = Ext.create('Ext.data.Store', { + fields : [ + 'macromoleculeId', 'macromoleculeAcronym', 'measurementCount', 'subtractionCount', 'averageCount', 'timeStart', 'concentrationArray', + 'bufferConditions', 'conditions' ], + data : this._prepareData(macromolecules) + }); - createTextArea : function(labelText, text, labelWidth, rows, cols) { - var div = document.createElement("div"); - div.appendChild(BUI.createDIV(labelText, labelWidth, "bLabel")); - var textArea = document.createElement("textarea"); - textArea.setAttribute("rows", rows); - textArea.setAttribute("cols", cols); - textArea.appendChild(document.createTextNode(text)); - div.appendChild(textArea); - return div.innerHTML; - }, + this.store.sort('macromoleculeAcronym'); - showBetaWarning : function() { - alert("ISPyB for Biosaxs version Beta has not this functionality enabled"); - }, + this.grid = Ext.create('Ext.grid.Panel', { + id : this.id, + title : 'Macromolecules', + store : this.store, + tbar : this._getTbar(), + bbar : [ this.getLegendPanel() ], + width : this.width, + height : this.height, + maxHeight : this.maxHeight, + sortableColumns : true, + columns : [ + { + text : '', + dataIndex : 'macromoleculeId', + width : 20, + hidden : false, + renderer : function(val, y, sample) { + return BUI.getRectangleColorDIV(BIOSAXS.proposal.macromoleculeColors[val], 10, 10); + } + }, + { + text : 'Macromolecule', + dataIndex : 'macromoleculeAcronym', + width : 200 + }, + { + text : 'Buffer Conditions', + dataIndex : 'conditions', + flex : 1, + renderer : function(val, style, record) { + return _this.getConditionHTMLTable(val, style, record); + } + }, + { + header : 'Average', + dataIndex : 'percentageCollected', + name : 'percentageCollected', + type : 'string', + hidden : true, + renderer : function(val, y, sample) { + return "
" + + BUI.getProgessBar((sample.raw.averageCount / sample.raw.measurementCount) * 100, sample.raw.averageCount + "/" + + sample.raw.measurementCount) + "
"; + }, + width : 100, + sorter : false + }, + { + header : 'Subtractions', + dataIndex : 'percentageCollected', + name : 'percentageCollected', + type : 'string', + hidden : true, + renderer : function(val, y, sample) { + return "
"+ BUI.getProgessBar((sample.raw.subtractionCount / sample.raw.measurementCount) * 100, sample.raw.subtractionCount + "/" + + sample.raw.measurementCount) + "
"; + }, + width : 100 + }, { + header : 'Date', + dataIndex : 'timeStart', + name : 'timeStart', + type : 'string', + renderer : function(value, metaData, record, rowIndex, colIndex, store) { + if (record.raw.timeStart != null) { + return moment(record.raw.timeStart).format("MMM Do YY"); + } + } + }, { + header : 'Download', + dataIndex : 'percentageCollected', + name : 'percentageCollected', + type : 'string', + renderer : function(val, y, sample) { + return BUI.getZipHTMLByMacromoleculeId(sample.raw.macromoleculeId); + }, + width : 100 + }, - formatValuesErrorUnitsScientificFormat : function(val, error, unit, args) { - var fontSize = 14; - var decimals = 2; - var errorFontSize = 10; - /** line break **/ - var lineBreak = true; - if (args != null) { - if (args.fontSize != null) { - fontSize = args.fontSize; - } - if (args.decimals != null) { - decimals = args.decimals; - } - if (args.errorFontSize != null) { - errorFontSize = args.errorFontSize; - } - if (args.lineBreak != null) { - lineBreak = args.lineBreak; + { + id : 'btnResultVisible', + width : 85, + sortable : false, + renderer : function(value, metaData, record, rowIndex, colIndex, store) { + return BUI.getGreenButton('GO'); + } + } ], + viewConfig : { + stripeRows : true, + listeners : { + afterrender : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + }, + celldblclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + _this.edit(record.data.macromoleculeId); + }, + cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + if (grid.getGridColumns()[cellIndex].getId() == 'buttonEditMacromolecule') { + _this.edit(record.data.macromoleculeId); + } + if (grid.getGridColumns()[cellIndex].getId() == 'buttonRemoveMacromolecule') { + BUI.showBetaWarning(); + } + if (grid.getGridColumns()[cellIndex].getId() == 'btnResultVisible') { + window.location = BUI.getMacromoleculeResultsURL(record.data.macromoleculeId); + } + } } - } + }); - if (val == "") { - return ""; - } - if (error == null) { - return "" + Number(val).toFixed(decimals) + ""; - } - var html = "" + Number(val).toFixed(decimals) + " " + unit + ""; - if (lineBreak) { - html = html + "
"; - } - return html + " ± " + Number(Number(error).toFixed(3)).toExponential() - + ""; - }, + /** Adding the tbar **/ + if (this.tbar) { + this.grid.addDocked({ + xtype : 'toolbar', + items : this.getTbar() + }); + } + return this.grid; +}; - formatValuesErrorUnits : function(val, error, unit, args) { - var fontSize = 16; - var decimals = 2; - var errorFontSize = 10; - /** line break **/ - var lineBreak = true; - if (args != null) { - if (args.fontSize != null) { - fontSize = args.fontSize; - } - if (args.decimals != null) { - decimals = args.decimals; - } - if (args.errorFontSize != null) { - errorFontSize = args.errorFontSize; - } - if (args.lineBreak != null) { - lineBreak = args.lineBreak; - } +ResultsAssemblyGrid.prototype.input = function(targetId) { + return { + data : DATADOC.getData_3367(), + proposal : DATADOC.getProposal_3367() + }; +}; - } +ResultsAssemblyGrid.prototype.test = function(targetId) { + var grid = new ResultsAssemblyGrid({ + height : 600, + searchBar : false, + tbar : false, + btnResultVisible : true, + width : 1000 + }); + BIOSAXS.proposal = new Proposal(grid.input().proposal); + var panel = grid.getPanel(grid.input().data); + panel.render(targetId); - if (val == "") { - return ""; +}; + + +function RigidModelGrid(args) { + this.height = null; + this.width = null; + this.id = BUI.id(); + + if (args != null) { + if (args.height != null) { + this.height = args.height; + + if (args.width != null) { + this.width = args.width; + } + } + } + + this.onSelected = new Event(this); +} + + +RigidModelGrid.prototype.refresh = function(subtractions) { + var data = []; + if (subtractions != null){ + if (subtractions.length > 0){ + for (j in subtractions){ + var subtraction = subtractions[j]; + if (subtraction.rigidBodyModeling3VOs != null){ + for (i in subtraction.rigidBodyModeling3VOs){ + data.push(subtraction.rigidBodyModeling3VOs[i]); + } + } + } + } + } + this.store.loadData(data); +}; + +RigidModelGrid.prototype.getPanel = function() { + var _this = this; + this.store = Ext.create('Ext.data.Store', { + fields : [ 'symmetry', 'subtractionId', 'subUnitConfigFilePath', 'rigidBodyModelingId', 'rigidBodyModelFilePath', 'logFilePath', 'fitFilePath', 'curveConfigFilePath', + 'crossCorrConfigFilePath', 'contactDescriptionFilePath' ], + data : [] + }); + + this.store.sort([ { + property : 'name', + direction : 'ASC' + } ]); + + this.panel = Ext.create('Ext.grid.Panel', { + store : this.store, + width : this.width, + height : this.height, + margin : 10, + deferredRender : false, + columns : [ { + text : 'RBM', + dataIndex : 'rigidBodyModelFilePath', + hidden : false, + flex : 1, + renderer : function(val){ + return BUI.getFileName(val); + } + }, { + text : 'Sub Unit Conf.', + dataIndex : 'subUnitConfigFilePath', + hidden : true, + flex : 1, + renderer : function(val){ + return BUI.getFileName(val); + } + }, { + text : 'Log', + dataIndex : 'logFilePath', + hidden : true, + flex : 1, + renderer : function(val){ + return BUI.getFileName(val); + } + }, { + text : 'Fit', + dataIndex : 'fitFilePath', + hidden : false, + flex : 1, + renderer : function(val){ + return BUI.getFileName(val); + } + }, { + text : 'Curve Conf.', + dataIndex : 'curveConfigFilePath', + hidden : false, + flex : 1, + renderer : function(val){ + return BUI.getFileName(val); + } + }, { + text : 'Cross Corr.', + dataIndex : 'crossCorrConfigFilePath', + hidden : false, + flex : 1, + renderer : function(val){ + return BUI.getFileName(val); + } + }, { + text : 'Contact Desc.', + dataIndex : 'contactDescriptionFilePath', + hidden : true, + flex : 1, + renderer : function(val){ + return BUI.getFileName(val); + } + } ], + + viewConfig : { + enableTextSelection : true, + preserveScrollOnRefresh : true + }, + listeners : { + cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + + }, + afterrender : function() { + } + } + }); + return this.panel; +}; + + +/** + * shows shipments + * + * @height + * @width + * @minHeight + * @btnEditVisible + */ +function ShipmentGrid(args) { + this.id = BUI.id(); + this.height = 100; + this.width = null; + this.minHeight = null; + this.btnEditVisible = true; + + if (args != null) { + if (args.height != null) { + this.height = args.height; } - if (error == null) { - return "" + Number(val).toFixed(decimals) + ""; + if (args.width != null) { + this.width = args.width; } - var html = "" + Number(val).toFixed(decimals) + " " + unit + ""; - if (lineBreak) { - html = html + "
"; + if (args.minHeight != null) { + this.minHeight = args.minHeight; } - return html + " ± " + Number(Number(error).toFixed(8)).toExponential() - + ""; - }, - - formatValuesUnits : function(val, unit, args) { - var fontSize = 12; - var decimals = 2; - var unitsFontSize = 10; - if (args != null) { - if (args.fontSize != null) { - fontSize = args.fontSize; - } - if (args.decimals != null) { - decimals = args.decimals; - } - if (args.unitsFontSize != null) { - unitsFontSize = args.unitsFontSize; - } - + if (args.btnEditVisible != null) { + this.btnEditVisible = args.btnEditVisible; } + } +} - if (val === "") { - return ""; - } - if (unit == null) { - return "" + Number(val).toFixed(decimals) + ""; - } - return "" + Number(val).toFixed(decimals) + " " + unit + ""; - }, +ShipmentGrid.prototype._getColumns = function() { + var _this = this; + var columns = [ + { + text : 'Name', + dataIndex : 'shippingName', + flex : 1 + }, + { + header : 'Type', + dataIndex : 'shippingType', + flex : 1, + hidden : true, + renderer : function(val, comp, record) { + if (val != null) { + return val.toUpperCase(); + } - getGreenButton : function(text, args) { - var width = 70; - var height = 20; - if (args != null) { - if (args.width != null) { - width = args.width; } - if (args.height != null) { - height = args.height; + }, + { + header : 'Status', + type : 'string', + flex : 1, + hidden : false, + renderer : function(comp, val, record) { + if (record.raw.shippingStatus != null) { + if (new String(record.raw.shippingStatus).toUpperCase() == 'OPENED') { + return "" + new String(record.raw.shippingStatus).toUpperCase() + ""; + } + return "" + new String(record.raw.shippingStatus).toUpperCase() + ""; + } + } + }, + { + text : 'Cases', + flex : 1, + hidden : false, + renderer : function(comp, val, record) { + var shipment = BIOSAXS.proposal.getShipmentById(record.data.shippingId); + var container = ""; + if (shipment.dewarVOs.length > 0) { + container = container + ""; + } else { + return "Empty"; + } + return container + "
" + shipment.dewarVOs.length + "x
"; } - } - - return ''; - }, -// getBlueButton : function(text, args) { -// var width = 70; -// var height = 20; -// if (args != null) { -// if (args.width != null) { -// width = args.width; -// } -// if (args.height != null) { -// height = args.height; -// } -// } -// -// return ''; -// }, + }, { + header : 'Comments', + dataIndex : 'comments', + flex : 1, + hidden : false + }, { + header : 'Creation Date', + dataIndex : 'creationDate', + hidden : true + } ]; - getBlueButton : function(text, args) { - var width = 70; - var height = 20; - var href = null; - if (args != null) { - if (args.width != null) { - width = args.width; - } - if (args.height != null) { - height = args.height; + if (this.btnEditVisible) { + columns.push({ + id : _this.id + 'buttonEdit', + text : '', + hidden : !_this.btnEditVisible, + width : 100, + sortable : false, + renderer : function(value, metaData, record, rowIndex, colIndex, store) { + return BUI.getGreenButton('EDIT'); } - if (args.href != null) { - href = args.href; + }); + } + + columns.push({ + id : _this.id + 'buttonRemove', + text : '', + width : 100, + sortable : false, + renderer : function(value, metaData, record, rowIndex, colIndex, store) { + var shipment = BIOSAXS.proposal.getShipmentById(record.data.shippingId); + if (shipment.dewarVOs.length == 0) { + return BUI.getRedButton('REMOVE'); } + } - if (href != null) { - return ''; - } else { - return ''; -// return ''; - } - }, - - getSubmitGreenButton : function(text) { - return ''; - }, + }); - getRedButton : function(text) { - return ''; + return columns; +}; - }, - getRectangleColorDIV : function(color, height, width) { - return '
'; - }, +ShipmentGrid.prototype._getTopButtons = function() { + var _this = this; + /** Actions buttons **/ + var actions = []; + actions.push(Ext.create('Ext.Action', { + icon : '../images/add.png', + text : 'Add Shipment', + handler : function(widget, event) { + //window.location = BUI.getCreateShipmentURL(); + var _this = this; - openBufferWindow : function(bufferId) { - var window = new BufferWindow(); - window.draw(tabs.experiment.getBufferById(bufferId), tabs.experiment); - }, + var shipmentForm = new ShipmentForm({ + creationMode : true, + showTitle : false + }); + shipmentForm.onSaved.attach(function(sender, shipment) { + _this.showShipmentTabs(shipment, targetId); + }); + + var window = Ext.create('Ext.window.Window', { + title : 'New Shipment', + height : 600, + width : 800, + layout : 'fit', + items : [ shipmentForm.getPanel() ] + }).show(); - /** Render for safety levels on grids **/ - safetyRenderer : function(val, y, specimen) { - var color = val; - if (val == "YELLOW") { - color = "#E9AB17"; } - return '' + val + ''; - }, + })); + return actions; +}; - getSampleColor : function() { - return '#CB181D'; - }, +ShipmentGrid.prototype.refresh = function(shippings) { + this.features = shippings; + this.store.loadData(this._prepareData(), false); +}; - getLightSampleColor : function() { - return '#FCBBA1'; - }, +ShipmentGrid.prototype._prepareData = function() { + var data = []; + for ( var i = 0; i < this.features.length; i++) { + data.push(this.features[i]); + } + return data; +}; - getBufferColor : function() { - return '#6A51A3'; - }, - getLightBufferColor : function() { - return '#BCBDDC'; - }, +ShipmentGrid.prototype.getPanel = function(shipments) { + this.features = shipments; + return this._renderGrid(); +}; - formatConcentration : function(val) { - if (val != null) { - return "" + Number(val).toFixed(2) + " mg/ml "; - } - return val; - }, +ShipmentGrid.prototype.edit = function(shippingId) { + window.location = BUI.getShippingURL(shippingId); +}; - formatVolume : function(sample, volume) { - if (Number(sample.data.volumeToLoad) > Number(sample.data.volume)) { - return "" + volume + " �l"; - } - if (Number(sample.data.volumeToLoad) == Number(sample.data.volume)) { - return "" + volume + " �l"; - } - return "" + volume + " �l"; - }, +ShipmentGrid.prototype._getStoreFields = function(data) { + var _this = this; + return [ { + name : 'shippingId' + }, { + name : 'shippingName' + }, { + name : 'shippingStatus' + }, { + name : 'shippingType' + }, { + name : 'creationDate' + }, { + name : 'comments' + } ]; +}; - getProposal : function() { - return new Proposal(); - }, +ShipmentGrid.prototype._renderGrid = function() { + var _this = this; - getSampleNameRenderer : function(val, y, record) { - var sample = record.data; - if (record.raw.macromolecule3VO == null) { - return '' + sample.code + ''; - } else { - return '' + sample.code + ''; - } - }, + /** Store **/ + var data = this._prepareData(); + this.store = Ext.create('Ext.data.Store', { + fields : this._getStoreFields(data), + autoload : true, + data : data + }); - getSafetyLevels : function() { - var safetyLevels = new Array(); - safetyLevels.push({ - safetyLevelId : "", - name : "UNKNOWN" - }); - safetyLevels.push({ - safetyLevelId : 1, - name : "GREEN" - }); - safetyLevels.push({ - safetyLevelId : 2, - name : "YELLOW" - }); - safetyLevels.push({ - safetyLevelId : 3, - name : "RED" - }); - return safetyLevels; - }, + this.store.loadData(data, false); + + this.grid = Ext.create('Ext.grid.Panel', { + title : "Shipping", + icon : '/ispyb/images/plane.gif', + width : this.width, + minWidth : this.minWidth, + height : this.height, + store : this.store, + columns : this._getColumns(), + viewConfig : { + stripeRows : true, + listeners : { + itemdblclick : function(dataview, record, item, e) { + _this.edit(record.raw.shippingId); + }, + cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonEdit') { + _this.edit(record.data.shippingId); + } - getErrorColor : function() { - return '#F6CED8'; - }, - getWarningColor : function() { - return '#F5DA81'; - }, - getValidColor : function() { - return '#E0F8E0'; - }, - getSamplePlateLetters : function() { - return [ "A", "B", "C", "D", "E", "F", "G", "H" ]; - }, - getSamplePositionHTML : function(sample, experiment) { - var plate = ""; - var row = ""; - var column = ""; - var rows = this.getSamplePlateLetters(); - if (sample.sampleplateposition3VO != null) { - var samplePlate = experiment.getSamplePlateById(sample.sampleplateposition3VO.samplePlateId); - if (samplePlate != null) { - plate = (samplePlate.slotPositionColumn); - row = (sample.sampleplateposition3VO.rowNumber); - column = (sample.sampleplateposition3VO.columnNumber); - // var html = "Plate: " + "" + plate + ""; - // html = html + ", Row: " + "" + rows[row - 1] + ""; - // html = html + ", Column: " + "" + column + ""; - var html = "Plate: " + plate - + "-" + rows[row - 1] + "" + column + ""; - return html; + if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonRemove') { + var shipment = BIOSAXS.proposal.getShipmentById(record.data.shippingId); + if (shipment.dewarVOs.length == 0) { + + var adapter = new BiosaxsDataAdapter(); + _this.grid.setLoading("ISPyB: Removing shipment"); + adapter.onSuccess.attach(function(sender) { + BIOSAXS.proposal.onInitialized.attach(function(sender) { + _this.refresh(BIOSAXS.proposal.getShipments()); + _this.grid.setLoading(false); + }); + BIOSAXS.proposal.init(); + }); + + adapter.onError.attach(function(sender) { + alert("Error"); + _this.grid.setLoading(false); + }); + adapter.removeShipment(record.data.shippingId); + } + } + } } + }, + selModel : { + mode : 'SINGLE' } - return ""; - }, + }); - getSafetyLabelName : function(safetyLevelId) { - var safetyLevels = BUI.getSafetyLevels(); - for ( var i = 0; i < safetyLevels.length; i++) { - if (safetyLevels[i].safetyLevelId == safetyLevelId) { - return safetyLevels[i].name; + var actions = _this._getTopButtons(); + this.grid.addDocked({ + xtype : 'toolbar', + items : actions + }); + + this.grid.getSelectionModel().on({ + selectionchange : function(sm, selections) { + if (selections.length) { + for ( var i = 0; i < actions.length; i++) { + if (actions[i].enable) { + actions[i].enable(); + } + } + } else { + for ( var i = 0; i < actions.length; i++) { + if (actions[i].alwaysEnabled == false) { + if (actions[i].disable) { + actions[i].disable(); + } + } + } } } - return "UNKNOWN"; - }, - /** generate random id **/ - id : function() { - var text = ""; - var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; - - for ( var i = 0; i < 5; i++) - text += possible.charAt(Math.floor(Math.random() * possible.length)); + }); + return this.grid; +}; - return text; - }, - showWarning : function(message) { - Ext.Msg.show({ - title : 'Warning', - msg : message, - icon : Ext.Msg.WARNING, - animEl : 'elId' - }); - }, - showError : function(message) { - Ext.Msg.show({ - title : 'Warning', - msg : message, - icon : Ext.Msg.ERROR, - animEl : 'elId' - }); - }, - getTipHTML : function(message) { - //return "
Tip
" - // + message + "
"; - return "
Tip
" - + message + "
"; - }, +ShipmentGrid.prototype.input = function() { + return { + proposal : new MeasurementGrid().input().proposal, + shippings : DATADOC.getShippings_10() + }; +}; - getWarningHTML : function(message) { - return "
Warning
" - + message + "
"; - }, +ShipmentGrid.prototype.test = function(targetId) { + var shipmentGrid = new ShipmentGrid({ + minHeight : 300, + height : 440 + }); + BIOSAXS.proposal = new Proposal(shipmentGrid.input().proposal); + var panel = shipmentGrid.getPanel(targetId); + panel.render(targetId); + shipmentGrid.refresh(shipmentGrid.input().shippings); +}; + +function SpecimenGrid(args) { + this.id = BUI.id(); + this.height = 500; + this.unitsFontSize = 9; + this.editEnabled = false; + this.isPositionColumnHidden = false; + this.removeBtnEnabled = false; - getErrorHTML : function(message) { - return "
Error
" - + message + "
"; - }, + this.selectionMode = "MULTI"; + this.updateRowEnabled = false; + this.grouped = true; + this.width = 900; + this.title = 'Specimens'; + + this.margin = "0 0 0 0"; +// this.experimentColorBased = false; - getProgessBar : function(percentage, text) { - /** percentage 100% green **/ - var color = "#0a0"; + if (args != null) { + if (args.height != null) { + this.height = args.height; + } - color = "#99CC00"; - if (percentage > 100) { - color = "yellow"; - percentage = 100; + if (args.showTitle == false) { + this.title = null; } - if (isNaN(percentage)) { - color = "white"; - percentage = 0; + + if (args.margin == false) { + this.margin = args.margin; } - var defaultText = percentage + "%"; - if (text != null) { - defaultText = text; + if (args.grouped == false) { + this.grouped = null; } - return "
" + defaultText + "
"; - }, - getFileName : function(filePath){ - if (filePath != null){ - return filePath.split("/")[filePath.split("/").length - 1] + if (args.width != null) { + this.width = args.width; } - return ""; - } -}; -Ext.ux.form.RequiredCombo = Ext.extend(Ext.form.ComboBox, { - config : { - cls : 'custom-field-text-required' - }, - initComponent : function() { - Ext.ux.form.RequiredCombo.superclass.initComponent.apply(this, arguments); - }, - checkChange : function() { - if ((this.getValue() == null) || String(this.getValue()).length == 0) { - this.addCls('custom-field-text-required'); - } else { - this.removeCls('custom-field-text-required'); + if (args.editEnabled != null) { + this.editEnabled = args.editEnabled; + } + if (args.removeBtnEnabled != null) { + this.removeBtnEnabled = args.removeBtnEnabled; + } + if (args.isPositionColumnHidden != null) { + this.isPositionColumnHidden = args.isPositionColumnHidden; + } + if (args.selectionMode != null) { + this.selectionMode = args.selectionMode; + } + if (args.updateRowEnabled != null) { + this.updateRowEnabled = args.updateRowEnabled; } + } -}); + this.onClick = new Event(this); + this.onSelected = new Event(this); + this.onRemoved = new Event(this); + this.onSpecimenChanged = new Event(); +} -Ext.define('Ext.form.field.RequiredNumber', { - extend : 'Ext.form.field.Number', - alias : 'widget.requirednumberfield', - alternateClassName : [ 'Ext.form.RequiredNumberField', 'Ext.form.RequiredNumber' ], - config : { - cls : 'custom-field-text-required' - }, +SpecimenGrid.prototype._prepareData = function(experiment) { + var data = []; - initComponent : function() { - var me = this; - me.callParent(); - me.setMinValue(me.minValue); - me.setMaxValue(me.maxValue); - }, + var samples = experiment.getSamples(); + for ( var i = 0; i < samples.length; i++) { + var sample = samples[i]; + if (sample.macromolecule3VO != null) { + sample.macromolecule = sample.macromolecule3VO.acronym; + sample.exposureTemperature = []; + sample.macromoleculeId = sample.macromolecule3VO.macromoleculeId; + } - onChange : function() { - if ((this.getValue() == null) || String(this.getValue()).length == 0) { - this.addCls('custom-field-text-required'); + if (sample.sampleplateposition3VO != null) { + if (sample.sampleplateposition3VO.samplePlateId != null) { + sample.samplePlateId = sample.sampleplateposition3VO.samplePlateId; + sample.rowNumber = sample.sampleplateposition3VO.rowNumber; + sample.columnNumber = sample.sampleplateposition3VO.columnNumber; + if (experiment.getSamplePlateById(sample.sampleplateposition3VO.samplePlateId).plategroup3VO != null) { + sample.plateGroupName = experiment.getSamplePlateById(sample.sampleplateposition3VO.samplePlateId).plategroup3VO.name; + sample.samplePlateName = experiment.getSamplePlateById(sample.sampleplateposition3VO.samplePlateId).name + " [" + sample.plateGroupName + "]"; + sample.slotPositionColumn = experiment.getSamplePlateById(sample.sampleplateposition3VO.samplePlateId).slotPositionColumn; + } + } } else { - this.removeCls('custom-field-text-required'); + sample.samplePlateName = "Unallocated Specimens"; } - this.toggleSpinners(); - this.callParent(arguments); - } -}); -Ext.define('Ext.form.field.RequiredText', { - extend : 'Ext.form.field.Text', - alias : 'widget.requiredtext', - requires : [ 'Ext.form.field.VTypes', 'Ext.layout.component.field.Text' ], - alternateClassName : [ 'Ext.form.RequiredTextField', 'Ext.form.RequiredText' ], - config : { - cls : 'custom-field-text-required' - }, - initComponent : function() { - var me = this; - if (me.allowOnlyWhitespace === false) { - me.allowBlank = false; + /** For grouping, because sencha has not option for multiple grouping I add a field to your store with a convert function that concatenates these two fields and then group by that field.**/ + sample.groupIndex = sample.bufferId + sample.macromoleculeId; + var macromolecule = BIOSAXS.proposal.getMacromoleculeById(sample.macromoleculeId); + + sample.acronym = "Buffers"; + if (macromolecule != null) { + sample.acronym = BIOSAXS.proposal.getMacromoleculeById(sample.macromoleculeId).acronym; } - me.callParent(); - me.addEvents( - /** - * @event autosize - * Fires when the **{@link #autoSize}** function is triggered and the field is resized according to the - * {@link #grow}/{@link #growMin}/{@link #growMax} configs as a result. This event provides a hook for the - * developer to apply additional logic at runtime to resize the field if needed. - * @param {Ext.form.field.Text} this This text field - * @param {Number} width The new field width - */ - 'autosize', - /** - * @event keydown - * Keydown input field event. This event only fires if **{@link #enableKeyEvents}** is set to true. - * @param {Ext.form.field.Text} this This text field - * @param {Ext.EventObject} e - */ - 'keydown', - /** - * @event keyup - * Keyup input field event. This event only fires if **{@link #enableKeyEvents}** is set to true. - * @param {Ext.form.field.Text} this This text field - * @param {Ext.EventObject} e - */ - 'keyup', - /** - * @event keypress - * Keypress input field event. This event only fires if **{@link #enableKeyEvents}** is set to true. - * @param {Ext.form.field.Text} this This text field - * @param {Ext.EventObject} e - */ - 'keypress'); - me.addStateEvents('change'); - me.setGrowSizePolicy(); - }, - checkChange : function() { - if ((this.getValue() == null) || String(this.getValue()).length == 0) { - this.addCls('custom-field-text-required'); - } else { - this.removeCls('custom-field-text-required'); + sample.buffer = experiment.getBufferById(sample.bufferId); + + sample.volumeToLoad = experiment.getVolumeToLoadBySampleId(sample.sampleId); + data.push(sample); + } + return data; +}; + +SpecimenGrid.prototype.deselectAll = function() { + this.grid.getSelectionModel().deselectAll(); +}; + +SpecimenGrid.prototype.selectById = function(specimenId) { + this.grid.getSelectionModel().deselectAll(); + for ( var i = 0; i < this.grid.getStore().data.items.length; i++) { + var item = this.grid.getStore().data.items[i].raw; + if (item.specimenId == specimenId) { + this.grid.getSelectionModel().select(i); } } -}); +}; -var BIOSAXS_COMBOMANAGER = { +SpecimenGrid.prototype.getStore = function() { + return this.store; +}; - getComboMacromoleculeByMacromolecules : function(macromolecules, args) { - var labelWidth = 150; - var margin = "0 0 5 0"; - var width = 300; +SpecimenGrid.prototype.getPlugins = function() { + var _this = this; - if (args != null) { - if (args.labelWidth != null) { - labelWidth = args.labelWidth; - } - if (args.margin != null) { - margin = args.margin; - } - if (args.width != null) { - width = args.width; - } - } + var plugins = []; - var store = Ext.create('Ext.data.Store', { - fields : [ 'macromoleculeId', 'acronym' ], - data : macromolecules, - sorters : [ 'acronym' ] - }); + if (this.updateRowEnabled) { + plugins.push(Ext.create('Ext.grid.plugin.RowEditing', { + clicksToEdit : 1, + listeners : { + validateedit : function(grid, e) { + var measurements = []; - return Ext.create('Ext.form.ComboBox', { - fieldLabel : 'Macromolecules', - labelWidth : labelWidth, - width : width, - margin : margin, - store : store, - editable: false, - queryMode : 'local', - displayField : 'acronym', - valueField : 'macromoleculeId' - }); - }, - getComboBuffers : function(buffers, args) { - var labelWidth = 150; - var margin = "0 0 5 0"; - var width = 300; - var fieldLabel = 'Buffer'; + if (e.newValues.bufferId != e.record.raw.bufferId) { + /** If buffer has changed we have to change all the specimens sharing same datacollection **/ + var dataCollections = []; + if (e.record.raw.macromoleculeId == null) { + dataCollections = dataCollections.concat(_this.experiment.getDataCollectionsBySpecimenId(e.record.raw.specimenId)); + } else { + var sampleDataCollections = _this.experiment.getDataCollectionsBySpecimenId(e.record.raw.specimenId); + for ( var i = 0; i < sampleDataCollections.length; i++) { + var sampleDataCollection = sampleDataCollections[i]; + if (sampleDataCollection != null) { + for ( var j = 0; j < sampleDataCollection.measurementtodatacollection3VOs.length; j++) { + var measurementTODc = sampleDataCollection.measurementtodatacollection3VOs[j]; + if (measurementTODc.dataCollectionOrder == 1) { + dataCollections = dataCollections.concat(_this.experiment.getDataCollectionsBySpecimenId(_this.experiment + .getMeasurementById(measurementTODc.measurementId).specimenId)); + } + } + } + } + } + var i = null; + for ( i = 0; i < dataCollections.length; i++) { + var dataCollection = dataCollections[i]; + var specimens = _this.experiment.getSpecimenByDataCollectionId(dataCollection.dataCollectionId); + measurements = measurements.concat(specimens); + } - if (args != null) { - if (args.labelWidth != null) { - labelWidth = args.labelWidth; - } - if (args.margin != null) { - margin = args.margin; - } - if (args.width != null) { - width = args.width; - } - if (args.noLabel != null) { - fieldLabel = null; - } - } + for ( i = 0; i < measurements.length; i++) { + var measurement = measurements[i]; + var specimen = _this.experiment.getSpecimenById(measurement.specimenId); + specimen.bufferId = e.newValues.bufferId; + new BiosaxsDataAdapter().saveSpecimen(specimen, _this.experiment); + } + } - var store = Ext.create('Ext.data.Store', { - fields : [ 'bufferId', 'acronym' ], - data : buffers, - sorters : [ 'acronym' ] - }); + /** Setting values **/ + e.record.raw.concentration = e.newValues.concentration; + e.record.raw.volume = e.newValues.volume; - return Ext.create('Ext.form.ComboBox', { - fieldLabel : fieldLabel, - labelWidth : labelWidth, - width : width, - margin : margin, - editable: false, - store : store, - queryMode : 'local', - displayField : 'acronym', - valueField : 'bufferId' - }); - }, - getComboSessions : function(sessions, args) { - var labelWidth = 150; - var margin = "0 0 5 0"; - var width = 300; + /** Position **/ + if (e.record.raw.sampleplateposition3VO != null) { + var samplePlate = _this.experiment.getSamplePlateBySlotPositionColumn(e.newValues.slotPositionColumn); + if (samplePlate != null) { + e.record.raw.sampleplateposition3VO = { + columnNumber : e.newValues.columnNumber, + rowNumber : e.newValues.rowNumber, + samplePlateId : samplePlate.samplePlateId + }; + } + } else { + if (e.newValues.slotPositionColumn != null) { + var samplePlate = _this.experiment.getSamplePlateBySlotPositionColumn(e.newValues.slotPositionColumn); + if (samplePlate != null) { + e.record.raw.sampleplateposition3VO = { + columnNumber : e.newValues.columnNumber, + rowNumber : e.newValues.rowNumber, + samplePlateId : samplePlate.samplePlateId + }; + } + } + } - if (args != null) { - if (args.labelWidth != null) { - labelWidth = args.labelWidth; - } - if (args.margin != null) { - margin = args.margin; - } - if (args.width != null) { - width = args.width; + var macromoleculeId = e.record.data.macromoleculeId; + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, specimen) { + /** Because macromolecule3VO is fecthed LAZY **/ + if (macromoleculeId != null) { + specimen.macromolecule3VO = BIOSAXS.proposal.getMacromoleculeById(macromoleculeId); + } + _this.onSpecimenChanged.notify(specimen); + _this.grid.setLoading(false); + }); + adapter.onError.attach(function() { + alert("Error"); + _this.grid.setLoading(false); + }); + _this.grid.setLoading(); + adapter.saveSpecimen(e.record.raw, _this.experiment); + } } - } - - for ( var i = 0; i < sessions.length; i++) { - sessions[i]["startDateFormatted"] = moment(sessions[i].startDate).format("MMM Do YY"); - sessions[i]["sorter"] = moment(sessions[i].startDate).format("YYYYMMDD"); - } + })); + } + return plugins; +}; - var store = Ext.create('Ext.data.Store', { - fields : [ 'sessionId', 'startDateFormatted', 'beamlineName', 'startDate', 'endDate', 'beamlineOperator' ], - data : sessions, - sorters : [ 'sorter' ] +SpecimenGrid.prototype._getRowCombo = function() { + var data = []; + for ( var i = 1; i <= 8; i++) { + data.push({ + rowNumber : i, + name : BUI.getSamplePlateLetters()[i - 1] }); + } - return Ext.create('Ext.form.ComboBox', { - fieldLabel : 'Sessions', - labelWidth : labelWidth, - width : width, - margin : margin, - store : store, - queryMode : 'local', - // displayField : 'startDate', - valueField : 'sessionId', - // Template for the dropdown menu. - // Note the use of "x-boundlist-item" class, - // this is required to make the items selectable. - tpl : Ext.create('Ext.XTemplate', '', - '
{startDateFormatted} {beamlineName}
', '
'), - // template for the content inside text field - displayTpl : Ext.create('Ext.XTemplate', '', '{startDateFormatted}', '') + var positionsStore = Ext.create('Ext.data.Store', { + fields : [ 'rowNumber', 'name' ], + data : data + }); - }); - } + return Ext.create('Ext.form.ComboBox', { + store : positionsStore, + queryMode : 'local', + displayField : 'name', + valueField : 'rowNumber' + }); }; - - function GenericWindow(args){ - this.title = "title"; - this.width = 700; - this.height = 500; - this.id = BUI.id(); - - this.close = false; - this.draggable = true; - this.modal = true; - - if (args != null){ - if (args.actions != null){ - this.actions = args.actions; - } - if (args.form != null){ - this.form = args.form; - } - if (args.width != null){ - this.width = args.width; - } - if (args.modal != null){ - this.modal = args.modal; - } - - if (args.height != null){ - this.height = args.height; - } - if (args.title != null){ - this.title = args.title; - } - if (args.form != null){ - this.form = args.form; - } - if (args.close != null){ - this.close = args.close; - } - if (args.draggable != null){ - this.draggable = args.draggable; - } - - } - /** Events **/ - this.onSaved = new Event(this); - }; - - -GenericWindow.prototype.getButtons = function() { - var _this = this; - - if (this.close){ - return [ { - text : 'Close', - handler : function() { - _this.panel.close(); - } - } ]; - } - else{ - return [ { - text : 'Save', - handler : function() { - _this.save(); - - } - }, { - text : 'Cancel', - handler : function() { - _this.panel.close(); - } - } ]; +SpecimenGrid.prototype._getColumnCombo = function() { + var data = []; + for ( var i = 1; i <= 12; i++) { + data.push({ + columnNumber : i + }); } -}; -GenericWindow.prototype.save = function (){ - alert("Method save of GenerciWindow class is abstract"); + var positionsStore = Ext.create('Ext.data.Store', { + fields : [ 'columnNumber' ], + data : data + }); + + return Ext.create('Ext.form.ComboBox', { + store : positionsStore, + queryMode : 'local', + displayField : 'columnNumber', + valueField : 'columnNumber' + }); }; -GenericWindow.prototype._postRender = function(data, experiment){ +SpecimenGrid.prototype._getSlotColumBombo = function() { + if (this.experiment){ + var length = this.experiment.getSamplePlates().length; + + var data = []; + for ( var i = 1; i <= length; i++) { + data.push({ + slotPositionColumn : i + }); + } + + var positionsStore = Ext.create('Ext.data.Store', { + fields : [ 'slotPositionColumn' ], + data : data + }); + return Ext.create('Ext.form.ComboBox', { + store : positionsStore, + queryMode : 'local', + displayField : 'slotPositionColumn', + valueField : 'slotPositionColumn' + }); + } }; - -GenericWindow.prototype.draw = function (data, experiment){ - this._render(data, experiment); +SpecimenGrid.prototype.getPanelByExperiment = function(experiment) { + this.experiment = experiment; + var data = this._prepareData(experiment); + return this.getPanel(data); }; -GenericWindow.prototype.refresh = function(data, experiment){ - this.data = data; - this.experiment = experiment; - this.form.refresh(data, experiment); +SpecimenGrid.prototype.refresh = function(experiment) { + this.experiment = experiment; + var data = this._prepareData(experiment); + this.store.loadData(data); }; -GenericWindow.prototype._render = function(data, experiment){ - this.data = data; - var _this = this; - if (this.panel == null){ - this.panel = Ext.create('Ext.Window', { - id : this.id, - title : this.title, - resizable : true, - constrain : true, - border : 1, - modal : this.modal, - frame : false, - draggable : this.draggable, - closable : true, - autoscroll : true, - layout : { type: 'vbox',align: 'stretch'}, - width : this.width, - height : this.form.height, - buttonAlign :'right', - buttons : this.getButtons(), - items : this.form.getPanel(data, experiment), - listeners: { - scope : this, - minimize : function(){ - this.panel.hide(); - }, - destroy : function(){ - delete this.panel; - } - } - }); - this.panel.setLoading(); - } - - this.panel.show(); - this._postRender(); -}; -function CalendarWidget(args) { - this.height = 740; +SpecimenGrid.prototype.getPanel = function() { + var _this = this; + this.store = Ext.create('Ext.data.Store', { + fields : [ + 'buffer', 'bufferId', 'code', 'macromolecule', 'acronym', 'macromoleculeId', 'concentration', 'volume', 'samplePlateId', + 'slotPositionColumn', 'rowNumber', 'columnNumber', 'groupIndex' ], + data : [], + groupField : 'acronym' + }); + this.store.sort([ { + property : 'concentration', + direction : 'ASC' + }, { + property : 'buffer', + direction : 'ASC' + } ]); - if (args != null) { - if (args.height != null) { - this.height = args.height; + var selModel = Ext.create('Ext.selection.RowModel', { + allowDeselect : true, + mode : this.selectionMode, + listeners : { + selectionchange : function(sm, selections) { + var selected = []; + for ( var i = 0; i < selections.length; i++) { + selected.push(selections[i].raw); + } + _this.onSelected.notify(selected); + } } + }); + + var features = []; + + if (this.grouped) { + features.push({ + ftype : 'grouping', + groupHeaderTpl : '{name}', + hideGroupedHeader : false, + startCollapsed : false, + id : 'myGroupedStore' + }); } - this.onClick = new Event(); -} + this.grid = Ext.create( + 'Ext.grid.Panel', + { + title : this.title, + height : this.height, + width : this.width, + selModel : selModel, + store : this.store, + features : features, + margin : this.margin, + plugins : this.getPlugins(), + columns : [ + { + text : '', + dataIndex : 'macromolecule', + width : 20, + renderer : function(val, y, sample) { + var macromoleculeId = null; + if (sample.raw.macromolecule3VO != null) { + macromoleculeId = sample.raw.macromolecule3VO.macromoleculeId; + } + else{ + macromoleculeId = sample.raw.macromoleculeId; + } + + if (macromoleculeId == null) return; + return BUI.getRectangleColorDIV(_this.experiment.macromoleculeColors[macromoleculeId], 10, 10); + } + }, + { + text : 'Macromolecule', + dataIndex : 'macromolecule', + width : 100 + }, + { + text : '', + dataIndex : 'buffer', + width : 20, + renderer : function(val, y, sample) { + var color = "black"; + if (sample.raw.bufferId != null) { + if (_this.experiment.getDataCollectionsBySpecimenId(sample.raw.specimenId)[0] != null){ + color = _this.experiment.getSpecimenColorByBufferId(_this.experiment.getMeasurementById(_this.experiment.getDataCollectionsBySpecimenId(sample.raw.specimenId)[0].measurementtodatacollection3VOs[0].measurementId).specimenId); + } + return BUI.getRectangleColorDIV(color, 10, 10); + } + } + }, { + text : 'Buffer', + dataIndex : 'bufferId', + width : 140, + renderer : function(val, y, sample) { + if (sample.raw.bufferId != null) { + return BIOSAXS.proposal.getBufferById(val).acronym; + } + }, + editor : BIOSAXS_COMBOMANAGER.getComboBuffers(BIOSAXS.proposal.getBuffers(), { + noLabel : true, + width : 300 + }) + }, { + text : 'Conc.', + dataIndex : 'concentration', + width : 100, + editor : { + allowBlank : false + }, + renderer : function(val, meta, sample) { + if (isNaN(val)) { + meta.tdCls = 'yellow-cell'; + return val; + } else { + if (val != 0) { + return BUI.formatValuesUnits(val, 'mg/ml', { + fontSize : 16, + decimals : 3, + unitsFontSize : this.unitsFontSize + }); + } else { + return; + } + } + } + }, { + text : 'Vol. Well', + dataIndex : 'volume', + width : 70, + editor : { + allowBlank : true + }, + renderer : function(val, y, sample) { + return BUI.formatValuesUnits(sample.data.volume, 'µl', { + fontSize : 12, + decimals : 2, + unitsFontSize : this.unitsFontSize + }); + } + }, { + text : 'Position', + hidden : true, + flex : 1, + renderer : function(val, y, sample) { + return BUI.getSamplePositionHTML(sample.raw, _this.experiment); + } + }, { + text : 'samplePlateId', + dataIndex : 'samplePlateId', + hidden : true + }, { + text : 'Plate', + hidden : this.isPositionColumnHidden, + dataIndex : 'slotPositionColumn', + editor : _this._getSlotColumBombo(), + flex : 1, + renderer : function(val, meta, sample) { + if ((val != null) & (val != "")) { + return val; + } else { + meta.tdCls = 'yellow-cell'; + } + } + }, { + text : 'Row', + hidden : this.isPositionColumnHidden, + dataIndex : 'rowNumber', + editor : this._getRowCombo(), + flex : 1, + renderer : function(val, meta, sample) { + if ((val != null) && (val != "")) { + return BUI.getSamplePlateLetters()[val - 1]; + } else { + meta.tdCls = 'yellow-cell'; + } + } + }, { + text : 'Well', + hidden : this.isPositionColumnHidden, + dataIndex : 'columnNumber', + editor : this._getColumnCombo(), + flex : 1, + renderer : function(val, meta, sample) { + if ((val != null) && (val != "")) { + return val; + } else { + meta.tdCls = 'yellow-cell'; + } + } + }, { + id : _this.id + 'buttonEditSample', + text : 'Edit', + width : 80, + sortable : false, + hidden : !_this.editEnabled, + renderer : function(value, metaData, record, rowIndex, colIndex, store) { + if (_this.editEnabled) { + return BUI.getGreenButton('EDIT'); + } + } + }, { + id : _this.id + 'buttonRemoveSample', + text : '', + hidden : !_this.removeBtnEnabled, + width : 100, + sortable : false, + renderer : function(value, metaData, record, rowIndex, colIndex, store) { + if (_this.removeBtnEnabled) { + return BUI.getRedButton('REMOVE'); + } + } + } -CalendarWidget.prototype.loadData = function(data) { - this.events = []; + ], + viewConfig : { + preserveScrollOnRefresh : true, + stripeRows : true, + getRowClass : function(record) { + var specimens = _this.experiment.getSampleByPosition(record.data.samplePlateId, record.data.rowNumber, + record.data.columnNumber); + if (specimens.length > 1) { + return 'red-row'; - for ( var i = 0; i < data.length; i++) { - var date = moment(data[i].creationDate); - var textColor = 'black'; + } + }, + listeners : { + selectionchange : function(grid, selected) { + _this.onClick.notify(record.raw); + }, + cellclick : function(grid, td, cellIndex, record, tr) { + if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonEditSample') { + } + if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonRemoveSample') { + grid.getStore().removeAt(rowIndex); + _this.onRemoved.notify(); + } - if (data[i].status == "FINISHED") { - textColor = 'green'; - } - if (data[i].status == "ABORTED") { - textColor = 'red'; - } - this.events.push({ - title : data[i].name, - start : date.format("YYYY-MM-DD HH:mm:ss"), - end : date.format("YYYY-MM-DD HH:mm:ss"), - date : date, - allDay : false, - color : textColor, - className : date.format("YYYY-MM-DD") - }); - } + } + } + } + }); + return this.grid; }; -CalendarWidget.prototype.draw = function(targetId) { - var _this = this; - $('#' + targetId).fullCalendar({ - eventClick : function(calEvent, jsEvent, view) { - _this.onClick.notify(calEvent.className[0]); - - }, - contentHeight : _this.height, - header : { - left : 'prev,next today', - center : 'title', - right : 'month,basicWeek,basicDay' - }, - editable : false, - events : this.events +SpecimenGrid.prototype.input = function() { + return { + experiment : DATADOC.getExperiment_10(), + proposal : DATADOC.getProposal_10() + }; +}; + +SpecimenGrid.prototype.test = function(targetId) { + var specimenGrid = new SpecimenGrid({ + height : 400, + maxHeight : 400, + width : 1000 }); + BIOSAXS.proposal = new Proposal(specimenGrid.input().proposal); + + var experiment = new Experiment(specimenGrid.input().experiment); + var panel = specimenGrid.getPanelByExperiment(experiment); + panel.render(targetId); }; /** - * It shows a table with the guinier and gnom data as well as passed and - * discarded measurements + * Shows a list of stock solutions with macromolecule, buffer, storage temperature, concentration, shipment and comments * - * @height - * @showBufferColumns + * @multiselect allows multiple selection + * @height + * @minHeight + * @width + * @tbar + * @showTitle + * @isPackedVisible shows is stock solution is in a box + * @btnEditVisible shows edit button + * @btnAddVisible + * @btnAddExisting + * @btnUnpackVisible allows to unpack a stock solution + * @btnRemoveVisible allow to remove a stock solution */ -function ConcentrationHTMLTableWidget(args) { + +function StockSolutionGrid(args) { this.id = BUI.id(); + this.height = 100; + this.width = null; + this.minHeight = null; + this.tbar = true; - this.showBufferColumns = true; + this.title = "Stock Solutions"; + + /** Visible buttons and actions **/ + this.btnEditVisible = true; + this.btnRemoveVisible = true; + this.btnAddVisible = true; + this.btnAddExisting = false; + this.isPackedVisible = true; + this.btnUnpackVisible = false; + + /** Selectors **/ + this.multiselect = false; + this.selectedStockSolutions = []; if (args != null) { + if (args.btnUnpackVisible != null) { + this.btnUnpackVisible = args.btnUnpackVisible; + } + if (args.multiselect != null) { + this.multiselect = args.multiselect; + } if (args.height != null) { this.height = args.height; } - if (args.showBufferColumns != null) { - this.showBufferColumns = args.showBufferColumns; + if (args.btnEditVisible != null) { + this.btnEditVisible = args.btnEditVisible; + } + if (args.btnAddVisible != null) { + this.btnAddVisible = args.btnAddVisible; + } + if (args.btnAddExisting != null) { + this.btnAddExisting = args.btnAddExisting; + } + if (args.width != null) { + this.width = args.width; + } + if (args.minHeight != null) { + this.minHeight = args.minHeight; + } + if (args.tbar != null) { + this.tbar = args.tbar; + } + if (args.btnRemoveVisible != null) { + this.btnRemoveVisible = args.btnRemoveVisible; + } + if (args.isPackedVisible != null) { + this.isPackedVisible = args.isPackedVisible; + } + if (args.showTitle != null) { + this.showTitle = args.showTitle; + if (this.showTitle == false) { + this.title = null; + } } - } -} - -ConcentrationHTMLTableWidget.prototype.refresh = function(parsedData) { - document.getElementById(this.id).innerHTML = this.getPanel(parsedData); -}; - -ConcentrationHTMLTableWidget.prototype.getPanel = function(parsedData) { - var html = "
"; - html = html + ""; - /** Title * */ - html = html + ""; - html = html + ""; - if (this.showBufferColumns) { - html = html + ""; } - html = html + ""; - html = html + ""; - html = html + ""; - html = html + ""; - html = html + ""; - html = html + ""; - html = html + ""; - html = html + ""; - html = html + ""; - html = html + ""; - html = html + ""; - html = html + ""; - html = html + ""; + /** Events **/ + this.onProposalChanged = new Event(this); + this.onStockSolutionSelected = new Event(this); +} - parsedData.concentration.concentrations.sort(function(a, b) { - return a.concentration - b.concentration; - }); - /** Row * */ - for ( var i = 0; i < parsedData.concentration.concentrations.length; i++) { - var row = parsedData[i]; +StockSolutionGrid.prototype._getColumns = function() { + var _this = this; + var columns = [ - var tr = ""; - if (i % 2 == 1) { - tr = ""; + { + header : 'Macromolecule', + dataIndex : 'macromolecule', + id : _this.id + 'macromolecule', + type : 'string', + renderer : function(val, y, specimen) { + return '' + val + ''; + }, + hidden : false, + flex : 1 + }, { + header : 'Buffer', + dataIndex : 'buffer', + name : 'buffer', + hidden : false, + renderer : function(val, y, specimen) { + return '' + val + ''; + }, + type : 'string', + flex : 1 + }, { + header : 'Acronym', + dataIndex : 'name', + name : 'name', + type : 'string', + flex : 1, + hidden : true + }, { + header : 'Storage Temp.', + dataIndex : 'storageTemperature', + name : 'storageTemperature', + type : 'string', + flex : 1, + hidden : false, + renderer : function(val) { + return BUI.formatValuesUnits(val, 'C', { + fontSize : 12, + decimals : 2, + unitsFontSize : 10 + }); } - var goodMeasurements = (parsedData.concentration.concentrations[i].frames.length - parsedData.concentration.concentrations[i].frames_warning); - if (goodMeasurements == 0) { - tr = ""; + }, { + header : 'Volume', + dataIndex : 'volume', + type : 'string', + flex : 1, + hidden : false, + renderer : function(val) { + return BUI.formatValuesUnits(val, 'µl', { + fontSize : 12, + decimals : 2, + unitsFontSize : 10 + }); } - html = html + tr; - html = html + ""; - if (this.showBufferColumns) { - html = html + ""; + }, { + header : 'Concentration', + dataIndex : 'concentration', + name : 'concentration', + type : 'string', + flex : 1, + renderer : function(val) { + return BUI.formatValuesUnits(val, 'mg/ml', { + fontSize : 12, + decimals : 2, + unitsFontSize : 10 + }); } - html = html + ""; - html = html + ""; - html = html + ""; - html = html + ""; - html = html + ""; - html = html + ""; - html = html + ""; - html = html + ""; + }, { + header : 'Packed', + dataIndex : 'comments', + id : _this.id + "box", + type : 'string', + width : 50, + hidden : !this.isPackedVisible, + renderer : function(val, cmp, a) { + if (a.raw.boxId != null) { + return "
"; + } + + } + }, { + header : 'Comments', + dataIndex : 'comments', + type : 'string', + flex : 1 + } ]; + + if (this.btnEditVisible) { + columns.push({ + id : _this.id + 'buttonEdit', + width : 85, + sortable : false, + renderer : function(value, metaData, record, rowIndex, colIndex, store) { + if (_this.btnEditVisible) { + return BUI.getGreenButton('EDIT'); + } + } + }); + } + + if (this.btnRemoveVisible) { + columns.push({ + id : _this.id + 'buttonRemove', + width : 85, + sortable : false, + renderer : function(value, metaData, record, rowIndex, colIndex, store) { + if (_this.btnRemoveVisible) { + return BUI.getRedButton('REMOVE'); + } + } + }); } - return html + "
ConcentrationBufferMeasurementsGuinierGnom
PassedDiscardedRgI0QualityRgdMax
" + parsedData.concentration.concentrations[i].concentration + "" + parsedData.concentration.concentrations[i].bufferAcronym + "" + goodMeasurements + " of " + parsedData.concentration.concentrations[i].frames.length + "" + parsedData.concentration.concentrations[i].frames_warning + "" + BUI.formatValuesErrorUnits(parsedData.concentration.concentrations[i].calculation.rgGuinier.avg, parsedData.concentration.concentrations[i].calculation.rgGuinier.std, "nm", { - lineBreak : false - }) + "" + BUI.formatValuesErrorUnits(parsedData.concentration.concentrations[i].calculation.i0Guinier.avg, parsedData.concentration.concentrations[i].calculation.i0Guinier.std, " ", { - lineBreak : false - }) + "" + BUI.formatValuesErrorUnits(parsedData.concentration.concentrations[i].calculation.quality.avg, parsedData.concentration.concentrations[i].calculation.quality.std, " ", { - lineBreak : false - }) + "" + BUI.formatValuesErrorUnits(parsedData.concentration.concentrations[i].calculation.rgGnom.avg, parsedData.concentration.concentrations[i].calculation.rgGnom.std, " ", { - lineBreak : false - }) + "" + BUI.formatValuesErrorUnits(parsedData.concentration.concentrations[i].calculation.dMax.avg, parsedData.concentration.concentrations[i].calculation.dMax.std, " ", { - lineBreak : false - }) + "
"; + + if (this.btnUnpackVisible) { + columns.push({ + id : _this.id + 'buttonUnpack', + width : 85, + sortable : false, + renderer : function(value, metaData, record, rowIndex, colIndex, store) { + if (_this.btnUnpackVisible) { + return BUI.getBlueButton('UNPACK'); + } + } + }); + } + return columns; }; -ConcentrationHTMLTableWidget.prototype.input = function() { - return { - data : { - "dataCollectionCount" : 4, - "buffers" : [ { - "bufferId" : "422" - } ], - "concentration" : { - "concentrations" : [ { - "concentration" : "7.17", - "id" : "7.1699999999999999", - "bufferId" : "422.00", - "bufferAcronym" : "B1", - "rgGuinier" : [ "5.12723" ], - "frames" : [ { - "total" : "0.515705406286", - "bufferBeforeMeasurementId" : 15045, - "sampleMergeId" : 8176, - "averagedModelId" : null, - "I0" : "183.457461646", - "proposalCode" : "OPD", - "averagedModel" : null, - "bufferAfterMergeId" : 8177, - "framesCount" : "10", - "bufferBeforeMergeId" : 8175, - "averageFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_065_ave.dat", - "bufferAfterMeasurementId" : 15048, - "rgGuinier" : "5.12723", - "subtractedFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_065_sub.dat", - "firstPointUsed" : "17", - "chi2RgFilePath" : null, - "abInitioModelId" : null, - "macromoleculeId" : 649, - "code" : "_4.0_7.17", - "transmission" : "100.0", - "timeStart" : "2013-06-05 15:43:54.348723", - "bufferAcronym" : "B1", - "quality" : "0.853011", - "shapeDeterminationModelId" : null, - "expermientComments" : "[BsxCube] Generated from BsxCube", - "bufferId" : 422, - "isagregated" : "False", - "dmax" : "25.63615", - "bufferBeforeAverageFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_064_ave.dat", - "exposureTemperature" : "4.0", - "subtractionId" : 3377, - "conc" : "7.1699999999999999", - "rapidShapeDeterminationModel" : null, - "experimentCreationDate" : "Jun 5, 2013 3:30:30 PM", - "lastPointUsed" : "36", - "modelListId" : null, - "framesMerge" : "9", - "rapidShapeDeterminationModelId" : null, - "bufferAfterAverageFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_066_ave.dat", - "nsdFilePath" : null, - "bufferAfterFramesMerged" : "10", - "experimentId" : 700, - "macromoleculeAcronym" : "L9", - "i0stdev" : "0.139364435146", - "sampleMeasurementId" : 15047, - "measurementComments" : "[5]", - "priorityLevelId" : 10, - "bufferBeforeFramesMerged" : "10", - "substractionCreationTime" : "Jun 5, 2013 3:46:31 PM", - "proposalNumber" : "29", - "rgGnom" : "5.40297914939", - "volume" : "475.302", - "shapeDeterminationModel" : null, - "comments" : null, - "groupeField" : "L9 B1" - } ], - "frames_warning" : 0, - "calculation" : { - "rgGuinier" : { - "std" : 0, - "sum" : 5.12723, - "avg" : 5.12723, - "validNumber" : 1, - "totalNumber" : 1, - "values" : [ "5.12723" ] - }, - "i0Guinier" : { - "std" : 0, - "sum" : 183.457461646, - "avg" : 183.457461646, - "validNumber" : 1, - "totalNumber" : 1, - "values" : [ "183.457461646" ] - }, - "quality" : { - "std" : 0, - "sum" : 0.853011, - "avg" : 0.853011, - "validNumber" : 1, - "totalNumber" : 1, - "values" : [ "0.853011" ] - }, - "rgGnom" : { - "std" : 0, - "sum" : 5.40297914939, - "avg" : 5.40297914939, - "validNumber" : 1, - "totalNumber" : 1, - "values" : [ "5.40297914939" ] - }, - "dMax" : { - "std" : 0, - "sum" : 25.63615, - "avg" : 25.63615, - "validNumber" : 1, - "totalNumber" : 1, - "values" : [ "25.63615" ] - } - } - }, { - "concentration" : "3.53", - "id" : "3.5299999999999998", - "bufferId" : "422.00", - "bufferAcronym" : "B1", - "rgGuinier" : [ "4.38278" ], - "frames" : [ { - "total" : "0.497164741078", - "bufferBeforeMeasurementId" : 15048, - "sampleMergeId" : 8178, - "averagedModelId" : null, - "I0" : "160.023229462", - "proposalCode" : "OPD", - "averagedModel" : null, - "bufferAfterMergeId" : 8179, - "framesCount" : "10", - "bufferBeforeMergeId" : 8177, - "averageFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_067_ave.dat", - "bufferAfterMeasurementId" : 15051, - "rgGuinier" : "4.38278", - "subtractedFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_067_sub.dat", - "firstPointUsed" : "36", - "chi2RgFilePath" : null, - "abInitioModelId" : null, - "macromoleculeId" : 649, - "code" : "_4.0_3.53", - "transmission" : "100.0", - "timeStart" : "2013-06-05 15:47:04.630129", - "bufferAcronym" : "B1", - "quality" : "0.742825", - "shapeDeterminationModelId" : null, - "expermientComments" : "[BsxCube] Generated from BsxCube", - "bufferId" : 422, - "isagregated" : "False", - "dmax" : "15.33973", - "bufferBeforeAverageFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_066_ave.dat", - "exposureTemperature" : "4.0", - "subtractionId" : 3378, - "conc" : "3.5299999999999998", - "rapidShapeDeterminationModel" : null, - "experimentCreationDate" : "Jun 5, 2013 3:30:30 PM", - "lastPointUsed" : "61", - "modelListId" : null, - "framesMerge" : "10", - "rapidShapeDeterminationModelId" : null, - "bufferAfterAverageFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_068_ave.dat", - "nsdFilePath" : null, - "bufferAfterFramesMerged" : "10", - "experimentId" : 700, - "macromoleculeAcronym" : "L9", - "i0stdev" : "0.1499898017", - "sampleMeasurementId" : 15050, - "measurementComments" : "[6]", - "priorityLevelId" : 12, - "bufferBeforeFramesMerged" : "10", - "substractionCreationTime" : "Jun 5, 2013 3:49:42 PM", - "proposalNumber" : "29", - "rgGnom" : "4.40615474861", - "volume" : "372.656", - "shapeDeterminationModel" : null, - "comments" : null, - "groupeField" : "L9 B1" - } ], - "frames_warning" : 0, - "calculation" : { - "rgGuinier" : { - "std" : 0, - "sum" : 4.38278, - "avg" : 4.38278, - "validNumber" : 1, - "totalNumber" : 1, - "values" : [ "4.38278" ] - }, - "i0Guinier" : { - "std" : 0, - "sum" : 160.023229462, - "avg" : 160.023229462, - "validNumber" : 1, - "totalNumber" : 1, - "values" : [ "160.023229462" ] - }, - "quality" : { - "std" : 0, - "sum" : 0.742825, - "avg" : 0.742825, - "validNumber" : 1, - "totalNumber" : 1, - "values" : [ "0.742825" ] - }, - "rgGnom" : { - "std" : 0, - "sum" : 4.40615474861, - "avg" : 4.40615474861, - "validNumber" : 1, - "totalNumber" : 1, - "values" : [ "4.40615474861" ] - }, - "dMax" : { - "std" : 0, - "sum" : 15.33973, - "avg" : 15.33973, - "validNumber" : 1, - "totalNumber" : 1, - "values" : [ "15.33973" ] +StockSolutionGrid.prototype._getTopButtons = function() { + var _this = this; + /** Actions buttons **/ + var actions = []; + + /** ADD BUTTON **/ + if (this.btnAddVisible) { + actions.push(Ext.create('Ext.Action', { + icon : '../images/add.png', + text : 'Add Stock Solution', + tooltip : 'Will create a new stock solution', + disabled : false, + alwaysEnabled : true, + handler : function(widget, event) { + _this.edit(); + } + })); + } + + if (this.btnAddExisting) { + actions.push(Ext.create('Ext.Action', { + icon : '../images/add.png', + text : 'Add Existing', + tooltip : 'Allows to select upacked stock solutions', + disabled : false, + alwaysEnabled : true, + handler : function(widget, event) { + var stockSolutionGrid = new StockSolutionGrid({ + btnAddVisible : false, + btnEditVisible : false, + btnRemoveVisible : false, + btnAddExisting : false, + isPackedVisible : true, + multiselect : true + }); + + var window = Ext.create('Ext.window.Window', { + title : 'Select', + height : 400, + width : 800, + layout : 'fit', + items : [ stockSolutionGrid.getPanel() ], + buttons : [ { + text : 'Pack', + handler : function() { + _this.onStockSolutionSelected.notify(stockSolutionGrid.selectedStockSolutions); + window.close(); + } + }, { + text : 'Cancel', + handler : function() { + window.close(); } + } ] + + }).show(); + + stockSolutionGrid.refresh(BIOSAXS.proposal.getUnpackedStockSolutions()); + } + })); + } + + return actions; +}; + +StockSolutionGrid.prototype.refresh = function(stockSolutions) { + this.features = stockSolutions; + this.store.loadData(this._prepareData(), false); +}; + +StockSolutionGrid.prototype._prepareData = function() { + var data = []; + for ( var i = 0; i < this.features.length; i++) { + var stockSolution = this.features[i]; + stockSolution.buffer = BIOSAXS.proposal.getBufferById(stockSolution.bufferId).acronym; + if (stockSolution.macromoleculeId != null) { + stockSolution.macromolecule = BIOSAXS.proposal.getMacromoleculeById(stockSolution.macromoleculeId).acronym; + } + data.push(stockSolution); + } + return data; +}; + +StockSolutionGrid.prototype.getPanel = function() { + return this._renderGrid(); +}; + +StockSolutionGrid.prototype.edit = function(stockSolutionId) { + var _this = this; + var stockSolutionWindow = new StockSolutionWindow(); + /** On stock solution SAVED **/ + stockSolutionWindow.onSaved.attach(function(sender, stockSolution) { + _this.onProposalChanged.notify(stockSolution); + }); + stockSolutionWindow.draw(BIOSAXS.proposal.getStockSolutionById(stockSolutionId)); +}; + +StockSolutionGrid.prototype._getStoreFields = function() { + return [ { + name : 'name', + type : 'string' + }, { + name : 'stockSolutionId', + type : 'string' + }, { + name : 'macromolecule', + type : 'string' + }, { + name : 'buffer', + type : 'string' + }, { + name : 'storageTemperature', + type : 'numeric' + }, { + name : 'volume', + type : 'string' + }, { + name : 'concentration', + type : 'string' + }, { + name : 'buffer', + type : 'string' + }, { + name : 'comments', + type : 'string' + } ]; +}; + +//StockSolutionGrid.prototype.refresh = function() { +// this.proposal.onInitialized.attach(function(sender){ +// +// }); +// this.proposal.init(Ext.urlDecode(window.location.href).sessionId); +//}; + +StockSolutionGrid.prototype._renderGrid = function() { + var _this = this; + + /** Store **/ + this.store = Ext.create('Ext.data.Store', { + fields : this._getStoreFields(), //columns, + autoload : true + }); + + var filters = { + ftype : 'filters', + local : true, + filters : this.filters + }; + + var selModel = null; + + if (this.multiselect) { + selModel = Ext.create('Ext.selection.CheckboxModel', { + //multiSelect : false,//this.multiselect, + mode : 'SINGLE', + listeners : { + selectionchange : function(sm, selections) { + _this.selectedStockSolutions = []; + for ( var i = 0; i < selections.length; i++) { + _this.selectedStockSolutions.push(selections[i].raw); } - }, { - "concentration" : "1.75", - "id" : "1.75", - "bufferId" : "422.00", - "bufferAcronym" : "B1", - "rgGuinier" : [], - "frames" : [ { - "total" : "0.5538035065", - "bufferBeforeMeasurementId" : 15051, - "sampleMergeId" : 8180, - "averagedModelId" : null, - "I0" : "146.927428571", - "proposalCode" : "OPD", - "averagedModel" : null, - "bufferAfterMergeId" : 8181, - "framesCount" : "10", - "bufferBeforeMergeId" : 8179, - "averageFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_069_ave.dat", - "bufferAfterMeasurementId" : 15054, - "rgGuinier" : "4.27139", - "subtractedFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_069_sub.dat", - "firstPointUsed" : "33", - "chi2RgFilePath" : null, - "abInitioModelId" : null, - "macromoleculeId" : 649, - "code" : "_4.0_1.75", - "transmission" : "100.0", - "timeStart" : "2013-06-05 15:50:09.395824", - "bufferAcronym" : "B1", - "quality" : "0.765999", - "shapeDeterminationModelId" : null, - "expermientComments" : "[BsxCube] Generated from BsxCube", - "bufferId" : 422, - "isagregated" : "False", - "dmax" : "14.949865", - "bufferBeforeAverageFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_068_ave.dat", - "exposureTemperature" : "4.0", - "subtractionId" : 3379, - "conc" : "1.75", - "rapidShapeDeterminationModel" : null, - "experimentCreationDate" : "Jun 5, 2013 3:30:30 PM", - "lastPointUsed" : "62", - "modelListId" : null, - "framesMerge" : "6", - "rapidShapeDeterminationModelId" : null, - "bufferAfterAverageFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_070_ave.dat", - "nsdFilePath" : null, - "bufferAfterFramesMerged" : "10", - "experimentId" : 700, - "macromoleculeAcronym" : "L9", - "i0stdev" : "0.191914285714", - "sampleMeasurementId" : 15053, - "measurementComments" : "[7]", - "priorityLevelId" : 14, - "bufferBeforeFramesMerged" : "10", - "substractionCreationTime" : "Jun 5, 2013 3:52:31 PM", - "proposalNumber" : "29", - "rgGnom" : "4.28379338749", - "volume" : "356.326", - "shapeDeterminationModel" : null, - "comments" : null, - "groupeField" : "L9 B1" - } ], - "frames_warning" : 1, - "calculation" : { - "rgGuinier" : { - "std" : null, - "sum" : 0, - "avg" : 0, - "validNumber" : 0, - "totalNumber" : 0, - "values" : [] - }, - "i0Guinier" : { - "std" : null, - "sum" : 0, - "avg" : 0, - "validNumber" : 0, - "totalNumber" : 0, - "values" : [] - }, - "quality" : { - "std" : null, - "sum" : 0, - "avg" : 0, - "validNumber" : 0, - "totalNumber" : 0, - "values" : [] - }, - "rgGnom" : { - "std" : null, - "sum" : 0, - "avg" : 0, - "validNumber" : 0, - "totalNumber" : 0, - "values" : [] - }, - "dMax" : { - "std" : null, - "sum" : 0, - "avg" : 0, - "validNumber" : 0, - "totalNumber" : 0, - "values" : [] + } + } + }); + } else { + selModel = { + mode : 'SINGLE' + }; + } + + this.store.sort("stockSolutionId", "desc"); + + this.grid = Ext.create('Ext.grid.Panel', { + style : { + padding : 5 + }, + icon : '/ispyb/images/SampleHolder_24x24_01.png', + title : this.title, + height : this.height, + width : this.width, + minWidth : this.minWidth, + selModel : selModel, + store : this.store, + columns : this._getColumns(), + viewConfig : { + stripeRows : true, + listeners : { + itemdblclick : function(dataview, record, item, e) { + _this.edit(record.raw.stockSolutionId); + }, + cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + var adapter = null; + if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonUnpack') { + _this.grid.setLoading("ISPyB: Unpacking stock solution"); + adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender) { + _this.onProposalChanged.notify(); + _this.grid.setLoading(false); + }); + adapter.onError.attach(function(sender) { + _this.onProposalChanged.notify(); + _this.grid.setLoading(false); + }); + record.raw.boxId = null; + adapter.saveStockSolution(record.raw); + } + + if (grid.getGridColumns()[cellIndex].getId() == _this.id + "box") { + window.location = BUI.getShippingURL(BIOSAXS.proposal.getShipmentByDewarId(record.raw.boxId).shippingId); + } + if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonEdit') { + _this.edit(record.data.stockSolutionId); + } + + if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonRemove') { + _this.grid.setLoading("ISPyB: Removing stock solution"); + adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender) { + _this.onProposalChanged.notify(); + _this.grid.setLoading(false); + }); + adapter.onError.attach(function(sender) { + _this.onProposalChanged.notify(); + _this.grid.setLoading(false); + }); + adapter.removeStockSolution(record.data.stockSolutionId); + } + } + } + } + + }); + + var actions = _this._getTopButtons(); + this.grid.addDocked({ + xtype : 'toolbar', + items : actions + }); + + var i = null; + this.grid.getSelectionModel().on({ + selectionchange : function(sm, selections) { + if (selections.length) { + for ( i = 0; i < actions.length; i++) { + if (actions[i].enable) { + actions[i].enable(); + } + } + } else { + for ( i = 0; i < actions.length; i++) { + if (actions[i].alwaysEnabled == false) { + if (actions[i].disable) { + actions[i].disable(); + } + } + } + } + } + }); + return this.grid; +}; + +StockSolutionGrid.prototype.input = function() { + return { + proposal : DATADOC.getProposal_10() + }; +}; + +StockSolutionGrid.prototype.test = function(targetId) { + var stockSolutionGrid = new StockSolutionGrid({ + height : 300, + width : 900 + }); + BIOSAXS.proposal = new Proposal(stockSolutionGrid.input().proposal); + var panel = stockSolutionGrid.getPanel(); + stockSolutionGrid.refresh(BIOSAXS.proposal.getStockSolutions()); + panel.render(targetId); +}; + + +function SuperpositionGrid(args) { + this.height = null; + this.width = null; + this.id = BUI.id(); + + if (args != null) { + if (args.height != null) { + this.height = args.height; + + if (args.width != null) { + this.width = args.width; + } + } + } + + this.onSelected = new Event(this); +} + +SuperpositionGrid.prototype._prepareData = function(data) { + return data; +}; + +SuperpositionGrid.prototype.refresh = function(subtractions) { + var data = []; + if (subtractions != null){ + if (subtractions.length > 0){ + for (j in subtractions){ + var subtraction = subtractions[j]; + if (subtraction.superposition3VOs != null){ + for (i in subtraction.superposition3VOs){ + data.push(subtraction.superposition3VOs[i]); + } + } + } + } + } + this.store.loadData(data); +}; + +SuperpositionGrid.prototype.getPanel = function() { + var _this = this; + this.store = Ext.create('Ext.data.Store', { + fields : [ 'abinitioModelPdbFilePath', 'aprioriPdbFilePath', 'alignedPdbFilePath' ], + data : [] + }); + + this.selModel = Ext.create('Ext.selection.RowModel', { + allowDeselect : true, + mode : 'MULTI', + listeners : { + selectionchange : function(sm, selections) { + var selected = []; + for (var i = 0; i < selections.length; i++) { + selected.push(selections[i].raw); + } + _this.onSelected.notify(selected); + } + } + }); + + this.store.sort([ { + property : 'name', + direction : 'ASC' + } ]); + + this.panel = Ext.create('Ext.grid.Panel', { + store : this.store, + selModel : this.selModel, + width : this.width, + height : this.height, + margin : 10, + deferredRender : false, + columns : [ { + text : 'Abinitio', + dataIndex : 'abinitioModelPdbFilePath', + hidden : false, + flex : 1, + renderer : function(val){ + return BUI.getFileName(val); + } + }, { + text : 'Apriori', + dataIndex : 'aprioriPdbFilePath', + hidden : false, + flex : 1, + renderer : function(val){ + return BUI.getFileName(val); + } + }, { + text : 'Aligned', + dataIndex : 'alignedPdbFilePath', + hidden : false, + flex : 1, + renderer : function(val){ + return BUI.getFileName(val); + } + } ], + + viewConfig : { + enableTextSelection : true, + preserveScrollOnRefresh : true + }, + listeners : { + cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + + }, + afterrender : function() { + } + } + }); + return this.panel; +}; + +/** + * See ExperimentGrid + * + */ +function TemplateGrid(args) { + + if (args == null) { + args = {}; + } + args.sorters = [ { + property : 'experimentId', + direction : 'DESC' + } ]; + + ExperimentGrid.prototype.constructor.call(this, args); +} + +TemplateGrid.prototype._getFilterTypes = ExperimentGrid.prototype._getFilterTypes; +TemplateGrid.prototype._prettyPrintMacromolecules = ExperimentGrid.prototype._prettyPrintMacromolecules; +TemplateGrid.prototype._getPercentage = ExperimentGrid.prototype._getPercentage; +TemplateGrid.prototype._getPercentageCollected = ExperimentGrid.prototype._getPercentageCollected; +TemplateGrid.prototype.getPercentageMerged = ExperimentGrid.prototype.getPercentageMerged; +TemplateGrid.prototype._prepareData = ExperimentGrid.prototype._prepareData; +TemplateGrid.prototype.getPanel = ExperimentGrid.prototype.getPanel; +TemplateGrid.prototype._renderGrid = ExperimentGrid.prototype._renderGrid; +TemplateGrid.prototype._editExperiment = ExperimentGrid.prototype._editExperiment; +TemplateGrid.prototype._removeExperimentById = ExperimentGrid.prototype._removeExperimentById; + +TemplateGrid.prototype._getTopButtons = function() { + /** Actions buttons * */ + var actions = []; + + actions.push(Ext.create('Ext.Action', { + icon : '../images/add.png', + text : 'Add experiment', + handler : function(widget, event) { + var wizardWidget = new WizardWidget({ + windowMode : true + }); + + wizardWidget.onFinished.attach(function(sender, result) { + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, data) { + var experiment = new Experiment(data); + BIOSAXS.openExperimentByExperiment(experiment); + wizardWidget.window.close(); + }); + wizardWidget.current.setLoading("ISPyB: Creating experiment"); + adapter.createTemplate(result.name, "comments", result.data); + }); + +// wizardWidget.draw(this.targetId, new ExperimentTypeWizardForm()); + wizardWidget.draw(this.targetId, new MeasurementCreatorStepWizardForm(BIOSAXS.proposal.getMacromolecules())); + } + })); + return actions; +}; + +TemplateGrid.prototype.refresh = function(data) { + var filtered = []; + for ( var i = 0; i < data.length; i++) { + if (data[i].experimentType == "TEMPLATE") { + filtered.push(data[i]); + } + } + this.store.loadData(this._prepareData(filtered), false); +}; + +TemplateGrid.prototype._getColumns = function() { + var _this = this; + function actionItemRenderer(value, meta, record, rowIx, ColIx, store) { + if (record.raw.buffer3VOs != null) { + if (record.raw.buffer3VOs.length > 0) { + return 'x-hide-display'; + } + } + + if (record.data.platesCount > 0) { + return 'x-hide-display'; + } + } + + return [ { + xtype : 'rownumberer', + width : 40 + }, { + text : 'experimentId', + dataIndex : 'experimentId', + name : 'experimentId', + type : 'string', + hidden : true + }, { + text : 'Name', + dataIndex : 'name', + name : 'name', + type : 'string', + flex : 1 + }, + + { + text : 'Type', + dataIndex : 'type', + name : 'type', + type : 'string', + hidden : true, + flex : 1, + renderer : function(val) { + return val; + } + }, { + text : 'Macromolecules', + name : 'macromolecules_names', + dataIndex : 'macromolecules_names', + flex : 1, + renderer : function(val) { + return " " + val + ""; + } + }, { + id : _this.id + 'GO', + width : 80, + sortable : false, + renderer : function(value, metaData, record, rowIndex, colIndex, store) { + return BUI.getGreenButton('EDIT'); + } + }, { + id : _this.id + 'REMOVE', + width : 100, + sortable : false, + renderer : function(value, metaData, record, rowIndex, colIndex, store) { + return BUI.getRedButton('REMOVE'); + } + } ]; +}; + +TemplateGrid.prototype.input = function() { + var experiments = DATADOC.getExperimentList_10(); + return { + experiments : experiments, + proposal : new MeasurementGrid().input().proposal + + }; +}; + +TemplateGrid.prototype.test = function(targetId) { + var experimentGrid = new TemplateGrid({ + height : 350, + minHeight : 350, + width : 1000, + gridType : 'Ext.grid.Panel', + title : 'Experiments', + grouping : false, + tbar : true + + }); + BIOSAXS.proposal = new Proposal(experimentGrid.input().proposal); + var panel = experimentGrid.getPanel(experimentGrid.input().experiments); + experimentGrid.refresh(experimentGrid.input().experiments); + panel.render(targetId); +}; + +function VolumeGrid() { + this.id = BUI.id(); +} + +VolumeGrid.prototype.getPanel = function(experiment) { + this.experiment = experiment; + return this.render(); +}; + +VolumeGrid.prototype.getVolumesPanel = function(data, title) { + var _this = this; + var store = Ext.create('Ext.data.Store', { + fields : [ 'name', 'volume', 'macromoleculeId', 'bufferId' ], + data : data + }); + store.sort([ { + property : 'name', + direction : 'ASC' + } ]); + + var grid = Ext.create('Ext.grid.Panel', { + title : title, + height : 400, + maxHeight : 400, + width : 900, + store : store, + margin : '10 0 50 10', + tbar : [ { + text : 'Go to Shipment', + icon : '../images/plane-small.gif', + handler : function() { + window.location = BUI.getCreateShipmentList(); + } + } ], + viewConfig : { + stripeRows : true, + listeners : { + cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonCreate') { + var stockSolutionWindow = new StockSolutionWindow(); + /** On stock solution SAVED **/ + stockSolutionWindow.onSaved.attach(function(sender, stockSolution) { + BIOSAXS.proposal.onInitialized.attach(function(sender, data) { + _this.refresh(_this.experiment); + }); + BIOSAXS.proposal.init(); + }); + var acronym = "ST"; + if (record.raw.macromoleculeId != null) { + acronym = acronym + "_" + BIOSAXS.proposal.getMacromoleculeById(record.raw.macromoleculeId).acronym; + } + if (record.raw.bufferId != null) { + acronym = acronym + "_" + BIOSAXS.proposal.getBufferById(record.raw.bufferId).acronym; } + stockSolutionWindow.draw({ + concentration : record.raw.concentration, + macromoleculeId : record.raw.macromoleculeId, + bufferId : record.raw.bufferId, + name : acronym, + volume : record.raw.volume + }); } - }, { - "concentration" : "0.91", - "id" : "0.91000000000000003", - "bufferId" : "422.00", - "bufferAcronym" : "B1", - "rgGuinier" : [], - "frames" : [ { - "total" : null, - "bufferBeforeMeasurementId" : 15054, - "sampleMergeId" : 8182, - "averagedModelId" : null, - "I0" : "0.0", - "proposalCode" : "OPD", - "averagedModel" : null, - "bufferAfterMergeId" : 8183, - "framesCount" : "10", - "bufferBeforeMergeId" : 8181, - "averageFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_071_ave.dat", - "bufferAfterMeasurementId" : 15057, - "rgGuinier" : null, - "subtractedFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_071_sub.dat", - "firstPointUsed" : "0", - "chi2RgFilePath" : null, - "abInitioModelId" : null, - "macromoleculeId" : 649, - "code" : "_4.0_.91", - "transmission" : "100.0", - "timeStart" : "2013-06-05 15:52:58.133705", - "bufferAcronym" : "B1", - "quality" : "0.0", - "shapeDeterminationModelId" : null, - "expermientComments" : "[BsxCube] Generated from BsxCube", - "bufferId" : 422, - "isagregated" : "False", - "dmax" : null, - "bufferBeforeAverageFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_070_ave.dat", - "exposureTemperature" : "4.0", - "subtractionId" : 3380, - "conc" : "0.91000000000000003", - "rapidShapeDeterminationModel" : null, - "experimentCreationDate" : "Jun 5, 2013 3:30:30 PM", - "lastPointUsed" : "0", - "modelListId" : null, - "framesMerge" : "10", - "rapidShapeDeterminationModelId" : null, - "bufferAfterAverageFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_072_ave.dat", - "nsdFilePath" : null, - "bufferAfterFramesMerged" : "10", - "experimentId" : 700, - "macromoleculeAcronym" : "L9", - "i0stdev" : "0.0", - "sampleMeasurementId" : 15056, - "measurementComments" : "[8]", - "priorityLevelId" : 16, - "bufferBeforeFramesMerged" : "10", - "substractionCreationTime" : "Jun 5, 2013 3:55:35 PM", - "proposalNumber" : "29", - "rgGnom" : null, - "volume" : null, - "shapeDeterminationModel" : null, - "comments" : null, - "groupeField" : "L9 B1" - } ], - "frames_warning" : 1, - "calculation" : { - "rgGuinier" : { - "std" : null, - "sum" : 0, - "avg" : 0, - "validNumber" : 0, - "totalNumber" : 0, - "values" : [] - }, - "i0Guinier" : { - "std" : null, - "sum" : 0, - "avg" : 0, - "validNumber" : 0, - "totalNumber" : 0, - "values" : [] + + if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonStockSolutions') { + var stockSolutionGrid = new StockSolutionGrid({ + btnAddVisible : false, + btnEditVisible : false, + btnRemoveVisible : false, + btnAddExisting : false, + isPackedVisible : true, + multiselect : false + }); + + var window = Ext.create('Ext.window.Window', { + title : 'Stock solutions by specimen', + height : 400, + width : 800, + layout : 'fit', + items : [ stockSolutionGrid.getPanel() ], + buttons : [ { + text : 'Close', + handler : function() { + window.close(); + } + } ] + + }).show(); + stockSolutionGrid.refresh(BIOSAXS.proposal.getStockSolutionsBySpecimen(record.raw.macromoleculeId, record.raw.bufferId)); + } + } + } + }, + columns : [ +// { +// text : '', +// dataIndex : 'macromoleculeId', +// width : 20, +// renderer : function(val, y, sample) { +// if (val != null) { +//// return BUI.getRectangleColorDIV(BIOSAXS.proposal.macromoleculeColors[val], 10, 10); +// return BUI.getRectangleColorDIV(_this.experiment.macromoleculeColors[val], 10, 10); +// } +// } +// }, +// { +// text : '', +// dataIndex : 'bufferId', +// width : 20, +// renderer : function(val, y, sample) { +// if (val != null) { +// return BUI.getRectangleColorDIV(_this.experiment.getSpecimenColorByBufferId(val), 10, 10); +// } +// } +// }, + { + text : 'Specimen', + dataIndex : 'name', + flex : 0.5 + }, + { + text : 'Estimated Volume', + dataIndex : 'volume', + tooltip : 'Estimation of the maximum volume needed for making this experiment', + flex : 0.5, + editor : { + allowBlank : true + }, + renderer : function(val, y, sample) { + return BUI.formatValuesUnits(sample.data.volume, 'µl', { + fontSize : 16, + decimals : 2, + unitsFontSize : this.unitsFontSize + }); + } + }, + { + text : 'Stock Solution', + id : _this.id + 'buttonStockSolutions', + dataIndex : 'name', + flex : 0.5, + tooltip : 'Stock Solutions containing this specimen in this proposal', + renderer : function(value, metaData, record, rowIndex, colIndex, store) { + var macromoleculeId = record.raw.macromoleculeId; + var bufferId = record.raw.bufferId; + var stockSolutions = BIOSAXS.proposal.getStockSolutionsBySpecimen(macromoleculeId, bufferId); + if (stockSolutions.length > 0) { + return "
" + stockSolutions.length + " x
"; + } + + } + }, { + id : _this.id + 'buttonCreate', + text : '', + tooltip : 'Create a new stock solution for shipping', + width : 170, + sortable : false, + renderer : function(value, metaData, record, rowIndex, colIndex, store) { + return BUI.getGreenButton('NEW STOCK SOLUTION', { + width : 160 + }); + } + } ] + }); + return grid; + +}; + +VolumeGrid.prototype._prepareData = function(experiment) { + var keys = {}; + for ( var i = 0; i < experiment.getSamples().length; i++) { + var sample = experiment.getSamples()[i]; + var key = ""; + if (sample.macromoleculeId == null) { + key = experiment.getBufferById(sample.bufferId).acronym; + if (keys[key] == null) { + keys[key] = { + macromoleculeId : sample.macromoleculeId, + name : key, + bufferId : sample.bufferId, + volume : 0 + }; + } + keys[key].volume = Number(sample.volume) + Number(keys[key].volume); + } + + if ((sample.macromolecule3VO != null) || (sample.macromoleculeId != null)) { + macromoleculeId = sample.macromoleculeId; + if (sample.macromoleculeId == null) { + sample.macromoleculeId = sample.macromolecule3VO.macromoleculeId; + } + key = BIOSAXS.proposal.getMacromoleculeById(sample.macromoleculeId).acronym + " + " + experiment.getBufferById(sample.bufferId).acronym; + if (keys[key] == null) { + keys[key] = { + macromoleculeId : sample.macromoleculeId, + name : key, + bufferId : sample.bufferId, + volume : 0 + }; + } + keys[key].volume = Number(sample.volume) + Number(keys[key].volume); + } + } + var data = []; + for (var keyId in keys) { + data.push(keys[keyId]); + } + + return data; +}; + +VolumeGrid.prototype.refresh = function(experiment) { + this.experiment = experiment; + this.macromoleculeGrid.getStore().loadData(this._prepareData(this.experiment), false); +}; + +VolumeGrid.prototype.render = function() { + this.macromoleculeGrid = this.getVolumesPanel(this._prepareData(this.experiment), "Estimation of required Volume"); + + return { + xtype : 'container', + layout : 'vbox', + margin : "0, 0, 0, 5", + items : [ { + xtype : 'container', + layout : 'hbox', + margin : "0, 0, 0, 0", + items : [ this.macromoleculeGrid ] + } ] + }; +}; + +VolumeGrid.prototype.input = function(experiment) { + return { + experiment : DATADOC.getExperiment_10() + }; +}; + +VolumeGrid.prototype.test = function(targetId) { + var volumeGrid = new VolumeGrid(); + BIOSAXS.proposal = new Proposal(new MeasurementGrid().input().proposal); + var panel = volumeGrid.getPanel(new Experiment(new VolumeGrid().input().experiment)); + Ext.create('Ext.panel.Panel', { + height : 500, + width : 1000, + renderTo : targetId, + items : [ panel ] + }); +}; + +/** + * Example of a tab panel to be populated with widgets + * + * @width width in pixels + * @height height in pixels + * + * #myEvent event that this class is supposed to throw + * + **/ +function ExampleTabs(args) { + this.width = 500; + this.height = 500; + + if (args != null){ + if (args.width != null){ + this.width= args.width; + } + if (args.height != null){ + this.height= args.height; + } + } + + /** Events **/ + this.myEvent = new Event(); +} + +/** Populate the widget **/ +ExampleTabs.prototype.refresh = function(macromolecule) { + +}; + +/** It creates a tab panel with the specified with and height **/ +ExampleTabs.prototype.getPanel = function() { + this.panel = Ext.createWidget('tabpanel', { + height : this.height, + width : this.width, + style : { + padding : 2 + }, + items : [ { + tabConfig : { + title : 'Test', + icon : '/ispyb/images/plane-small.gif' + }, + items : [ { + xtype : 'container', + margin : '5 5 5 5', + items : [ ] + } ] + } ] + }); + return this.panel; +}; + + +/** + * Shows an experiments with the specimens, measurements, analysis tabs where + * results are shown and the frames widget + * + * @targetId + */ +function ExperimentTabs(targetId) { + this.height = 900; + this.targetId = targetId; + + this.id = BUI.id(); + var _this = this; + + this.INTERVAL_UPDATE = BUI.getUpdateInterval(); + + this.gridHeight = 1000; + /** data * */ + this.experiment = null; + + + /** Specimen Widget contains a specimenGrid and a sampleChangerWidget than can be displayed with are vertical or horizontal layout **/ + this.specimenWidget = new SpecimenWidget({ + height : 600 + }); + + + /** For Overview * */ + /*this.samplePlateGroupWidget = new SamplePlateGroupWidget({ + showTitle : false, + height : 250, + margin : 5, + border : 0 + + });*/ + + /** For Measurements * */ + /*this.measurementSamplePlateGroupWidget = new SamplePlateGroupWidget({ + showTitle : false, + height : 250, + margin : '5 0 0 0', + border : 0 + });*/ + + this.measurementGridDone = new MeasurementGrid({ + height : 600, + minHeight : 400, + maxHeight : 800, + positionColumnsHidden : true, + showTitle : false, + estimateTime : true, + width : 900, + maxWidth : 1500, + addBtnEnable : false, + markDone : true, + removeBtnEnabled : false + }); + this.measurementGridDone.onSelected.attach(function(sender, measurements) { + var specimens = []; + for ( var i = 0; i < measurements.length; i++) { + specimens.push(_this.experiment.getSampleById(measurements[i].specimenId)); + } + //_this.measurementSamplePlateGroupWidget.selectSpecimens(specimens); + }); + + /** AnalysisGrid * */ + this.analysisGrid = new AnalysisGrid({ + height : Ext.getBody().getViewSize().height * 0.9 - 300, + positionColumnsHidden : true, + sorters : [ { + property : 'priorityLevelId', + direction : 'ASC' + } ] + }); + + + /** Queue * */ + this.queueGrid = new QueueGrid({ + url : BUI.getQueueUrlByExperiment(), + height : Ext.getBody().getViewSize().height * 0.9 - 300, + width : Ext.getBody().getViewSize().width * 0.9 - 300, + isGrouped : true, + tbar : false, + bbar : false, + sorter : [{ + property: 'measurementId', + direction: 'DESC' + }] + }); + +} + +ExperimentTabs.prototype.error = function(error) { + var e = JSON.parse(error); + showError(e); + this.panel.setLoading(false); +}; + +ExperimentTabs.prototype.draw = function(experiment) { + this.renderDataAcquisition(experiment); +}; + +ExperimentTabs.prototype.refreshAnalysisData = function() { + var _this = this; + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, data) { + _this.analysisGrid.refresh(data, {experiment : _this.experiment}); + }); + adapter.getAnalysisInformationByExperimentId(this.experiment.experimentId); + + /** Auto load **/ + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, data) { + _this.queueGrid.refresh(data); + }); + adapter.getCompactAnalysisByExperimentId(this.experiment.experimentId); + this.queueGrid.store.proxy.url = BUI.getQueueUrlByExperiment(this.experiment.experimentId); + this.queueGrid.refresh(); +}; + +ExperimentTabs.prototype.getSpecimenContainerHeight = function(experiment) { + var maxItems = 0; + if (maxItems < experiment.getSamples().length + 1) { + maxItems = experiment.getSamples().length + 1; + } + + var height = (maxItems + 1) * 40 + 40; + if (height > 400) { + height = 400; + } + return height; +}; + +ExperimentTabs.prototype.getExperimentTitle = function() { + var experimentHeaderForm = new ExperimentHeaderForm(); + return experimentHeaderForm.getPanel(this.experiment); +}; + +ExperimentTabs.prototype.renderDataAcquisition = function(experiment) { + var _this = this; + this.experiment = experiment; + + var specimenGrid = new SpecimenGrid({ + height : 400, + maxHeight : 500, + width : 890 + }); + + specimenGrid.onClick.attach(function(sender, args) { + }); + + specimenGrid.onSelected.attach(function(sender, specimens) { + //_this.samplePlateGroupWidget.selectSpecimens(specimens); + }); + var specimenContainer = this.specimenWidget.getPanel(); + this.specimenWidget.refresh(experiment); + /* + var specimenContainer = Ext.create('Ext.container.Container', { + layout : 'hbox', + width : 900, + padding : '10 0 0 2', + items : [ specimenGrid.getPanel() ] + }); + + specimenGrid.refresh(experiment);*/ + + var experimentList = new ExperimentList([ _this.experiment ]); + var measurementContainer = Ext.create('Ext.container.Container', { + layout : 'vbox', + padding : '5px 0px 0px 10px', + items : [] + }); + measurementContainer.insert(0, _this.measurementGridDone.getPanel(this.experiment.getMeasurements(), experimentList)); +// measurementContainer.insert(1, _this.measurementSamplePlateGroupWidget.getPanel(experiment)); + + // this.dataCollectionCurveVisualizer = new DataCollectionCurveVisualizer(); + // this.dataCollectionCurveVisualizer.experiments = [experiment]; + // this.dataCollectionCurveVisualizer.dataCollectionFrameTree.experiments = + // [experiment]; + // this.dataCollectionCurveVisualizer.dataCollections = + // experiment.getDataCollections(); + + this.panel = Ext.createWidget('tabpanel', { + plain : true, + style : { + padding : 2 + }, + items : [ { + tabConfig : { + id : 'genralTabl', + title : "Overview" + }, + items : [ specimenContainer ] + }, { + tabConfig : { + title : 'Measurements' + }, + items : [ measurementContainer] + }, { + tabConfig : { + id : 'SpecimenTab', + title : 'Analysis', + hidden : this.isTemplate() + }, + items : [ { + xtype : 'container', + layout : 'vbox', + padding : '5px 0px 10px 10px', + items : [ _this.analysisGrid.getPanel([]) ] + } ] + }, + { + tabConfig : { + id : 'newAnalysisTab', + title : 'Analysis BETA', + hidden : this.isTemplate() + }, + items : [ { + xtype : 'container', + layout : 'vbox', + padding : '5px 0px 10px 10px', + items : [ _this.queueGrid.getPanel([]) ] + } ] + } + ] + }); + + return this.getPanel(this.panel); +}; + +ExperimentTabs.prototype.isTemplate = function() { + if (this.experiment.json.type == "TEMPLATE") { + return true; + } + return false; +}; + +ExperimentTabs.prototype.update = function() { + var _this = this; + var inter; + if (!_this.isTemplate()) { + function updateExperiments() { + _this.refreshAnalysisData(); + window.clearInterval(inter); + inter = setInterval(function() { + updateExperiments(); + }, _this.INTERVAL_UPDATE); + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, data) { + var experiment = new Experiment(data); + _this.measurementGridDone.refresh(experiment.getMeasurements(), new ExperimentList([ experiment ])); + }); + adapter.getExperimentById(_this.experiment.json.experimentId, "MEDIUM"); + } + inter = setInterval(function() { + updateExperiments(); + }, _this.INTERVAL_UPDATE); + } +}; + +ExperimentTabs.prototype.getPanel = function(panel) { + var _this = this; + if (this.experimentPanel == null) { + this.experimentPanel = Ext.create('Ext.container.Container', { + bodyPadding : 2, + width : Ext.getBody().getViewSize().width * 0.9, + renderTo : this.targetId, + style : { + padding : 2 + }, + items : [ this.getExperimentTitle(), this.panel ], + listeners : { + afterrender : function() { + _this.refreshAnalysisData(); + _this.update(); + } + } + }); + } + + return this.experimentPanel; +}; + +ExperimentTabs.prototype.input = function() { + return { + experiment : DATADOC.getExperiment_10(), + proposal : DATADOC.getProposal_10() + }; +}; + +ExperimentTabs.prototype.test = function(targetId) { + var experimentTabs = new ExperimentTabs(targetId); + BIOSAXS.proposal = new Proposal(experimentTabs.input().proposal); + experimentTabs.draw(new Experiment(experimentTabs.input().experiment)); +}; + + + +/** + * Shows an experiments with the specimens, measurements, analysis tabs where results are shown and the frames widget + * + * @targetId + */ +function HPLCTabs(targetId) { + this.height = 1400; + this.targetId = targetId; + + this.id = BUI.id(); + + this.gridHeight = 600; + this.pointsCount = 1036; + this.plotHeight = 350; + this.plotWidth = 800; + this.plotPanelPadding = 5; + + var _this = this; + + this.analysisGrid = new HPLCAnalysisGrid({ + height : Ext.getBody().getViewSize().height * 0.9 - 300, + positionColumnsHidden : true, + sorters : [ { + property : 'priorityLevelId', + direction : 'ASC' + } ] + }); + + this.frameGrid = new FrameGrid({ + width : 600, + height : 60, + collapsed : false, + tbar : false + }); + + this.peakGrid = new PeakGrid({ + width : 550, + height : 500, + collapsed : false, + tbar : false + }); + + this.peakGrid2 = new PeakGrid({ + width : 102, + height : this.plotHeight, + collapsed : false, + tbar : false, + showExtendedColumns : true + }); + + this.mainPlotPanel = new HPLCGraph({ + title : 'I0', + width : this.plotWidth - 110, + height : this.plotHeight, + bbar : true, + plots : { + "I0" : true, + "Rg" : true + }, + xlabel : "HPLC Frames", + scaled : true, + interactionModel : { + 'dblclick' : function(event, g, context) { + _this._selectFrame(g.lastx_); + var annotations = []; + annotations.push({ + series : g.selPoints_[0].name, + x : g.lastx_, + width : 100, + height : 23, + tickHeight : 4, + shortText : g.lastx_, + text : g.lastx_, + attachAtBottom : true + }); + g.setAnnotations(annotations); + + } + } + }); + + this.intensityPlotPanel = new MergesHPLCGraph({ + title : 'Scattering', + width : this.plotWidth, + height : 500, + showRangeSelector : false, + xParam : 0, + xlabel : "scattering_I", + plots : { + "scattering_I" : true, + "subtracted_I" : true, + "buffer_I" : true + } + }); + +} + +HPLCTabs.prototype._selectFrame = function(frameNumber) { + try{ + this._renderScatteringCurve(frameNumber); + this.frameGrid.refresh([this.mainPlotPanel.getDataByFrameNumber(frameNumber)], this.experiment.experimentId); + } + catch(e){ + console.log(e); + } +}; + +HPLCTabs.prototype.error = function(error) { + var e = JSON.parse(error); + showError(e); + this.panel.setLoading(false); +}; + +HPLCTabs.prototype.loadDataMainPlot = function(data) { + var zeroArray = []; + for ( var i = 0; i < data.I0.length; i++) { + zeroArray.push(0); + } + data = [ { + param : "I0", + data : data.I0, + std : data.I0_Stdev, + color : '#0066CC', + label : "I0" + }, + { + param : "sum_I", + label : "sum_I", + color : "#00FF00", + data : data.sum_I, + std : zeroArray + }, + { + param : "Rg", + label : "Rg", + color : "#21610B", + data : data.Rg, + std : data.Rg_Stdev + }, { + param : "Mass", + data : data.mass, + std : data.mass_Stdev, + color : '#FF9900', + label : "Mass" + }, { + param : "Vc", + data : data.Vc, + std : data.Vc_Stdev, + color : '#990099', + label : "Vc" + }, { + param : "Qr", + data : data.Qr, + std : data.Qr_Stdev, + color : '#FF0066', + label : "Qr" + }, { + param : "quality", + label : "quality", + color : "#FF00FF", + data : data.quality, + std : zeroArray + } ]; + this.data = data; + this.mainPlotPanel.loadData(data); +}; + +HPLCTabs.prototype._loadIntensityPlotByFrameNumber = function(frameNumber) { + var _this = this; + + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, data) { + var array = []; + data = [ { + param : "q", + data : data[frameNumber].q, + std : data[frameNumber].q, + fstd : function(a) { + return parseFloat(a); + }, + color : '#green', + label : "q", + showOnMenu : false + }, + { + param : "scattering_I", + data : data[frameNumber].scattering_I, + fdata : function(a) { + return Math.log(parseFloat(a)); + }, + std : data[frameNumber].scattering_Stdev, + fstd : function(a) { + return Math.log(Math.abs(parseFloat(a))); + }, + color : 'green', + label : "Log(I) Sample" + }, + { + param : "buffer_I", + data : data[frameNumber].buffer_I, + fdata : function(a) { + return Math.log(parseFloat(a)); + }, + fstd : function(a) { + return Math.log(Math.abs(parseFloat(a))); + }, + std : data[frameNumber].subtracted_Stdev, + color : '#0000FF', + label : "Log(I) Avg Buf." + }, + { + param : "subtracted_I", + data : data[frameNumber].subtracted_I, + fdata : function(a) { + var value = (Math.log(parseFloat(a))); + if (isNumber(value)) + return value; + }, + fstd : function(a) { + var value = Math.log(Math.abs(parseFloat(a))); + if (isNumber(value)) + return value; + }, + std : data[frameNumber].subtracted_Stdev, + color : 'red', + label : "Log(I) Sub." + } + ]; + + _this.intensityPlotPanel.xlabel = "Frame " + frameNumber + " (q, nm-1)"; + + if (_this.intensityPlotPanel.hplcData == null) { + /** It creates also top bar **/ + _this.intensityPlotPanel.loadData(data); + } else { + _this.intensityPlotPanel.reloadData(data); + + } + _this.intensityPlotPanel.panel.setLoading(false); + }); + adapter.onError.attach(function(sender, data) { + _this.intensityPlotPanel.panel.setLoading(false); + }); + this.intensityPlotPanel.panel.setLoading("Retrieving Frame " + frameNumber); + adapter.getH5FrameScattering(this.experiment.json.experimentId, frameNumber); +}; + +HPLCTabs.prototype._renderScatteringCurve = function(frameNumber) { + var _this = this; + + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, data) { + _this.intensityPlotPanel.setPeaks(data); + _this._loadIntensityPlotByFrameNumber(frameNumber); + try{ + var peaks = []; + for(key in data){ + peaks.push({ + start : key.split("-")[0], + end : key.split("-")[1], + experimentId : _this.experiment.json.experimentId + }); + } + _this.peakGrid2.refresh(JSON.parse(JSON.stringify(peaks))); + _this.peakGrid.refresh(peaks); +// console.log(peaks); + } + catch(e){ + showError(e); + } + }); + adapter.onError.attach(function(sender, data) { + _this.intensityPlotPanel.setLoading(false); + }); + this.intensityPlotPanel.panel.setLoading("Reading HDF5 File "); + adapter.getH5FramesMerge(this.experiment.json.experimentId); +}; + +HPLCTabs.prototype._postRenderOverviewPanel = function() { + var _this = this; + var adapter = new BiosaxsDataAdapter(); + adapter.onError.attach(function(sender, data) { + data = data.replace(/NaN/g, '0'); + _this.loadDataMainPlot(JSON.parse(data)); + + + }); + adapter.onSuccess.attach(function(sender, data) { + _this.loadDataMainPlot(data); + _this._renderScatteringCurve(0); + }); + adapter.getH5fileParameters(this.experiment.json.experimentId, [ + "I0", "I0_Stdev", "sum_I","Rg", "Rg_Stdev", "Vc", "Vc_Stdev", "Qr", "Qr_Stdev", "mass", "mass_Stdev", "quality" ]); + +}; + +HPLCTabs.prototype._postRenderDetailsPanel = function() { + if (this.data != null) { + this.I0PlotPanel.loadData(this.data); + this.RgPlotPanel.loadData(this.data); + this.MassPlotPanel.loadData(this.data); + this.VcPlotPanel.loadData(this.data); + } + +}; + +HPLCTabs.prototype.draw = function(experiment) { + var _this = this; + this.renderDataAcquisition(experiment); +}; + +HPLCTabs.prototype.getExperimentTitle = function() { + var experimentHeaderForm = new ExperimentHeaderForm(); + return experimentHeaderForm.getPanel(this.experiment); +}; + +HPLCTabs.prototype.renderDataAcquisition = function(experiment) { + var _this = this; + this.experiment = experiment; + if (this.experimentPanel == null) { + this.experimentPanel = Ext.create('Ext.container.Container', { + bodyPadding : 2, + height : this.height, + width : Ext.getBody().getViewSize().width * 0.9, + renderTo : this.targetId, + style : { + padding : 2 + }, + items : [ this.getExperimentTitle(), this.getPanel() ], + listeners : { + afterrender : function() { + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, data) { + _this.analysisGrid.refresh(data); + }); + adapter.getAnalysisInformationByExperimentId(_this.experiment.experimentId); + } + } + }); + } + return this.experimentPanel; +}; + +HPLCTabs.prototype.getPanel = function() { + var _this = this; + this.panel = Ext.createWidget('tabpanel', + { + plain : true, + height : this.height, +// width : 900, + style : { + padding : 2 + }, + items : [ + { + tabConfig : { + title : "Overview" + }, - "quality" : { - "std" : null, - "sum" : 0, - "avg" : 0, - "validNumber" : 0, - "totalNumber" : 0, - "values" : [] + items : [ { + xtype : 'container', + layout : 'hbox', + border : 1, + flex : 1, + items : [ { + xtype : 'container', + layout : 'vbox', + border : 1, + items : [ { + xtype : 'container', + margin : '10px', + border : 1, + items : [ + { + xtype : 'container', + margin : '0px', + layout : 'hbox', + border : 1, + items : [ + this.peakGrid2.getPanel([]), + this.mainPlotPanel.getPanel() + ] + }, + { + xtype : 'container', + layout : 'vbox', + border : 1, + items : [ + { + html: '
Select a frame by double-clicking on the HPLC Frames plot
', + margin: 5, + border : 0 + }, + this.frameGrid.getPanel([]), + this.intensityPlotPanel.getPanel() + ] + } + + ] + }], + listeners : { + afterrender : function() { + _this._postRenderOverviewPanel(); + } + } + } ] + } ] + }, +// { +// tabConfig : { +// title : "Details" +// }, +// items : [ { +// xtype : 'container', +// layout : 'hbox', +// border : 1, +// flex : 1, +// items : [ { +// xtype : 'container', +// layout : 'vbox', +// border : 1, +// items : [ { +// xtype : 'container', +// padding : '1px', +// items : [ +// this.I0PlotPanel.getPanel(), this.RgPlotPanel.getPanel(), this.MassPlotPanel.getPanel(), +// this.VcPlotPanel.getPanel() ] +// } ], +// listeners : { +// afterrender : function() { +// _this._postRenderDetailsPanel(); +// } +// } +// } ] +// } ] +// }, + { + tabConfig : { + title : "Analysis" }, - "rgGnom" : { - "std" : null, - "sum" : 0, - "avg" : 0, - "validNumber" : 0, - "totalNumber" : 0, - "values" : [] + items : [ { + xtype : 'container', + layout : 'vbox', + padding : '5px 0px 10px 10px', + items : [ _this.analysisGrid.getPanel([]) ] + } ] + }, + { + tabConfig : { + title : "File Manager" }, - "dMax" : { - "std" : null, - "sum" : 0, - "avg" : 0, - "validNumber" : 0, - "totalNumber" : 0, - "values" : [] + items : [ { + xtype : 'container', + layout : 'vbox', + padding : '5px 0px 10px 10px', + items : [ + { + html: '
Custom Download
', + margin : '10 0 10 5', + border : 0 + }, + { + html: '
Download has been temporary disabled. Contact your labcontact if you want to extract the frames from the HDF5 file
', + margin : '10 0 10 5', + hidden : _this.experiment.experimentId == 2602, + border : 0 + }, + { + xtype : 'container', + layout : 'hbox', + margin : '0 0 0 20', + flex : 1, + items :[ + { + xtype: 'numberfield', + id: 'field_start', + fieldLabel: 'Frames from', + value: 0, + minValue: 0 + }, + { + xtype: 'numberfield', + id: 'field_end', + fieldLabel: 'to', + labelWidth : 20, + margin : '0 0 0 10', + minValue: 0 + }, + { + xtype: 'button', + /** allowing test account to download frames **/ + disabled : _this.experiment.experimentId != 2602, + text : 'Download', + margin : '0 0 0 10', + handler : function() { + var experimentId = _this.experiment.experimentId; + var start = Ext.getCmp("field_start").getValue(); + var end = Ext.getCmp("field_end").getValue(); + /** Checking if start is bigger than end **/ + if (start > end){ + var aux = end; + end = start; + start = aux; + } + var url = BUI.getZipFrameHPLCUrl(experimentId, start, end); + window.open(url) + } + } + ] + }, + _this.peakGrid.getPanel([]) + + ] + } + ] + } + ] + }); + + return this.panel; +}; + +/** + * Main form tab for macromolecule + * + * @width + * @height + * + * #onClose when user clicks on close button in the MacromoleculeForm + * #onSave when macromole is saved by macromoleculeForm + */ +function MacromoleculeTabs(args) { + this.width = 500; + this.height = 500; + + if (args != null) { + if (args.width != null) { + this.width = args.width; + } + if (args.height != null) { + this.height = args.height; + } + } + + var _this = this; + + /** Widgets **/ + + /** Macromolecule Form **/ + this.macromoleculeForm = new MacromoleculeForm({ + width : this.width - 30, + height : this.height - 50, + }); + + this.macromoleculeForm.onClose.attach(function(sender) { + _this.onClose.notify(); + }); + + this.macromoleculeForm.onSave.attach(function(sender, macromolecule) { + _this.onSave.notify(macromolecule); + }); + + this.assemblyForm = new AssemblyForm({ + width : this.width - 30, + height : this.height - 50, + }); + + this.rigibBodyModelingForm = new RigibBodyModelingForm({ + width : this.width - 30, + height : this.height - 50, + }); + + this.rigibBodyModelingForm.onSave.attach(function(sender, macromolecule) { + _this.onSave.notify(macromolecule); + }); + + + /** Events **/ + this.onClose = new Event(this); + this.onSave = new Event(this); +} + +/** Populate the widget **/ +MacromoleculeTabs.prototype.refresh = function(macromolecule) { + this.macromoleculeForm.refresh(macromolecule); + this.assemblyForm.refresh(macromolecule); + this.rigibBodyModelingForm.refresh(macromolecule); + + if (macromolecule != null){ + if (macromolecule.macromoleculeId == null){ + Ext.getCmp(this.id + "_advancedTab").disable(); + Ext.getCmp(this.id + "_assemblyTab").disable(); + } + else{ + Ext.getCmp(this.id + "_advancedTab").enable(); + Ext.getCmp(this.id + "_assemblyTab").enable(); + } + } + else{ + Ext.getCmp(this.id + "_advancedTab").disable(); + Ext.getCmp(this.id + "_assemblyTab").disable(); + } +}; + +MacromoleculeTabs.prototype.getItems = function() { + return [ { + tabConfig : { + title : 'General' + }, + items : [ { + xtype : 'container', + items : [ this.macromoleculeForm.getPanel() ] + } ] + }, { + id : this.id + "_assemblyTab", + tabConfig : { + title : 'Assembly' + }, + items : [ { + xtype : 'container', + items : [ this.assemblyForm.getPanel() ] + } ] + },{ + id : this.id + "_advancedTab", + tabConfig : { + title : 'Advanced' + }, + items : [ this.rigibBodyModelingForm.getPanel() ] + } ]; +}; + +MacromoleculeTabs.prototype.getPanel = function() { + this.panel = Ext.createWidget('tabpanel', { + height : this.height, + width : this.width, + plain : true, + margin : 5, + border : 0, + items : this.getItems() + }); + return this.panel; +}; + +function ResultTabs() { +} + +ResultTabs.prototype.draw = function(targetId, data, macromoleculeId) { + var panel = this.getPanel(targetId, data, macromoleculeId); + return Ext.create('Ext.container.Container', { + renderTo : targetId, + items : [ BUI.getMacromoleculeHeader(macromoleculeId), panel ] + }); + +}; + +ResultTabs.prototype._splitBySpecimen = function(data) { + var splitted = {}; + + for ( var i = 0; i < data.length; i++) { + var row = data[i]; + if (splitted[row.macromoleculeId + "_" + row.bufferId] == null) { + splitted[row.macromoleculeId + "_" + row.bufferId] = []; + } + splitted[row.macromoleculeId + "_" + row.bufferId].push(row); + } + return splitted; + +}; + +ResultTabs.prototype._getTabTitle = function(key, data) { + var macromoleculeId = key.split("_")[0]; + var bufferId = key.split("_")[1]; + + return BIOSAXS.proposal.getMacromoleculeById(macromoleculeId).acronym + " + " + BIOSAXS.proposal.getBufferById(bufferId).acronym + "(" + data.length + ")"; +}; + +ResultTabs.prototype.getPanel = function(targetId, data, macromoleculeId) { + + var dataFiltered = new AnalysisGrid({ + hideNulls : true, + grouped : true, + sorters : [ { + property : 'quality', + direction : 'DESC' + } ] + })._prepareData(data); + + var items = [ { + tabConfig : { + title : 'Analysis (' + dataFiltered.length + ')' + }, + items : [ { + xtype : 'container', + layout : 'vbox', + padding : 10, + items : [ new AnalysisGrid({ + isScatteringPlotVisible : false + }).getPanel(dataFiltered) ] + } ] + }, { + tabConfig : { + title : 'Concentration Effects' + }, + items : [ new ResultSummaryForm().getPanel(data) ] + } ]; + + var splitted = this._splitBySpecimen(dataFiltered); + for ( var key in splitted) { + items.push({ + tabConfig : { + title : this._getTabTitle(key, splitted[key]) + }, + items : [ new ResultSummaryForm().getPanel(splitted[key]) ] + }); + } + + this.panel = Ext.createWidget('tabpanel', { + style : { + padding : 2 + }, + items : items + }); + return this.panel; + +}; + + +function ShipmentTabs(targetId) { + this.targetId = targetId; + + var _this = this; + this.gridHeight = 500; + /** data **/ + this.shipment = null; + + /** Shipment Form **/ + this.shipmentForm = new ShipmentForm({ + creationMode : false, + showTitle : false + }); + this.shipmentForm.onSaved.attach(function(sender, data) { + _this.refresh(data); + + }); + + + /** Cases grid **/ + this.caseGrid = new CaseGrid({ + height : this.gridHeight + }); + + this.caseGrid.onAddButtonClicked.attach(function(sender, dewar) { + _this.caseGrid.grid.setLoading("ISPyB: Creating a new case"); + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, shipment) { + /** updateing shipment on proposal **/ + for ( var i = 0; i < BIOSAXS.proposal.shippings.length; i++) { + if (BIOSAXS.proposal.shippings[i].shippingId == shipment.shippingId) { + BIOSAXS.proposal.shippings[i] = shipment; + } + } + _this.refresh(shipment); + }); + adapter.onError.attach(function(sender, shipment) { + _this.caseGrid.grid.setLoading(false); + }); + adapter.addCase(_this.shipment.json.shippingId); + }); + + this.caseGrid.onRemoveButtonClicked.attach(function(sender, dewarId) { + _this.panel.setLoading("ISPyB: removing case"); + _this.shipment.onSaved.attach(function(sender, shipment) { + _this.refresh(shipment); + + }); + _this.shipment.removeCase(dewarId); + }); +} + +ShipmentTabs.prototype.refresh = function(shipment) { + + var _this = this; + this.shipment = shipment; + var proposal = new Proposal(); + proposal.onDataRetrieved.attach(function(sender, plates) { + + _this.refreshWithPlates(shipment, plates); + _this.caseGrid.grid.setLoading(false); + }); + proposal.getPlatesByProposal(); +}; + +ShipmentTabs.prototype.refreshWithPlates = function(shipment, plates) { + this.shipment = new Shipment(shipment); + + this.caseGrid.refresh(this.shipment.getDewars(), plates); + this.panel.setLoading(false); +}; + +ShipmentTabs.prototype.error = function(error) { + var e = JSON.parse(error); + showError(e); + this.panel.setLoading(false); +}; + +//ShipmentTabs.prototype.refreshTabTitles = function() { +/*Ext.getCmp("MacromoleculeTab").setText(this.getMacromoleculeTitle()); +Ext.getCmp("BufferTab").setText(this.getBuffersTitle()); +Ext.getCmp("SpecimenTab").setText(this.getSpecimenTitle()); +Ext.getCmp("PlatesTab").setText(this.getPlatesTitle()); +Ext.getCmp("AssembliesTab").setText(this.getShipmentTitle()); +Ext.getCmp("PlateGroupsTab").setText(this.getPlateGroupsTitle());*/ +//}; +ShipmentTabs.prototype.draw = function(shipment) { + var _this = this; + _this.plates = []; + _this.shipment = shipment; + _this.render(shipment); + + // var proposal = new Proposal(); + // proposal.onDataRetrieved.attach(function(sender, plates){ + // _this.plates = plates; + // _this.shipment = shipment; + // _this.render(shipment); + // + // }); + // proposal.getPlatesByProposal(); +}; + +//ShipmentTabs.prototype.getShipmentTitle = function() { +// return 'Shipment'; +//}; + +//ShipmentTabs.prototype.getMacromoleculeTitle = function() { +// return 'Macromolecules (' + this.experiment.getMacromolecules().length + ')'; +//}; + +//ShipmentTabs.prototype.getBuffersTitle = function() { +// return 'Buffers (' + this.experiment.getBuffers().length + ')'; +//}; + +//ShipmentTabs.prototype.getPlateGroupsTitle = function() { +// return 'Plate Groups (' + this.experiment.getPlateGroups().length + ')'; +//}; + +//ShipmentTabs.prototype.getSampleChangerTitle = function() { +// return 'Sample Changer'; +//}; + +//ShipmentTabs.prototype.getSpecimenTitle = function() { +// return 'Specimens(' + this.experiment.getSpecimenCount() + ')'; +//}; + +//ShipmentTabs.prototype.getPlatesTitle = function() { +// return 'Plates(' + this.experiment.getSamplePlates().length + ')'; +//}; + +//ShipmentTabs.prototype.getBuffersTip = function() { +/*if (this.experiment.getBuffers().length == 0){ + return BUI.getWarningHTML("There are no buffers. Click on add to create new ones. Click on edit button or double click to edit them"); + +} +else{ + return BUI.getTipHTML("Click on edit button or double click to edit them. Click on duplicate to create an identical buffer including its additives") +}*/ +//}; + +//ShipmentTabs.prototype.refreshTips = function() { +/*Ext.getCmp("BufferTabTip").update(this.getBuffersTip()); +Ext.getCmp("SpecimenTabTip").update(this.getSpecimensTip());*/ +//}; + +ShipmentTabs.prototype.render = function(shipment) { + return this.getPanel(shipment); +}; + +ShipmentTabs.prototype.getShipmentHeader = function(shipment) { + var _this = this; + function getHTMLSource() { + var name = shipment.json.shippingName; + var status = shipment.json.shippingStatus; + var creationDate = shipment.json.creationDate; + var html = BUI.createFormLabel("Name :", name, 75, 400); + html = html + BUI.createFormLabel("Status :", status, 75, 400); + html = html + BUI.createFormLabel("Date :", creationDate, 75, 400); + return html; + } + + return Ext.create('Ext.container.Container', { + frame : false, + layout : 'hbox', + title : 'General', + padding : 5, + bodyPadding : '5 5 0 0', + width : 890, + margin : '0 0 10 0', + height : 100, + style : { + borderColor : '#BDBDBD', + borderStyle : 'solid', + borderWidth : '1px' + }, + fieldDefaults : { + msgTarget : 'side', + labelWidth : 100 + }, + items : [ { + margin : "0 0 0 0", + width : 475, + border : 0, + html : getHTMLSource() + } + ] + }); +}; + +ShipmentTabs.prototype.getTabPanel = function(shipment) { + this.panel = Ext.createWidget('tabpanel', { + height : 600, + style : { + padding : 2 + }, + items : [ { + tabConfig : { + id : 'Shipment', + title : 'Shipment', + icon : '/ispyb/images/plane-small.gif' + }, + items : [ { + xtype : 'container', + margin : '5 5 5 5', + items : [ this.shipmentForm.getPanel(shipment) ] + } ] + }, { + tabConfig : { + id : 'Cases', + title : 'Cases', + icon : '../images/box-icon-very-small.png' + }, + items : [ { + xtype : 'container', + margin : '5 5 5 5', + items : [ this.caseGrid.getPanel(shipment.getDewars(), this.plates) ] + } ] + } ] + }); + return this.panel; +}; + +ShipmentTabs.prototype.getPanel = function(shipment) { + var _this = this; + this.shipment = shipment; + if (this.plates == null) { + this.plates = []; + } + + if (this.shipPanel == null) { + this.shipPanel = Ext.create('Ext.container.Container', { + bodyPadding : 2, + width : Ext.getBody().getViewSize().width * 0.9, + renderTo : this.targetId, + style : { + padding : 2 + }, + items : [ this.getShipmentHeader(shipment), this.getTabPanel(shipment) ] + }); + } + + return this.shipPanel; +}; + +/** + * Shows an template with the specimens, measurements and the experiment's + * requirement + * + * @targetId + */ +function TemplateTabs(targetId) { + this.height = 600; + this.targetId = targetId; + + this.id = BUI.id(); + var _this = this; + + this.gridHeight = 1000; + /** data * */ + this.experiment = null; + + this.specimenSelected = null; + + /** Specimen Widget contains a specimenGrid and a sampleChangerWidget than can be displayed with are vertical or horizontal layout **/ + this.specimenWidget = new SpecimenWidget({ + height : this.height + }); + + this.samplePlateGroupWidget = new SamplePlateGroupWidget({ + showTitle : false, + height : 250, + margin : 5, + bbar : true + }); + + this.samplePlateGroupWidget.onExperimentChanged.attach(function(sender, experiment) { + _this.refresh(experiment); + }); + + this.samplePlateGroupWidget.onClick.attach(function(sender, args) { + }); + + this.volumePlanificator = new VolumeGrid(); + + /** For Measurements * */ + var storeViscosity = Ext.create('Ext.data.Store', { + fields : [ 'name' ], + data : [ { + "name" : "low" + }, { + "name" : "medium" + }, { + "name" : "high" + } ] + }); + + // Create the combo box, attached to the states data store + var viscosityEditor = Ext.create('Ext.form.ComboBox', { + fieldLabel : '', + store : storeViscosity, + queryMode : 'local', + displayField : 'name', + valueField : 'name' + }); + + this.measurementGrid = new MeasurementGrid({ + maxWidth : 1500, + estimateTime : false, + positionColumnsHidden : true, + isPriorityColumnHidden : true, + isStatusColumnHidden : true, + isTimeColumnHidden : true, + updateRowEnabled : true, + collapsed : true, + removeBtnEnabled : true, + showTitle : false, + collapseBtnEnable : false, + addBtnMultipleEdit : true, + sortingBtnEnable : true, + editor : { + exposureTemperature : { + xtype : 'textfield', + allowBlank : true + }, + comments : { + xtype : 'textfield', + allowBlank : true + }, + volumeToLoad : { + xtype : 'numberfield', + allowBlank : true + }, + transmission : { + xtype : 'numberfield', + allowBlank : true + }, + viscosity : viscosityEditor, + waitTime : { + xtype : 'numberfield', + allowBlank : true + }, + flow : { + xtype : 'checkbox', + allowBlank : true + } + } + }); + + this.measurementGrid.onSelected.attach(function(sender, measurements) { + var specimens = []; + for ( var i = 0; i < measurements.length; i++) { + specimens.push(_this.experiment.getSampleById(measurements[i].specimenId)); + } + }); + + this.measurementGrid.onMeasurementChanged.attach(function(sender, measurement) { + _this.experiment.setMeasurement(measurement); + _this.refresh(_this.experiment); + }); + + this.measurementGrid.onExperimentChanged.attach(function(sender, json) { + _this.refresh(new Experiment(json)); + }); + + this.measurementGrid.onRemoved.attach(function(sender, experiment) { + _this.refreshSpecimen(new Experiment(experiment)); + }); + + this.measurementGrid.onUpdateTime.attach(function(sender, args) { + document.getElementById(_this.id + "_counter").innerHTML = args.hours + 'h, ' + args.minutes + 'min and ' + args.seconds + ' seconds'; + }); + +} + +TemplateTabs.prototype.refreshMeasurement = function(experiment) { + this.experiment = experiment; + this.experiment.onSaved = new Event(this); + this.experiment.onSpecimenSaved = new Event(this); + var experimentList = new ExperimentList([ this.experiment ]); + this.measurementGrid.refresh(experimentList.getMeasurementsNotCollected(), experimentList); +}; + +TemplateTabs.prototype.refreshSpecimen = function(experiment) { + this.experiment = experiment; + this.experiment.onSaved = new Event(this); + this.experiment.onSpecimenSaved = new Event(this); + this.samplePlateGroupWidget.refresh(this.experiment); + this.specimenWidget.refresh(this.experiment); + this.volumePlanificator.refresh(this.experiment); +}; + +TemplateTabs.prototype.refresh = function(experiment) { + // var start = new Date().getTime(); + this.experiment = experiment; + this.experiment.onSaved = new Event(this); + this.experiment.onSpecimenSaved = new Event(this); + + // var experimentList = new ExperimentList([this.experiment]); + this.refreshMeasurement(experiment); + this.refreshSpecimen(experiment); + /** Refreshing grids * */ + this.panel.setLoading(false); + +}; + +TemplateTabs.prototype.error = function(error) { + var e = JSON.parse(error); + showError(e); + this.panel.setLoading(false); + +}; + +TemplateTabs.prototype.draw = function(experiment) { + this.render(experiment); +}; + + +TemplateTabs.prototype.getExperimentTitle = function() { + var _this = this; + var experimentHeaderForm = new ExperimentHeaderForm(); + return experimentHeaderForm.getPanel(this.experiment); +}; + +TemplateTabs.prototype.render = function(experiment) { + var _this = this; + this.experiment = experiment; + + /** + * + * Depending on the sample Changer configuration we want to display the plates vertically or horizontally + * Default is vertical + * + * */ + + var specimenContainer = this.specimenWidget.getPanel(); + this.specimenWidget.refresh(experiment); + + var experimentList = new ExperimentList([ _this.experiment ]); + var measurementContainer = Ext.create('Ext.container.Container', { + layout : { + type : 'vbox' + }, + defaults : { + style : { + padding : '5px 0px 0px 10px' + } + }, + items : [ _this.measurementGrid.getPanel(experimentList.getMeasurementsNotCollected(), experimentList) ] + }); + + this.panel = Ext + .createWidget( + 'tabpanel', + { + plain : true, + items : [ + { + tabConfig : { + title : 'Measurements' + }, + items : [ { + xtype : 'container', + layout : 'vbox', + border : 1, +// height : _this.gridHeight, + margin : "0 0 10 0", + items : [ measurementContainer ] + } + + ] + }, + { + tabConfig : { + id : 'genralTabl', + title : "Specimens" + // width : 900, + // border : 3 + + }, + items : [ specimenContainer ] + }, + { + tabConfig : { + title : "Requirements" + + }, + items : [ + { + html : BUI.getTipHTML("Estimated volume is the maximum volume required. Depending on the order of your measurements you may use less. Click on create stock solutions if you plan to ship these stock solutions"), + margin : "10 10 10 10", + border : 0 + }, this.volumePlanificator.getPanel(experiment) ] + } ] + }); + // ); + return this.getPanel(this.panel); +}; + +TemplateTabs.prototype.isTemplate = function() { + if (this.experiment.json.type == "TEMPLATE") { + return true; + } + return false; +}; + + +TemplateTabs.prototype.getPanel = function(panel) { + var _this = this; + if (this.experimentPanel == null) { + this.experimentPanel = Ext.create('Ext.container.Container', { + bodyPadding : 2, + width : 1000,//Ext.getBody().getViewSize().width * 0.9, + renderTo : this.targetId, + height : 500, +// style : { +// padding : 2 +// }, + items : [ + this.getExperimentTitle(), + this.panel + ], + listeners : { + afterrender : function(thisCmp) { + $("#SchemeReport" + _this.experiment.experimentId).click(function() { + $(this).target = "_blank"; + window.open('viewProjectList.do?reqCode=display&menu=platescheme&experimentId=' + _this.experiment.experimentId); + return false; + }); + + } + } + }); + } + return this.experimentPanel; +}; + +TemplateTabs.prototype.input = function(targetId) { + return new ExperimentTabs().input(); +}; + +TemplateTabs.prototype.test = function(targetId) { + var experimentTabs = new TemplateTabs(targetId); + BIOSAXS.proposal = new Proposal(experimentTabs.input().proposal); + experimentTabs.draw(new Experiment(experimentTabs.input().experiment)); +}; + +var BUI = { + //interval : 60000, + interval : 40000, + rainbow : function(numOfSteps, step) { + // This function generates vibrant, "evenly spaced" colours (i.e. no clustering). This is ideal for creating easily distinguishable vibrant markers in Google Maps and other apps. + // Adam Cole, 2011-Sept-14 + // HSV to RBG adapted from: http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript + var r, g, b; + var h = step / numOfSteps; + var i = ~~(h * 6); + var f = h * 6 - i; + var q = 1 - f; + switch (i % 6) { + case 0: + r = 1, g = f, b = 0; + break; + case 1: + r = q, g = 1, b = 0; + break; + case 2: + r = 0, g = 1, b = f; + break; + case 3: + r = 0, g = q, b = 1; + break; + case 4: + r = f, g = 0, b = 1; + break; + case 5: + r = 1, g = 0, b = q; + break; + } + var c = "#" + ("00" + (~~(r * 255)).toString(16)).slice(-2) + ("00" + (~~(g * 255)).toString(16)).slice(-2) + + ("00" + (~~(b * 255)).toString(16)).slice(-2); + return (c); + }, + getFileNameByPath : function(filePath) { + if (filePath != null){ + var split = filePath.split("/"); + if (split.length > 0){ + return split[split.length - 1]; + } + } + return "Not file"; + }, + getUpdateInterval : function() { + this.interval = this.interval + 2000; + return this.interval; + }, + getRadiationDamageThreshold : function() { + return 0.7; + }, + getQualityThreshold : function() { + return 0.7; + }, + getCreateShipmentURL : function() { + return '/ispyb/user/viewProjectList.do?reqCode=display&menu=create_shipment'; + }, + getCreateShipmentList : function() { + return '/ispyb/user/viewProjectList.do?reqCode=display&menu=list_shipment'; + }, + getShippingURL : function(shippingId) { + return '/ispyb/user/viewProjectList.do?reqCode=display&menu=shipment&shippingId=' + shippingId; + }, + + getMacromoleculeResultsURLByMultipleSearch : function(array) { + return '/ispyb/user/viewProjectList.do?reqCode=display&menu=macromolecule&search=' + + JSON.stringify(array).replace(new RegExp("\"", 'g'), "'"); + }, + getMacromoleculeResultsURL : function(macromoleculeId) { + return '/ispyb/user/viewProjectList.do?reqCode=display&menu=macromolecule¯omoleculeId=' + macromoleculeId; + }, + getMacromoleculeBufferResultsURL : function(macromoleculeId, bufferId) { + return '/ispyb/user/viewProjectList.do?reqCode=display&menu=macromolecule&bufferId=' + bufferId + '¯omoleculeId=' + macromoleculeId; + }, + + getMacromoleculeHeader : function(macromoleculeId) { + + function getHTMLSource(macromoleculeId) { + if (macromoleculeId != null) { + var html = BUI.createFormLabel("Name :", BIOSAXS.proposal.getMacromoleculeById(macromoleculeId).name, 75, 400); + html = html + BUI.createFormLabel("Acronym :", BIOSAXS.proposal.getMacromoleculeById(macromoleculeId).acronym, 75, 400); + if (BIOSAXS.proposal.getMacromoleculeById(macromoleculeId).comments != null) { + html = html + BUI.createFormLabel("Comments :", BIOSAXS.proposal.getMacromoleculeById(macromoleculeId).comments, 75, 400); + } + return html; + } + } + + return Ext.create('Ext.container.Container', { + frame : false, + layout : 'hbox', + title : 'Macromolecule', + bodyPadding : '5', + width : 890, + margin : '0 0 10 0', + height : 100, + style : { + borderColor : '#BDBDBD', + borderStyle : 'solid', + borderWidth : '1px' + }, + fieldDefaults : { + msgTarget : 'side', + labelWidth : 100 + }, + items : [ { + margin : "10 0 0 10", + width : 475, + border : 0, + html : getHTMLSource(macromoleculeId) + }, { + margin : "10 0 0 10", + width : 475, + border : 0, + html : BUI.getZipHTMLByMacromoleculeId(macromoleculeId) + } + ] + }); + }, + + getZipHTMLByMacromoleculeId : function(macromoleculeId) { + if (macromoleculeId != null) { + var fileName = BIOSAXS.proposal.getMacromoleculeById(macromoleculeId).acronym; + return ""; + } + }, + getZipHTMLByExperimentId : function(experimentId, filename) { + if (filename == null){ + filename = "experiment"; + } + return ""; + }, + + getZipURLByAverageId : function(averageId, filename) { + if (filename == null){ + filename = "experiment"; + } + return "/ispyb/user/dataadapter.do?reqCode=getZipFileByAverageListId&f&mergeIdsList=" + averageId + "&fileName=" + filename; + }, + getZipURLBySubtractionId : function(subtractionId, filename) { + if (filename == null){ + filename = "experiment"; + } + return "/ispyb/user/dataadapter.do?reqCode=getZipFileByAverageListId&f&subtractionIdList=" + subtractionId + "&fileName=" + filename; + }, + + + getZipHTMLByFrameRangeId : function(experimentId, start, end) { + var fileName = "experiment"; + return ""; + }, + getZipFrameHPLCUrl : function(experimentId, start, end) { + return "/ispyb/user/dataadapter.do?reqCode=getZipFileH5ByFramesRange&f&experimentId=" + experimentId + "&start=" + Number(start) +"&end="+ Number(end); + }, + + getQueueUrl : function() { + return "/ispyb/user/dataadapter.do?reqCode=getPagingCompactAnalysisByProposalId"; + }, + + getQueueUrlByExperiment: function(experimentId) { + return "/ispyb/user/dataadapter.do?reqCode=getPagingCompactAnalysisByExperimentId&f&experimentId=" + experimentId; + }, + getStandardDeviation : function(values) { + var sum = 0; + var count = 0; + var avg = null; + + var curatedValues = new Array(); + for ( var i = 0; i < values.length; i++) { + var value = values[i]; + if (value != null) { + if (!isNaN(value)) { + count = count + 1; + sum = sum + Number(value); + curatedValues.push(Number(value)); + } + } + } + + if (count > 0) { + avg = sum / count; + } else { + avg = sum; + } + var aux = 0; + for ( var i = 0; i < curatedValues.length; i++) { + var value = curatedValues[i]; + aux = aux + Math.pow(value - avg, 2); + } + /** std **/ + var std = Math.sqrt(aux / count); + return { + std : (std), + sum : (sum), + avg : (avg), + validNumber : count, + totalNumber : values.length, + values : values + }; + }, + + getHTMLTableForFrameAveraged : function(bufferAcronym, macromoleculeAcronym, bbmerges, molmerges, bamerges, totalframes, bufferId,macromoleculeId, macromoleculeColor) { + + function getFrameSpan(framesMerged, total) { + return "(" + framesMerged + " of " + total + ")"; + } + + function getColorFrame(framesMerged, total) { + if (framesMerged / total < 0.5) { + return "#FA5858"; + } + if ((framesMerged / total >= 0.5) && (framesMerged / total < 0.8)) { + return "#FF9900"; + } + return "white"; + } + + function getRow(color, acroynm, framesMerged, totalframes) { + return " " + + BUI.getRectangleColorDIV(color, 10, 10) + + " " + acroynm + "" + + getFrameSpan(framesMerged, totalframes) + ""; + } + + var html = ""; + + /** Buffer Before **/ + if (bufferAcronym != null) { + if (bbmerges != null) { + color = BIOSAXS.proposal.bufferColors[bufferId]; + html = html + getRow(color, bufferAcronym, bbmerges, totalframes); + } + } + + /** Molecule **/ + if (macromoleculeAcronym != null) { + if (molmerges != null) { + if (macromoleculeColor == null){ + color = BIOSAXS.proposal.macromoleculeColors[macromoleculeId]; + } + else{ + color = macromoleculeColor; //BIOSAXS.proposal.macromoleculeColors[macromoleculeId]; + } + html = html + getRow(color, macromoleculeAcronym, molmerges, totalframes); + } + } + + /** Buffer After **/ + if (bufferAcronym != null) { + if (bamerges != null) { + color = BIOSAXS.proposal.bufferColors[bufferId]; + html = html + getRow(color, bufferAcronym, bamerges, totalframes); + } + } + return html + "
"; + }, + isWebGLEnabled : function(return_context) { + if (!!window.WebGLRenderingContext) { + var canvas = document.createElement("canvas"); + names = [ "webgl", "experimental-webgl", "moz-webgl", "webkit-3d" ]; + context = false; + for ( var i = 0; i < 4; i++) { + try { + context = canvas.getContext(names[i]); + if (context && typeof context.getParameter == "function") { + // WebGL is enabled + if (return_context) { + // return WebGL object if the function's argument is present + return { + name : names[i], + gl : context + }; } + // else, return just true + return true; } - } ] - }, - "concentrationLabel" : "7.17 mg/ml ,3.53 mg/ml ,1.75 mg/ml ,0.91 mg/ml " + } catch (e) { + } + } + // WebGL is supported, but disabled + return false; + } + // WebGL not supported28. + return false; + }, + getHTMLTableForPrefixes : function(bufferBeforeaverageFilePath, averageFilePath, bufferAfterAverageFilePath) { + + function getRow(bufferBeforeaverageFilePath) { + file = bufferBeforeaverageFilePath; + try { + file = bufferBeforeaverageFilePath.split("/")[bufferBeforeaverageFilePath.split("/").length - 1]; + } catch (e) { + file = "NA"; + } + return "" + file + ""; + } + var html = ""; + + /** Buffer Before **/ + if (bufferBeforeaverageFilePath != null) { + html = html + getRow(bufferBeforeaverageFilePath); + } + + /** Molecule **/ + if (averageFilePath != null) { + html = html + getRow(averageFilePath); + } + + /** Buffer After **/ + if (bufferAfterAverageFilePath != null) { + html = html + getRow(bufferAfterAverageFilePath); + } + return html + "
"; + }, + + getBaseURL : function() { + return '/ispyb/user/dataadapter.do'; + }, + + getPrintcomponentURL : function(dewarId) { + return '/ispyb/user/viewDewarAction.do?reqCode=generateLabels&dewarId=' + dewarId; + + }, + getPDBVisualizerURL : function(modelId, subtractionId, experimentId) { + return '/ispyb/user/viewProjectList.do?reqCode=display&menu=PDBVisualizer&modelId=' + modelId + '&experimentId=' + experimentId + + '&subtractionId=' + subtractionId; + }, + + getURL : function() { + return this.getBaseURL() + '?reqCode=getImage'; + + }, + getAbinitioImageURL : function() { + return this.getBaseURL() + '?reqCode=getAbinitioImage'; + }, + getNSDImageURL : function(modelListId) { + return BUI.getAbinitioImageURL() + '&type=NSD&modelListId=' + modelListId; + }, + getCHI2ImageURL : function(modelListId) { + return BUI.getAbinitioImageURL() + '&type=CHI2&modelListId=' + modelListId; + }, + getModelFile : function(type, modelId, format) { + return this.getBaseURL() + '?reqCode=getModelFile' + "&type=" + type + "&modelId=" + modelId + "&format=" + format; + }, + getPdbURL : function() { + return this.getBaseURL() + '?reqCode=getPdbFiles'; + }, + getStvArray : function(value, error) { + value = Number(value); + error = Number(error); + return [ value - error, value, value + error ]; + }, + getPointArrayForDygraph : function(x, y, error) { + return [ x, BUI.getStvArray(y, error) ]; + }, + createDIV : function(text, width, className, backgroundColor) { + var nameContainer = document.createElement("div"); + var nameSpan = document.createElement("span"); + if (className != null) { + nameSpan.setAttribute("class", className); + } + if (backgroundColor != null) { + nameSpan.setAttribute("style", "float:left;width:" + width + "px;height:18px;background-color:" + backgroundColor); + } else { + nameSpan.setAttribute("style", "float:left;width:" + width + "px;height:18px;"); + } + nameSpan.appendChild(document.createTextNode(text)); + nameContainer.appendChild(nameSpan); + return nameContainer; + }, + + createFormLabel : function(labelText, text, labelWidth, textWidth, backgroundColor) { + var div = document.createElement("div"); + + div.appendChild(BUI.createDIV(labelText, labelWidth, "bLabel", backgroundColor)); + div.appendChild(BUI.createDIV(text, textWidth, "btext", backgroundColor)); + return div.innerHTML; + }, + + createTextArea : function(labelText, text, labelWidth, rows, cols) { + var div = document.createElement("div"); + div.appendChild(BUI.createDIV(labelText, labelWidth, "bLabel")); + var textArea = document.createElement("textarea"); + textArea.setAttribute("rows", rows); + textArea.setAttribute("cols", cols); + textArea.appendChild(document.createTextNode(text)); + div.appendChild(textArea); + return div.innerHTML; + }, + + showBetaWarning : function() { + alert("ISPyB for Biosaxs version Beta has not this functionality enabled"); + }, + + formatValuesErrorUnitsScientificFormat : function(val, error, unit, args) { + var fontSize = 14; + var decimals = 2; + var errorFontSize = 10; + /** line break **/ + var lineBreak = true; + if (args != null) { + if (args.fontSize != null) { + fontSize = args.fontSize; + } + if (args.decimals != null) { + decimals = args.decimals; + } + if (args.errorFontSize != null) { + errorFontSize = args.errorFontSize; + } + if (args.lineBreak != null) { + lineBreak = args.lineBreak; + } + + } + + if (val == "") { + return ""; + } + if (error == null) { + return "" + Number(val).toFixed(decimals) + ""; + } + var html = "" + Number(val).toFixed(decimals) + " " + unit + ""; + if (lineBreak) { + html = html + "
"; + } + return html + " ± " + Number(Number(error).toFixed(3)).toExponential() + + ""; + }, + + formatValuesErrorUnits : function(val, error, unit, args) { + var fontSize = 16; + var decimals = 2; + var errorFontSize = 10; + /** line break **/ + var lineBreak = true; + if (args != null) { + if (args.fontSize != null) { + fontSize = args.fontSize; + } + if (args.decimals != null) { + decimals = args.decimals; + } + if (args.errorFontSize != null) { + errorFontSize = args.errorFontSize; + } + if (args.lineBreak != null) { + lineBreak = args.lineBreak; + } + + } + + if (val == "") { + return ""; + } + if (error == null) { + return "" + Number(val).toFixed(decimals) + ""; + } + var html = "" + Number(val).toFixed(decimals) + " " + unit + ""; + if (lineBreak) { + html = html + "
"; + } + return html + " ± " + Number(Number(error).toFixed(8)).toExponential() + + ""; + }, + + formatValuesUnits : function(val, unit, args) { + var fontSize = 12; + var decimals = 2; + var unitsFontSize = 10; + if (args != null) { + if (args.fontSize != null) { + fontSize = args.fontSize; + } + if (args.decimals != null) { + decimals = args.decimals; + } + if (args.unitsFontSize != null) { + unitsFontSize = args.unitsFontSize; + } + + } + + if (val === "") { + return ""; + } + if (unit == null) { + return "" + Number(val).toFixed(decimals) + ""; + } + return "" + Number(val).toFixed(decimals) + " " + unit + ""; + }, + + getGreenButton : function(text, args) { + var width = 70; + var height = 20; + if (args != null) { + if (args.width != null) { + width = args.width; + } + if (args.height != null) { + height = args.height; + } + } + + return ''; + }, +// getBlueButton : function(text, args) { +// var width = 70; +// var height = 20; +// if (args != null) { +// if (args.width != null) { +// width = args.width; +// } +// if (args.height != null) { +// height = args.height; +// } +// } +// +// return ''; +// }, + + getBlueButton : function(text, args) { + var width = 70; + var height = 20; + var href = null; + if (args != null) { + if (args.width != null) { + width = args.width; + } + if (args.height != null) { + height = args.height; + } + if (args.href != null) { + href = args.href; + } + } + if (href != null) { + return ''; + } else { + return ''; +// return ''; + } + }, + + getSubmitGreenButton : function(text) { + return ''; + }, + + getRedButton : function(text) { + return ''; + + }, + getRectangleColorDIV : function(color, height, width) { + return '
'; + }, + + openBufferWindow : function(bufferId) { + var window = new BufferWindow(); + window.draw(tabs.experiment.getBufferById(bufferId), tabs.experiment); + }, + + /** Render for safety levels on grids **/ + safetyRenderer : function(val, y, specimen) { + var color = val; + if (val == "YELLOW") { + color = "#E9AB17"; + } + return '' + val + ''; + }, + + getSampleColor : function() { + return '#CB181D'; + }, + + getLightSampleColor : function() { + return '#FCBBA1'; + }, + + getBufferColor : function() { + return '#6A51A3'; + }, + getLightBufferColor : function() { + return '#BCBDDC'; + }, + + formatConcentration : function(val) { + if (val != null) { + return "" + Number(val).toFixed(2) + " mg/ml "; + } + return val; + }, + + formatVolume : function(sample, volume) { + if (Number(sample.data.volumeToLoad) > Number(sample.data.volume)) { + return "" + volume + " �l"; + } + if (Number(sample.data.volumeToLoad) == Number(sample.data.volume)) { + return "" + volume + " �l"; + } + return "" + volume + " �l"; + }, + + getProposal : function() { + return new Proposal(); + }, + + getSampleNameRenderer : function(val, y, record) { + var sample = record.data; + if (record.raw.macromolecule3VO == null) { + return '' + sample.code + ''; + } else { + return '' + sample.code + ''; + } + }, + + getSafetyLevels : function() { + var safetyLevels = new Array(); + safetyLevels.push({ + safetyLevelId : "", + name : "UNKNOWN" + }); + safetyLevels.push({ + safetyLevelId : 1, + name : "GREEN" + }); + safetyLevels.push({ + safetyLevelId : 2, + name : "YELLOW" + }); + safetyLevels.push({ + safetyLevelId : 3, + name : "RED" + }); + return safetyLevels; + }, + + getErrorColor : function() { + return '#F6CED8'; + }, + getWarningColor : function() { + return '#F5DA81'; + }, + getValidColor : function() { + return '#E0F8E0'; + }, + getSamplePlateLetters : function() { + return [ "A", "B", "C", "D", "E", "F", "G", "H" ]; + }, + getSamplePositionHTML : function(sample, experiment) { + var plate = ""; + var row = ""; + var column = ""; + var rows = this.getSamplePlateLetters(); + if (sample.sampleplateposition3VO != null) { + var samplePlate = experiment.getSamplePlateById(sample.sampleplateposition3VO.samplePlateId); + if (samplePlate != null) { + plate = (samplePlate.slotPositionColumn); + row = (sample.sampleplateposition3VO.rowNumber); + column = (sample.sampleplateposition3VO.columnNumber); + // var html = "Plate: " + "" + plate + ""; + // html = html + ", Row: " + "" + rows[row - 1] + ""; + // html = html + ", Column: " + "" + column + ""; + var html = "Plate: " + plate + + "-" + rows[row - 1] + "" + column + ""; + return html; + } + } + return ""; + }, + + getSafetyLabelName : function(safetyLevelId) { + var safetyLevels = BUI.getSafetyLevels(); + for ( var i = 0; i < safetyLevels.length; i++) { + if (safetyLevels[i].safetyLevelId == safetyLevelId) { + return safetyLevels[i].name; + } + } + return "UNKNOWN"; + }, + /** generate random id **/ + id : function() { + var text = ""; + var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + + for ( var i = 0; i < 5; i++) + text += possible.charAt(Math.floor(Math.random() * possible.length)); + + return text; + }, + showWarning : function(message) { + Ext.Msg.show({ + title : 'Warning', + msg : message, + icon : Ext.Msg.WARNING, + animEl : 'elId' + }); + }, + showError : function(message) { + Ext.Msg.show({ + title : 'Warning', + msg : message, + icon : Ext.Msg.ERROR, + animEl : 'elId' + }); + }, + getTipHTML : function(message) { + //return "
Tip
" + // + message + "
"; + return "
Tip
" + + message + "
"; + }, + + getWarningHTML : function(message) { + return "
Warning
" + + message + "
"; + }, + + getErrorHTML : function(message) { + return "
Error
" + + message + "
"; + }, + + getProgessBar : function(percentage, text) { + /** percentage 100% green **/ + var color = "#0a0"; + + color = "#99CC00"; + if (percentage > 100) { + color = "yellow"; + percentage = 100; + } + if (isNaN(percentage)) { + color = "white"; + percentage = 0; + } + + var defaultText = percentage + "%"; + if (text != null) { + defaultText = text; + } + + return "
" + defaultText + "
"; + }, + getFileName : function(filePath){ + if (filePath != null){ + return filePath.split("/")[filePath.split("/").length - 1] + } + return ""; + } + +}; + +Ext.ux.form.RequiredCombo = Ext.extend(Ext.form.ComboBox, { + config : { + cls : 'custom-field-text-required' + }, + initComponent : function() { + Ext.ux.form.RequiredCombo.superclass.initComponent.apply(this, arguments); + }, + checkChange : function() { + if ((this.getValue() == null) || String(this.getValue()).length == 0) { + this.addCls('custom-field-text-required'); + } else { + this.removeCls('custom-field-text-required'); + } + } +}); + +Ext.define('Ext.form.field.RequiredNumber', { + extend : 'Ext.form.field.Number', + alias : 'widget.requirednumberfield', + alternateClassName : [ 'Ext.form.RequiredNumberField', 'Ext.form.RequiredNumber' ], + config : { + cls : 'custom-field-text-required' + }, + + initComponent : function() { + var me = this; + me.callParent(); + me.setMinValue(me.minValue); + me.setMaxValue(me.maxValue); + }, + + onChange : function() { + if ((this.getValue() == null) || String(this.getValue()).length == 0) { + this.addCls('custom-field-text-required'); + } else { + this.removeCls('custom-field-text-required'); + } + this.toggleSpinners(); + this.callParent(arguments); + } +}); + +Ext.define('Ext.form.field.RequiredText', { + extend : 'Ext.form.field.Text', + alias : 'widget.requiredtext', + requires : [ 'Ext.form.field.VTypes', 'Ext.layout.component.field.Text' ], + alternateClassName : [ 'Ext.form.RequiredTextField', 'Ext.form.RequiredText' ], + config : { + cls : 'custom-field-text-required' + }, + initComponent : function() { + var me = this; + if (me.allowOnlyWhitespace === false) { + me.allowBlank = false; } - }; -}; + me.callParent(); + me.addEvents( + /** + * @event autosize + * Fires when the **{@link #autoSize}** function is triggered and the field is resized according to the + * {@link #grow}/{@link #growMin}/{@link #growMax} configs as a result. This event provides a hook for the + * developer to apply additional logic at runtime to resize the field if needed. + * @param {Ext.form.field.Text} this This text field + * @param {Number} width The new field width + */ + 'autosize', -ConcentrationHTMLTableWidget.prototype.test = function(targetId) { - var concentrationHTMLTableWidget = new ConcentrationHTMLTableWidget(); - document.getElementById(targetId).innerHTML = concentrationHTMLTableWidget.getPanel(concentrationHTMLTableWidget.input().data); -}; - -function DataCollectionWidget() { + /** + * @event keydown + * Keydown input field event. This event only fires if **{@link #enableKeyEvents}** is set to true. + * @param {Ext.form.field.Text} this This text field + * @param {Ext.EventObject} e + */ + 'keydown', + /** + * @event keyup + * Keyup input field event. This event only fires if **{@link #enableKeyEvents}** is set to true. + * @param {Ext.form.field.Text} this This text field + * @param {Ext.EventObject} e + */ + 'keyup', + /** + * @event keypress + * Keypress input field event. This event only fires if **{@link #enableKeyEvents}** is set to true. + * @param {Ext.form.field.Text} this This text field + * @param {Ext.EventObject} e + */ + 'keypress'); + me.addStateEvents('change'); + me.setGrowSizePolicy(); + }, + checkChange : function() { + if ((this.getValue() == null) || String(this.getValue()).length == 0) { + this.addCls('custom-field-text-required'); + } else { + this.removeCls('custom-field-text-required'); + } + } +}); -}; +var BIOSAXS_COMBOMANAGER = { -DataCollectionWidget.prototype.refresh = function(subtractionId) { + getComboMacromoleculeByMacromolecules : function(macromolecules, args) { + var labelWidth = 150; + var margin = "0 0 5 0"; + var width = 300; -}; + if (args != null) { + if (args.labelWidth != null) { + labelWidth = args.labelWidth; + } + if (args.margin != null) { + margin = args.margin; + } + if (args.width != null) { + width = args.width; + } + } -DataCollectionWidget.prototype.getMacromolecule = function(data) { - for (var i = 0; i < data.length; i++) { - if (data[i].macromoleculeId != null) { - return BIOSAXS.proposal.getMacromoleculeById(data[i].macromoleculeId); + var store = Ext.create('Ext.data.Store', { + fields : [ 'macromoleculeId', 'acronym' ], + data : macromolecules, + sorters : [ 'acronym' ] + }); + + return Ext.create('Ext.form.ComboBox', { + fieldLabel : 'Macromolecules', + labelWidth : labelWidth, + width : width, + margin : margin, + store : store, + editable: false, + queryMode : 'local', + displayField : 'acronym', + valueField : 'macromoleculeId' + }); + }, + getComboBuffers : function(buffers, args) { + var labelWidth = 150; + var margin = "0 0 5 0"; + var width = 300; + var fieldLabel = 'Buffer'; + + if (args != null) { + if (args.labelWidth != null) { + labelWidth = args.labelWidth; + } + if (args.margin != null) { + margin = args.margin; + } + if (args.width != null) { + width = args.width; + } + if (args.noLabel != null) { + fieldLabel = null; + } } - } - return null; -}; -DataCollectionWidget.prototype.getMacromoleculeContainer = function(data) { - var macromolecule = this.getMacromolecule(data); + var store = Ext.create('Ext.data.Store', { + fields : [ 'bufferId', 'acronym' ], + data : buffers, + sorters : [ 'acronym' ] + }); - var disabled = false; - if (macromolecule == null) { - disabled = true; - } + return Ext.create('Ext.form.ComboBox', { + fieldLabel : fieldLabel, + labelWidth : labelWidth, + width : width, + margin : margin, + editable: false, + store : store, + queryMode : 'local', + displayField : 'acronym', + valueField : 'bufferId' + }); + }, + getComboSessions : function(sessions, args) { + var labelWidth = 150; + var margin = "0 0 5 0"; + var width = 300; - var acronym = (macromolecule == null ? "" : macromolecule.acronym); - var mm = (macromolecule == null ? "" : macromolecule.molecularMass); - var comments = (macromolecule == null ? "" : macromolecule.comments); + if (args != null) { + if (args.labelWidth != null) { + labelWidth = args.labelWidth; + } + if (args.margin != null) { + margin = args.margin; + } + if (args.width != null) { + width = args.width; + } + } - return Ext.create('Ext.Panel', { - width : 500, - height : 200, - disabled : disabled, - title : "Macromolecule", - layout : 'form', - bodyPadding : 5, - defaultType : 'textfield', - tbar : { - defaultButtonUI : 'default', - items : [ { - xtype : 'button', - text : 'Edit', - cls : 'btn-with-border', - handler : function() { - var macromoleculeWindow = new MacromoleculeWindow(); - macromoleculeWindow.draw(macromolecule); - /** TODO: update when save **/ - } - } ] - }, - items : [ { - fieldLabel : 'Acronym', - name : 'first', - readOnly : true, - value : acronym - }, { - fieldLabel : 'Molecular Mass', - name : 'last', - readOnly : true, - value : mm - }, { - xtype : 'textareafield', - fieldLabel : 'Comments', - value : '', - name : 'last', - readOnly : true, - value : comments - } ] - }); -}; + for ( var i = 0; i < sessions.length; i++) { + sessions[i]["startDateFormatted"] = moment(sessions[i].startDate).format("MMM Do YY"); + sessions[i]["sorter"] = moment(sessions[i].startDate).format("YYYYMMDD"); + } -DataCollectionWidget.prototype.getBufferContainer = function(data) { - var _this = this; - var buffer = BIOSAXS.proposal.getBufferById(data[0].bufferId); + var store = Ext.create('Ext.data.Store', { + fields : [ 'sessionId', 'startDateFormatted', 'beamlineName', 'startDate', 'endDate', 'beamlineOperator' ], + data : sessions, + sorters : [ 'sorter' ] + }); - return Ext.create('Ext.Panel', { - width : 500, - height : 200, - title : "Buffer", - layout : 'form', - // collapsed : true, - // collapsible : true, - bodyPadding : 5, - defaultType : 'textfield', - style : { - padding : '0px 0px 0px 2px' - }, - tbar : { - defaultButtonUI : 'default', - items : [ { - xtype : 'button', - text : 'Edit', - cls : 'btn-with-border', - handler : function() { - var bufferWindow = new BufferWindow(); - bufferWindow.draw(BIOSAXS.proposal.getBufferById(data[0].bufferId)); - /** TODO: update when save **/ - } + return Ext.create('Ext.form.ComboBox', { + fieldLabel : 'Sessions', + labelWidth : labelWidth, + width : width, + margin : margin, + store : store, + queryMode : 'local', + // displayField : 'startDate', + valueField : 'sessionId', + // Template for the dropdown menu. + // Note the use of "x-boundlist-item" class, + // this is required to make the items selectable. + tpl : Ext.create('Ext.XTemplate', '', + '
{startDateFormatted} {beamlineName}
', '
'), + // template for the content inside text field + displayTpl : Ext.create('Ext.XTemplate', '', '{startDateFormatted}', '') - } ] - }, - items : [ { - fieldLabel : 'Buffer', - name : 'acronym', - readOnly : true, - value : buffer.acronym - }, { - fieldLabel : 'Composition', - name : 'last', - readOnly : true, - value : buffer.composition + }); + } +}; + + function GenericWindow(args){ + this.title = "title"; + this.width = 700; + this.height = 500; + this.id = BUI.id(); + + this.close = false; + this.draggable = true; + this.modal = true; + + if (args != null){ + if (args.actions != null){ + this.actions = args.actions; + } + if (args.form != null){ + this.form = args.form; + } + if (args.width != null){ + this.width = args.width; + } + if (args.modal != null){ + this.modal = args.modal; + } + + if (args.height != null){ + this.height = args.height; + } + if (args.title != null){ + this.title = args.title; + } + if (args.form != null){ + this.form = args.form; + } + if (args.close != null){ + this.close = args.close; + } + if (args.draggable != null){ + this.draggable = args.draggable; + } + + } + /** Events **/ + this.onSaved = new Event(this); + }; + + + +GenericWindow.prototype.getButtons = function() { + var _this = this; + + if (this.close){ + return [ { + text : 'Close', + handler : function() { + _this.panel.close(); + } + } ]; + } + else{ + return [ { + text : 'Save', + handler : function() { + _this.save(); + + } }, { - xtype : 'textareafield', - fieldLabel : 'Comments', - value : buffer.comments, - name : 'last', - readOnly : true - - } ] - }); + text : 'Cancel', + handler : function() { + _this.panel.close(); + } + } ]; + } }; -DataCollectionWidget.prototype.getSpecimenContainer = function(data) { - return Ext.create('Ext.container.Container', { - layout : { - type : 'hbox' - }, - style : { - margin : '10px 10px 10px 10px' - }, - border : 0, - style : { - borderColor : '#000000', - borderStyle : 'solid', - borderWidth : '1px' - }, - defaults : { - labelWidth : 80, - // implicitly create Container by specifying xtype - xtype : 'datefield', - flex : 1, - - }, - items : [ this.getMacromoleculeContainer(data), this.getBufferContainer(data) ] - }); +GenericWindow.prototype.save = function (){ + alert("Method save of GenerciWindow class is abstract"); }; -DataCollectionWidget.prototype.getSeparator = function() { - return { - html : "
", - border : 0 - } +GenericWindow.prototype._postRender = function(data, experiment){ + }; -DataCollectionWidget.prototype.getFitStructurePanel = function(data) { - var _this = this; - - return Ext.create('Ext.container.Container', { - layout : { - type : 'hbox' - }, - style : { - margin : '10px 10px 10px 10px' - }, - border : 0, - style : { - borderColor : '#000000', - borderStyle : 'solid', - borderWidth : '1px' - }, - defaults : { - labelWidth : 80, - xtype : 'datefield', - flex : 1 - }, - items : [ - ] - }); +GenericWindow.prototype.draw = function (data, experiment){ + this._render(data, experiment); }; -DataCollectionWidget.prototype.getFitStructurePanelWorkflow = function(data) { - var _this = this; - var macromolecule = this.getMacromolecule(data); - - var items = []; +GenericWindow.prototype.refresh = function(data, experiment){ + this.data = data; + this.experiment = experiment; + this.form.refresh(data, experiment); +}; - /** Adding all the pdb files linked to the macromolecule **/ - // if (macromolecule.structure3VOs != null) { - // if (macromolecule.structure3VOs.length > 0) { - // - // items.push(); - // } - // } else { - // items.push({ - // html : "No apriori information added to this macromolecule. Apriori information needed for further analysis", - // margin : "5 5 5 5" - // }); - // } - var fitStructureToExperimentDataGrid = new FitStructureToExperimentDataGrid(); - - fitStructureToExperimentDataGrid.refresh(_this.data[0].subtractionId, _this.getMacromolecule(data)); - return Ext.create('Ext.container.Container', { - style : { - margin : '10px 10px 10px 10px' - }, - border : 0, +GenericWindow.prototype._render = function(data, experiment){ + this.data = data; + var _this = this; + if (this.panel == null){ + this.panel = Ext.create('Ext.Window', { + id : this.id, + title : this.title, + resizable : true, + constrain : true, + border : 1, + modal : this.modal, + frame : false, + draggable : this.draggable, + closable : true, + autoscroll : true, + layout : { type: 'vbox',align: 'stretch'}, + width : this.width, + height : this.form.height, + buttonAlign :'right', + buttons : this.getButtons(), + items : this.form.getPanel(data, experiment), + listeners: { + scope : this, + minimize : function(){ + this.panel.hide(); + }, + destroy : function(){ + delete this.panel; + } + } + }); + this.panel.setLoading(); + } + + this.panel.show(); + this._postRender(); +}; +function CalendarWidget(args) { + this.height = 740; - style : { - borderColor : '#000000', - borderStyle : 'solid', - borderWidth : '1px' - }, - defaults : { - labelWidth : 80, - xtype : 'datefield', - flex : 1 - }, - items : [ fitStructureToExperimentDataGrid.getPanel() ], - listeners : { - afterrender : function() { - } + if (args != null) { + if (args.height != null) { + this.height = args.height; } - }); -}; -/** Static method **/ -DataCollectionWidget.prototype.RUNFitScattering = function(subtractionId, structureId) { - var _this = this; - /** Add to Workflow **/ - var adapter = new BiosaxsDataAdapter(); - var workflow = { - 'workflowTitle' : 'FitExperimentalDatatoStructure', - 'comments' : 'FitExperimentalDatatoStructure run from ISPyB for subtractionId: ' + subtractionId + ' and structureId ' + structureId } - var inputs = [ { - name : 'subtractionId', - value : subtractionId - }, { - name : 'structureId', - value : structureId - } ]; + this.onClick = new Event(); +} - adapter.onSuccess.attach(function(sender, data) { - /** Add to Fit **/ - var adapter2 = new BiosaxsDataAdapter(); - var fit = { - 'workflowId' : data.workflowId, - 'subtractionId' : subtractionId, - 'structureId' : structureId, - 'comments' : 'Sent to workflow engine' - } - adapter2.onSuccess.attach(function(sender, fit) { +CalendarWidget.prototype.loadData = function(data) { + this.events = []; - }); - adapter2.addFitStructureData(fit); + for ( var i = 0; i < data.length; i++) { + var date = moment(data[i].creationDate); + var textColor = 'black'; - }); - adapter.addWorkflow(workflow, inputs); + if (data[i].status == "FINISHED") { + textColor = 'green'; + } + if (data[i].status == "ABORTED") { + textColor = 'red'; + } + this.events.push({ + title : data[i].name, + start : date.format("YYYY-MM-DD HH:mm:ss"), + end : date.format("YYYY-MM-DD HH:mm:ss"), + date : date, + allDay : false, + color : textColor, + className : date.format("YYYY-MM-DD") + }); + } }; -DataCollectionWidget.prototype.getSuperpositionTab = function(data) { +CalendarWidget.prototype.draw = function(targetId) { var _this = this; + $('#' + targetId).fullCalendar({ + eventClick : function(calEvent, jsEvent, view) { + _this.onClick.notify(calEvent.className[0]); - this.superpositionGrid = new SuperpositionGrid(); - - return Ext.create('Ext.container.Container', { - style : { - margin : '10px 10px 10px 10px' - }, - border : 0, - style : { - borderColor : '#000000', - borderStyle : 'solid', - borderWidth : '1px' - }, - items : [ _this.superpositionGrid.getPanel() ], - listeners : { - cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { - - }, - afterrender : function() { - /** Getting Rigid bodies **/ - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, result) { - _this.superpositionGrid.refresh(result); - }); - adapter.getSuperpositionBySubtractionId(data[1].subtractionId); - } - } - }); -}; - -DataCollectionWidget.prototype.getAdvancedTab = function(data) { - var _this = this; - return Ext.create('Ext.container.Container', { - style : { - margin : '10px 10px 10px 10px' }, - border : 0, - style : { - borderColor : '#000000', - borderStyle : 'solid', - borderWidth : '1px' + contentHeight : _this.height, + header : { + left : 'prev,next today', + center : 'title', + right : 'month,basicWeek,basicDay' }, - items : [ _this.getFitStructurePanel(data), _this.getFitStructurePanelWorkflow(data) ] + editable : false, + events : this.events }); -}; - -DataCollectionWidget.prototype.getRigiBodyForm = function(data) { - var _this = this; - _this.rigiBodyGrid = new RigidModelGrid(); - return _this.rigiBodyGrid.getPanel(); }; + +/** + * It shows a table with the guinier and gnom data as well as passed and + * discarded measurements + * + * @height + * @showBufferColumns + */ +function ConcentrationHTMLTableWidget(args) { + this.id = BUI.id(); -DataCollectionWidget.prototype.getRigidBodyModelingTab = function(data) { - var _this = this; - return Ext.create('Ext.container.Container', { - style : { - margin : '10px 10px 10px 10px' - }, - border : 0, - style : { - borderColor : '#000000', - borderStyle : 'solid', - borderWidth : '1px' - }, - items : [ _this.getRigiBodyForm(data) ], - listeners : { - cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + this.showBufferColumns = true; - }, - afterrender : function() { - /** Getting Rigid bodies **/ - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, result) { - _this.rigiBodyGrid.refresh(result); - }); - adapter.getRigidBodyModelingBySubtractionId(data[1].subtractionId); - } + if (args != null) { + if (args.height != null) { + this.height = args.height; } - }); -}; - -DataCollectionWidget.prototype.getTabs = function(data) { - var _this = this; - - this.subtractionCurveVisualizer = new SubtractionCurveVisualizer(); - - this.tabs = Ext.createWidget('tabpanel', { - activeTab : 1, - plain : true, - defaults : { - autoScroll : true, - bodyPadding : 10 - }, - items : [ { - title : 'Solution', - items : [ _this.getSpecimenContainer(data) ] - }, { - title : 'Primary Data Reduction', - active : true, - tabConfig : { - xtype : 'tab', - margins : '0 0 0 20', - }, - items : [ _this.subtractionCurveVisualizer.getPanel() ] - }, { - title : 'Abinitio', - items : [ _this.getAbinitioModellingContainer(data), _this.getSeparator(), _this.getModelViz(data) ] - }, { - title : 'Mixtures', - items : [ _this.getAdvancedTab(data) ] - }, { - title : 'Superpositions', - items : [ _this.getSuperpositionTab(data) ] - }, { - title : 'Rigid Body Modeling', - items : [ _this.getRigidBodyModelingTab(data) ] - } ] - }); - return this.tabs; -}; + if (args.showBufferColumns != null) { + this.showBufferColumns = args.showBufferColumns; + } + } +} -DataCollectionWidget.prototype.getPanel = function(data) { - var _this = this; - _this.data = data; - this.panel = Ext.create('Ext.container.Container', { - width : 1000, - layout : { - type : 'vbox', - align : 'stretch', - padding : 5 - }, - items : [ _this.getTabs(data) +ConcentrationHTMLTableWidget.prototype.refresh = function(parsedData) { + document.getElementById(this.id).innerHTML = this.getPanel(parsedData); +}; - ] - }); +ConcentrationHTMLTableWidget.prototype.getPanel = function(parsedData) { + var html = "
"; + html = html + ""; - this.panel.on("afterrender", function() { - _this.subtractionCurveVisualizer.refresh([ _this.data[1].mergeId ], [ _this.data[1].subtractionId ]); - }); - return this.panel; -}; + /** Title * */ + html = html + ""; + html = html + ""; + if (this.showBufferColumns) { + html = html + ""; + } + html = html + ""; + html = html + ""; + html = html + ""; + html = html + ""; -DataCollectionWidget.prototype.getModelViz = function(data) { - this.modelViz = new ModelVisualizerForm({ - height : 800, - width : 1000 - }); - return this.modelViz.getPanel(); -}; + html = html + ""; + html = html + ""; + html = html + ""; + html = html + ""; + html = html + ""; + html = html + ""; + html = html + ""; + html = html + ""; + html = html + ""; -/** - * PRIMARY DATA PROCESSING - */ -DataCollectionWidget.prototype.getPrimaryDataProcessingContainer = function(data) { - var _this = this; - this.primaryDataProcessingGrid = new PrimaryDataProcessingGrid({ - height : 250, - width : 1000, - title : 'Primary Data Processing' + parsedData.concentration.concentrations.sort(function(a, b) { + return a.concentration - b.concentration; }); + /** Row * */ + for ( var i = 0; i < parsedData.concentration.concentrations.length; i++) { + var row = parsedData[i]; - this.primaryDataProcessingGrid.onSelected.attach(function(sender, selected) { - /** Refresh tabs **/ - var averagesId = []; - var subtractionIds = []; - - var subtractionKeys = {}; - for (var i = 0; i < selected.length; i++) { - if (selected[i].mergeId != null) { - averagesId.push(selected[i].mergeId); - } + var tr = ""; + if (i % 2 == 1) { + tr = ""; + } + var goodMeasurements = (parsedData.concentration.concentrations[i].frames.length - parsedData.concentration.concentrations[i].frames_warning); + if (goodMeasurements == 0) { + tr = ""; + } + html = html + tr; + html = html + ""; + if (this.showBufferColumns) { + html = html + ""; + } + html = html + ""; + html = html + ""; + html = html + ""; + html = html + ""; + html = html + ""; + html = html + ""; + html = html + ""; + html = html + ""; + } + return html + "
ConcentrationBufferMeasurementsGuinierGnom
PassedDiscardedRgI0QualityRgdMax
" + parsedData.concentration.concentrations[i].concentration + "" + parsedData.concentration.concentrations[i].bufferAcronym + "" + goodMeasurements + " of " + parsedData.concentration.concentrations[i].frames.length + "" + parsedData.concentration.concentrations[i].frames_warning + "" + BUI.formatValuesErrorUnits(parsedData.concentration.concentrations[i].calculation.rgGuinier.avg, parsedData.concentration.concentrations[i].calculation.rgGuinier.std, "nm", { + lineBreak : false + }) + "" + BUI.formatValuesErrorUnits(parsedData.concentration.concentrations[i].calculation.i0Guinier.avg, parsedData.concentration.concentrations[i].calculation.i0Guinier.std, " ", { + lineBreak : false + }) + "" + BUI.formatValuesErrorUnits(parsedData.concentration.concentrations[i].calculation.quality.avg, parsedData.concentration.concentrations[i].calculation.quality.std, " ", { + lineBreak : false + }) + "" + BUI.formatValuesErrorUnits(parsedData.concentration.concentrations[i].calculation.rgGnom.avg, parsedData.concentration.concentrations[i].calculation.rgGnom.std, " ", { + lineBreak : false + }) + "" + BUI.formatValuesErrorUnits(parsedData.concentration.concentrations[i].calculation.dMax.avg, parsedData.concentration.concentrations[i].calculation.dMax.std, " ", { + lineBreak : false + }) + "
"; +}; - if (selected[i].subtractionId != null) { - if (selected[i].macromoleculeId != null) { - if (subtractionKeys[selected[i].subtractionId] == null) { - subtractionIds.push(selected[i].subtractionId); +ConcentrationHTMLTableWidget.prototype.input = function() { + return { + data : { + "dataCollectionCount" : 4, + "buffers" : [ { + "bufferId" : "422" + } ], + "concentration" : { + "concentrations" : [ { + "concentration" : "7.17", + "id" : "7.1699999999999999", + "bufferId" : "422.00", + "bufferAcronym" : "B1", + "rgGuinier" : [ "5.12723" ], + "frames" : [ { + "total" : "0.515705406286", + "bufferBeforeMeasurementId" : 15045, + "sampleMergeId" : 8176, + "averagedModelId" : null, + "I0" : "183.457461646", + "proposalCode" : "OPD", + "averagedModel" : null, + "bufferAfterMergeId" : 8177, + "framesCount" : "10", + "bufferBeforeMergeId" : 8175, + "averageFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_065_ave.dat", + "bufferAfterMeasurementId" : 15048, + "rgGuinier" : "5.12723", + "subtractedFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_065_sub.dat", + "firstPointUsed" : "17", + "chi2RgFilePath" : null, + "abInitioModelId" : null, + "macromoleculeId" : 649, + "code" : "_4.0_7.17", + "transmission" : "100.0", + "timeStart" : "2013-06-05 15:43:54.348723", + "bufferAcronym" : "B1", + "quality" : "0.853011", + "shapeDeterminationModelId" : null, + "expermientComments" : "[BsxCube] Generated from BsxCube", + "bufferId" : 422, + "isagregated" : "False", + "dmax" : "25.63615", + "bufferBeforeAverageFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_064_ave.dat", + "exposureTemperature" : "4.0", + "subtractionId" : 3377, + "conc" : "7.1699999999999999", + "rapidShapeDeterminationModel" : null, + "experimentCreationDate" : "Jun 5, 2013 3:30:30 PM", + "lastPointUsed" : "36", + "modelListId" : null, + "framesMerge" : "9", + "rapidShapeDeterminationModelId" : null, + "bufferAfterAverageFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_066_ave.dat", + "nsdFilePath" : null, + "bufferAfterFramesMerged" : "10", + "experimentId" : 700, + "macromoleculeAcronym" : "L9", + "i0stdev" : "0.139364435146", + "sampleMeasurementId" : 15047, + "measurementComments" : "[5]", + "priorityLevelId" : 10, + "bufferBeforeFramesMerged" : "10", + "substractionCreationTime" : "Jun 5, 2013 3:46:31 PM", + "proposalNumber" : "29", + "rgGnom" : "5.40297914939", + "volume" : "475.302", + "shapeDeterminationModel" : null, + "comments" : null, + "groupeField" : "L9 B1" + } ], + "frames_warning" : 0, + "calculation" : { + "rgGuinier" : { + "std" : 0, + "sum" : 5.12723, + "avg" : 5.12723, + "validNumber" : 1, + "totalNumber" : 1, + "values" : [ "5.12723" ] + }, + "i0Guinier" : { + "std" : 0, + "sum" : 183.457461646, + "avg" : 183.457461646, + "validNumber" : 1, + "totalNumber" : 1, + "values" : [ "183.457461646" ] + }, + "quality" : { + "std" : 0, + "sum" : 0.853011, + "avg" : 0.853011, + "validNumber" : 1, + "totalNumber" : 1, + "values" : [ "0.853011" ] + }, + "rgGnom" : { + "std" : 0, + "sum" : 5.40297914939, + "avg" : 5.40297914939, + "validNumber" : 1, + "totalNumber" : 1, + "values" : [ "5.40297914939" ] + }, + "dMax" : { + "std" : 0, + "sum" : 25.63615, + "avg" : 25.63615, + "validNumber" : 1, + "totalNumber" : 1, + "values" : [ "25.63615" ] + } + } + }, { + "concentration" : "3.53", + "id" : "3.5299999999999998", + "bufferId" : "422.00", + "bufferAcronym" : "B1", + "rgGuinier" : [ "4.38278" ], + "frames" : [ { + "total" : "0.497164741078", + "bufferBeforeMeasurementId" : 15048, + "sampleMergeId" : 8178, + "averagedModelId" : null, + "I0" : "160.023229462", + "proposalCode" : "OPD", + "averagedModel" : null, + "bufferAfterMergeId" : 8179, + "framesCount" : "10", + "bufferBeforeMergeId" : 8177, + "averageFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_067_ave.dat", + "bufferAfterMeasurementId" : 15051, + "rgGuinier" : "4.38278", + "subtractedFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_067_sub.dat", + "firstPointUsed" : "36", + "chi2RgFilePath" : null, + "abInitioModelId" : null, + "macromoleculeId" : 649, + "code" : "_4.0_3.53", + "transmission" : "100.0", + "timeStart" : "2013-06-05 15:47:04.630129", + "bufferAcronym" : "B1", + "quality" : "0.742825", + "shapeDeterminationModelId" : null, + "expermientComments" : "[BsxCube] Generated from BsxCube", + "bufferId" : 422, + "isagregated" : "False", + "dmax" : "15.33973", + "bufferBeforeAverageFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_066_ave.dat", + "exposureTemperature" : "4.0", + "subtractionId" : 3378, + "conc" : "3.5299999999999998", + "rapidShapeDeterminationModel" : null, + "experimentCreationDate" : "Jun 5, 2013 3:30:30 PM", + "lastPointUsed" : "61", + "modelListId" : null, + "framesMerge" : "10", + "rapidShapeDeterminationModelId" : null, + "bufferAfterAverageFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_068_ave.dat", + "nsdFilePath" : null, + "bufferAfterFramesMerged" : "10", + "experimentId" : 700, + "macromoleculeAcronym" : "L9", + "i0stdev" : "0.1499898017", + "sampleMeasurementId" : 15050, + "measurementComments" : "[6]", + "priorityLevelId" : 12, + "bufferBeforeFramesMerged" : "10", + "substractionCreationTime" : "Jun 5, 2013 3:49:42 PM", + "proposalNumber" : "29", + "rgGnom" : "4.40615474861", + "volume" : "372.656", + "shapeDeterminationModel" : null, + "comments" : null, + "groupeField" : "L9 B1" + } ], + "frames_warning" : 0, + "calculation" : { + "rgGuinier" : { + "std" : 0, + "sum" : 4.38278, + "avg" : 4.38278, + "validNumber" : 1, + "totalNumber" : 1, + "values" : [ "4.38278" ] + }, + "i0Guinier" : { + "std" : 0, + "sum" : 160.023229462, + "avg" : 160.023229462, + "validNumber" : 1, + "totalNumber" : 1, + "values" : [ "160.023229462" ] + }, + "quality" : { + "std" : 0, + "sum" : 0.742825, + "avg" : 0.742825, + "validNumber" : 1, + "totalNumber" : 1, + "values" : [ "0.742825" ] + }, + "rgGnom" : { + "std" : 0, + "sum" : 4.40615474861, + "avg" : 4.40615474861, + "validNumber" : 1, + "totalNumber" : 1, + "values" : [ "4.40615474861" ] + }, + "dMax" : { + "std" : 0, + "sum" : 15.33973, + "avg" : 15.33973, + "validNumber" : 1, + "totalNumber" : 1, + "values" : [ "15.33973" ] + } + } + }, { + "concentration" : "1.75", + "id" : "1.75", + "bufferId" : "422.00", + "bufferAcronym" : "B1", + "rgGuinier" : [], + "frames" : [ { + "total" : "0.5538035065", + "bufferBeforeMeasurementId" : 15051, + "sampleMergeId" : 8180, + "averagedModelId" : null, + "I0" : "146.927428571", + "proposalCode" : "OPD", + "averagedModel" : null, + "bufferAfterMergeId" : 8181, + "framesCount" : "10", + "bufferBeforeMergeId" : 8179, + "averageFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_069_ave.dat", + "bufferAfterMeasurementId" : 15054, + "rgGuinier" : "4.27139", + "subtractedFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_069_sub.dat", + "firstPointUsed" : "33", + "chi2RgFilePath" : null, + "abInitioModelId" : null, + "macromoleculeId" : 649, + "code" : "_4.0_1.75", + "transmission" : "100.0", + "timeStart" : "2013-06-05 15:50:09.395824", + "bufferAcronym" : "B1", + "quality" : "0.765999", + "shapeDeterminationModelId" : null, + "expermientComments" : "[BsxCube] Generated from BsxCube", + "bufferId" : 422, + "isagregated" : "False", + "dmax" : "14.949865", + "bufferBeforeAverageFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_068_ave.dat", + "exposureTemperature" : "4.0", + "subtractionId" : 3379, + "conc" : "1.75", + "rapidShapeDeterminationModel" : null, + "experimentCreationDate" : "Jun 5, 2013 3:30:30 PM", + "lastPointUsed" : "62", + "modelListId" : null, + "framesMerge" : "6", + "rapidShapeDeterminationModelId" : null, + "bufferAfterAverageFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_070_ave.dat", + "nsdFilePath" : null, + "bufferAfterFramesMerged" : "10", + "experimentId" : 700, + "macromoleculeAcronym" : "L9", + "i0stdev" : "0.191914285714", + "sampleMeasurementId" : 15053, + "measurementComments" : "[7]", + "priorityLevelId" : 14, + "bufferBeforeFramesMerged" : "10", + "substractionCreationTime" : "Jun 5, 2013 3:52:31 PM", + "proposalNumber" : "29", + "rgGnom" : "4.28379338749", + "volume" : "356.326", + "shapeDeterminationModel" : null, + "comments" : null, + "groupeField" : "L9 B1" + } ], + "frames_warning" : 1, + "calculation" : { + "rgGuinier" : { + "std" : null, + "sum" : 0, + "avg" : 0, + "validNumber" : 0, + "totalNumber" : 0, + "values" : [] + }, + "i0Guinier" : { + "std" : null, + "sum" : 0, + "avg" : 0, + "validNumber" : 0, + "totalNumber" : 0, + "values" : [] + }, + "quality" : { + "std" : null, + "sum" : 0, + "avg" : 0, + "validNumber" : 0, + "totalNumber" : 0, + "values" : [] + }, + "rgGnom" : { + "std" : null, + "sum" : 0, + "avg" : 0, + "validNumber" : 0, + "totalNumber" : 0, + "values" : [] + }, + "dMax" : { + "std" : null, + "sum" : 0, + "avg" : 0, + "validNumber" : 0, + "totalNumber" : 0, + "values" : [] + } + } + }, { + "concentration" : "0.91", + "id" : "0.91000000000000003", + "bufferId" : "422.00", + "bufferAcronym" : "B1", + "rgGuinier" : [], + "frames" : [ { + "total" : null, + "bufferBeforeMeasurementId" : 15054, + "sampleMergeId" : 8182, + "averagedModelId" : null, + "I0" : "0.0", + "proposalCode" : "OPD", + "averagedModel" : null, + "bufferAfterMergeId" : 8183, + "framesCount" : "10", + "bufferBeforeMergeId" : 8181, + "averageFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_071_ave.dat", + "bufferAfterMeasurementId" : 15057, + "rgGuinier" : null, + "subtractedFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_071_sub.dat", + "firstPointUsed" : "0", + "chi2RgFilePath" : null, + "abInitioModelId" : null, + "macromoleculeId" : 649, + "code" : "_4.0_.91", + "transmission" : "100.0", + "timeStart" : "2013-06-05 15:52:58.133705", + "bufferAcronym" : "B1", + "quality" : "0.0", + "shapeDeterminationModelId" : null, + "expermientComments" : "[BsxCube] Generated from BsxCube", + "bufferId" : 422, + "isagregated" : "False", + "dmax" : null, + "bufferBeforeAverageFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_070_ave.dat", + "exposureTemperature" : "4.0", + "subtractionId" : 3380, + "conc" : "0.91000000000000003", + "rapidShapeDeterminationModel" : null, + "experimentCreationDate" : "Jun 5, 2013 3:30:30 PM", + "lastPointUsed" : "0", + "modelListId" : null, + "framesMerge" : "10", + "rapidShapeDeterminationModelId" : null, + "bufferAfterAverageFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_072_ave.dat", + "nsdFilePath" : null, + "bufferAfterFramesMerged" : "10", + "experimentId" : 700, + "macromoleculeAcronym" : "L9", + "i0stdev" : "0.0", + "sampleMeasurementId" : 15056, + "measurementComments" : "[8]", + "priorityLevelId" : 16, + "bufferBeforeFramesMerged" : "10", + "substractionCreationTime" : "Jun 5, 2013 3:55:35 PM", + "proposalNumber" : "29", + "rgGnom" : null, + "volume" : null, + "shapeDeterminationModel" : null, + "comments" : null, + "groupeField" : "L9 B1" + } ], + "frames_warning" : 1, + "calculation" : { + "rgGuinier" : { + "std" : null, + "sum" : 0, + "avg" : 0, + "validNumber" : 0, + "totalNumber" : 0, + "values" : [] + }, + "i0Guinier" : { + "std" : null, + "sum" : 0, + "avg" : 0, + "validNumber" : 0, + "totalNumber" : 0, + "values" : [] + }, + "quality" : { + "std" : null, + "sum" : 0, + "avg" : 0, + "validNumber" : 0, + "totalNumber" : 0, + "values" : [] + }, + "rgGnom" : { + "std" : null, + "sum" : 0, + "avg" : 0, + "validNumber" : 0, + "totalNumber" : 0, + "values" : [] + }, + "dMax" : { + "std" : null, + "sum" : 0, + "avg" : 0, + "validNumber" : 0, + "totalNumber" : 0, + "values" : [] + } } - subtractionKeys[selected[i].subtractionId] = true; - } - } + } ] + }, + "concentrationLabel" : "7.17 mg/ml ,3.53 mg/ml ,1.75 mg/ml ,0.91 mg/ml " } - - /** Refreshing Primary Data Reduction **/ - _this.subtractionCurveVisualizer.refresh(averagesId, subtractionIds); - - }); - return this.primaryDataProcessingGrid.getPanel(data); + }; }; -/** - * getAbinitioModellingContainer - */ -DataCollectionWidget.prototype.getAbinitioModellingContainer = function(data) { - var _this = this; - - this.abinitioGrid = new AbinitioGrid(); - this.abinitioGrid.onSelected.attach(function(sender, models) { - _this.modelViz.refresh(models); - - }); - /** It may be abinitio models linked to the buffers **/ - var abinitioIdList = []; - for (var i = 0; i < data.length; i++) { - abinitioIdList.push(data[i].abInitioId); - } - - var uniqueIds = []; - $.each(abinitioIdList, function(i, el) { - if ($.inArray(el, uniqueIds) === -1) - uniqueIds.push(el); - }); - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, data) { - _this.abinitioGrid.refresh(data); - }); - adapter.getAbinitioByIdsList(uniqueIds); - return this.abinitioGrid.getPanel([]); +ConcentrationHTMLTableWidget.prototype.test = function(targetId) { + var concentrationHTMLTableWidget = new ConcentrationHTMLTableWidget(); + document.getElementById(targetId).innerHTML = concentrationHTMLTableWidget.getPanel(concentrationHTMLTableWidget.input().data); }; +function DataCollectionWidget() { + +}; + +DataCollectionWidget.prototype.refresh = function(subtractionId) { + +}; + +DataCollectionWidget.prototype.getMacromolecule = function(data) { + for (var i = 0; i < data.length; i++) { + if (data[i].macromoleculeId != null) { + return BIOSAXS.proposal.getMacromoleculeById(data[i].macromoleculeId); + } + } + return null; +}; + +DataCollectionWidget.prototype.getMacromoleculeContainer = function(data) { + var macromolecule = this.getMacromolecule(data); + + var disabled = false; + if (macromolecule == null) { + disabled = true; + } + + var acronym = (macromolecule == null ? "" : macromolecule.acronym); + var mm = (macromolecule == null ? "" : macromolecule.molecularMass); + var comments = (macromolecule == null ? "" : macromolecule.comments); + + return Ext.create('Ext.Panel', { + width : 500, + height : 200, + disabled : disabled, + title : "Macromolecule", + layout : 'form', + bodyPadding : 5, + defaultType : 'textfield', + tbar : { + defaultButtonUI : 'default', + items : [ { + xtype : 'button', + text : 'Edit', + cls : 'btn-with-border', + handler : function() { + var macromoleculeWindow = new MacromoleculeWindow(); + macromoleculeWindow.draw(macromolecule); + /** TODO: update when save **/ + } + } ] + }, + items : [ { + fieldLabel : 'Acronym', + name : 'first', + readOnly : true, + value : acronym + }, { + fieldLabel : 'Molecular Mass', + name : 'last', + readOnly : true, + value : mm + }, { + xtype : 'textareafield', + fieldLabel : 'Comments', + value : '', + name : 'last', + readOnly : true, + value : comments + } ] + }); +}; + +DataCollectionWidget.prototype.getBufferContainer = function(data) { + var _this = this; + var buffer = BIOSAXS.proposal.getBufferById(data[0].bufferId); + + return Ext.create('Ext.Panel', { + width : 500, + height : 200, + title : "Buffer", + layout : 'form', + // collapsed : true, + // collapsible : true, + bodyPadding : 5, + defaultType : 'textfield', + style : { + padding : '0px 0px 0px 2px' + }, + tbar : { + defaultButtonUI : 'default', + items : [ { + xtype : 'button', + text : 'Edit', + cls : 'btn-with-border', + handler : function() { + var bufferWindow = new BufferWindow(); + bufferWindow.draw(BIOSAXS.proposal.getBufferById(data[0].bufferId)); + /** TODO: update when save **/ + } + + } ] + }, + items : [ { + fieldLabel : 'Buffer', + name : 'acronym', + readOnly : true, + value : buffer.acronym + }, { + fieldLabel : 'Composition', + name : 'last', + readOnly : true, + value : buffer.composition + }, { + xtype : 'textareafield', + fieldLabel : 'Comments', + value : buffer.comments, + name : 'last', + readOnly : true + + } ] + }); +}; + +DataCollectionWidget.prototype.getSpecimenContainer = function(data) { + return Ext.create('Ext.container.Container', { + layout : { + type : 'hbox' + }, + style : { + margin : '10px 10px 10px 10px' + }, + border : 0, + style : { + borderColor : '#000000', + borderStyle : 'solid', + borderWidth : '1px' + }, + defaults : { + labelWidth : 80, + // implicitly create Container by specifying xtype + xtype : 'datefield', + flex : 1, + + }, + items : [ this.getMacromoleculeContainer(data), this.getBufferContainer(data) ] + }); +}; + +DataCollectionWidget.prototype.getSeparator = function() { + return { + html : "
", + border : 0 + } +}; + +DataCollectionWidget.prototype.getFitStructurePanel = function(data) { + var _this = this; + + return Ext.create('Ext.container.Container', { + layout : { + type : 'hbox' + }, + style : { + margin : '10px 10px 10px 10px' + }, + border : 0, + style : { + borderColor : '#000000', + borderStyle : 'solid', + borderWidth : '1px' + }, + defaults : { + labelWidth : 80, + xtype : 'datefield', + flex : 1 + }, + items : [ + + ] + }); +}; + +DataCollectionWidget.prototype.getFitStructurePanelWorkflow = function(data) { + var _this = this; + var macromolecule = this.getMacromolecule(data); + + var items = []; + + /** Adding all the pdb files linked to the macromolecule **/ + // if (macromolecule.structure3VOs != null) { + // if (macromolecule.structure3VOs.length > 0) { + // + // items.push(); + // } + // } else { + // items.push({ + // html : "No apriori information added to this macromolecule. Apriori information needed for further analysis", + // margin : "5 5 5 5" + // }); + // } + var fitStructureToExperimentDataGrid = new FitStructureToExperimentDataGrid(); + + fitStructureToExperimentDataGrid.refresh(_this.data[0].subtractionId, _this.getMacromolecule(data)); + return Ext.create('Ext.container.Container', { + style : { + margin : '10px 10px 10px 10px' + }, + border : 0, + + style : { + borderColor : '#000000', + borderStyle : 'solid', + borderWidth : '1px' + }, + defaults : { + labelWidth : 80, + xtype : 'datefield', + flex : 1 + }, + items : [ fitStructureToExperimentDataGrid.getPanel() ], + listeners : { + afterrender : function() { + } + } + }); +}; +/** Static method **/ +DataCollectionWidget.prototype.RUNFitScattering = function(subtractionId, structureId) { + var _this = this; + /** Add to Workflow **/ + var adapter = new BiosaxsDataAdapter(); + var workflow = { + 'workflowTitle' : 'FitExperimentalDatatoStructure', + 'comments' : 'FitExperimentalDatatoStructure run from ISPyB for subtractionId: ' + subtractionId + ' and structureId ' + structureId + } + + var inputs = [ { + name : 'subtractionId', + value : subtractionId + }, { + name : 'structureId', + value : structureId + } ]; + + adapter.onSuccess.attach(function(sender, data) { + /** Add to Fit **/ + var adapter2 = new BiosaxsDataAdapter(); + var fit = { + 'workflowId' : data.workflowId, + 'subtractionId' : subtractionId, + 'structureId' : structureId, + 'comments' : 'Sent to workflow engine' + } + adapter2.onSuccess.attach(function(sender, fit) { + + }); + adapter2.addFitStructureData(fit); + + }); + adapter.addWorkflow(workflow, inputs); + +}; + +DataCollectionWidget.prototype.getSuperpositionTab = function(data) { + var _this = this; + + this.superpositionGrid = new SuperpositionGrid(); + + return Ext.create('Ext.container.Container', { + style : { + margin : '10px 10px 10px 10px' + }, + border : 0, + style : { + borderColor : '#000000', + borderStyle : 'solid', + borderWidth : '1px' + }, + items : [ _this.superpositionGrid.getPanel() ], + listeners : { + cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + + }, + afterrender : function() { + /** Getting Rigid bodies **/ + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, result) { + _this.superpositionGrid.refresh(result); + }); + adapter.getSuperpositionBySubtractionId(data[1].subtractionId); + } + } + }); +}; + +DataCollectionWidget.prototype.getAdvancedTab = function(data) { + var _this = this; + return Ext.create('Ext.container.Container', { + style : { + margin : '10px 10px 10px 10px' + }, + border : 0, + style : { + borderColor : '#000000', + borderStyle : 'solid', + borderWidth : '1px' + }, + items : [ _this.getFitStructurePanel(data), _this.getFitStructurePanelWorkflow(data) ] + }); +}; + +DataCollectionWidget.prototype.getRigiBodyForm = function(data) { + var _this = this; + _this.rigiBodyGrid = new RigidModelGrid(); + return _this.rigiBodyGrid.getPanel(); + +}; + +DataCollectionWidget.prototype.getRigidBodyModelingTab = function(data) { + var _this = this; + return Ext.create('Ext.container.Container', { + style : { + margin : '10px 10px 10px 10px' + }, + border : 0, + style : { + borderColor : '#000000', + borderStyle : 'solid', + borderWidth : '1px' + }, + items : [ _this.getRigiBodyForm(data) ], + listeners : { + cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + + }, + afterrender : function() { + /** Getting Rigid bodies **/ + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, result) { + _this.rigiBodyGrid.refresh(result); + }); + adapter.getRigidBodyModelingBySubtractionId(data[1].subtractionId); + } + } + }); +}; + +DataCollectionWidget.prototype.getTabs = function(data) { + var _this = this; + + this.subtractionCurveVisualizer = new SubtractionCurveVisualizer(); + + this.tabs = Ext.createWidget('tabpanel', { + activeTab : 1, + plain : true, + defaults : { + autoScroll : true, + bodyPadding : 10 + }, + items : [ { + title : 'Solution', + items : [ _this.getSpecimenContainer(data) ] + }, { + title : 'Primary Data Reduction', + active : true, + tabConfig : { + xtype : 'tab', + margins : '0 0 0 20', + }, + items : [ _this.subtractionCurveVisualizer.getPanel() ] + }, { + title : 'Abinitio', + items : [ _this.getAbinitioModellingContainer(data), _this.getSeparator(), _this.getModelViz(data) ] + }, { + title : 'Mixtures', + items : [ _this.getAdvancedTab(data) ] + }, { + title : 'Superpositions', + items : [ _this.getSuperpositionTab(data) ] + }, { + title : 'Rigid Body Modeling', + items : [ _this.getRigidBodyModelingTab(data) ] + } ] + }); + return this.tabs; +}; + +DataCollectionWidget.prototype.getPanel = function(data) { + var _this = this; + _this.data = data; + this.panel = Ext.create('Ext.container.Container', { + width : 1000, + layout : { + type : 'vbox', + align : 'stretch', + padding : 5 + }, + items : [ _this.getTabs(data) + + ] + }); + + this.panel.on("afterrender", function() { + _this.subtractionCurveVisualizer.refresh([ _this.data[1].mergeId ], [ _this.data[1].subtractionId ]); + }); + return this.panel; +}; + +DataCollectionWidget.prototype.getModelViz = function(data) { + this.modelViz = new ModelVisualizerForm({ + height : 800, + width : 1000 + }); + return this.modelViz.getPanel(); +}; + +/** + * PRIMARY DATA PROCESSING + */ +DataCollectionWidget.prototype.getPrimaryDataProcessingContainer = function(data) { + var _this = this; + this.primaryDataProcessingGrid = new PrimaryDataProcessingGrid({ + height : 250, + width : 1000, + title : 'Primary Data Processing' + }); + + this.primaryDataProcessingGrid.onSelected.attach(function(sender, selected) { + /** Refresh tabs **/ + var averagesId = []; + var subtractionIds = []; + + var subtractionKeys = {}; + for (var i = 0; i < selected.length; i++) { + if (selected[i].mergeId != null) { + averagesId.push(selected[i].mergeId); + } + + if (selected[i].subtractionId != null) { + if (selected[i].macromoleculeId != null) { + if (subtractionKeys[selected[i].subtractionId] == null) { + subtractionIds.push(selected[i].subtractionId); + } + subtractionKeys[selected[i].subtractionId] = true; + } + } + } + + /** Refreshing Primary Data Reduction **/ + _this.subtractionCurveVisualizer.refresh(averagesId, subtractionIds); + + }); + return this.primaryDataProcessingGrid.getPanel(data); +}; +/** + * getAbinitioModellingContainer + */ +DataCollectionWidget.prototype.getAbinitioModellingContainer = function(data) { + var _this = this; + + this.abinitioGrid = new AbinitioGrid(); + this.abinitioGrid.onSelected.attach(function(sender, models) { + _this.modelViz.refresh(models); + + }); + /** It may be abinitio models linked to the buffers **/ + var abinitioIdList = []; + for (var i = 0; i < data.length; i++) { + abinitioIdList.push(data[i].abInitioId); + } + + var uniqueIds = []; + $.each(abinitioIdList, function(i, el) { + if ($.inArray(el, uniqueIds) === -1) + uniqueIds.push(el); + }); + + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, data) { + _this.abinitioGrid.refresh(data); + }); + adapter.getAbinitioByIdsList(uniqueIds); + return this.abinitioGrid.getPanel([]); +}; + /** * Edit the information of a buffer * @@ -29163,258 +29163,258 @@ ShippingWidget.prototype.refresh = function() { }; -/** - * Widget container of Specimen grid and samplePlate widget - * Depending of the sample changer layout it may be displayed vertically or horizontally - * - * @param args - * - * #onExperimentChanged It happens when specimen are modified - */ -function SpecimenWidget(args){ - - this.width = 1000; - this.height = 600; - - if (args != null){ - if (args.width != null){ - this.width = args.width; - } - if (args.height != null){ - this.height = args.height; - } - } - - var _this = this; - - /** Specimen Grid **/ - this.specimenGrid = new SpecimenGrid({ - minHeight : 425, - selectionMode : "SINGLE", - editEnabled : false, - updateRowEnabled : true, - width : 900, - showTitle : false - }); - - - this.specimenGrid.onSpecimenChanged.attach(function(sender, specimen) { - _this.experiment.setSpecimenById(specimen); - _this.refresh(_this.experiment); - }); - - this.specimenGrid.onSelected.attach(function(sender, specimens) { - if (specimens.length > 0) { - _this.specimenSelected = specimens[0]; - } else { - _this.specimenSelected = null; - } - _this.samplePlateGroupWidget.selectSpecimens(specimens); - }); - - - /** Sample plate Widget **/ - this.samplePlateGroupWidget = new SamplePlateGroupWidget({ - showTitle : false, - height : 250, - margin : 5, - bbar : true - }); - - - this.samplePlateGroupWidget.onExperimentChanged.attach(function(sender, json) { - _this.refresh(new Experiment(json)); - }); - - this.samplePlateGroupWidget.onClick.attach(function(sender, args) { - /** Clicking on a plate * */ - var row = args.row; - var column = args.column; - var samplePlateId = args.samplePlate.samplePlateId; - - /** is specimen selected on the grid? * */ - if (_this.specimenSelected != null) { - /** Is position target empty * */ - if (_this.experiment.getSampleByPosition(args.samplePlate.samplePlateId, args.row, args.column).length == 0) { - var specimen = _this.experiment.getSampleById(_this.specimenSelected.specimenId); - if (specimen.sampleplateposition3VO == null) { - specimen.sampleplateposition3VO = {}; - } - - specimen.sampleplateposition3VO = { - columnNumber : column, - rowNumber : row, - samplePlateId : samplePlateId - }; - - _this.samplePlateGroupWidget.panel.setLoading("ISPyB: Saving specimen"); - var adapter = new BiosaxsDataAdapter(); - /** If success * */ - adapter.onSuccess.attach(function(sender, experiment) { - _this.samplePlateGroupWidget.panel.setLoading(false); - }); - - adapter.onError.attach(function(sender, error) { - _this.samplePlateGroupWidget.panel.setLoading(false); - showError(error); - }); - - adapter.saveSpecimen(specimen, _this.experiment); - - _this.samplePlateGroupWidget.refresh(_this.experiment); - _this.specimenGrid.refresh(_this.experiment); - //_this.refresh(_this.experiment); - _this.specimenGrid.deselectAll(); - - } else { - /** - * Can we merge? We can merge when specimen are the - * same. So, same buffer, macromolecule, concentration * - */ - var target = _this.experiment.getSampleByPosition(args.samplePlate.samplePlateId, args.row, args.column)[0]; - var specimen = _this.experiment.getSampleById(_this.specimenSelected.specimenId); - - if ((specimen.bufferId == target.bufferId) && (specimen.concentration == target.concentration)) { - if (((specimen.macromolecule3VO != null) && (target.macromolecule3VO != null) && (specimen.macromolecule3VO.macromoleculeId == target.macromolecule3VO.macromoleculeId)) || - ((specimen.macromolecule3VO == null) && (target.macromolecule3VO == null))) { - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, data) { - _this.refresh(new Experiment(data)); - _this.samplePlateGroupWidget.panel.setLoading(false); - - _this.onExperimentChanged.notify(experiment); - }); - adapter.onError.attach(function(sender, error) { - _this.samplePlateGroupWidget.panel.setLoading(false); - showError(error); - }); - _this.samplePlateGroupWidget.panel.setLoading("ISPyB: Merging specimens"); - adapter.mergeSpecimens(specimen.specimenId, target.specimenId); - } - } else { - alert("Well is not empty. Select another well!"); - } - } - } else { - var specimen = _this.experiment.getSampleByPosition(args.samplePlate.samplePlateId, args.row, args.column)[0]; - if (specimen != null) { - _this.specimenGrid.selectById(specimen.specimenId); - } - } - }); - - /** Events **/ - this.onExperimentChanged = new Event(this); -}; - -/** - * Return vbox or hbox depending on the slot positions of the plates - */ -SpecimenWidget.prototype.getContainerLayoutConfiguration = function(experiment){ - var dimensions = this.samplePlateGroupWidget.getDimensions(experiment.getSamplePlates()); - if (dimensions.maxSlotPositionRow < dimensions.maxSlotPositionColumn){ - return { - layout : "vbox", - specimenGridWidth : this.width - 10, - specimenGridHeight : this.height - 260, - samplePlateGroupWidth : this.width - 10, - samplePlateGroupHeight : 250 - }; - } - return { - layout : "hbox", - samplePlateGroupWidth : this.width*1/3 -10, - samplePlateGroupHeight : this.height - 10, - specimenGridWidth : this.width*2/3, - specimenGridHeight : this.height - 10 - }; - -}; - - -SpecimenWidget.prototype.refresh = function(experiment){ - this.experiment = experiment; - - /** Removing all components **/ - this.panel.removeAll(); - - var layoutConfiguration = this.getContainerLayoutConfiguration(experiment); - - /** Setting new width and height for layout vbox and hbox **/ - this.specimenGrid.width = layoutConfiguration.specimenGridWidth; - this.specimenGrid.height = layoutConfiguration.specimenGridHeight; - - this.samplePlateGroupWidget.width = layoutConfiguration.samplePlateGroupWidth; - this.samplePlateGroupWidget.height = layoutConfiguration.samplePlateGroupHeight; - - if (layoutConfiguration.layout == "hbox"){ - this.specimenGrid.margin = "0 0 0 5"; - this.specimenGrid.width =this.specimenGrid.width - 5; - } - /** Insert container depending on layout [vertical|horizontal] */ - var container = Ext.create('Ext.container.Container', { - layout : layoutConfiguration.layout, - height : this.height, - width : this.width, - padding : '2px', - items : [ ] - }); - if (layoutConfiguration.layout == "vbox"){ - container.insert(this.specimenGrid.getPanel()); - container.insert(this.samplePlateGroupWidget.getPanel()); - } - else{ - container.insert(this.samplePlateGroupWidget.getPanel()); - container.insert(this.specimenGrid.getPanel()); - } - - /** Insert Widget **/ - this.panel.insert(container); - - /** Load data **/ - this.specimenGrid.refresh(experiment); - this.samplePlateGroupWidget.refresh(experiment); - - -}; - -/** It creates a dummy container to be inserted the plates once the method refresh has been called - * This is necessay because we can not know the sample changer layout before hand - * **/ -SpecimenWidget.prototype.getPanel = function(){ - this.panel = Ext.create('Ext.container.Container', { - layout : 'vbox', - height : this.height, - border : 0, - margin : 5, - width : this.width, - items : [] - }); - return this.panel; -}; - - -SpecimenWidget.prototype.input = function() { - return { - experiment : DATADOC.getExperiment_10(), - proposal : DATADOC.getProposal_10() - }; -}; - -SpecimenWidget.prototype.test = function(targetId) { - var specimenWidget = new SpecimenWidget({ - height : 500, - width : 1000 - }); - BIOSAXS.proposal = new Proposal(specimenWidget.input().proposal); - var experiment = new Experiment(specimenWidget.input().experiment); - var panel = specimenWidget.getPanel(); - panel.render(targetId); - specimenWidget.refresh(experiment); - -}; - - +/** + * Widget container of Specimen grid and samplePlate widget + * Depending of the sample changer layout it may be displayed vertically or horizontally + * + * @param args + * + * #onExperimentChanged It happens when specimen are modified + */ +function SpecimenWidget(args){ + + this.width = 1000; + this.height = 600; + + if (args != null){ + if (args.width != null){ + this.width = args.width; + } + if (args.height != null){ + this.height = args.height; + } + } + + var _this = this; + + /** Specimen Grid **/ + this.specimenGrid = new SpecimenGrid({ + minHeight : 425, + selectionMode : "SINGLE", + editEnabled : false, + updateRowEnabled : true, + width : 900, + showTitle : false + }); + + + this.specimenGrid.onSpecimenChanged.attach(function(sender, specimen) { + _this.experiment.setSpecimenById(specimen); + _this.refresh(_this.experiment); + }); + + this.specimenGrid.onSelected.attach(function(sender, specimens) { + if (specimens.length > 0) { + _this.specimenSelected = specimens[0]; + } else { + _this.specimenSelected = null; + } + _this.samplePlateGroupWidget.selectSpecimens(specimens); + }); + + + /** Sample plate Widget **/ + this.samplePlateGroupWidget = new SamplePlateGroupWidget({ + showTitle : false, + height : 250, + margin : 5, + bbar : true + }); + + + this.samplePlateGroupWidget.onExperimentChanged.attach(function(sender, json) { + _this.refresh(new Experiment(json)); + }); + + this.samplePlateGroupWidget.onClick.attach(function(sender, args) { + /** Clicking on a plate * */ + var row = args.row; + var column = args.column; + var samplePlateId = args.samplePlate.samplePlateId; + + /** is specimen selected on the grid? * */ + if (_this.specimenSelected != null) { + /** Is position target empty * */ + if (_this.experiment.getSampleByPosition(args.samplePlate.samplePlateId, args.row, args.column).length == 0) { + var specimen = _this.experiment.getSampleById(_this.specimenSelected.specimenId); + if (specimen.sampleplateposition3VO == null) { + specimen.sampleplateposition3VO = {}; + } + + specimen.sampleplateposition3VO = { + columnNumber : column, + rowNumber : row, + samplePlateId : samplePlateId + }; + + _this.samplePlateGroupWidget.panel.setLoading("ISPyB: Saving specimen"); + var adapter = new BiosaxsDataAdapter(); + /** If success * */ + adapter.onSuccess.attach(function(sender, experiment) { + _this.samplePlateGroupWidget.panel.setLoading(false); + }); + + adapter.onError.attach(function(sender, error) { + _this.samplePlateGroupWidget.panel.setLoading(false); + showError(error); + }); + + adapter.saveSpecimen(specimen, _this.experiment); + + _this.samplePlateGroupWidget.refresh(_this.experiment); + _this.specimenGrid.refresh(_this.experiment); + //_this.refresh(_this.experiment); + _this.specimenGrid.deselectAll(); + + } else { + /** + * Can we merge? We can merge when specimen are the + * same. So, same buffer, macromolecule, concentration * + */ + var target = _this.experiment.getSampleByPosition(args.samplePlate.samplePlateId, args.row, args.column)[0]; + var specimen = _this.experiment.getSampleById(_this.specimenSelected.specimenId); + + if ((specimen.bufferId == target.bufferId) && (specimen.concentration == target.concentration)) { + if (((specimen.macromolecule3VO != null) && (target.macromolecule3VO != null) && (specimen.macromolecule3VO.macromoleculeId == target.macromolecule3VO.macromoleculeId)) || + ((specimen.macromolecule3VO == null) && (target.macromolecule3VO == null))) { + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, data) { + _this.refresh(new Experiment(data)); + _this.samplePlateGroupWidget.panel.setLoading(false); + + _this.onExperimentChanged.notify(experiment); + }); + adapter.onError.attach(function(sender, error) { + _this.samplePlateGroupWidget.panel.setLoading(false); + showError(error); + }); + _this.samplePlateGroupWidget.panel.setLoading("ISPyB: Merging specimens"); + adapter.mergeSpecimens(specimen.specimenId, target.specimenId); + } + } else { + alert("Well is not empty. Select another well!"); + } + } + } else { + var specimen = _this.experiment.getSampleByPosition(args.samplePlate.samplePlateId, args.row, args.column)[0]; + if (specimen != null) { + _this.specimenGrid.selectById(specimen.specimenId); + } + } + }); + + /** Events **/ + this.onExperimentChanged = new Event(this); +}; + +/** + * Return vbox or hbox depending on the slot positions of the plates + */ +SpecimenWidget.prototype.getContainerLayoutConfiguration = function(experiment){ + var dimensions = this.samplePlateGroupWidget.getDimensions(experiment.getSamplePlates()); + if (dimensions.maxSlotPositionRow < dimensions.maxSlotPositionColumn){ + return { + layout : "vbox", + specimenGridWidth : this.width - 10, + specimenGridHeight : this.height - 260, + samplePlateGroupWidth : this.width - 10, + samplePlateGroupHeight : 250 + }; + } + return { + layout : "hbox", + samplePlateGroupWidth : this.width*1/3 -10, + samplePlateGroupHeight : this.height - 10, + specimenGridWidth : this.width*2/3, + specimenGridHeight : this.height - 10 + }; + +}; + + +SpecimenWidget.prototype.refresh = function(experiment){ + this.experiment = experiment; + + /** Removing all components **/ + this.panel.removeAll(); + + var layoutConfiguration = this.getContainerLayoutConfiguration(experiment); + + /** Setting new width and height for layout vbox and hbox **/ + this.specimenGrid.width = layoutConfiguration.specimenGridWidth; + this.specimenGrid.height = layoutConfiguration.specimenGridHeight; + + this.samplePlateGroupWidget.width = layoutConfiguration.samplePlateGroupWidth; + this.samplePlateGroupWidget.height = layoutConfiguration.samplePlateGroupHeight; + + if (layoutConfiguration.layout == "hbox"){ + this.specimenGrid.margin = "0 0 0 5"; + this.specimenGrid.width =this.specimenGrid.width - 5; + } + /** Insert container depending on layout [vertical|horizontal] */ + var container = Ext.create('Ext.container.Container', { + layout : layoutConfiguration.layout, + height : this.height, + width : this.width, + padding : '2px', + items : [ ] + }); + if (layoutConfiguration.layout == "vbox"){ + container.insert(this.specimenGrid.getPanel()); + container.insert(this.samplePlateGroupWidget.getPanel()); + } + else{ + container.insert(this.samplePlateGroupWidget.getPanel()); + container.insert(this.specimenGrid.getPanel()); + } + + /** Insert Widget **/ + this.panel.insert(container); + + /** Load data **/ + this.specimenGrid.refresh(experiment); + this.samplePlateGroupWidget.refresh(experiment); + + +}; + +/** It creates a dummy container to be inserted the plates once the method refresh has been called + * This is necessay because we can not know the sample changer layout before hand + * **/ +SpecimenWidget.prototype.getPanel = function(){ + this.panel = Ext.create('Ext.container.Container', { + layout : 'vbox', + height : this.height, + border : 0, + margin : 5, + width : this.width, + items : [] + }); + return this.panel; +}; + + +SpecimenWidget.prototype.input = function() { + return { + experiment : DATADOC.getExperiment_10(), + proposal : DATADOC.getProposal_10() + }; +}; + +SpecimenWidget.prototype.test = function(targetId) { + var specimenWidget = new SpecimenWidget({ + height : 500, + width : 1000 + }); + BIOSAXS.proposal = new Proposal(specimenWidget.input().proposal); + var experiment = new Experiment(specimenWidget.input().experiment); + var panel = specimenWidget.getPanel(); + panel.render(targetId); + specimenWidget.refresh(experiment); + +}; + + function WarningWidget(args) { this.actions = []; diff --git a/ispyb-ws/pom.xml b/ispyb-ws/pom.xml index ffe75402e..e3f4f6072 100644 --- a/ispyb-ws/pom.xml +++ b/ispyb-ws/pom.xml @@ -38,7 +38,6 @@ javax.ejb javax.ejb-api 3.2 - provided @@ -136,4 +135,4 @@ - + \ No newline at end of file From a6663f27e0ab4c6ce248dbd4cf9dca86690b58c4 Mon Sep 17 00:00:00 2001 From: Olof Svensson Date: Fri, 22 Sep 2017 09:43:44 +0200 Subject: [PATCH 02/38] Issue #174: Added MXPressP_SAD to LIST_EXPERIMENT_KIND_ESRF --- ispyb-ejb/src/main/java/ispyb/common/util/Constants.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ispyb-ejb/src/main/java/ispyb/common/util/Constants.java b/ispyb-ejb/src/main/java/ispyb/common/util/Constants.java index 5f9495e1f..f7be66d07 100644 --- a/ispyb-ejb/src/main/java/ispyb/common/util/Constants.java +++ b/ispyb-ejb/src/main/java/ispyb/common/util/Constants.java @@ -875,7 +875,7 @@ public static String getSAXSBeamline() { // public static final String[] LIST_EXPERIMENT_KIND_ESRF = { "Default", "MXPressE", "MXPressO", "MXPressE_SAD", "MXScore", "MXPressM","OSC", "SAD", // "MAD", "Fixed", "Ligand binding", "Refinement", "MAD - Inverse Beam", "SAD - Inverse Beam" }; - public static final String[] LIST_EXPERIMENT_KIND_ESRF = { "Default", "MXPressE", "MXPressO", "MXPressI", "MXPressE_SAD", "MXScore", "MXPressM", "MXPressP" }; + public static final String[] LIST_EXPERIMENT_KIND_ESRF = { "Default", "MXPressE", "MXPressO", "MXPressI", "MXPressE_SAD", "MXScore", "MXPressM", "MXPressP", "MXPressP_SAD" }; public static final String[] LIST_EXPERIMENT_KIND = (SITE_IS_ESRF()) ? LIST_EXPERIMENT_KIND_ESRF : LIST_EXPERIMENT_KIND_MAXIV; From 4e7a78716593738a5058d99fbc327614e6e0738a Mon Sep 17 00:00:00 2001 From: Olof Svensson Date: Fri, 22 Sep 2017 09:45:27 +0200 Subject: [PATCH 03/38] Update of release notes for issue #174 --- ispyb-ui/src/main/webapp/help/release_notes.txt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ispyb-ui/src/main/webapp/help/release_notes.txt b/ispyb-ui/src/main/webapp/help/release_notes.txt index ca4a293d1..fd87424b4 100644 --- a/ispyb-ui/src/main/webapp/help/release_notes.txt +++ b/ispyb-ui/src/main/webapp/help/release_notes.txt @@ -1,3 +1,9 @@ +************** +* 22.09.2017 * +************** + +- fixes: #174 + ************** * 12.09.2017 * ************** From fcb77f682b02b38c730bde03ba076bac3f236068 Mon Sep 17 00:00:00 2001 From: Alejandro De Maria Antolinos Date: Fri, 6 Oct 2017 12:11:42 +0200 Subject: [PATCH 04/38] Added index to README --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 3631d405f..ca45cc71a 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,11 @@ # ISPyB Project [![Build Status](https://travis-ci.org/antolinos/ispyb.png)](https://travis-ci.org/antolinos/ispyb) +- [Installing](#Installing) +- [Versioning](#Versioning) +- [Database creation and update](#Database creation and update) +- [Database schema](#Database schema) + ## Installing 1. Clone or fork the ISPyB repository and then clone it by typing: From 140fab907fa05028474d758c8d4e3d7d1d73549b Mon Sep 17 00:00:00 2001 From: Alejandro De Maria Antolinos Date: Fri, 6 Oct 2017 12:12:35 +0200 Subject: [PATCH 05/38] Added index to README --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index ca45cc71a..df0655767 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# ISPyB Project [![Build Status](https://travis-ci.org/antolinos/ispyb.png)](https://travis-ci.org/antolinos/ispyb) +ISPyB Project [![Build Status](https://travis-ci.org/antolinos/ispyb.png)](https://travis-ci.org/antolinos/ispyb) - [Installing](#Installing) @@ -6,7 +6,7 @@ - [Database creation and update](#Database creation and update) - [Database schema](#Database schema) -## Installing +# Installing 1. Clone or fork the ISPyB repository and then clone it by typing: ``` From cd9c6eee33f86e050baee79c563654d6797e9d66 Mon Sep 17 00:00:00 2001 From: Alejandro De Maria Antolinos Date: Fri, 6 Oct 2017 12:13:45 +0200 Subject: [PATCH 06/38] Added index to README --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index df0655767..3649a9ca2 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@ -ISPyB Project [![Build Status](https://travis-ci.org/antolinos/ispyb.png)](https://travis-ci.org/antolinos/ispyb) +# ISPyB Project [![Build Status](https://travis-ci.org/antolinos/ispyb.png)](https://travis-ci.org/antolinos/ispyb) -- [Installing](#Installing) -- [Versioning](#Versioning) -- [Database creation and update](#Database creation and update) -- [Database schema](#Database schema) +1. [Installing](#Installing) +2. [Versioning](#Versioning) +3. [Database creation and update](#Database creation and update) +4. [Database schema](#Database schema) # Installing From fdedb062cfe0d9de600115ccac60b4948a96c3d9 Mon Sep 17 00:00:00 2001 From: Alejandro De Maria Antolinos Date: Fri, 6 Oct 2017 12:15:01 +0200 Subject: [PATCH 07/38] Added index to README --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 3649a9ca2..a8cb48fcc 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@ # ISPyB Project [![Build Status](https://travis-ci.org/antolinos/ispyb.png)](https://travis-ci.org/antolinos/ispyb) -1. [Installing](#Installing) -2. [Versioning](#Versioning) -3. [Database creation and update](#Database creation and update) -4. [Database schema](#Database schema) +1. [Installing](#installing) +2. [Versioning](#versioning) +3. [Database creation and update](#database-creation-and-update) +4. [Database schema](#database-schema) # Installing From 76f9e775b224f2f6905bba2fed9ee2747171228d Mon Sep 17 00:00:00 2001 From: Alejandro De Maria Antolinos Date: Fri, 6 Oct 2017 16:19:28 +0200 Subject: [PATCH 08/38] Added index to README --- README.md | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/README.md b/README.md index a8cb48fcc..b65360ba2 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,7 @@ 2. [Versioning](#versioning) 3. [Database creation and update](#database-creation-and-update) 4. [Database schema](#database-schema) +5. [Graylog on Widlfly 8.2](#graylog) # Installing @@ -152,3 +153,41 @@ Please do not forget to update the database schema : https://github.com/ispyb/ISPyB/blob/master/documentation/database/ISPyB_DataModel_5.mwb This schema can be updated using MySQLWorkbench (free tool from MySQL). + + +### Graylog on wildfly 8.2 + +Download [biz.paluch.logging](http://logging.paluch.biz) that provides logging to logstash using the Graylog Extended Logging Format (GELF) 1.0 and 1.1. + +Then create a custom handler on standalone.xml +``` + + + ... + + + + + + + + + + + ... + + + + + + + +``` + +I had some problems with the unvalid messages because of timestapPatter. It was fixed by using: +``` + +``` + + + From 8a29d10f2548ec22af84f895d4b6b0f6c300d1e8 Mon Sep 17 00:00:00 2001 From: Alejandro De Maria Antolinos Date: Fri, 6 Oct 2017 16:20:21 +0200 Subject: [PATCH 09/38] Added index to README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b65360ba2..7d8bdd6cd 100644 --- a/README.md +++ b/README.md @@ -155,7 +155,7 @@ Please do not forget to update the database schema : This schema can be updated using MySQLWorkbench (free tool from MySQL). -### Graylog on wildfly 8.2 +### Graylog Download [biz.paluch.logging](http://logging.paluch.biz) that provides logging to logstash using the Graylog Extended Logging Format (GELF) 1.0 and 1.1. From 4a4b6b566fd96263c36630f528f112d37d39d0a5 Mon Sep 17 00:00:00 2001 From: Alejandro De Maria Antolinos Date: Mon, 9 Oct 2017 09:54:40 +0200 Subject: [PATCH 10/38] Organizing imports --- .../java/ispyb/ws/soap/mx/ToolsForAutoprocessingWebService.java | 1 - 1 file changed, 1 deletion(-) diff --git a/ispyb-ws/src/main/java/ispyb/ws/soap/mx/ToolsForAutoprocessingWebService.java b/ispyb-ws/src/main/java/ispyb/ws/soap/mx/ToolsForAutoprocessingWebService.java index c37fd0ab0..3d5a9c407 100644 --- a/ispyb-ws/src/main/java/ispyb/ws/soap/mx/ToolsForAutoprocessingWebService.java +++ b/ispyb-ws/src/main/java/ispyb/ws/soap/mx/ToolsForAutoprocessingWebService.java @@ -102,7 +102,6 @@ import javax.jws.soap.SOAPBinding; import javax.jws.soap.SOAPBinding.Style; import javax.naming.NamingException; -import javax.ws.rs.core.Response; import org.apache.log4j.Logger; import org.jboss.ejb3.annotation.SecurityDomain; From 4e8c9e5ee8e49f3914544b43acb2c488206b7a2b Mon Sep 17 00:00:00 2001 From: delageniere Date: Mon, 9 Oct 2017 11:05:46 +0200 Subject: [PATCH 11/38] min file updated --- .../src/main/webapp/js/ispyb/min/ispyb-bx.js | 42726 ++++++++-------- 1 file changed, 21363 insertions(+), 21363 deletions(-) diff --git a/ispyb-ui/src/main/webapp/js/ispyb/min/ispyb-bx.js b/ispyb-ui/src/main/webapp/js/ispyb/min/ispyb-bx.js index a98a06520..23e07209a 100644 --- a/ispyb-ui/src/main/webapp/js/ispyb/min/ispyb-bx.js +++ b/ispyb-ui/src/main/webapp/js/ispyb/min/ispyb-bx.js @@ -38,321 +38,321 @@ Event.prototype = { } }; -function GenericGraph(args) { - this.width = 600; - this.height = 400; - - this.targetId = null; - /** Free spaces in the borders * */ - this.top = 10; - this.left = 10; - this.bottom = 50; - this.right = 40; - - /** Ruler * */ - this.rulerHeight = 50; - this.rulerWidth = 50; - this.rulerStroke = 2; - this.rulerVerticalMarksNumber = 5; - - this.rulerMaxValue = null; - this.rulerMinValue = null; - - /** plot options * */ - this.plotPoints = true; - this.pointRadius = 2; - this.fillOpacityPoint = 0.2; - this.strokeOpacityPoint = 0.2; - - /** Cluster titles * */ - this.clusterTitleHeight = 20; - this.interClassesSpace = 2; - this.interClustersSpace = 4; - this.fontSize = 7; - - /** - * If true classes and title will be rendender in the rule otherwise will be - * integer values - */ - this.plotHorizontalByCluster = true; - - if (args != null) { - if (args.targetId != null) { - this.targetId = args.targetId; - } - if (args.plotHorizontalByCluster != null) { - this.plotHorizontalByCluster = args.plotHorizontalByCluster; - } - if (args.height != null) { - this.height = args.height; - } - if (args.width != null) { - this.width = args.width; - } - if (args.rulerMinValue != null) { - this.rulerMinValue = args.rulerMinValue; - } - if (args.rulerMaxValue != null) { - this.rulerMaxValue = args.rulerMaxValue; - } - if (args.rulerHeight != null) { - this.rulerHeight = args.rulerHeight; - } - - } -} - -GenericGraph.prototype.calculate = function(data) { - var result = {}; - - var checked = this.cleanArray(data); - - /** sorting array * */ - checked.sort(function(a, b) { - return a - b; - }); - - var median = this.getMedian(checked); - - result.median = median; - result.Q1 = Number(this.getQ1(checked)); - result.Q2 = Number(this.getQ2(checked)); - result.Q3 = Number(this.getQ3(checked)); - result.population = checked; - - result.IQR = Number(result.Q3 - result.Q1); - result.outlier_below_limit = result.Q1 - (1.5 * result.IQR); - result.outlier_above_limit = result.Q3 + (1.5 * result.IQR); - result.below_outliers = this.getBelowOutliers(result.outlier_below_limit, checked); - result.above_outliers = this.getAboveOutliers(result.outlier_above_limit, checked); - - result.min_whisker = this.minValueWhisker(result.outlier_below_limit, result.Q1, checked); - result.max_whisker = this.maxValueWhisker(result.outlier_above_limit, result.Q3, checked); - - return result; -}; - -GenericGraph.prototype.drawSVGVerticalText = function(x, y, text, properties) { - var transform = [ "transform", "translate(" + x + ", " + y + "), rotate(-90) " ]; - properties.push(transform); - SVG.drawText(0, 0, text, this.svg, properties); -}; - -/** Plot the numbers on the axis * */ -GenericGraph.prototype.plotRuler = function(rulerProperties) { - - SVG.drawRectangle(rulerProperties.vertical.x, rulerProperties.vertical.y, rulerProperties.vertical.width, rulerProperties.vertical.height, - this.svg, [ [ "fill", "black" ] ]); - var distance = rulerProperties.vertical.height / (this.rulerVerticalMarksNumber + 1); - for ( var i = 0; i <= this.rulerVerticalMarksNumber + 1; i++) { - var deltaHeight = distance * i; - var aux = rulerProperties.vertical.height - deltaHeight; - - var text = Number((aux / rulerProperties.vertical.height) * (rulerProperties.maxPoint - rulerProperties.minPoint) + rulerProperties.minPoint) - .toFixed(3); - - SVG.drawLine(rulerProperties.horizontal.x, this.top + deltaHeight, rulerProperties.horizontal.x - rulerProperties.markWidth, - this.top + deltaHeight, this.svg, [ [ "stroke", "black" ] ]); - SVG.drawText(this.left, this.top + distance * i + 5, text, this.svg, [ [ "style", "font-size:xx-small" ] ]); - /** Drawing the mark up to the end * */ - SVG.drawLine(rulerProperties.horizontal.x, this.top + deltaHeight, this.width - this.right, this.top + deltaHeight, this.svg, [ - [ "stroke", rulerProperties.markColor ], [ "stroke-width", "0.3" ] ]); - - if (i == this.rulerVerticalMarksNumber + 1) { - SVG.drawLine(rulerProperties.horizontal.x, this.top + deltaHeight, this.width - this.right, this.top + deltaHeight, this.svg, [ [ - "stroke", rulerProperties.markColor ] ]); - } - } - - /** Drawing horizontal rulers * */ - if (!this.plotHorizontalByCluster) { - var width = rulerProperties.horizontal.width; - var ratio = width / (rulerProperties.horizontal.xValues.range); - for ( i = 0; i < rulerProperties.horizontal.xValues.values.length; i++) { - var coorX = rulerProperties.horizontal.xValues.values[i] - rulerProperties.horizontal.xValues.min; - SVG.drawText(rulerProperties.horizontal.x + coorX * ratio, this.height - (this.bottom + (this.clusterTitleHeight)), - rulerProperties.horizontal.xValues.values[i], this.svg, [ [ "style", "font-size:small" ] ]); - } - } -}; - -GenericGraph.prototype.plotAxes = function(properties) { - /** - * Drawing canvas plot-free spaces SVG.drawRectangle(0, this.top, this.left, - * this.height, this.svg, [["fill", "pink"]]); SVG.drawRectangle(this.width - - * this.right, this.top, plot.width, plot.height, this.svg, [["fill", - * "pink"]]); SVG.drawRectangle(0, 0, this.width, this.top, this.svg, - * [["fill", "red"]]); SVG.drawRectangle(0, this.height - this.bottom, - * this.width, this.bottom, this.svg, [["fill", "red"]]); - */ - - /** Drawing ruler Space * */ - this.plotRuler({ - minPoint : Number(properties.minPoint), - maxPoint : Number(properties.maxPoint), - markColor : "black", - markWidth : 20, - vertical : { - x : this.left + this.rulerWidth - this.rulerStroke, - y : this.top, - width : this.rulerStroke, - height : this.height - (this.top + this.bottom + this.clusterTitleHeight) - this.rulerHeight - }, - horizontal : { - x : this.left + this.rulerWidth - this.rulerStroke, - y : this.height - (this.bottom + this.clusterTitleHeight + this.rulerHeight), - width : properties.width, - height : this.rulerStroke, - xValues : properties.xValues - } - }); - -}; - -/** Remove nulls and NaN elements in the array * */ -GenericGraph.prototype.cleanArray = function(data) { - var checked = []; - - /** checking data are numbers * */ - for ( var i = 0; i < data.length; i++) { - if (data[i] != null) { - if (!isNaN(data[i])) { - checked.push(data[i]); - } - } - } - return checked; -}; - -GenericGraph.prototype.refresh = function(data) { - document.getElementById(this.targetId).innerHTML = ""; - this.draw(this.targetId, data); -}; - -GenericGraph.prototype.getClassColor = function(className) { - for ( var i = 0; i < this.data.clusters.length; i++) { - var cluster = this.data.clusters[i]; - for ( var j = 0; j < cluster.classes.length; j++) { - var classes = cluster.classes[j]; - if (classes.name == className) { - if (classes.color != null) { - return classes.color; - } - } - } - } - return "black"; -}; - -GenericGraph.prototype.getDimensions = function(data) { - var results = {}; - var points = []; - - this.data = data; - var classesNumber = 0; - var xValues = []; - - for ( var i = 0; i < data.clusters.length; i++) { - var cluster = data.clusters[i]; - if (!this.plotHorizontalByCluster) { - xValues.push(data.clusters[i].x); - } - for ( var j = 0; j < cluster.classes.length; j++) { - var classes = cluster.classes[j]; - points = points.concat(classes.values); - classesNumber = classesNumber + 1; - } - } - - var checked = this.cleanArray(points); - - checked.sort(function(a, b) { - return a - b; - }); - - results.minPoint = checked[0]; - if (this.rulerMinValue != null) { - results.minPoint = this.rulerMinValue; - } - results.maxPoint = checked[checked.length - 1]; - if (this.rulerMaxValue != null) { - results.maxPoint = this.rulerMaxValue; - } - - results.classesNumber = classesNumber; - results.clusterNumber = data.clusters.length; - - var totalInterClassesFreeSpace = (classesNumber - data.clusters.length) * this.interClassesSpace; - var totalInterClusterFreeSpace = (data.clusters.length) * this.interClustersSpace; - results.classWidth = (this.width - (this.rulerWidth + this.left + this.right + totalInterClassesFreeSpace + totalInterClusterFreeSpace))/ classesNumber; - results.width = this.width - (this.right + this.left) - this.rulerWidth + this.rulerStroke; - - results.xValues = {}; - - xValues.sort(function(a, b) { - return a - b; - }); - results.xValues.values = xValues; - if (xValues.length > 0) { - results.xValues.min = xValues[0]; - results.xValues.max = xValues[xValues.length - 1]; - results.xValues.range = results.xValues.max - results.xValues.min; - } - return results; - -}; - -GenericGraph.prototype.draw = function(targetId, data) { - this.targetId = targetId; - var properties = (this.getDimensions(data)); - this.svg = SVG.createSVGCanvas(document.getElementById(this.targetId), [ [ "width", this.width ], [ "height", this.height ] ]); - this.plotAxes(properties); -}; - -GenericGraph.prototype.getMedian = function(checked) { - /** Calculating median * */ - if (checked.length % 2 == 1) { - return checked[Math.floor(checked.length / 2)]; - } else { - return (Number(checked[(checked.length / 2) - 1]) + Number(checked[checked.length / 2])) / 2; - } -}; - -GenericGraph.prototype.pointToPixel = function(value, boxProperties) { - var ratio = (value - boxProperties.minPoint) / (boxProperties.maxPoint - boxProperties.minPoint); - var pixelLength = boxProperties.height - boxProperties.y + this.top; - return (-1 * ratio) * (pixelLength) + (boxProperties.y + boxProperties.height); -}; - -GenericGraph.prototype.maxValueWhisker = function(aboveLimit, q3, array) { - var points = []; - for ( var i = 0; i < array.length; i++) { - if ((array[i] > q3) && (array[i]) <= aboveLimit) { - points.push(array[i]); - } - } - if (points.length > 0) { - points.sort(function(a, b) { - return a - b; - }); - return points[points.length - 1]; - } - return null; -}; - -GenericGraph.prototype.input = function() { - return DATADOC.getBoxWhikerData(); -}; - -GenericGraph.prototype.test = function(targetId) { - var plot = new GenericGraph({ - targetId : targetId, - height : 350, - width : 450, - maxBoxWidth : 25 - }); - plot.refresh(this.input()); -}; +function GenericGraph(args) { + this.width = 600; + this.height = 400; + + this.targetId = null; + /** Free spaces in the borders * */ + this.top = 10; + this.left = 10; + this.bottom = 50; + this.right = 40; + + /** Ruler * */ + this.rulerHeight = 50; + this.rulerWidth = 50; + this.rulerStroke = 2; + this.rulerVerticalMarksNumber = 5; + + this.rulerMaxValue = null; + this.rulerMinValue = null; + + /** plot options * */ + this.plotPoints = true; + this.pointRadius = 2; + this.fillOpacityPoint = 0.2; + this.strokeOpacityPoint = 0.2; + + /** Cluster titles * */ + this.clusterTitleHeight = 20; + this.interClassesSpace = 2; + this.interClustersSpace = 4; + this.fontSize = 7; + + /** + * If true classes and title will be rendender in the rule otherwise will be + * integer values + */ + this.plotHorizontalByCluster = true; + + if (args != null) { + if (args.targetId != null) { + this.targetId = args.targetId; + } + if (args.plotHorizontalByCluster != null) { + this.plotHorizontalByCluster = args.plotHorizontalByCluster; + } + if (args.height != null) { + this.height = args.height; + } + if (args.width != null) { + this.width = args.width; + } + if (args.rulerMinValue != null) { + this.rulerMinValue = args.rulerMinValue; + } + if (args.rulerMaxValue != null) { + this.rulerMaxValue = args.rulerMaxValue; + } + if (args.rulerHeight != null) { + this.rulerHeight = args.rulerHeight; + } + + } +} + +GenericGraph.prototype.calculate = function(data) { + var result = {}; + + var checked = this.cleanArray(data); + + /** sorting array * */ + checked.sort(function(a, b) { + return a - b; + }); + + var median = this.getMedian(checked); + + result.median = median; + result.Q1 = Number(this.getQ1(checked)); + result.Q2 = Number(this.getQ2(checked)); + result.Q3 = Number(this.getQ3(checked)); + result.population = checked; + + result.IQR = Number(result.Q3 - result.Q1); + result.outlier_below_limit = result.Q1 - (1.5 * result.IQR); + result.outlier_above_limit = result.Q3 + (1.5 * result.IQR); + result.below_outliers = this.getBelowOutliers(result.outlier_below_limit, checked); + result.above_outliers = this.getAboveOutliers(result.outlier_above_limit, checked); + + result.min_whisker = this.minValueWhisker(result.outlier_below_limit, result.Q1, checked); + result.max_whisker = this.maxValueWhisker(result.outlier_above_limit, result.Q3, checked); + + return result; +}; + +GenericGraph.prototype.drawSVGVerticalText = function(x, y, text, properties) { + var transform = [ "transform", "translate(" + x + ", " + y + "), rotate(-90) " ]; + properties.push(transform); + SVG.drawText(0, 0, text, this.svg, properties); +}; + +/** Plot the numbers on the axis * */ +GenericGraph.prototype.plotRuler = function(rulerProperties) { + + SVG.drawRectangle(rulerProperties.vertical.x, rulerProperties.vertical.y, rulerProperties.vertical.width, rulerProperties.vertical.height, + this.svg, [ [ "fill", "black" ] ]); + var distance = rulerProperties.vertical.height / (this.rulerVerticalMarksNumber + 1); + for ( var i = 0; i <= this.rulerVerticalMarksNumber + 1; i++) { + var deltaHeight = distance * i; + var aux = rulerProperties.vertical.height - deltaHeight; + + var text = Number((aux / rulerProperties.vertical.height) * (rulerProperties.maxPoint - rulerProperties.minPoint) + rulerProperties.minPoint) + .toFixed(3); + + SVG.drawLine(rulerProperties.horizontal.x, this.top + deltaHeight, rulerProperties.horizontal.x - rulerProperties.markWidth, + this.top + deltaHeight, this.svg, [ [ "stroke", "black" ] ]); + SVG.drawText(this.left, this.top + distance * i + 5, text, this.svg, [ [ "style", "font-size:xx-small" ] ]); + /** Drawing the mark up to the end * */ + SVG.drawLine(rulerProperties.horizontal.x, this.top + deltaHeight, this.width - this.right, this.top + deltaHeight, this.svg, [ + [ "stroke", rulerProperties.markColor ], [ "stroke-width", "0.3" ] ]); + + if (i == this.rulerVerticalMarksNumber + 1) { + SVG.drawLine(rulerProperties.horizontal.x, this.top + deltaHeight, this.width - this.right, this.top + deltaHeight, this.svg, [ [ + "stroke", rulerProperties.markColor ] ]); + } + } + + /** Drawing horizontal rulers * */ + if (!this.plotHorizontalByCluster) { + var width = rulerProperties.horizontal.width; + var ratio = width / (rulerProperties.horizontal.xValues.range); + for ( i = 0; i < rulerProperties.horizontal.xValues.values.length; i++) { + var coorX = rulerProperties.horizontal.xValues.values[i] - rulerProperties.horizontal.xValues.min; + SVG.drawText(rulerProperties.horizontal.x + coorX * ratio, this.height - (this.bottom + (this.clusterTitleHeight)), + rulerProperties.horizontal.xValues.values[i], this.svg, [ [ "style", "font-size:small" ] ]); + } + } +}; + +GenericGraph.prototype.plotAxes = function(properties) { + /** + * Drawing canvas plot-free spaces SVG.drawRectangle(0, this.top, this.left, + * this.height, this.svg, [["fill", "pink"]]); SVG.drawRectangle(this.width - + * this.right, this.top, plot.width, plot.height, this.svg, [["fill", + * "pink"]]); SVG.drawRectangle(0, 0, this.width, this.top, this.svg, + * [["fill", "red"]]); SVG.drawRectangle(0, this.height - this.bottom, + * this.width, this.bottom, this.svg, [["fill", "red"]]); + */ + + /** Drawing ruler Space * */ + this.plotRuler({ + minPoint : Number(properties.minPoint), + maxPoint : Number(properties.maxPoint), + markColor : "black", + markWidth : 20, + vertical : { + x : this.left + this.rulerWidth - this.rulerStroke, + y : this.top, + width : this.rulerStroke, + height : this.height - (this.top + this.bottom + this.clusterTitleHeight) - this.rulerHeight + }, + horizontal : { + x : this.left + this.rulerWidth - this.rulerStroke, + y : this.height - (this.bottom + this.clusterTitleHeight + this.rulerHeight), + width : properties.width, + height : this.rulerStroke, + xValues : properties.xValues + } + }); + +}; + +/** Remove nulls and NaN elements in the array * */ +GenericGraph.prototype.cleanArray = function(data) { + var checked = []; + + /** checking data are numbers * */ + for ( var i = 0; i < data.length; i++) { + if (data[i] != null) { + if (!isNaN(data[i])) { + checked.push(data[i]); + } + } + } + return checked; +}; + +GenericGraph.prototype.refresh = function(data) { + document.getElementById(this.targetId).innerHTML = ""; + this.draw(this.targetId, data); +}; + +GenericGraph.prototype.getClassColor = function(className) { + for ( var i = 0; i < this.data.clusters.length; i++) { + var cluster = this.data.clusters[i]; + for ( var j = 0; j < cluster.classes.length; j++) { + var classes = cluster.classes[j]; + if (classes.name == className) { + if (classes.color != null) { + return classes.color; + } + } + } + } + return "black"; +}; + +GenericGraph.prototype.getDimensions = function(data) { + var results = {}; + var points = []; + + this.data = data; + var classesNumber = 0; + var xValues = []; + + for ( var i = 0; i < data.clusters.length; i++) { + var cluster = data.clusters[i]; + if (!this.plotHorizontalByCluster) { + xValues.push(data.clusters[i].x); + } + for ( var j = 0; j < cluster.classes.length; j++) { + var classes = cluster.classes[j]; + points = points.concat(classes.values); + classesNumber = classesNumber + 1; + } + } + + var checked = this.cleanArray(points); + + checked.sort(function(a, b) { + return a - b; + }); + + results.minPoint = checked[0]; + if (this.rulerMinValue != null) { + results.minPoint = this.rulerMinValue; + } + results.maxPoint = checked[checked.length - 1]; + if (this.rulerMaxValue != null) { + results.maxPoint = this.rulerMaxValue; + } + + results.classesNumber = classesNumber; + results.clusterNumber = data.clusters.length; + + var totalInterClassesFreeSpace = (classesNumber - data.clusters.length) * this.interClassesSpace; + var totalInterClusterFreeSpace = (data.clusters.length) * this.interClustersSpace; + results.classWidth = (this.width - (this.rulerWidth + this.left + this.right + totalInterClassesFreeSpace + totalInterClusterFreeSpace))/ classesNumber; + results.width = this.width - (this.right + this.left) - this.rulerWidth + this.rulerStroke; + + results.xValues = {}; + + xValues.sort(function(a, b) { + return a - b; + }); + results.xValues.values = xValues; + if (xValues.length > 0) { + results.xValues.min = xValues[0]; + results.xValues.max = xValues[xValues.length - 1]; + results.xValues.range = results.xValues.max - results.xValues.min; + } + return results; + +}; + +GenericGraph.prototype.draw = function(targetId, data) { + this.targetId = targetId; + var properties = (this.getDimensions(data)); + this.svg = SVG.createSVGCanvas(document.getElementById(this.targetId), [ [ "width", this.width ], [ "height", this.height ] ]); + this.plotAxes(properties); +}; + +GenericGraph.prototype.getMedian = function(checked) { + /** Calculating median * */ + if (checked.length % 2 == 1) { + return checked[Math.floor(checked.length / 2)]; + } else { + return (Number(checked[(checked.length / 2) - 1]) + Number(checked[checked.length / 2])) / 2; + } +}; + +GenericGraph.prototype.pointToPixel = function(value, boxProperties) { + var ratio = (value - boxProperties.minPoint) / (boxProperties.maxPoint - boxProperties.minPoint); + var pixelLength = boxProperties.height - boxProperties.y + this.top; + return (-1 * ratio) * (pixelLength) + (boxProperties.y + boxProperties.height); +}; + +GenericGraph.prototype.maxValueWhisker = function(aboveLimit, q3, array) { + var points = []; + for ( var i = 0; i < array.length; i++) { + if ((array[i] > q3) && (array[i]) <= aboveLimit) { + points.push(array[i]); + } + } + if (points.length > 0) { + points.sort(function(a, b) { + return a - b; + }); + return points[points.length - 1]; + } + return null; +}; + +GenericGraph.prototype.input = function() { + return DATADOC.getBoxWhikerData(); +}; + +GenericGraph.prototype.test = function(targetId) { + var plot = new GenericGraph({ + targetId : targetId, + height : 350, + width : 450, + maxBoxWidth : 25 + }); + plot.refresh(this.input()); +}; /** * Using dygraph it plots a chart. Params: targetId, labelsContainerId, args @@ -2087,338 +2087,338 @@ MacromoleculeConditionGrid.prototype.test = function(targetId) { panel.render(targetId); }; - -function ItemGraphFormatter(id, defaultFormat, selectedFormat, overFormat, draggingFormat){ - this.id = id; - this.args = new Object(); - - this.defaultFormat = new ItemFormat(defaultFormat); - - if(selectedFormat != null){ - this.selected = new ItemFormat(selectedFormat); - } - else{ - this.selected = new ItemFormat(defaultFormat); - } - - if(overFormat != null){ - this.over = new ItemFormat(overFormat); - } - else{ - this.over = new ItemFormat(defaultFormat); - } - - if(draggingFormat != null){ - this.dragging = new ItemFormat(draggingFormat); - } - else{ - this.dragging = new ItemFormat(defaultFormat); - } - - //Events - this.stateChanged = new Event(this); - - - //Attaching events - var _this = this; - this._setEvents(); -}; - -ItemGraphFormatter.prototype.getType = function(){ - return this.args.type; -}; - - -ItemGraphFormatter.prototype.toJSON = function(){ - var json = this.args; - json.defaultFormat = this.getDefault().toJSON(); - json.over = this.getOver().toJSON(); - json.selected = this.getSelected().toJSON(); - json.dragging = this.getDragging().toJSON(); - json.id = this.id; - return json; -}; - -ItemGraphFormatter.prototype.loadFromJSON = function(json){ - this.args = json; - this.defaultFormat = new ItemFormat(json.defaultFormat); - this.over = new ItemFormat(json.over); - this.selected = new ItemFormat(json.selected); - this.dragging = new ItemFormat(json.dragging); - this._setEvents(); -}; - -ItemGraphFormatter.prototype._setEvents = function(){ - //Attaching events - var _this = this; - - this.defaultFormat.changed.attach(function (sender, item){ - _this.over.setSize(_this.defaultFormat.getSize()); - _this.selected.setSize(_this.defaultFormat.getSize()); - _this.dragging.setSize(_this.defaultFormat.getSize()); - _this.stateChanged.notify(_this); - }); - - this.selected.changed.attach(function (sender, item){ - _this.stateChanged.notify(_this); - }); - - this.over.changed.attach(function (sender, item){ - _this.stateChanged.notify(_this); - }); - - this.dragging.changed.attach(function (sender, item){ - _this.stateChanged.notify(_this); - }); -}; - -/** Getters **/ -ItemGraphFormatter.prototype.getId = function(){return this.id;}; -ItemGraphFormatter.prototype.getDefault = function(){return this.defaultFormat;}; -ItemGraphFormatter.prototype.getSelected = function(){return this.selected;}; -ItemGraphFormatter.prototype.getOver = function(){return this.over;}; -ItemGraphFormatter.prototype.getDragging = function(){return this.dragging;}; - -function ItemFormat(args){ - this.defaultFormat = new Object(); - this.args = new Object(); - this.args.title = new Object(); - //Defult properties - this.args.visible = true; - this.args.hidden = false; - this.args.stroke = "#000000"; - this.args.strokeOpacity = 0.8; - this.args["stroke-width"] = 1; - this.args.fill = "#000000"; - this.args["fill-opacity"] = 1; - this.args.size = 1; - this.args.opacity = 1; - this.args.fontSize = "8"; - this.args.fontColor = "#000000"; - - /** For directed edge with arrow **/ - //this.args.arrowSize = 1; - - - if (args != null){ - if (args.visible != null){ - this.args.visible = args.visible; - } - if (args.opacity != null){ - this.args.opacity = args.opacity; - } - if (args.size != null){ - this.args.size = args.size; - } - if (args.hidden != null){ - this.args.hidden = args.hidden; - } - if (args.stroke != null){ - this.args.stroke = this._fixColor(args.stroke); - } - if (args.strokeOpacity != null){ - this.args.strokeOpacity = args.strokeOpacity; - } - if (args["stroke-width"]!=null){ - this.args["stroke-width"] = args["stroke-width"]; - } - if (args["fill-opacity"]!=null){ - this.args["fill-opacity"] = args["fill-opacity"]; - } - if (args.shape!=null){ - this.args.shape = args.shape; - } - if (args.fill!=null){ - this.args.fill = this._fixColor(args.fill); - } - - - if (args.title!=null){ - if (args.title.fontSize!=null){ - this.args.title.fontSize = args.title.fontSize; - } - if (args.title.fill!=null){ - this.args.title.fill = this._fixColor(args.title.fill); - } - } - - /** For directed edge with arrow **/ - /*if (args.arrowSize!=null){ - this.args.arrowSize = args.arrowSize; - }*/ - - } - - this.changed = new Event(); -}; - -ItemFormat.prototype._fixColor = function(color){ - var fixed = color; - if (color.indexOf("green") != -1){ - fixed = '#04B431'; - } - - if (color.indexOf("blue") != -1){ - fixed = '#045FB4'; - } - - if (color.indexOf("red") != -1){ - fixed = '#DF0101'; - } - - if (color.indexOf("black") != -1){ - fixed = '#000000'; - } - - if (color.indexOf("white") != -1){ - fixed = '#FFFFFF'; - } - - if (color.indexOf("#") == -1){ - fixed = "#" + color; - } - return fixed; -}; - -ItemFormat.prototype.toJSON = function(){ - if(this.args.strokeOpacity != null){ - this.args["stroke-opacity"] = this.args.strokeOpacity; - delete this.args.strokeOpacity; - } - -// if(this.args.strokeWidth != null){ -// this.args["stroke-width"] = this.args.strokeWidth; -// delete this.args["stroke-width"]; -// } - - if(this.args.title.fontColor != null){ - this.args.title["font-color"] = this.args.title.fontColor; - } - else{ - this.args.title["font-color"] = this.args.fontColor;//;this.args.title.fontColor; - } - - if(this.args.title.fontSize != null){ - this.args.title["font-size"] = this.args.title.fontSize;//;this.args.title.fontColor; - } - else{ - this.args.title["font-size"] = this.args.fontSize;//;this.args.title.fontColor; - } - //return this.args; - return this.args; -}; - -ItemFormat.prototype.getAttribute = function(name){return this.args[name];}; - -//Getters and Setters -ItemFormat.prototype.setVisible = function(visible){ - if (this.args.visible != visible){ - this.args.visible = visible; - this.changed.notify(this); - } -}; - -ItemFormat.prototype.getVisible = function(){return this.args.visible;}; - -ItemFormat.prototype.setHidden = function(hidden){ - if (this.args.hidden != hidden){ - this.args.hidden = hidden; - this.changed.notify(this); - } -}; - -ItemFormat.prototype.getHidden = function(){return this.args.hidden;}; - - -ItemFormat.prototype.setStroke = function(stroke){ - if (this.args.stroke != stroke){ - this.args.stroke = stroke; - this.changed.notify(this); - } -}; - -ItemFormat.prototype.getStroke = function(){return this.args.stroke;}; - -ItemFormat.prototype.setStrokeOpacity = function(strokeOpacity){ - if (this.args.strokeOpacity != strokeOpacity){ - this.args.strokeOpacity = strokeOpacity; - this.changed.notify(this); - } -}; - -ItemFormat.prototype.getStrokeOpacity = function(){return this.args["stroke-opacity"];}; - -ItemFormat.prototype.setStrokeWidth = function(strokeWidth){ - if (this.args["stroke-width"] != strokeWidth){ - this.args["stroke-width"] = strokeWidth; - this.changed.notify(this); - } -}; - - -ItemFormat.prototype.getFillOpacity = function(){return this.args["fill-opacity"];}; - -ItemFormat.prototype.setfillOpacity = function(strokeWidth){ - if (this.args["fill-opacity"] != strokeWidth){ - this.args["fill-opacity"] = strokeWidth; - this.changed.notify(this); - } -}; - - -ItemFormat.prototype.getStrokeWidth = function(){ - return this.args["stroke-width"]; -}; - -ItemFormat.prototype.setFill = function(fill){ - if (this.args.fill != fill){ - this.args.fill = fill; - this.changed.notify(this); - } -}; - -ItemFormat.prototype.getFill = function(){return this.args.fill;}; - -ItemFormat.prototype.setSize = function(size){ - if (this.args.size != size){ - this.args.size = size; - this.changed.notify(this); - } -}; - -ItemFormat.prototype.getSize = function(){return this.args.size;}; - -ItemFormat.prototype.setOpacity = function(opacity){ - if (this.args.opacity != opacity){ - this.args.opacity = opacity; - this.changed.notify(this); - } -}; - -ItemFormat.prototype.getOpacity = function(){return this.args.opacity;}; - -ItemFormat.prototype.getArrowSize = function(){return this.args.arrowSize;}; - -ItemFormat.prototype.setArrowSize = function(arrowSize){ - if (this.args.arrowSize != arrowSize){ - this.args.arrowSize = arrowSize; - this.changed.notify(this); - } -}; - -ItemFormat.prototype.getFontSize = function(){return this.args.title.fontSize;}; - -ItemFormat.prototype.setFontSize = function(fontSize){ - - if (this.args.title.fontSize != fontSize){ - this.args.title.fontSize = fontSize; - this.changed.notify(this); - } -}; - - - - + +function ItemGraphFormatter(id, defaultFormat, selectedFormat, overFormat, draggingFormat){ + this.id = id; + this.args = new Object(); + + this.defaultFormat = new ItemFormat(defaultFormat); + + if(selectedFormat != null){ + this.selected = new ItemFormat(selectedFormat); + } + else{ + this.selected = new ItemFormat(defaultFormat); + } + + if(overFormat != null){ + this.over = new ItemFormat(overFormat); + } + else{ + this.over = new ItemFormat(defaultFormat); + } + + if(draggingFormat != null){ + this.dragging = new ItemFormat(draggingFormat); + } + else{ + this.dragging = new ItemFormat(defaultFormat); + } + + //Events + this.stateChanged = new Event(this); + + + //Attaching events + var _this = this; + this._setEvents(); +}; + +ItemGraphFormatter.prototype.getType = function(){ + return this.args.type; +}; + + +ItemGraphFormatter.prototype.toJSON = function(){ + var json = this.args; + json.defaultFormat = this.getDefault().toJSON(); + json.over = this.getOver().toJSON(); + json.selected = this.getSelected().toJSON(); + json.dragging = this.getDragging().toJSON(); + json.id = this.id; + return json; +}; + +ItemGraphFormatter.prototype.loadFromJSON = function(json){ + this.args = json; + this.defaultFormat = new ItemFormat(json.defaultFormat); + this.over = new ItemFormat(json.over); + this.selected = new ItemFormat(json.selected); + this.dragging = new ItemFormat(json.dragging); + this._setEvents(); +}; + +ItemGraphFormatter.prototype._setEvents = function(){ + //Attaching events + var _this = this; + + this.defaultFormat.changed.attach(function (sender, item){ + _this.over.setSize(_this.defaultFormat.getSize()); + _this.selected.setSize(_this.defaultFormat.getSize()); + _this.dragging.setSize(_this.defaultFormat.getSize()); + _this.stateChanged.notify(_this); + }); + + this.selected.changed.attach(function (sender, item){ + _this.stateChanged.notify(_this); + }); + + this.over.changed.attach(function (sender, item){ + _this.stateChanged.notify(_this); + }); + + this.dragging.changed.attach(function (sender, item){ + _this.stateChanged.notify(_this); + }); +}; + +/** Getters **/ +ItemGraphFormatter.prototype.getId = function(){return this.id;}; +ItemGraphFormatter.prototype.getDefault = function(){return this.defaultFormat;}; +ItemGraphFormatter.prototype.getSelected = function(){return this.selected;}; +ItemGraphFormatter.prototype.getOver = function(){return this.over;}; +ItemGraphFormatter.prototype.getDragging = function(){return this.dragging;}; + +function ItemFormat(args){ + this.defaultFormat = new Object(); + this.args = new Object(); + this.args.title = new Object(); + //Defult properties + this.args.visible = true; + this.args.hidden = false; + this.args.stroke = "#000000"; + this.args.strokeOpacity = 0.8; + this.args["stroke-width"] = 1; + this.args.fill = "#000000"; + this.args["fill-opacity"] = 1; + this.args.size = 1; + this.args.opacity = 1; + this.args.fontSize = "8"; + this.args.fontColor = "#000000"; + + /** For directed edge with arrow **/ + //this.args.arrowSize = 1; + + + if (args != null){ + if (args.visible != null){ + this.args.visible = args.visible; + } + if (args.opacity != null){ + this.args.opacity = args.opacity; + } + if (args.size != null){ + this.args.size = args.size; + } + if (args.hidden != null){ + this.args.hidden = args.hidden; + } + if (args.stroke != null){ + this.args.stroke = this._fixColor(args.stroke); + } + if (args.strokeOpacity != null){ + this.args.strokeOpacity = args.strokeOpacity; + } + if (args["stroke-width"]!=null){ + this.args["stroke-width"] = args["stroke-width"]; + } + if (args["fill-opacity"]!=null){ + this.args["fill-opacity"] = args["fill-opacity"]; + } + if (args.shape!=null){ + this.args.shape = args.shape; + } + if (args.fill!=null){ + this.args.fill = this._fixColor(args.fill); + } + + + if (args.title!=null){ + if (args.title.fontSize!=null){ + this.args.title.fontSize = args.title.fontSize; + } + if (args.title.fill!=null){ + this.args.title.fill = this._fixColor(args.title.fill); + } + } + + /** For directed edge with arrow **/ + /*if (args.arrowSize!=null){ + this.args.arrowSize = args.arrowSize; + }*/ + + } + + this.changed = new Event(); +}; + +ItemFormat.prototype._fixColor = function(color){ + var fixed = color; + if (color.indexOf("green") != -1){ + fixed = '#04B431'; + } + + if (color.indexOf("blue") != -1){ + fixed = '#045FB4'; + } + + if (color.indexOf("red") != -1){ + fixed = '#DF0101'; + } + + if (color.indexOf("black") != -1){ + fixed = '#000000'; + } + + if (color.indexOf("white") != -1){ + fixed = '#FFFFFF'; + } + + if (color.indexOf("#") == -1){ + fixed = "#" + color; + } + return fixed; +}; + +ItemFormat.prototype.toJSON = function(){ + if(this.args.strokeOpacity != null){ + this.args["stroke-opacity"] = this.args.strokeOpacity; + delete this.args.strokeOpacity; + } + +// if(this.args.strokeWidth != null){ +// this.args["stroke-width"] = this.args.strokeWidth; +// delete this.args["stroke-width"]; +// } + + if(this.args.title.fontColor != null){ + this.args.title["font-color"] = this.args.title.fontColor; + } + else{ + this.args.title["font-color"] = this.args.fontColor;//;this.args.title.fontColor; + } + + if(this.args.title.fontSize != null){ + this.args.title["font-size"] = this.args.title.fontSize;//;this.args.title.fontColor; + } + else{ + this.args.title["font-size"] = this.args.fontSize;//;this.args.title.fontColor; + } + //return this.args; + return this.args; +}; + +ItemFormat.prototype.getAttribute = function(name){return this.args[name];}; + +//Getters and Setters +ItemFormat.prototype.setVisible = function(visible){ + if (this.args.visible != visible){ + this.args.visible = visible; + this.changed.notify(this); + } +}; + +ItemFormat.prototype.getVisible = function(){return this.args.visible;}; + +ItemFormat.prototype.setHidden = function(hidden){ + if (this.args.hidden != hidden){ + this.args.hidden = hidden; + this.changed.notify(this); + } +}; + +ItemFormat.prototype.getHidden = function(){return this.args.hidden;}; + + +ItemFormat.prototype.setStroke = function(stroke){ + if (this.args.stroke != stroke){ + this.args.stroke = stroke; + this.changed.notify(this); + } +}; + +ItemFormat.prototype.getStroke = function(){return this.args.stroke;}; + +ItemFormat.prototype.setStrokeOpacity = function(strokeOpacity){ + if (this.args.strokeOpacity != strokeOpacity){ + this.args.strokeOpacity = strokeOpacity; + this.changed.notify(this); + } +}; + +ItemFormat.prototype.getStrokeOpacity = function(){return this.args["stroke-opacity"];}; + +ItemFormat.prototype.setStrokeWidth = function(strokeWidth){ + if (this.args["stroke-width"] != strokeWidth){ + this.args["stroke-width"] = strokeWidth; + this.changed.notify(this); + } +}; + + +ItemFormat.prototype.getFillOpacity = function(){return this.args["fill-opacity"];}; + +ItemFormat.prototype.setfillOpacity = function(strokeWidth){ + if (this.args["fill-opacity"] != strokeWidth){ + this.args["fill-opacity"] = strokeWidth; + this.changed.notify(this); + } +}; + + +ItemFormat.prototype.getStrokeWidth = function(){ + return this.args["stroke-width"]; +}; + +ItemFormat.prototype.setFill = function(fill){ + if (this.args.fill != fill){ + this.args.fill = fill; + this.changed.notify(this); + } +}; + +ItemFormat.prototype.getFill = function(){return this.args.fill;}; + +ItemFormat.prototype.setSize = function(size){ + if (this.args.size != size){ + this.args.size = size; + this.changed.notify(this); + } +}; + +ItemFormat.prototype.getSize = function(){return this.args.size;}; + +ItemFormat.prototype.setOpacity = function(opacity){ + if (this.args.opacity != opacity){ + this.args.opacity = opacity; + this.changed.notify(this); + } +}; + +ItemFormat.prototype.getOpacity = function(){return this.args.opacity;}; + +ItemFormat.prototype.getArrowSize = function(){return this.args.arrowSize;}; + +ItemFormat.prototype.setArrowSize = function(arrowSize){ + if (this.args.arrowSize != arrowSize){ + this.args.arrowSize = arrowSize; + this.changed.notify(this); + } +}; + +ItemFormat.prototype.getFontSize = function(){return this.args.title.fontSize;}; + +ItemFormat.prototype.setFontSize = function(fontSize){ + + if (this.args.title.fontSize != fontSize){ + this.args.title.fontSize = fontSize; + this.changed.notify(this); + } +}; + + + + /** * This class executes the actions @@ -4952,1101 +4952,2095 @@ function openExperiment(experimentId) { BIOSAXS.openExperiment(experimentId); } -var SITE_CONF = { - SAMPLE_CHANGER_CONFIGURATION : { - "3":{ - "platetype3VO": {"plateTypeId":4,"name":"96 Well plate","rowCount":8,"columnCount":12,"shape":"REC"}, - "name":"96 Well plate", - "slotPositionRow":"1", - "slotPositionColumn":"3", - "storageTemperature":"0", - "sampleplateposition3VOs":[], - "experimentId":0 - }, - "2":{ - "platetype3VO":{"plateTypeId":2,"name":" 4 x ( 8 + 3 ) Block","rowCount":4,"columnCount":11,"shape":"REC"}, - "name":" 4 x ( 8 + 3 ) Block", - "slotPositionRow":"1", - "slotPositionColumn":"2", - "storageTemperature":"0", - "sampleplateposition3VOs":[],"experimentId":0 - }, - "1":{ - "platetype3VO":{"plateTypeId":1,"name":"Deep Well","rowCount":8,"columnCount":12,"shape":"REC"}, - "name":"Deep Well", - "slotPositionRow":"1", - "slotPositionColumn":"1", - "storageTemperature":"0", - "sampleplateposition3VOs":[], - "experimentId":0 - } - } +var SITE_CONF = { + SAMPLE_CHANGER_CONFIGURATION : { + "3":{ + "platetype3VO": {"plateTypeId":4,"name":"96 Well plate","rowCount":8,"columnCount":12,"shape":"REC"}, + "name":"96 Well plate", + "slotPositionRow":"1", + "slotPositionColumn":"3", + "storageTemperature":"0", + "sampleplateposition3VOs":[], + "experimentId":0 + }, + "2":{ + "platetype3VO":{"plateTypeId":2,"name":" 4 x ( 8 + 3 ) Block","rowCount":4,"columnCount":11,"shape":"REC"}, + "name":" 4 x ( 8 + 3 ) Block", + "slotPositionRow":"1", + "slotPositionColumn":"2", + "storageTemperature":"0", + "sampleplateposition3VOs":[],"experimentId":0 + }, + "1":{ + "platetype3VO":{"plateTypeId":1,"name":"Deep Well","rowCount":8,"columnCount":12,"shape":"REC"}, + "name":"Deep Well", + "slotPositionRow":"1", + "slotPositionColumn":"1", + "storageTemperature":"0", + "sampleplateposition3VOs":[], + "experimentId":0 + } + } }; - -var ISPYB_CONF = { - load : function(config) { - for (var key in config) { - ISPYB_CONF[key] = config[key]; - } - }, - SAMPLE_CHANGER_CONFIGURATION : { - "1": { - "platetype3VO": {"plateTypeId": 1, "name": "Deep Well", "rowCount": 8, "columnCount": 12, "shape": "REC"}, - "name": "Deep Well", - "slotPositionRow": "1", - "slotPositionColumn": "1", - "storageTemperature": "0", - "sampleplateposition3VOs": [], - "experimentId": 0 - } - } + +var ISPYB_CONF = { + load : function(config) { + for (var key in config) { + ISPYB_CONF[key] = config[key]; + } + }, + SAMPLE_CHANGER_CONFIGURATION : { + "1": { + "platetype3VO": {"plateTypeId": 1, "name": "Deep Well", "rowCount": 8, "columnCount": 12, "shape": "REC"}, + "name": "Deep Well", + "slotPositionRow": "1", + "slotPositionColumn": "1", + "storageTemperature": "0", + "sampleplateposition3VOs": [], + "experimentId": 0 + } + } }; -var SITE_CONF = { - -}; +var SITE_CONF = { + +}; -/** - * Example form - * - * @witdh - * @height - */ -function AbinitioForm(args) { - this.id = BUI.id(); - this.width = null; - this.height = null; - - if (args != null) { - if (args.width != null) { - this.width = args.width; - } - if (args.height != null) { - this.height = args.height; - } - } - - var _this = this; - /** Widgets **/ - this.abinitioGrid = new AbinitioGrid({ - width : null, - height : 200 - }); - - this.abinitioGrid.onSelected.attach(function(sender, models) { - var modelsIdList = []; - for ( var i in models) { - modelsIdList.push(models[i].modelId); - } - _this._renderPlot(_this.subtractions[0].subtractionId, modelsIdList); - _this._renderPDB(modelsIdList); - }); - - /** Dygraph Widget that plots fir files**/ - this.plotWidget = new PlotWidget({ - width : 745 / 2, - height : 300, - margin : "10 0 5 10" - }); - - /** PDB viewer **/ - this.viewer = new PDBViewer({ - width : 745 / 2, - height : 300 - }); - -} - -AbinitioForm.prototype._renderPlot = function(subtractionId, modelsIdList) { - /** Trying to plot tje subtraction and the models **/ - try { - var colors = [ "#669900", "#0000FF" ]; - this.plotWidget.refresh([], [ ], modelsIdList, [], [], colors); - } catch (e) { - console.log(e); - } -}; - -AbinitioForm.prototype._renderPDB = function(modelsIdList) { - /** Trying to plot the PDB file **/ - try { - var viz = []; - for (var i = 0; i < modelsIdList.length; i++) { - viz.push({ - modelId : modelsIdList[i], - color : "0xFF6600", - opacity : 0.8 - }); - } - this.viewer.refresh(viz); - } catch (e) { - console.log(e); - } -}; - -AbinitioForm.prototype._getItems = function() { - var _this = this; - return [ { - xtype : 'label', - forId : 'myFieldId', - text : 'INLINE HELP: To be updated', - margin : '15 0 20 10', - cls : "inline-help" - }, this.abinitioGrid.getPanel(), { - xtype : 'container', - layout : 'hbox', - items : [ this.plotWidget.getPanel(), this.viewer.getPanel() ] - } ] -}; - -AbinitioForm.prototype._getButtons = function() { - return []; -}; - -AbinitioForm.prototype.getPanel = function() { - var _this = this; - this.panel = Ext.create('Ext.form.Panel', { - width : this.width, - height : this.height, - margin : 10, - border : 1, - defaultType : 'textfield', - items : this._getItems(), - buttons : this._getButtons(), - listeners : { - afterrender : function() { - _this._populate(); - - } - } - }); - return this.panel; -}; - -/** Populates could be call when the DOM is not filled yet **/ -AbinitioForm.prototype._populate = function() { -}; - -/** It populates the form * */ -AbinitioForm.prototype.refresh = function(subtractions) { - this.subtractions = subtractions; - this.abinitioGrid.refresh(subtractions); -}; - -AbinitioForm.prototype.input = function() { - return {}; -}; - -/** It populates the form **/ -AbinitioForm.prototype.test = function(targetId) { - var macromoleculeForm = new AbinitioForm(); - var panel = macromoleculeForm.getPanel(); - panel.render(targetId); -}; +/** + * Example form + * + * @witdh + * @height + */ +function AbinitioForm(args) { + this.id = BUI.id(); + this.width = null; + this.height = null; -/** - * Example form - * - * @witdh - * @height - */ -function DataReductionForm(args) { - this.id = BUI.id(); - this.width = null; - this.height = null; - - if (args != null) { - if (args.width != null) { - this.width = args.width; - } - if (args.height != null) { - this.height = args.height; - } - } - + if (args != null) { + if (args.width != null) { + this.width = args.width; + } + if (args.height != null) { + this.height = args.height; + } + } + + var _this = this; + /** Widgets **/ + this.abinitioGrid = new AbinitioGrid({ + width : null, + height : 200 + }); + + this.abinitioGrid.onSelected.attach(function(sender, models) { + var modelsIdList = []; + for ( var i in models) { + modelsIdList.push(models[i].modelId); + } + _this._renderPlot(_this.subtractions[0].subtractionId, modelsIdList); + _this._renderPDB(modelsIdList); + }); + + /** Dygraph Widget that plots fir files**/ + this.plotWidget = new PlotWidget({ + width : 745 / 2, + height : 300, + margin : "10 0 5 10" + }); + + /** PDB viewer **/ + this.viewer = new PDBViewer({ + width : 745 / 2, + height : 300 + }); + +} + +AbinitioForm.prototype._renderPlot = function(subtractionId, modelsIdList) { + /** Trying to plot tje subtraction and the models **/ + try { + var colors = [ "#669900", "#0000FF" ]; + this.plotWidget.refresh([], [ ], modelsIdList, [], [], colors); + } catch (e) { + console.log(e); + } +}; + +AbinitioForm.prototype._renderPDB = function(modelsIdList) { + /** Trying to plot the PDB file **/ + try { + var viz = []; + for (var i = 0; i < modelsIdList.length; i++) { + viz.push({ + modelId : modelsIdList[i], + color : "0xFF6600", + opacity : 0.8 + }); + } + this.viewer.refresh(viz); + } catch (e) { + console.log(e); + } +}; + +AbinitioForm.prototype._getItems = function() { + var _this = this; + return [ { + xtype : 'label', + forId : 'myFieldId', + text : 'INLINE HELP: To be updated', + margin : '15 0 20 10', + cls : "inline-help" + }, this.abinitioGrid.getPanel(), { + xtype : 'container', + layout : 'hbox', + items : [ this.plotWidget.getPanel(), this.viewer.getPanel() ] + } ] +}; + +AbinitioForm.prototype._getButtons = function() { + return []; +}; + +AbinitioForm.prototype.getPanel = function() { + var _this = this; + this.panel = Ext.create('Ext.form.Panel', { + width : this.width, + height : this.height, + margin : 10, + border : 1, + defaultType : 'textfield', + items : this._getItems(), + buttons : this._getButtons(), + listeners : { + afterrender : function() { + _this._populate(); + + } + } + }); + return this.panel; +}; + +/** Populates could be call when the DOM is not filled yet **/ +AbinitioForm.prototype._populate = function() { +}; + +/** It populates the form * */ +AbinitioForm.prototype.refresh = function(subtractions) { + this.subtractions = subtractions; + this.abinitioGrid.refresh(subtractions); +}; + +AbinitioForm.prototype.input = function() { + return {}; +}; + +/** It populates the form **/ +AbinitioForm.prototype.test = function(targetId) { + var macromoleculeForm = new AbinitioForm(); + var panel = macromoleculeForm.getPanel(); + panel.render(targetId); +}; + +/** + * Example form + * + * @witdh + * @height + */ +function DataReductionForm(args) { + this.id = BUI.id(); + this.width = null; + this.height = null; + + if (args != null) { + if (args.width != null) { + this.width = args.width; + } + if (args.height != null) { + this.height = args.height; + } + } + + var _this = this; + + /** Widgets **/ + this.plotWidget = new PlotWidget({ + width : 650, + height : 490 + }); + + /** Selected frames to be displayed **/ + this.selectedItems = { + frames : [], + averages : [], + subtractions : [] + }; + +} + +DataReductionForm.prototype._parseSelectedItemsToIds = function(selectedArray) { + var ids = []; + if (selectedArray != null) { + for (var i = 0; i < selectedArray.length; i++) { + ids.push(selectedArray[i].id); + } + } + return ids; +}; + +DataReductionForm.prototype.onSelectionChanged = function(columnName, selected) { + if (selected != null) { + if (columnName == "Frames") { + this.selectedItems.frames = selected; + this.selectedItems.subtractions = []; + } + if (columnName == "Averages") { + this.selectedItems.averages = selected; + } + if (columnName == "Subtractions") { + this.selectedItems.frames = []; + this.selectedItems.subtractions = selected; + } + } + this.plotWidget.refresh(this._parseSelectedItemsToIds(this.selectedItems.frames), this._parseSelectedItemsToIds(this.selectedItems.subtractions)); +}; + +DataReductionForm.prototype._getFramesExtPanel = function(store, columnName, height) { + var _this = this; + + var selModel = Ext.create('Ext.selection.RowModel', { + allowDeselect : true, + mode : 'MULTI', + listeners : { + selectionchange : function(sm, selections) { + var selected = []; + for (var i = 0; i < selections.length; i++) { + selected.push(selections[i].raw); + } + _this.onSelectionChanged(columnName, selected); + } + } + }); + + return Ext.create('Ext.grid.Panel', { + store : store, + margin : 10, + height : height, + width : 200, + selModel : selModel, + columns : [ { + text : columnName, + dataIndex : 'fileName', + flex : 1 + } ], + viewConfig : { + } + }); +}; + +DataReductionForm.prototype._getFramesPanel = function() { + var fields = [ 'fileName', 'type', 'id' ]; + + this.framesStore = Ext.create('Ext.data.Store', { + fields : fields, + sorters : 'fileName' + }); + + this.averagesStore = Ext.create('Ext.data.Store', { + fields : fields + }); + + this.subtractionStore = Ext.create('Ext.data.Store', { + fields : fields + }); + + var gridFrames = this._getFramesExtPanel(this.framesStore, "Frames", 375); + var gridAvgs = this._getFramesExtPanel(this.averagesStore, "Averages", 125); + var subtractionAvgs = this._getFramesExtPanel(this.subtractionStore, "Subtractions", 75); + + return { + xtype : 'container', + layout : 'vbox', + items : [ gridFrames, subtractionAvgs ] + }; +}; + +DataReductionForm.prototype._getImageContainer = function(name, help) { + var html = "
" + name + "
" + return { + xtype : 'container', + layout : 'vbox', + items : [ { + html : html, + margin : "5 0 0 0", + height : 95, + width : 100 + }, { + xtype : 'label', + forId : 'myFieldId', + text : help, + margin : '5 0 0 0', + cls : "inline-help" + } ] + } +}; + +DataReductionForm.prototype._getItems = function() { + var _this = this; + return [ { + xtype : 'container', + layout : 'hbox', + items : [ + this._getFramesPanel(), + this.plotWidget.getPanel(), + { + xtype : 'panel', + width : 110, + frame : true, + margin : "10 5 5 5", + border : 0, + layout : 'vbox', + items : [ this._getImageContainer("scattering", "Scattering"), this._getImageContainer("guinier", "Guinier Region"), + this._getImageContainer("kratky", "Kratky Plot"), this._getImageContainer("gnom", "P(r) distribution") ] + } ] + } ] +}; + +DataReductionForm.prototype._getButtons = function() { + return []; +}; + +DataReductionForm.prototype.getPanel = function() { + var _this = this; + this.panel = Ext.create('Ext.form.Panel', { + width : this.width, + height : this.height, + border : 0, + items : this._getItems(), + buttons : this._getButtons(), + listeners : { + afterrender : function() { + _this._populate(); + } + } + }); + return this.panel; +}; + +/** Populates could be call when the DOM is not filled yet **/ +DataReductionForm.prototype._populate = function() { +}; + +/** It populates the form * */ +DataReductionForm.prototype.refresh = function(subtractions) { + if (subtractions != null) { + for (var i = 0; i < subtractions.length; i++) { + /** Loading frame grids **/ + var subtraction = subtractions[i]; + var averages = [ { + fileName : BUI.getFileName(subtraction.bufferAverageFilePath), + type : 'bufferAvg', + id : subtraction.subtractionId + }, { + fileName : BUI.getFileName(subtraction.sampleAverageFilePath), + type : 'sampleAvg', + id : subtraction.subtractionId + } + + ]; + this.averagesStore.loadData(averages, true); + this.subtractionStore.loadData([ { + fileName : BUI.getFileName(subtraction.substractedFilePath), + type : 'SUBTRACTION', + id : subtraction.subtractionId + } ], true); + + var frames = []; + /** Buffers **/ + if (subtraction.bufferOneDimensionalFiles != null) { + if (subtraction.bufferOneDimensionalFiles.frametolist3VOs != null) { + for (var j = 0; j < subtraction.bufferOneDimensionalFiles.frametolist3VOs.length; j++) { + var frametolist3VO = subtraction.bufferOneDimensionalFiles.frametolist3VOs[j]; + if (frametolist3VO.frame3VO != null) { + frames.push({ + fileName : BUI.getFileName(frametolist3VO.frame3VO.filePath), + type : 'BUFFER', + id : frametolist3VO.frame3VO.frameId + }); + } + } + } + } + /** Samples **/ + if (subtraction.sampleOneDimensionalFiles != null) { + if (subtraction.sampleOneDimensionalFiles.frametolist3VOs != null) { + for (var j = 0; j < subtraction.sampleOneDimensionalFiles.frametolist3VOs.length; j++) { + var frametolist3VO = subtraction.sampleOneDimensionalFiles.frametolist3VOs[j]; + if (frametolist3VO.frame3VO != null) { + frames.push({ + fileName : BUI.getFileName(frametolist3VO.frame3VO.filePath), + type : 'SAMPLE', + id : frametolist3VO.frame3VO.frameId + }); + } + } + } + } + + this.framesStore.loadData(frames, true); + + /** Loading images **/ + this._displayImage("scattering", subtraction.subtractionId); + this._displayImage("kratky", subtraction.subtractionId); + this._displayImage("guinier", subtraction.subtractionId); + this._displayImage("gnom", subtraction.subtractionId); + } + } +}; + +DataReductionForm.prototype._displayImage = function(name, subtractionId) { + var url = BUI.getURL() + '&type=' + name + '&subtractionId=' + subtractionId; + var event = "OnClick= window.open('" + url + "')"; + document.getElementById(this.id + "_" + name).innerHTML = ''; +}; + +DataReductionForm.prototype.input = function() { + return {}; +}; + +/** It populates the form **/ +DataReductionForm.prototype.test = function(targetId) { + var macromoleculeForm = new DataReductionForm(); + var panel = macromoleculeForm.getPanel(); + panel.render(targetId); +}; + +function PlotWidget(args) { + this.width = 600; + this.height = 600; + this.id = BUI.id(); + + this.linear = false; + + this.margin = "10 0 0 0"; + /** for each stat the max and minimum value when it is scaled in order to show correctly in the legend **/ + this.ranges = {}; + if (args != null) { + if (args.width != null) { + this.width = args.width; + } + if (args.height != null) { + this.height = args.height; + } + if (args.margin != null) { + this.margin = args.margin; + } + } + +} + +PlotWidget.prototype.getMenu = function() { + var _this = this; + /** Actions buttons **/ + var actions = []; + actions.push("->"); + actions.push({ + text : "Export as Image", + scope : this, + icon : '../images/save.gif', + handler : function(item, pressed) { + var largeImage = document.createElement("img"); + largeImage.style.display = 'block'; + largeImage.style.width = 200 + "px"; + largeImage.style.height = 200 + "px"; + largeImage.setAttribute('src', Dygraph.Export.asCanvas(this.dygraphObject.dygraph).toDataURL()); + window.open(Dygraph.Export.asCanvas(this.dygraphObject.dygraph).toDataURL(), 'Image', ''); + } + }); + + return actions; +}; + +/** Looks for the maximum value and then divide everything but that value **/ +PlotWidget.prototype.scaledData = function(data) { + for (var i = 0; i < data.length; i++) { + var values = this.getMaxAndMinValue(data[i]); + data[i] = this.divideValuesByMax(data[i], values.max); + this.ranges[data[i].label] = values; + } + return data; +}; + +/** Given a stat float[] and a max number it will divide each value by max **/ +PlotWidget.prototype.divideValuesByMax = function(stat, max) { + for (var j = 0; j < stat.data.length; j++) { + if (max != 0) { + stat.data[j] = Number(stat.data[j]) / max; + stat.std[j] = Number(stat.std[j]) / max; + } + } + return stat; +}; + +/** returns max value of a stat **/ +PlotWidget.prototype.getMaxAndMinValue = function(stat) { + var max = 0; + var min = stat.data[0]; + for (var j = 0; j < stat.data.length; j++) { + if (Number(stat.data[j]) > max) { + max = Number(stat.data[j]); + } + if (Number(stat.std[j]) > max) { + max = Number(stat.std[j]); + } + if (Number(stat.data[j]) < min) { + min = Number(stat.data[j]); + } + } + return { + max : Number(max), + min : Number(min) + }; +}; + +PlotWidget.prototype._renderDygraph = function(parsed, colors, labels) { + this.dygraphObject = new StdDevDyGraph(this.id, { + width : this.width - 20, + height : this.height - 40, + xlabel : "", + }); + + this.dygraphObject.draw(parsed, colors, labels); + +}; + +PlotWidget.prototype.getPanel = function() { + this.panel = Ext.create('Ext.panel.Panel', { + width : this.width, + height : this.height, + margin : this.margin, + tbar : this.getMenu(), + items : [ { + html : "", + id : this.id, + width : this.width, + height : this.height, + padding : 10, + margin : "0 0 0 -30", + border : 0 + } ] + }); + + return this.panel; +}; + +PlotWidget.prototype.getPoint = function(y, error) { + var minus = y - error; + var max = y + error; + + if (this.linear) { + return [ Math.abs(minus), y, Math.abs(max) ]; + } + if ((minus != 0) && (max != 0)) { + return [ Math.log(Math.abs(minus)), Math.log(y), Math.log(Math.abs(max)) ]; + } else { + return [ Math.log(y), Math.log(y), Math.log(y) ]; + } + +}; + +PlotWidget.prototype.getDataPlot = function(frames, subtractions, models, fits, rbms) { + var files = []; + var labels = [ "Intensity" ]; + if (frames != null) { + for (var i = 0; i < frames.length; i++) { + files.push(frames[i].data); + labels.push(frames[i].fileName); + } + } + function splitData(data, column, errorColumn, name){ + var result = [] + for (var j = 0; j < data.length; j++) { + console.log(data[j][column]); + result.push([j,data[j][column],data[j][errorColumn]]);//[0, data[i][column],0]]); + } + files.push(result); + labels.push(name); + } + + if (subtractions != null) { + for (var i = 0; i < subtractions.length; i++) { + /** For subtraction **/ + files.push(subtractions[i].subtraction.data); + labels.push(subtractions[i].subtraction.fileName); + /** For sample average **/ +// files.push(subtractions[i].sampleAvg.data); +// labels.push(subtractions[i].sampleAvg.fileName); + /** For buffer average **/ +// files.push(subtractions[i].bufferAvg.data); +// labels.push(subtractions[i].bufferAvg.fileName); + } + } + + if (models != null) { + for (var i = 0; i < models.length; i++) { + for ( var key in models[i]) { + splitData(models[i][key].fir.data, 1, 2, "Intensity"); + splitData(models[i][key].fir.data, 3, 3, "Fit"); + } + } + } + + if (fits != null) { + for (var i = 0; i < fits.length; i++) { + for ( var key in fits[i]) { + + /** adding fit file to be plotted **/ + if (fits[i][key].fit.data[0].length == 3){ + splitData(fits[i][key].fit.data, 1, 1, "Intensity"); + splitData(fits[i][key].fit.data, 2, 2, "Fit"); + } + + /** s, Iexp(s), err, Ifit(s). **/ + if (fits[i][key].fit.data[0].length == 4){ + splitData(fits[i][key].fit.data, 1, 2, "Intensity"); + splitData(fits[i][key].fit.data, 3, 3, "Fit"); + } + + if (fits[i][key].fit.data[0].length == 5){ + /** X Intensity Fit Error Residues **/ + + splitData(fits[i][key].fit.data, 1, 3, "Intensity"); + splitData(fits[i][key].fit.data, 2, 2, "Fit"); + } + } + } + } + + var dataPoints = []; + if (files.length > 0) { + for (var i = 0; i < files[0].length; i++) { + dataPoints.push([ files[0][i][0], this.getPoint(files[0][i][1], files[0][i][2]) ]); + } + if (files.length > 1) { + for (var i = 1; i < files.length; i++) { + for (var j = 0; j < dataPoints.length; j++) { + if (files[i][j] != null){ + dataPoints[j].push(this.getPoint(files[i][j][1], files[i][j][2])); + } + else{ + dataPoints[j].push([0,0,0]); + } + } + } + } + } + + return { + dataPoints : dataPoints, + labels : labels + } +}; + +PlotWidget.prototype.refresh = function(frames, subtractions, models, fits, rbms, colors) { + + var _this = this; + this.panel.setLoading("Reading Files"); + + var dataAdapter = new BiosaxsDataAdapter(); + dataAdapter.onSuccess.attach(function(sender, data) { + _this.panel.setLoading("Rendering"); + var parsed = _this.getDataPlot(data.frames, data.subtractions, data.models, data.fits, rbms); + _this._renderDygraph(parsed.dataPoints, colors, parsed.labels); + _this.panel.setLoading(false); + }); + dataAdapter.onError.attach(function(sender, data) { + _this.panel.setLoading(false); + }); + dataAdapter.getDataPlot(frames, subtractions, models, fits, rbms); +}; + +PlotWidget.prototype.input = function() { + return DATADOC.getHPLCData(); +}; + + +/** + * Fit form + * + * @witdh + * @height + */ +function FitForm(args) { + this.id = BUI.id(); + this.width = null; + this.height = null; + + if (args != null) { + if (args.width != null) { + this.width = args.width; + } + if (args.height != null) { + this.height = args.height; + } + } + + var _this = this; + + /** Widgets **/ + this.fitGrid = new FitStructureToExperimentDataGrid({ + width : null, + height : 200 + }); + + this.fitGrid.onSelected.attach(function(sender, fits) { + var modelsIdList = []; + for ( var i in fits) { + modelsIdList.push(fits[i].fitStructureToExperimentalDataId); + } + _this._renderPlot(_this.subtractions[0].subtractionId, modelsIdList); +// var adapter = new BiosaxsDataAdapter(); +// adapter.onSuccess.attach(function(sender, data) { +// +// }); +// adapter.getScatteringCurveByFrameIdsList([], [], [], ids); + }); + + + /** Dygraph Widget that plots fir files**/ + this.plotWidget = new PlotWidget({ + width : 745, + height : 300, + margin : "10 0 10 10" + }); +} + +FitForm.prototype._renderPlot = function(subtractionId, modelsIdList) { + /** Trying to plot tje subtraction and the models **/ + try { + var colors = [ "#669900", "#0000FF" ]; + + this.plotWidget.refresh([], [ ], [], modelsIdList, [], colors); + } catch (e) { + console.log(e); + } +}; + +FitForm.prototype._getItems = function() { + var _this = this; + return [ { + xtype : 'label', + forId : 'myFieldId', + text : 'INLINE HELP: To be updated', + margin : '15 0 20 10', + cls : "inline-help" + }, this.fitGrid.getPanel(), this.plotWidget.getPanel() ] + +}; + +FitForm.prototype._getButtons = function() { + return []; +}; + +FitForm.prototype.getPanel = function() { + var _this = this; + this.panel = Ext.create('Ext.form.Panel', { + width : this.width, + height : this.height, + margin : 10, + border : 1, + defaultType : 'textfield', + items : this._getItems(), + buttons : this._getButtons(), + listeners : { + afterrender : function() { + _this._populate(); + + } + } + }); + return this.panel; +}; + +/** Populates could be call when the DOM is not filled yet **/ +FitForm.prototype._populate = function() { +}; + +/** It populates the form * */ +FitForm.prototype.refresh = function(subtractions) { + this.subtractions = subtractions; + this.fitGrid.refresh(subtractions); +}; + +FitForm.prototype.input = function() { + return {}; +}; + +/** It populates the form **/ +FitForm.prototype.test = function(targetId) { + var macromoleculeForm = new FitForm(); + var panel = macromoleculeForm.getPanel(); + panel.render(targetId); +}; + +/** + * Example form + * + * @witdh + * @height + */ +function RigidBodyModelingResultForm(args) { + this.id = BUI.id(); + this.width = null; + this.height = null; + + if (args != null) { + if (args.width != null) { + this.width = args.width; + } + if (args.height != null) { + this.height = args.height; + } + } + + var _this = this; + /** Widgets **/ + this.rigidModelGrid = new RigidModelGrid({ + width : null, + height : 200 + }); + + this.rigidModelGrid.onSelected.attach(function(sender, fits){ + var ids = []; + for ( var i in fits) { + ids.push(fits[i].fitStructureToExperimentalDataId); + } + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, data){ +// debugger + + }); + + adapter.getScatteringCurveByFrameIdsList([], [], [], ids); + }); + +} + +RigidBodyModelingResultForm.prototype._getItems = function() { + var _this = this; + return [ { + xtype : 'label', + forId : 'myFieldId', + text : 'INLINE HELP: To be updated', + margin : '15 0 20 10', + cls : "inline-help" + }, this.rigidModelGrid.getPanel() ] + +}; + +RigidBodyModelingResultForm.prototype._getButtons = function() { + return []; +}; + +RigidBodyModelingResultForm.prototype.getPanel = function() { + var _this = this; + this.panel = Ext.create('Ext.form.Panel', { + width : this.width, + height : this.height, + margin : 10, + border : 1, + defaultType : 'textfield', + items : this._getItems(), + buttons : this._getButtons(), + listeners : { + afterrender : function() { + _this._populate(); + + } + } + }); + return this.panel; +}; + +/** Populates could be call when the DOM is not filled yet **/ +RigidBodyModelingResultForm.prototype._populate = function() { +}; + +/** It populates the form * */ +RigidBodyModelingResultForm.prototype.refresh = function(subtractions) { + this.rigidModelGrid.refresh(subtractions); +}; + +RigidBodyModelingResultForm.prototype.input = function() { + return {}; +}; + +/** It populates the form **/ +RigidBodyModelingResultForm.prototype.test = function(targetId) { + var macromoleculeForm = new RigidBodyModelingResultForm(); + var panel = macromoleculeForm.getPanel(); + panel.render(targetId); +}; + +/** + * Example form + * + * @witdh + * @height + */ +function SuperpositionForm(args) { + this.id = BUI.id(); + this.width = null; + this.height = null; + + if (args != null) { + if (args.width != null) { + this.width = args.width; + } + if (args.height != null) { + this.height = args.height; + } + } + + var _this = this; + + /** Widgets **/ + this.superpositionGrid = new SuperpositionGrid({ + width : null, + height : 200 + }); + + this.superpositionGrid.onSelected.attach(function(sender, superpositions) { + var ids = []; + for ( var i in superpositions) { + ids.push(superpositions[i].superpositionId); + } +// _this._renderAbinitio(ids); +// _this.aprioriPDBViewer.refresh(); + _this._renderAligned(ids); + // getAlignedPDBContentBySuperpositionList + }); + + /** PDB viewer **/ +// this.abinitioPDBViewer = new PDBViewer({ +// width : 860 / 2, +// height : 300 / 2, +// margin : 0 +// }); +// +// /** PDB viewer **/ +// this.aprioriPDBViewer = new StructurePDBViewer({ +// width : 860 / 2, +// height : 300 / 2, +// margin : 0 +// }); + + this.alignedPDBViewer = new AlignedSuperpositionPDBViewer({ + width : 860 , + height : 300 + }); + +} + +SuperpositionForm.prototype._renderAligned = function(modelsIdList) { + /** Trying to plot the PDB file **/ + try { + this.alignedPDBViewer.refresh(modelsIdList); + } catch (e) { + console.log(e); + } +}; + +SuperpositionForm.prototype._renderAbinitio = function(modelsIdList) { + /** Trying to plot the PDB file **/ + try { + var viz = []; + for (var i = 0; i < modelsIdList.length; i++) { + viz.push({ + modelId : modelsIdList[i], + color : "0xFF6600", + opacity : 0.8 + }); + } + this.abinitioPDBViewer.refresh(viz); + } catch (e) { + console.log(e); + } +}; + +SuperpositionForm.prototype._getItems = function() { + var _this = this; + return [ { + xtype : 'label', + forId : 'myFieldId', + text : 'INLINE HELP: To be updated', + margin : '15 0 20 10', + cls : "inline-help" + }, this.superpositionGrid.getPanel(), { + xtype : 'container', + layout : 'hbox', + items : [ +// { +// xtype : 'container', +// layout : 'vbox', +// items : [ this.abinitioPDBViewer.getPanel(), this.aprioriPDBViewer.getPanel() +// +// ] +// }, + + this.alignedPDBViewer.getPanel() ] + } ] + +}; + +SuperpositionForm.prototype._getButtons = function() { + return []; +}; + +SuperpositionForm.prototype.getPanel = function() { + var _this = this; + this.panel = Ext.create('Ext.form.Panel', { + width : this.width, + height : this.height, + margin : 10, + border : 1, + defaultType : 'textfield', + items : this._getItems(), + buttons : this._getButtons(), + listeners : { + afterrender : function() { + _this._populate(); + + } + } + }); + return this.panel; +}; + +/** Populates could be call when the DOM is not filled yet **/ +SuperpositionForm.prototype._populate = function() { +}; + +/** It populates the form * */ +SuperpositionForm.prototype.refresh = function(subtractions) { + this.subtractions = subtractions; + this.superpositionGrid.refresh(subtractions); +}; + +SuperpositionForm.prototype.input = function() { + return {}; +}; + +/** It populates the form **/ +SuperpositionForm.prototype.test = function(targetId) { + var macromoleculeForm = new SuperpositionForm(); + var panel = macromoleculeForm.getPanel(); + panel.render(targetId); +}; + +/** + * Example form + * + * @witdh + * @height + */ +function AssemblyForm(args) { + this.id = BUI.id(); + this.width = 700; + this.height = 500; + + if (args != null) { + if (args.width != null) { + this.width = args.width; + } + if (args.height != null) { + this.height = args.height; + } + } + + this.molarityGrid = new MolarityGrid({height : this.height - 50}); +} + +AssemblyForm.prototype._getItems = function() { + return [ { + xtype : 'label', + forId : 'myFieldId', + text : 'List of previously defined macromolecules present in the assembly. This information will be used for additional cross-checks where possible', + margin : '15 0 20 10', + cls : "inline-help" + }, this.molarityGrid.getPanel() ]; +}; + +AssemblyForm.prototype._getButtons = function() { + return []; +}; + +AssemblyForm.prototype.getPanel = function() { + this.panel = Ext.create('Ext.form.Panel', { + width : this.width, + height : this.height, + margin : 10, + border : 0, + defaultType : 'textfield', + items : this._getItems(), + buttons : this._getButtons() + }); + return this.panel; +}; + + +/** It populates the form **/ +AssemblyForm.prototype.refresh = function(macromolecule) { + this.macromolecule = macromolecule; + this.molarityGrid.refresh(macromolecule); +}; + +AssemblyForm.prototype.input = function() { + return {}; +}; + + +AssemblyForm.prototype.test = function(targetId) { + var assemblyForm = new AssemblyForm(); + + var panel = assemblyForm.getPanel(); + panel.render(targetId); +}; +/** + * Edit the information of a buffer + * + * #onSaved + * #onRemoveAdditive + */ +function BufferForm() { var _this = this; - /** Widgets **/ - this.plotWidget = new PlotWidget({ - width : 650, - height : 490 + this.additiveGrid = new AdditiveGrid(); + this.additiveGrid.onRemoveButtonClicked.attach(function(sender, args) { + _this.onRemoveAdditive.notify(args); }); - /** Selected frames to be displayed **/ - this.selectedItems = { - frames : [], - averages : [], - subtractions : [] - }; - + this.onSaved = new Event(this); + this.onRemoveAdditive = new Event(this); } -DataReductionForm.prototype._parseSelectedItemsToIds = function(selectedArray) { - var ids = []; - if (selectedArray != null) { - for (var i = 0; i < selectedArray.length; i++) { - ids.push(selectedArray[i].id); - } - } - return ids; -}; - -DataReductionForm.prototype.onSelectionChanged = function(columnName, selected) { - if (selected != null) { - if (columnName == "Frames") { - this.selectedItems.frames = selected; - this.selectedItems.subtractions = []; - } - if (columnName == "Averages") { - this.selectedItems.averages = selected; - } - if (columnName == "Subtractions") { - this.selectedItems.frames = []; - this.selectedItems.subtractions = selected; - } - } - this.plotWidget.refresh(this._parseSelectedItemsToIds(this.selectedItems.frames), this._parseSelectedItemsToIds(this.selectedItems.subtractions)); -}; - -DataReductionForm.prototype._getFramesExtPanel = function(store, columnName, height) { - var _this = this; - - var selModel = Ext.create('Ext.selection.RowModel', { - allowDeselect : true, - mode : 'MULTI', - listeners : { - selectionchange : function(sm, selections) { - var selected = []; - for (var i = 0; i < selections.length; i++) { - selected.push(selections[i].raw); - } - _this.onSelectionChanged(columnName, selected); - } - } - }); - - return Ext.create('Ext.grid.Panel', { - store : store, - margin : 10, - height : height, - width : 200, - selModel : selModel, - columns : [ { - text : columnName, - dataIndex : 'fileName', - flex : 1 - } ], - viewConfig : { - } - }); +BufferForm.prototype.getBuffer = function() { + this.buffer.name = Ext.getCmp("buffer_name").getValue(); + this.buffer.acronym = Ext.getCmp("buffer_acronym").getValue(); + this.buffer.comments = Ext.getCmp("buffer_comments").getValue(); + this.buffer.ph = Ext.getCmp("buffer_ph").getValue(); + this.buffer.composition = Ext.getCmp("buffer_composition").getValue(); + this.buffer.bufferhasadditive3VOs = this.additiveGrid.getAdditives(); + return this.buffer; }; -DataReductionForm.prototype._getFramesPanel = function() { - var fields = [ 'fileName', 'type', 'id' ]; - - this.framesStore = Ext.create('Ext.data.Store', { - fields : fields, - sorters : 'fileName' - }); - - this.averagesStore = Ext.create('Ext.data.Store', { - fields : fields - }); - - this.subtractionStore = Ext.create('Ext.data.Store', { - fields : fields - }); - - var gridFrames = this._getFramesExtPanel(this.framesStore, "Frames", 375); - var gridAvgs = this._getFramesExtPanel(this.averagesStore, "Averages", 125); - var subtractionAvgs = this._getFramesExtPanel(this.subtractionStore, "Subtractions", 75); - - return { - xtype : 'container', - layout : 'vbox', - items : [ gridFrames, subtractionAvgs ] - }; -}; - -DataReductionForm.prototype._getImageContainer = function(name, help) { - var html = "
" + name + "
" +BufferForm.prototype._getTopPanel = function() { return { xtype : 'container', - layout : 'vbox', + layout : 'hbox', + border : 0, + frame : true, items : [ { - html : html, - margin : "5 0 0 0", - height : 95, - width : 100 + xtype : 'container', + layout : 'hbox', + items : [ { + xtype : 'container', + flex : 1, + border : false, + layout : 'anchor', + defaultType : 'textfield', + items : [ { + xtype : 'requiredtext', + id : 'buffer_name', + fieldLabel : 'Name', + name : 'name', + width : '200px', + value : this.buffer.name + }, { + xtype : 'requiredtext', + id : 'buffer_acronym', + fieldLabel : 'Acronym', + name : 'acronym', + width : '200px', + value : this.buffer.acronym + } ] + } ] }, { - xtype : 'label', - forId : 'myFieldId', - text : help, - margin : '5 0 0 0', - cls : "inline-help" + xtype : 'container', + flex : 1, + layout : 'anchor', + defaultType : 'textfield', + margin : '0 0 0 10', + items : [ { + id : 'buffer_ph', + fieldLabel : 'pH', + name : 'ph', + value : this.buffer.ph, + xtype : 'numberfield', + width : 200, + minValue : 0, + maxValue : 15 + }, { + xtype : 'requiredtext', + id : 'buffer_composition', + fieldLabel : 'Composition', + name : 'composition', + width : 200, + value : this.buffer.composition + } ] } ] - } + }; }; -DataReductionForm.prototype._getItems = function() { - var _this = this; - return [ { +BufferForm.prototype.getPanel = function(buffer) { + this.buffer = buffer; + this.panel = Ext.createWidget({ xtype : 'container', - layout : 'hbox', - items : [ - this._getFramesPanel(), - this.plotWidget.getPanel(), - { - xtype : 'panel', - width : 110, - frame : true, - margin : "10 5 5 5", - border : 0, - layout : 'vbox', - items : [ this._getImageContainer("scattering", "Scattering"), this._getImageContainer("guinier", "Guinier Region"), - this._getImageContainer("kratky", "Kratky Plot"), this._getImageContainer("gnom", "P(r) distribution") ] - } ] - } ] -}; - -DataReductionForm.prototype._getButtons = function() { - return []; -}; + layout : 'vbox', + style : { + padding : '10px' + }, + fieldDefaults : { + labelAlign : 'left', + labelWidth : 50 -DataReductionForm.prototype.getPanel = function() { - var _this = this; - this.panel = Ext.create('Ext.form.Panel', { - width : this.width, - height : this.height, - border : 0, - items : this._getItems(), - buttons : this._getButtons(), - listeners : { - afterrender : function() { - _this._populate(); - } - } + }, + items : [ this._getTopPanel(), { + id : 'buffer_comments', + xtype : 'textareafield', + name : 'comments', + fieldLabel : 'Comments', + width : '100%', + value : buffer.comments + }, this.additiveGrid.getPanel(buffer) ] }); return this.panel; }; -/** Populates could be call when the DOM is not filled yet **/ -DataReductionForm.prototype._populate = function() { -}; - -/** It populates the form * */ -DataReductionForm.prototype.refresh = function(subtractions) { - if (subtractions != null) { - for (var i = 0; i < subtractions.length; i++) { - /** Loading frame grids **/ - var subtraction = subtractions[i]; - var averages = [ { - fileName : BUI.getFileName(subtraction.bufferAverageFilePath), - type : 'bufferAvg', - id : subtraction.subtractionId - }, { - fileName : BUI.getFileName(subtraction.sampleAverageFilePath), - type : 'sampleAvg', - id : subtraction.subtractionId - } - - ]; - this.averagesStore.loadData(averages, true); - this.subtractionStore.loadData([ { - fileName : BUI.getFileName(subtraction.substractedFilePath), - type : 'SUBTRACTION', - id : subtraction.subtractionId - } ], true); - - var frames = []; - /** Buffers **/ - if (subtraction.bufferOneDimensionalFiles != null) { - if (subtraction.bufferOneDimensionalFiles.frametolist3VOs != null) { - for (var j = 0; j < subtraction.bufferOneDimensionalFiles.frametolist3VOs.length; j++) { - var frametolist3VO = subtraction.bufferOneDimensionalFiles.frametolist3VOs[j]; - if (frametolist3VO.frame3VO != null) { - frames.push({ - fileName : BUI.getFileName(frametolist3VO.frame3VO.filePath), - type : 'BUFFER', - id : frametolist3VO.frame3VO.frameId - }); - } - } - } - } - /** Samples **/ - if (subtraction.sampleOneDimensionalFiles != null) { - if (subtraction.sampleOneDimensionalFiles.frametolist3VOs != null) { - for (var j = 0; j < subtraction.sampleOneDimensionalFiles.frametolist3VOs.length; j++) { - var frametolist3VO = subtraction.sampleOneDimensionalFiles.frametolist3VOs[j]; - if (frametolist3VO.frame3VO != null) { - frames.push({ - fileName : BUI.getFileName(frametolist3VO.frame3VO.filePath), - type : 'SAMPLE', - id : frametolist3VO.frame3VO.frameId - }); - } - } - } - } - - this.framesStore.loadData(frames, true); - - /** Loading images **/ - this._displayImage("scattering", subtraction.subtractionId); - this._displayImage("kratky", subtraction.subtractionId); - this._displayImage("guinier", subtraction.subtractionId); - this._displayImage("gnom", subtraction.subtractionId); +BufferForm.prototype.input = function() { + return { + buffer : { + "bufferId" : 422, + "proposalId" : 10, + "safetyLevelId" : null, + "name" : "B1", + "acronym" : "B1", + "ph" : null, + "composition" : null, + "bufferhasadditive3VOs" : [], + "comments" : null } - } -}; - -DataReductionForm.prototype._displayImage = function(name, subtractionId) { - var url = BUI.getURL() + '&type=' + name + '&subtractionId=' + subtractionId; - var event = "OnClick= window.open('" + url + "')"; - document.getElementById(this.id + "_" + name).innerHTML = ''; -}; - -DataReductionForm.prototype.input = function() { - return {}; + }; }; -/** It populates the form **/ -DataReductionForm.prototype.test = function(targetId) { - var macromoleculeForm = new DataReductionForm(); - var panel = macromoleculeForm.getPanel(); +BufferForm.prototype.test = function(targetId) { + var bufferForm = new BufferForm(); + var panel = bufferForm.getPanel(bufferForm.input().buffer); panel.render(targetId); }; - -function PlotWidget(args) { - this.width = 600; - this.height = 600; - this.id = BUI.id(); - - this.linear = false; - - this.margin = "10 0 0 0"; - /** for each stat the max and minimum value when it is scaled in order to show correctly in the legend **/ - this.ranges = {}; + +/** + * @showTitle + * + * #onSaved + * #onAddPlates + * #onRemovePlates + **/ +function CaseForm(args) { + this.width = 700; + this.showTitle = true; if (args != null) { - if (args.width != null) { - this.width = args.width; - } - if (args.height != null) { - this.height = args.height; - } - if (args.margin != null) { - this.margin = args.margin; + if (args.showTitle != null) { + this.showTitle = args.showTitle; } } -} - -PlotWidget.prototype.getMenu = function() { var _this = this; - /** Actions buttons **/ - var actions = []; - actions.push("->"); - actions.push({ - text : "Export as Image", - scope : this, - icon : '../images/save.gif', - handler : function(item, pressed) { - var largeImage = document.createElement("img"); - largeImage.style.display = 'block'; - largeImage.style.width = 200 + "px"; - largeImage.style.height = 200 + "px"; - largeImage.setAttribute('src', Dygraph.Export.asCanvas(this.dygraphObject.dygraph).toDataURL()); - window.open(Dygraph.Export.asCanvas(this.dygraphObject.dygraph).toDataURL(), 'Image', ''); + this.stockSolutionGrid = new StockSolutionGrid({ + width : this.width - 10, + minHeight : 300, + height : 300, + tbar : true, + showTitle : true, + isPackedVisible : false, + btnAddExisting : true, + btnRemoveVisible : false, + btnUnpackVisible : true + }); + + /** When selecting existing stock solutions **/ + this.stockSolutionGrid.onStockSolutionSelected.attach(function(sender, stockSolutions) { + if (stockSolutions != null) { + for ( var i = 0; i < stockSolutions.length; i++) { + _this.saveStockSolution(stockSolutions[i]); + } } }); - return actions; -}; + /** it can be because it has been added a new one or removed **/ + this.stockSolutionGrid.onProposalChanged.attach(function(sender, stockSolution) { + if (stockSolution != null) { + _this.saveStockSolution(stockSolution); + } else { + BIOSAXS.proposal.onInitialized.attach(function() { + _this.stockSolutionGrid.refresh(BIOSAXS.proposal.getStockSolutionsByDewarId(_this.dewar.dewarId)); -/** Looks for the maximum value and then divide everything but that value **/ -PlotWidget.prototype.scaledData = function(data) { - for (var i = 0; i < data.length; i++) { - var values = this.getMaxAndMinValue(data[i]); - data[i] = this.divideValuesByMax(data[i], values.max); - this.ranges[data[i].label] = values; - } - return data; -}; + _this.stockSolutionGrid.grid.setLoading(false); + }); + BIOSAXS.proposal.init(); -/** Given a stat float[] and a max number it will divide each value by max **/ -PlotWidget.prototype.divideValuesByMax = function(stat, max) { - for (var j = 0; j < stat.data.length; j++) { - if (max != 0) { - stat.data[j] = Number(stat.data[j]) / max; - stat.std[j] = Number(stat.std[j]) / max; } - } - return stat; -}; -/** returns max value of a stat **/ -PlotWidget.prototype.getMaxAndMinValue = function(stat) { - var max = 0; - var min = stat.data[0]; - for (var j = 0; j < stat.data.length; j++) { - if (Number(stat.data[j]) > max) { - max = Number(stat.data[j]); - } - if (Number(stat.std[j]) > max) { - max = Number(stat.std[j]); - } - if (Number(stat.data[j]) < min) { - min = Number(stat.data[j]); - } - } - return { - max : Number(max), - min : Number(min) - }; -}; - -PlotWidget.prototype._renderDygraph = function(parsed, colors, labels) { - this.dygraphObject = new StdDevDyGraph(this.id, { - width : this.width - 20, - height : this.height - 40, - xlabel : "", }); - this.dygraphObject.draw(parsed, colors, labels); + this.onSaved = new Event(this); + this.onAddPlates = new Event(this); + this.onRemovePlates = new Event(this); +} +CaseForm.prototype.saveStockSolution = function(stockSolution) { + var _this = this; + var adapter = new BiosaxsDataAdapter(); + this.stockSolutionGrid.grid.setLoading("ISPyB: setting case to Stock solution"); + adapter.onSuccess.attach(function(sender, stockSolution) { + BIOSAXS.proposal.onInitialized.attach(function() { + _this.stockSolutionGrid.refresh(BIOSAXS.proposal.getStockSolutionsByDewarId(_this.dewar.dewarId)); + _this.stockSolutionGrid.grid.setLoading(false); + }); + BIOSAXS.proposal.init(); + }); + adapter.onError.attach(function(sender, data) { + _this.stockSolutionGrid.grid.setLoading(false); + BUI.showError(data); + }); + stockSolution.boxId = _this.dewar.dewarId; + adapter.saveStockSolution(stockSolution); }; -PlotWidget.prototype.getPanel = function() { - this.panel = Ext.create('Ext.panel.Panel', { - width : this.width, - height : this.height, - margin : this.margin, - tbar : this.getMenu(), - items : [ { - html : "", - id : this.id, - width : this.width, - height : this.height, - padding : 10, - margin : "0 0 0 -30", - border : 0 - } ] +CaseForm.prototype.fillStores = function() { + var _this = this; + this.panel.setLoading("Loading Labcontacts from database"); + + var proposal = BUI.getProposal(); + proposal.onDataRetrieved.attach(function(sender, data) { + _this.labContactForSendingStore.loadData(data, false); + _this.labContactForReturnStore.loadData(data, false); + _this.panel.setLoading(false); }); + proposal.getLabContactsByProposalId(); - return this.panel; }; -PlotWidget.prototype.getPoint = function(y, error) { - var minus = y - error; - var max = y + error; - - if (this.linear) { - return [ Math.abs(minus), y, Math.abs(max) ]; - } - if ((minus != 0) && (max != 0)) { - return [ Math.log(Math.abs(minus)), Math.log(y), Math.log(Math.abs(max)) ]; - } else { - return [ Math.log(y), Math.log(y), Math.log(y) ]; - } - +CaseForm.prototype.refresh = function(dewar) { + this.setDewar(dewar); + this.stockSolutionGrid.refresh(BIOSAXS.proposal.getStockSolutionsByDewarId(dewar.dewarId)); }; -PlotWidget.prototype.getDataPlot = function(frames, subtractions, models, fits, rbms) { - var files = []; - var labels = [ "Intensity" ]; - if (frames != null) { - for (var i = 0; i < frames.length; i++) { - files.push(frames[i].data); - labels.push(frames[i].fileName); - } - } - function splitData(data, column, errorColumn, name){ - var result = [] - for (var j = 0; j < data.length; j++) { - console.log(data[j][column]); - result.push([j,data[j][column],data[j][errorColumn]]);//[0, data[i][column],0]]); - } - files.push(result); - labels.push(name); - } - - if (subtractions != null) { - for (var i = 0; i < subtractions.length; i++) { - /** For subtraction **/ - files.push(subtractions[i].subtraction.data); - labels.push(subtractions[i].subtraction.fileName); - /** For sample average **/ -// files.push(subtractions[i].sampleAvg.data); -// labels.push(subtractions[i].sampleAvg.fileName); - /** For buffer average **/ -// files.push(subtractions[i].bufferAvg.data); -// labels.push(subtractions[i].bufferAvg.fileName); - } - } - - if (models != null) { - for (var i = 0; i < models.length; i++) { - for ( var key in models[i]) { - splitData(models[i][key].fir.data, 1, 2, "Intensity"); - splitData(models[i][key].fir.data, 3, 3, "Fit"); - } - } - } - - if (fits != null) { - for (var i = 0; i < fits.length; i++) { - for ( var key in fits[i]) { - - /** adding fit file to be plotted **/ - if (fits[i][key].fit.data[0].length == 3){ - splitData(fits[i][key].fit.data, 1, 1, "Intensity"); - splitData(fits[i][key].fit.data, 2, 2, "Fit"); - } - - /** s, Iexp(s), err, Ifit(s). **/ - if (fits[i][key].fit.data[0].length == 4){ - splitData(fits[i][key].fit.data, 1, 2, "Intensity"); - splitData(fits[i][key].fit.data, 3, 3, "Fit"); - } - - if (fits[i][key].fit.data[0].length == 5){ - /** X Intensity Fit Error Residues **/ - - splitData(fits[i][key].fit.data, 1, 3, "Intensity"); - splitData(fits[i][key].fit.data, 2, 2, "Fit"); - } - } - } - } - - var dataPoints = []; - if (files.length > 0) { - for (var i = 0; i < files[0].length; i++) { - dataPoints.push([ files[0][i][0], this.getPoint(files[0][i][1], files[0][i][2]) ]); - } - if (files.length > 1) { - for (var i = 1; i < files.length; i++) { - for (var j = 0; j < dataPoints.length; j++) { - if (files[i][j] != null){ - dataPoints[j].push(this.getPoint(files[i][j][1], files[i][j][2])); - } - else{ - dataPoints[j].push([0,0,0]); - } - } - } - } - } +CaseForm.prototype.getDewar = function() { + this.dewar.code = Ext.getCmp("dewar_code").getValue(); + this.dewar.comments = Ext.getCmp("dewar_comments").getValue(); + this.dewar.trackingNumberFromSynchrotron = Ext.getCmp("dewar_trackingNumberFromSynchrotron").getValue(); + this.dewar.trackingNumberToSynchrotron = Ext.getCmp("dewar_trackingNumberToSynchrotron").getValue(); + this.dewar.transportValue = Ext.getCmp("dewar_transportValue").getValue(); + this.dewar.storageLocation = Ext.getCmp("dewar_storageLocation").getValue(); + this.dewar.firstExperimentId = this.macromoleculeCombo.getValue(); + return this.dewar; +}; - return { - dataPoints : dataPoints, - labels : labels +CaseForm.prototype.setDewar = function(dewar) { + this.dewar = dewar; + Ext.getCmp("dewar_code").setValue(this.dewar.code); + Ext.getCmp("dewar_dewarStatus").setText(new String(this.dewar.dewarStatus).toUpperCase()); + Ext.getCmp("dewar_comments").setValue(this.dewar.comments); + Ext.getCmp("dewar_trackingNumberFromSynchrotron").setValue(this.dewar.trackingNumberFromSynchrotron); + Ext.getCmp("dewar_trackingNumberToSynchrotron").setValue(this.dewar.trackingNumberToSynchrotron); + Ext.getCmp("dewar_transportValue").setValue(this.dewar.transportValue); + Ext.getCmp("dewar_storageLocation").setValue(this.dewar.storageLocation); + if (dewar.sessionVO != null) { + this.macromoleculeCombo.setValue(dewar.sessionVO.sessionId); } }; -PlotWidget.prototype.refresh = function(frames, subtractions, models, fits, rbms, colors) { - - var _this = this; - this.panel.setLoading("Reading Files"); - - var dataAdapter = new BiosaxsDataAdapter(); - dataAdapter.onSuccess.attach(function(sender, data) { - _this.panel.setLoading("Rendering"); - var parsed = _this.getDataPlot(data.frames, data.subtractions, data.models, data.fits, rbms); - _this._renderDygraph(parsed.dataPoints, colors, parsed.labels); - _this.panel.setLoading(false); - }); - dataAdapter.onError.attach(function(sender, data) { - _this.panel.setLoading(false); +CaseForm.prototype.getSessionCombo = function() { + this.macromoleculeCombo = BIOSAXS_COMBOMANAGER.getComboSessions(BIOSAXS.proposal.getSessions(), { + labelWidth : 100, + margin : '5 0 00 0', + width : 250 }); - dataAdapter.getDataPlot(frames, subtractions, models, fits, rbms); -}; - -PlotWidget.prototype.input = function() { - return DATADOC.getHPLCData(); + return this.macromoleculeCombo; }; - -/** - * Fit form - * - * @witdh - * @height - */ -function FitForm(args) { - this.id = BUI.id(); - this.width = null; - this.height = null; - - if (args != null) { - if (args.width != null) { - this.width = args.width; - } - if (args.height != null) { - this.height = args.height; - } - } +CaseForm.prototype.getInformationPanel = function() { + if (this.panel == null) { + this.informationPanel = Ext.create('Ext.form.Panel', { + width : this.width - 10, + border : 0, + items : [ { + xtype : 'container', + margin : "2 2 2 2", + collapsible : false, + defaultType : 'textfield', + layout : 'anchor', + items : [ { + xtype : 'container', + layout : 'hbox', + items : [ { + xtype : 'requiredtext', + fieldLabel : 'Code', + allowBlank : false, + name : 'code', + id : 'dewar_code', + anchor : '50%' + }, { + xtype : 'label', + margin : '0 0 0 20', + readOnly : true, + id : 'dewar_dewarStatus', + anchor : '50%' + } ] + }, this.getSessionCombo(), { + margin : '5 0 0 0', + xtype : 'textareafield', + name : 'comments', + id : 'dewar_comments', + width : this.width - 50, + fieldLabel : 'Comments' + } ] + }, { + xtype : 'fieldset', + title : 'Courier Accounts Details for Return', + collapsible : false, + layout : 'anchor', + defaults : { + anchor : '100%' + }, + items : [ { + xtype : 'container', + layout : 'hbox', + items : [ { + xtype : 'textfield', + labelWidth : 200, + width : 280, + fieldLabel : 'Track Number From Synchrotron', + id : 'dewar_trackingNumberFromSynchrotron' + }, { + xtype : 'numberfield', + width : 190, + labelWidth : 110, + margin : '0 0 0 30', + fieldLabel : 'Transport Value', + id : 'dewar_transportValue' + } - var _this = this; - - /** Widgets **/ - this.fitGrid = new FitStructureToExperimentDataGrid({ - width : null, - height : 200 - }); + ] + }, { + xtype : 'container', + layout : 'hbox', + margin : '10 0 0 0', + items : [ - this.fitGrid.onSelected.attach(function(sender, fits) { - var modelsIdList = []; - for ( var i in fits) { - modelsIdList.push(fits[i].fitStructureToExperimentalDataId); - } - _this._renderPlot(_this.subtractions[0].subtractionId, modelsIdList); -// var adapter = new BiosaxsDataAdapter(); -// adapter.onSuccess.attach(function(sender, data) { -// -// }); -// adapter.getScatteringCurveByFrameIdsList([], [], [], ids); - }); - - - /** Dygraph Widget that plots fir files**/ - this.plotWidget = new PlotWidget({ - width : 745, - height : 300, - margin : "10 0 10 10" - }); -} + { + xtype : 'textfield', + labelWidth : 200, + width : 280, + fieldLabel : 'Track Number To Synchrotron', + id : 'dewar_trackingNumberToSynchrotron' + }, { + xtype : 'textfield', + margin : '0 0 0 30', + width : 190, + labelWidth : 110, + fieldLabel : 'Storage Location', + id : 'dewar_storageLocation' + } -FitForm.prototype._renderPlot = function(subtractionId, modelsIdList) { - /** Trying to plot tje subtraction and the models **/ - try { - var colors = [ "#669900", "#0000FF" ]; - - this.plotWidget.refresh([], [ ], [], modelsIdList, [], colors); - } catch (e) { - console.log(e); + ] + } ] + } ] + }); } -}; - -FitForm.prototype._getItems = function() { - var _this = this; - return [ { - xtype : 'label', - forId : 'myFieldId', - text : 'INLINE HELP: To be updated', - margin : '15 0 20 10', - cls : "inline-help" - }, this.fitGrid.getPanel(), this.plotWidget.getPanel() ] - -}; -FitForm.prototype._getButtons = function() { - return []; + return this.informationPanel; }; -FitForm.prototype.getPanel = function() { - var _this = this; - this.panel = Ext.create('Ext.form.Panel', { +CaseForm.prototype.getPanel = function(dewar) { + this.dewar = dewar; + this.panel = Ext.createWidget({ + xtype : 'container', + layout : 'vbox', width : this.width, - height : this.height, - margin : 10, - border : 1, - defaultType : 'textfield', - items : this._getItems(), - buttons : this._getButtons(), - listeners : { - afterrender : function() { - _this._populate(); + border : 0, + items : [ { + items : { + xtype : "container", + layout : "vbox", + margin : "5 5 5 5", + items : [ this.getInformationPanel(dewar), this.stockSolutionGrid.getPanel() ] } - } + } ] }); + + this.refresh(dewar); return this.panel; -}; -/** Populates could be call when the DOM is not filled yet **/ -FitForm.prototype._populate = function() { }; -/** It populates the form * */ -FitForm.prototype.refresh = function(subtractions) { - this.subtractions = subtractions; - this.fitGrid.refresh(subtractions); -}; +CaseForm.prototype.input = function() { + return { + dewar : { + "dewarId" : 305861, + "code" : "ESRF-TEST", + "comments" : "comments", + "storageLocation" : "FRIDGE", + "dewarStatus" : "opened", + "timeStamp" : null, + "isStorageDewar" : null, + "barCode" : "ESRF305861", + "customsValue" : null, + "transportValue" : null, + "trackingNumberToSynchrotron" : "3333", + "trackingNumberFromSynchrotron" : "224466", + "type" : "Dewar", + "sessionVO" : { + "sessionId" : 31697, + "expSessionPk" : null, + "projectCode" : null, + "startDate" : "2012 07 21", + "endDate" : "2012 07 23", + "beamlineName" : "BM29", + "scheduled" : 1, + "nbShifts" : 2, + "comments" : null, + "beamlineOperator" : "PERNOT P", + "usedFlag" : null, + "sessionTitle" : null, + "structureDeterminations" : null, + "dewarTransport" : null, + "databackupFrance" : null, + "databackupEurope" : null, + "visit_number" : null, + "operatorSiteNumber" : "14061", + "timeStamp" : "2012 04 25" + } + }, + proposal : { + "assemblies" : [], + "sessions" : [ { + "sessionId" : 31697, + "expSessionPk" : null, + "projectCode" : null, + "startDate" : "2012 07 21", + "endDate" : "2012 07 23", + "beamlineName" : "BM29", + "scheduled" : 1, + "nbShifts" : 2, + "comments" : null, + "beamlineOperator" : "PERNOT P", + "usedFlag" : null, + "sessionTitle" : null, + "structureDeterminations" : null, + "dewarTransport" : null, + "databackupFrance" : null, + "databackupEurope" : null, + "visit_number" : null, + "operatorSiteNumber" : "14061", + "timeStamp" : "2012 04 25" + } ], + "labcontacts" : [ { + "labContactId" : 787, + "personVO" : { + "personId" : 304252, + "personUUID" : null, + "familyName" : "KIM", + "givenName" : "Henry", + "title" : null, + "emailAddress" : "henry-sung.kim@ibs.fr", + "phoneNumber" : "", + "login" : "", + "passwd" : "", + "faxNumber" : "" + }, + "cardName" : "KIM-Institut de Bio", + "defaultCourrierCompany" : "22", + "courierAccount" : "", + "billingReference" : "", + "dewarAvgCustomsValue" : 0, + "dewarAvgTransportValue" : 0 + } ], + "buffers" : [ { + "bufferId" : 811, + "proposalId" : 3124, + "safetyLevelId" : null, + "name" : "EDTA", + "acronym" : "EDTA", + "ph" : null, + "composition" : "", + "bufferhasadditive3VOs" : [], + "comments" : "" + }, { + "bufferId" : 810, + "proposalId" : 3124, + "safetyLevelId" : null, + "name" : "HEPES", + "acronym" : "HEPES", + "ph" : null, + "composition" : "", + "bufferhasadditive3VOs" : [], + "comments" : "" + } ], + "shippings" : [ { + "shippingId" : 304107, + "shippingName" : "TEST", + "deliveryAgentAgentName" : null, + "deliveryAgentShippingDate" : null, + "deliveryAgentDeliveryDate" : null, + "deliveryAgentAgentCode" : null, + "deliveryAgentFlightCode" : null, + "shippingStatus" : "opened", + "timeStamp" : "2013 09 25", + "laboratoryId" : null, + "isStorageShipping" : null, + "creationDate" : "2013 09 25", + "comments" : "test", + "sendingLabContactVO" : { + "labContactId" : 787, + "personVO" : { + "personId" : 304252, + "personUUID" : null, + "familyName" : "KIM", + "givenName" : "Henry", + "title" : null, + "emailAddress" : "henry-sung.kim@ibs.fr", + "phoneNumber" : "", + "login" : "", + "passwd" : "", + "faxNumber" : "" + }, + "cardName" : "KIM-Institut de Bio", + "defaultCourrierCompany" : "22", + "courierAccount" : "", + "billingReference" : "", + "dewarAvgCustomsValue" : 0, + "dewarAvgTransportValue" : 0 + }, + "returnLabContactVO" : { + "labContactId" : 787, + "personVO" : { + "personId" : 304252, + "personUUID" : null, + "familyName" : "KIM", + "givenName" : "Henry", + "title" : null, + "emailAddress" : "henry-sung.kim@ibs.fr", + "phoneNumber" : "", + "login" : "", + "passwd" : "", + "faxNumber" : "" + }, + "cardName" : "KIM-Institut de Bio", + "defaultCourrierCompany" : "22", + "courierAccount" : "", + "billingReference" : "", + "dewarAvgCustomsValue" : 0, + "dewarAvgTransportValue" : 0 + }, + "returnCourier" : null, + "dateOfShippingToUser" : null, + "shippingType" : "DewarTracking", + "dewarVOs" : [ { + "dewarId" : 305861, + "code" : "ESRF-TEST", + "comments" : "comments", + "storageLocation" : "FRIDGE", + "dewarStatus" : "opened", + "timeStamp" : null, + "isStorageDewar" : null, + "barCode" : "ESRF305861", + "customsValue" : null, + "transportValue" : null, + "trackingNumberToSynchrotron" : "3333", + "trackingNumberFromSynchrotron" : "224466", + "type" : "Dewar", + "sessionVO" : { + "sessionId" : 31697, + "expSessionPk" : null, + "projectCode" : null, + "startDate" : "2012 07 21", + "endDate" : "2012 07 23", + "beamlineName" : "BM29", + "scheduled" : 1, + "nbShifts" : 2, + "comments" : null, + "beamlineOperator" : "PERNOT P", + "usedFlag" : null, + "sessionTitle" : null, + "structureDeterminations" : null, + "dewarTransport" : null, + "databackupFrance" : null, + "databackupEurope" : null, + "visit_number" : null, + "operatorSiteNumber" : "14061", + "timeStamp" : "2012 04 25" + } + } ] + } ], + "macromolecules" : [ { + "macromoleculeId" : 5933, + "safetylevelId" : null, + "proposalId" : 3124, + "name" : "A", + "acronym" : "A", + "molecularMass" : "", + "extintionCoefficient" : "", + "sequence" : null, + "creationDate" : null, + "comments" : "", + "macromoleculeregion3VOs" : [], + "stoichiometry3VOsForHostMacromoleculeId" : [], + "structure3VOs" : [] + } ], + "stockSolutions" : [ { + "stockSolutionId" : 6, + "proposalId" : 3124, + "macromoleculeId" : 5933, + "bufferId" : 811, + "instructionSet3VO" : null, + "boxId" : 305861, + "storageTemperature" : "20", + "volume" : "300", + "concentration" : "1.2", + "comments" : "Buffer EDTA with A", + "name" : "A_EDTA_1.2", + "samples" : [] + } ] + } -FitForm.prototype.input = function() { - return {}; + }; }; -/** It populates the form **/ -FitForm.prototype.test = function(targetId) { - var macromoleculeForm = new FitForm(); - var panel = macromoleculeForm.getPanel(); - panel.render(targetId); -}; - -/** - * Example form - * - * @witdh - * @height - */ -function RigidBodyModelingResultForm(args) { +CaseForm.prototype.test = function(targetId) { + var caseForm = new CaseForm(); + BIOSAXS.proposal = new Proposal(caseForm.input().proposal); + var panel = caseForm.getPanel(caseForm.input().dewar); + panel.render(targetId); +}; + +/** + * Example form + * + * @witdh + * @height + */ +function ExampleForm(args) { + this.id = BUI.id(); + this.width = 700; + this.height = 500; + + if (args != null) { + if (args.width != null) { + this.width = args.width; + } + if (args.height != null) { + this.height = args.height; + } + } +} + +ExampleForm.prototype._getItems = function() { + return [{ + fieldLabel : 'First Name', + name : 'first', + allowBlank : false + }, { + fieldLabel : 'Last Name', + name : 'last', + allowBlank : false + } ]; +}; + +ExampleForm.prototype._getItems = function() { + return [ ]; +}; + +ExampleForm.prototype._getButtons = function() { + return [ ]; +}; + +ExampleForm.prototype.getPanel = function() { + this.panel = Ext.create('Ext.form.Panel', { + width : this.width, + height : this.height, + margin : 10, + border : 1, + defaultType : 'textfield', + items : this._getItems(), + buttons : this._getButtons() + }); + return this.panel; +}; + +/** It populates the form **/ +ExampleForm.prototype.refresh = function(macromolecule) { +}; + +function ExperimentForm(args) { this.id = BUI.id(); - this.width = null; - this.height = null; if (args != null) { - if (args.width != null) { - this.width = args.width; - } - if (args.height != null) { - this.height = args.height; - } } - var _this = this; - /** Widgets **/ - this.rigidModelGrid = new RigidModelGrid({ - width : null, - height : 200 + this.onSaved = new Event(this); +} + +ExperimentForm.prototype._getItems = function(experiment) { + this.experiment = experiment; + var typeCombo = Ext.create('Ext.form.ComboBox', { + id : this.id + 'type', + fieldLabel : 'Type', + store : [ "STATIC", "CALIBRATION", "HPLC" ], + queryMode : 'local', + labelWidth : 120, + displayField : 'name', + valueField : 'name', + value : experiment.json.type, + disabled : (experiment.json.type == 'TEMPLATE') }); - this.rigidModelGrid.onSelected.attach(function(sender, fits){ - var ids = []; - for ( var i in fits) { - ids.push(fits[i].fitStructureToExperimentalDataId); + var items = []; + + + if (experiment.json.type == "HPLC" ){ + var typeMacromolecule = BIOSAXS_COMBOMANAGER.getComboMacromoleculeByMacromolecules(BIOSAXS.proposal.getMacromolecules(), {labelWidth : 120, width: "120px"}); + if (experiment.getHPLCMacromolecule() != null){ + typeMacromolecule.setValue(experiment.getHPLCMacromolecule().macromoleculeId); + items.push(typeMacromolecule); } - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, data){ -// debugger - - }); - - adapter.getScatteringCurveByFrameIdsList([], [], [], ids); + } + + + items.push(typeCombo, { + id : this.id + 'name', + xtype : 'textfield', + fieldLabel : 'Name', + labelWidth : 120, + width : '100%', + value : experiment.json.name + }, { + id : this.id + 'comments', + xtype : 'textareafield', + name : 'comments', + fieldLabel : 'Comments', + labelWidth : 120, + height : 120, + width : '100%', + value : experiment.json.comments }); - -} - -RigidBodyModelingResultForm.prototype._getItems = function() { - var _this = this; - return [ { - xtype : 'label', - forId : 'myFieldId', - text : 'INLINE HELP: To be updated', - margin : '15 0 20 10', - cls : "inline-help" - }, this.rigidModelGrid.getPanel() ] - -}; - -RigidBodyModelingResultForm.prototype._getButtons = function() { - return []; + return items; }; - -RigidBodyModelingResultForm.prototype.getPanel = function() { +ExperimentForm.prototype.getPanel = function(experiment) { var _this = this; - this.panel = Ext.create('Ext.form.Panel', { - width : this.width, - height : this.height, - margin : 10, - border : 1, - defaultType : 'textfield', - items : this._getItems(), - buttons : this._getButtons(), - listeners : { - afterrender : function() { - _this._populate(); - - } - } + + this.panel = Ext.createWidget({ + xtype : 'container', + layout : 'vbox', + border : 0, + style : { + padding : '10px' + }, + fieldDefaults : { + labelAlign : 'left', + labelWidth : 50 + }, + items : this._getItems(experiment) }); return this.panel; }; -/** Populates could be call when the DOM is not filled yet **/ -RigidBodyModelingResultForm.prototype._populate = function() { -}; - -/** It populates the form * */ -RigidBodyModelingResultForm.prototype.refresh = function(subtractions) { - this.rigidModelGrid.refresh(subtractions); -}; - -RigidBodyModelingResultForm.prototype.input = function() { - return {}; +ExperimentForm.prototype.input = function() { + return new ExperimentHeaderForm().input(); }; -/** It populates the form **/ -RigidBodyModelingResultForm.prototype.test = function(targetId) { - var macromoleculeForm = new RigidBodyModelingResultForm(); - var panel = macromoleculeForm.getPanel(); +ExperimentForm.prototype.test = function(targetId) { + var experimentForm = new ExperimentForm(); + var panel = experimentForm.getPanel(new Experiment(experimentForm.input().experiment)); panel.render(targetId); }; /** - * Example form + * Shows the header for the experiments changing the color and parameters depending on experiment type * - * @witdh - * @height */ -function SuperpositionForm(args) { +function ExperimentHeaderForm(args) { this.id = BUI.id(); - this.width = null; - this.height = null; + this.backgroundColor = '#FFFFFF'; +} - if (args != null) { - if (args.width != null) { - this.width = args.width; - } - if (args.height != null) { - this.height = args.height; +ExperimentHeaderForm.prototype.getHTMLSource = function(experiment) { + var html = BUI.createFormLabel("Name :", experiment.json.name, 75, 400, this.backgroundColor); + if (experiment.json.type == "HPLC") { + if (experiment.getHPLCMacromolecule() != null){ + html = html + BUI.createFormLabel("Molecule :", experiment.getHPLCMacromolecule().acronym, 75, 400, this.backgroundColor); } } + else{ + html = html + BUI.createFormLabel("Type :", experiment.json.type, 75, 400, this.backgroundColor); + } + + html = html + BUI.createFormLabel("Date :", experiment.json.creationDate, 75, 400, this.backgroundColor); + return html; +}; - var _this = this; - - /** Widgets **/ - this.superpositionGrid = new SuperpositionGrid({ - width : null, - height : 200 - }); - - this.superpositionGrid.onSelected.attach(function(sender, superpositions) { - var ids = []; - for ( var i in superpositions) { - ids.push(superpositions[i].superpositionId); - } -// _this._renderAbinitio(ids); -// _this.aprioriPDBViewer.refresh(); - _this._renderAligned(ids); - // getAlignedPDBContentBySuperpositionList - }); - - /** PDB viewer **/ -// this.abinitioPDBViewer = new PDBViewer({ -// width : 860 / 2, -// height : 300 / 2, -// margin : 0 -// }); -// -// /** PDB viewer **/ -// this.aprioriPDBViewer = new StructurePDBViewer({ -// width : 860 / 2, -// height : 300 / 2, -// margin : 0 -// }); +ExperimentHeaderForm.prototype.getHTMLDownload = function(experiment) { + var bgcolor = "background-color:" + this.backgroundColor + ";"; + var html = "
"; + if ((experiment.json.type == "CALIBATION") || (experiment.json.type == "STATIC")) { + html = html + " Download Source File
"; + html = html + + ""; + } + if (experiment.json.type == "TEMPLATE") { + html = html + + " Download Source File
"; + } - this.alignedPDBViewer = new AlignedSuperpositionPDBViewer({ - width : 860 , - height : 300 - }); + if (experiment.json.type == "HPLC") { + html = html + " Download h5 File
"; + } -} + return html; +}; -SuperpositionForm.prototype._renderAligned = function(modelsIdList) { - /** Trying to plot the PDB file **/ - try { - this.alignedPDBViewer.refresh(modelsIdList); - } catch (e) { - console.log(e); - } +ExperimentHeaderForm.prototype.getTopPanel = function(experiment) { + return { + xtype : 'container', + layout : 'hbox', + items : [ { + margin : "0 0 0 0", + width : 475, + border : 0, + html : this.getHTMLSource(experiment) + }, { + margin : "0 0 0 0", + width : 475, + border : 0, + html : this.getHTMLDownload(experiment) + } ] + }; }; -SuperpositionForm.prototype._renderAbinitio = function(modelsIdList) { - /** Trying to plot the PDB file **/ - try { - var viz = []; - for (var i = 0; i < modelsIdList.length; i++) { - viz.push({ - modelId : modelsIdList[i], - color : "0xFF6600", - opacity : 0.8 +ExperimentHeaderForm.prototype.getButton = function(experiment) { + var _this = this; + return Ext.create('Ext.Button', { + text : 'EDIT', + minWidth : '100', + margin : '10 0 0 30', + handler : function() { + var experimentWindow = new ExperimentWindow(); + experimentWindow.onSaved.attach(function(sender, data) { + _this.experiment.json.name = data.name; + _this.experiment.json.type = data.type; + _this.experiment.json.comments = data.comments; + _this.panel.remove(_this.panel.items.items[0]); + _this.panel.remove(_this.panel.items.items[0]); + _this.panel.insert(_this.getTopPanel(_this.experiment)); + _this.panel.insert(_this.getBottomPanel(_this.experiment)); }); + experimentWindow.show(experiment); } - this.abinitioPDBViewer.refresh(viz); - } catch (e) { - console.log(e); - } + }); }; -SuperpositionForm.prototype._getItems = function() { - var _this = this; - return [ { - xtype : 'label', - forId : 'myFieldId', - text : 'INLINE HELP: To be updated', - margin : '15 0 20 10', - cls : "inline-help" - }, this.superpositionGrid.getPanel(), { +ExperimentHeaderForm.prototype.getBottomPanel = function(experiment) { + return { xtype : 'container', layout : 'hbox', - items : [ -// { -// xtype : 'container', -// layout : 'vbox', -// items : [ this.abinitioPDBViewer.getPanel(), this.aprioriPDBViewer.getPanel() -// -// ] -// }, - - this.alignedPDBViewer.getPanel() ] - } ] - -}; - -SuperpositionForm.prototype._getButtons = function() { - return []; + margin : '10 0 0 0', + items : [ this.getComments(experiment), this.getButton(experiment) ] + }; }; -SuperpositionForm.prototype.getPanel = function() { - var _this = this; - this.panel = Ext.create('Ext.form.Panel', { - width : this.width, - height : this.height, - margin : 10, - border : 1, - defaultType : 'textfield', - items : this._getItems(), - buttons : this._getButtons(), - listeners : { - afterrender : function() { - _this._populate(); - - } - } - }); - return this.panel; +ExperimentHeaderForm.prototype.getComments = function(experiment) { + return { + xtype : 'textareafield', + labelStyle : 'font-weight: bold;', + name : 'comments', + fieldLabel : 'Comments', + labelWidth : 70, + height : 40, + minWidth : '450', + readOnly : true, + value : experiment.json.comments + }; }; -/** Populates could be call when the DOM is not filled yet **/ -SuperpositionForm.prototype._populate = function() { -}; +ExperimentHeaderForm.prototype.getPanel = function(experiment) { + this.experiment = experiment; + + if (experiment.json.type == 'CALIBRATION') { + this.backgroundColor = '#EFFBFB'; + } + if (experiment.json.type == 'TEMPLATE') { + this.backgroundColor = '#E0F8E6'; + } -/** It populates the form * */ -SuperpositionForm.prototype.refresh = function(subtractions) { - this.subtractions = subtractions; - this.superpositionGrid.refresh(subtractions); + this.panel = Ext.create('Ext.container.Container', { + frame : false, + layout : 'vbox', + padding : 5, + bodyPadding : '5 5 0 0', + width : 890, + margin : '0 0 10 0', + height : 120, + style : { + borderColor : '#99bce8', + borderStyle : 'solid', + borderWidth : '1px', + 'background-color' : this.backgroundColor + }, + fieldDefaults : { + msgTarget : 'side', + labelWidth : 100 + }, + items : [ this.getTopPanel(experiment), this.getBottomPanel(experiment) ] + }); + return this.panel; }; -SuperpositionForm.prototype.input = function() { - return {}; +ExperimentHeaderForm.prototype.input = function() { + return { + experiment : DATADOC.getExperiment_10() + }; }; -/** It populates the form **/ -SuperpositionForm.prototype.test = function(targetId) { - var macromoleculeForm = new SuperpositionForm(); - var panel = macromoleculeForm.getPanel(); +ExperimentHeaderForm.prototype.test = function(targetId) { + var experimentHeaderForm = new ExperimentHeaderForm(); + var panel = experimentHeaderForm.getPanel(new Experiment(experimentHeaderForm.input().experiment)); panel.render(targetId); + }; /** - * Example form + * Macromolecule form with the general parameters of a macromolecule * * @witdh * @height + * + * #onSave button save has been clicked + * #onClose button close has been clicked */ -function AssemblyForm(args) { +function MacromoleculeForm(args) { this.id = BUI.id(); this.width = 700; this.height = 500; @@ -6059,15944 +7053,9793 @@ function AssemblyForm(args) { this.height = args.height; } } - - this.molarityGrid = new MolarityGrid({height : this.height - 50}); + + /** Events **/ + this.onSave = new Event(this); + this.onClose = new Event(this); } -AssemblyForm.prototype._getItems = function() { - return [ { - xtype : 'label', - forId : 'myFieldId', - text : 'List of previously defined macromolecules present in the assembly. This information will be used for additional cross-checks where possible', - margin : '15 0 20 10', - cls : "inline-help" - }, this.molarityGrid.getPanel() ]; -}; - -AssemblyForm.prototype._getButtons = function() { - return []; +/** Type : is the Ext type then requiredtext or textfield * */ +MacromoleculeForm.prototype._getFieldTextWithHelp = function(type, fieldLabel, fieldName, help) { + return Ext.create('Ext.container.Container', { + margin : "10 0 0 10", + items : [ { + xtype : type, + fieldLabel : fieldLabel, + name : fieldName, + id : this.id + fieldName + }, { + xtype : 'label', + forId : 'myFieldId', + text : help, + margin : "5 0 0 105", + cls : "inline-help" + } ] + }); }; -AssemblyForm.prototype.getPanel = function() { - this.panel = Ext.create('Ext.form.Panel', { - width : this.width, - height : this.height, - margin : 10, - border : 0, - defaultType : 'textfield', - items : this._getItems(), - buttons : this._getButtons() +MacromoleculeForm.prototype._getNumericWithHelp = function(type, fieldLabel, fieldName, help) { + return Ext.create('Ext.container.Container', { + margin : "0 0 0 10", + items : [ { + xtype : type, + fieldLabel : fieldLabel, + name : fieldName, + id : this.id + fieldName, + decimalPrecision : 6, + width : 220 + }, { + xtype : 'label', + forId : 'myFieldId', + text : help, + margin : "5 0 0 10", + cls : "inline-help" + } ] }); - return this.panel; }; - -/** It populates the form **/ -AssemblyForm.prototype.refresh = function(macromolecule) { - this.macromolecule = macromolecule; - this.molarityGrid.refresh(macromolecule); +MacromoleculeForm.prototype._getButtons = function() { + var _this = this; + return [ { + text : 'Save', + handler : function() { + _this._save(); + } + },{ + text : 'Close', + handler : function() { + _this.onClose.notify(); + + } + } ]; }; -AssemblyForm.prototype.input = function() { - return {}; +/** It persits the macromolecule in the database **/ +MacromoleculeForm.prototype._persist = function(macromoleculeId, acronym, name, molecularMass, extintionCoefficient, comments, refractiveIndex, solventViscosity) { + + /** Checking not duplicated acronym **/ + if (((macromoleculeId == null) && (BIOSAXS.proposal.getMacromoleculeByAcronym(acronym) != null))== true){ + BUI.showError("Duplicated acronym"); + return; + } + + + if (macromoleculeId == null){ + /** new macromolecule **/ + this.macromolecule = {}; + this.macromolecule.macromoleculeId = null; + } + else{ + this.macromolecule.macromoleculeId = macromoleculeId; + } + + this.macromolecule["acronym"] = acronym; + this.macromolecule["name"] = name; + this.macromolecule["molecularMass"] = molecularMass; + this.macromolecule["extintionCoefficient"] = extintionCoefficient; + this.macromolecule["comments"] = comments; + this.macromolecule["symmetry"] = Ext.getCmp(this.id + 'comboSym').getValue(); + this.macromolecule["refractiveIndex"] = refractiveIndex; + this.macromolecule["solventViscosity"] = solventViscosity; + + var _this = this; + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, proposal) { + /** Updating the proposal global object **/ + BIOSAXS.proposal.setItems(proposal); + _this.panel.setLoading(false); + + var saved = BIOSAXS.proposal.getMacromoleculeByAcronym(acronym); + /** Refreshing to check that everything is ok **/ + _this.refresh(saved); + _this.onSave.notify(saved); + }); + + this.panel.setLoading("Saving Macromolecule") + adapter.saveMacromolecule(this.macromolecule); }; - -AssemblyForm.prototype.test = function(targetId) { - var assemblyForm = new AssemblyForm(); +/** Save the macromolecule in the DB **/ +MacromoleculeForm.prototype._save = function() { - var panel = assemblyForm.getPanel(); - panel.render(targetId); -}; -/** - * Edit the information of a buffer - * - * #onSaved - * #onRemoveAdditive - */ -function BufferForm() { var _this = this; + + var acronym = this._getField("acronym"); + var name = this._getField("name"); + var molecularMass = this._getField("molecularMass"); + var extintionCoefficient = this._getField("extintionCoefficient"); + var comments = this._getField("comments"); + + var refractiveIndex = this._getField("refractiveIndex"); + var solventViscosity = this._getField("solventViscosity"); + + /** Checking required fields **/ + if (name == "") { + BUI.showError("Name field is mandatory"); + return; + } + if (acronym == "") { + BUI.showError("Acroynm field is mandatory"); + return; + } - this.additiveGrid = new AdditiveGrid(); - this.additiveGrid.onRemoveButtonClicked.attach(function(sender, args) { - _this.onRemoveAdditive.notify(args); - }); + if (this.macromolecule != null){ + /** Checking if it is a new macromolecule **/ + if (this.macromolecule.macromoleculeId == null){ + /** Check if the acronym exists already **/ + this._persist(null, acronym, name, molecularMass, extintionCoefficient, comments, refractiveIndex, solventViscosity); + } + else{ + /** It is an update **/ + this._persist(this.macromolecule.macromoleculeId, acronym, name, molecularMass, extintionCoefficient, comments, refractiveIndex, solventViscosity); + } + } + else{ + /** It is a new macromolecule **/ + this._persist(null, acronym, name, molecularMass, extintionCoefficient, comments, refractiveIndex, solventViscosity); + } +}; - this.onSaved = new Event(this); - this.onRemoveAdditive = new Event(this); -} -BufferForm.prototype.getBuffer = function() { - this.buffer.name = Ext.getCmp("buffer_name").getValue(); - this.buffer.acronym = Ext.getCmp("buffer_acronym").getValue(); - this.buffer.comments = Ext.getCmp("buffer_comments").getValue(); - this.buffer.ph = Ext.getCmp("buffer_ph").getValue(); - this.buffer.composition = Ext.getCmp("buffer_composition").getValue(); - this.buffer.bufferhasadditive3VOs = this.additiveGrid.getAdditives(); - return this.buffer; -}; -BufferForm.prototype._getTopPanel = function() { - return { - xtype : 'container', - layout : 'hbox', - border : 0, - frame : true, - items : [ { - xtype : 'container', - layout : 'hbox', - items : [ { +MacromoleculeForm.prototype._getItems = function() { + var _this = this; + /** Symmetry combo box **/ + var symmetry = Ext.create('Ext.data.Store', { + fields : [ 's' ], + data : this._getSymmetries() + }); + + this.symmetryComboBox = Ext.create('Ext.form.ComboBox', { + fieldLabel : 'Symmetry', + store : symmetry, + id : this.id + 'comboSym', + queryMode : 'local', + displayField : 's', + valueField : 's', + value : "P1", + margin : "0 0 0 30", + width : 220 + }); + + return [this._getFieldTextWithHelp("requiredtext", "Name", "name", "Long name. i.e: Bovine serum albumin"), + this._getFieldTextWithHelp("requiredtext", "Acronym", "acronym", "Acronym will be used in the files and analisys. i.e: BSA"), + this._getFieldTextWithHelp("textfield", "Mol. Mass (Da)", "molecularMass", "Atomic mass estimation measured in Da"), + { xtype : 'container', - flex : 1, - border : false, - layout : 'anchor', - defaultType : 'textfield', - items : [ { - xtype : 'requiredtext', - id : 'buffer_name', - fieldLabel : 'Name', - name : 'name', - width : '200px', - value : this.buffer.name - }, { - xtype : 'requiredtext', - id : 'buffer_acronym', - fieldLabel : 'Acronym', - name : 'acronym', - width : '200px', - value : this.buffer.acronym - } ] - } ] - }, { - xtype : 'container', - flex : 1, - layout : 'anchor', - defaultType : 'textfield', - margin : '0 0 0 10', - items : [ { - id : 'buffer_ph', - fieldLabel : 'pH', - name : 'ph', - value : this.buffer.ph, - xtype : 'numberfield', - width : 200, - minValue : 0, - maxValue : 15 - }, { - xtype : 'requiredtext', - id : 'buffer_composition', - fieldLabel : 'Composition', - name : 'composition', - width : 200, - value : this.buffer.composition - } ] - } ] - }; + layout : 'hbox', + margin : "10 0 0 0", + items :[ + this._getNumericWithHelp("numberfield", "Extinction coef.", "extintionCoefficient", ""), + this.symmetryComboBox + ] + }, + { + xtype : 'container', + layout : 'hbox', + margin : "5 0 0 0", + items :[ + this._getNumericWithHelp("numberfield", "Refractive Index", "refractiveIndex", "How radiation propagates through the medium"), + this._getNumericWithHelp("numberfield", "Solvent Viscosity", "solventViscosity", "") + ] + }, + { + id : this.id + "comments", + xtype : 'textareafield', + name : 'comments', + margin : '35 0 0 10', + fieldLabel : 'Comments', + width : this.width - 100 + } ]; }; -BufferForm.prototype.getPanel = function(buffer) { - this.buffer = buffer; - this.panel = Ext.createWidget({ - xtype : 'container', - layout : 'vbox', - style : { - padding : '10px' - }, - fieldDefaults : { - labelAlign : 'left', - labelWidth : 50 +MacromoleculeForm.prototype._getSymmetries = function() { + return [ { + "s" : "P1" + }, { + "s" : "P2" + }, { + "s" : "P3" + }, { + "s" : "P4" + }, { + "s" : "P5" + }, { + "s" : "P6" + }, { + "s" : "P32" + }, { + "s" : "P42" + }, { + "s" : "P52" + }, { + "s" : "P62" + }, { + "s" : "P222" + } ] +}; - }, - items : [ this._getTopPanel(), { - id : 'buffer_comments', - xtype : 'textareafield', - name : 'comments', - fieldLabel : 'Comments', - width : '100%', - value : buffer.comments - }, this.additiveGrid.getPanel(buffer) ] +MacromoleculeForm.prototype.getPanel = function() { + this.panel = Ext.create('Ext.form.Panel', { + width : this.width, + height : this.height, + margin : 10, + border : 1, + defaultType : 'textfield', + items : this._getItems(), + buttons : this._getButtons() }); return this.panel; }; -BufferForm.prototype.input = function() { - return { - buffer : { - "bufferId" : 422, - "proposalId" : 10, - "safetyLevelId" : null, - "name" : "B1", - "acronym" : "B1", - "ph" : null, - "composition" : null, - "bufferhasadditive3VOs" : [], - "comments" : null - } - }; + +/** Populates each text field by field name and value **/ +MacromoleculeForm.prototype._populateField = function(fieldName, value) { + if (value != null){ + Ext.getCmp(this.id + fieldName).setValue(value); + } }; -BufferForm.prototype.test = function(targetId) { - var bufferForm = new BufferForm(); - var panel = bufferForm.getPanel(bufferForm.input().buffer); - panel.render(targetId); +/** Gets the value of a textfield **/ +MacromoleculeForm.prototype._getField = function(fieldName) { + return Ext.getCmp(this.id + fieldName).getValue(); }; - -/** - * @showTitle - * - * #onSaved - * #onAddPlates - * #onRemovePlates - **/ -function CaseForm(args) { - this.width = 700; - this.showTitle = true; - if (args != null) { - if (args.showTitle != null) { - this.showTitle = args.showTitle; - } - } - var _this = this; - this.stockSolutionGrid = new StockSolutionGrid({ - width : this.width - 10, - minHeight : 300, - height : 300, - tbar : true, - showTitle : true, - isPackedVisible : false, - btnAddExisting : true, - btnRemoveVisible : false, - btnUnpackVisible : true - }); - /** When selecting existing stock solutions **/ - this.stockSolutionGrid.onStockSolutionSelected.attach(function(sender, stockSolutions) { - if (stockSolutions != null) { - for ( var i = 0; i < stockSolutions.length; i++) { - _this.saveStockSolution(stockSolutions[i]); - } +/** It populates the form **/ +MacromoleculeForm.prototype.refresh = function(macromolecule) { + this.macromolecule = macromolecule; + if (macromolecule != null){ + this._populateField("name", macromolecule.name); + this._populateField("acronym", macromolecule.acronym); + this._populateField("extintionCoefficient", macromolecule.extintionCoefficient); + this._populateField("molecularMass", macromolecule.molecularMass); + this._populateField("comments", macromolecule.comments); + this._populateField("refractiveIndex", macromolecule.refractiveIndex); + this._populateField("solventViscosity", macromolecule.solventViscosity); + if (macromolecule.symmetry != null){ + Ext.getCmp(this.id + 'comboSym').setValue(macromolecule.symmetry); } - }); + } +}; - /** it can be because it has been added a new one or removed **/ - this.stockSolutionGrid.onProposalChanged.attach(function(sender, stockSolution) { - if (stockSolution != null) { - _this.saveStockSolution(stockSolution); - } else { - BIOSAXS.proposal.onInitialized.attach(function() { - _this.stockSolutionGrid.refresh(BIOSAXS.proposal.getStockSolutionsByDewarId(_this.dewar.dewarId)); - _this.stockSolutionGrid.grid.setLoading(false); - }); - BIOSAXS.proposal.init(); +MacromoleculeForm.prototype.input = function() { + return {}; +}; - } +/** It populates the form **/ +MacromoleculeForm.prototype.test = function(targetId) { + var macromoleculeForm = new MacromoleculeForm(); + macromoleculeForm.onClose.attach(function(sender){ + alert("Click on close"); }); + var panel = macromoleculeForm.getPanel(); + panel.render(targetId); +}; - this.onSaved = new Event(this); - this.onAddPlates = new Event(this); - this.onRemovePlates = new Event(this); -} -CaseForm.prototype.saveStockSolution = function(stockSolution) { - var _this = this; - var adapter = new BiosaxsDataAdapter(); - this.stockSolutionGrid.grid.setLoading("ISPyB: setting case to Stock solution"); - adapter.onSuccess.attach(function(sender, stockSolution) { - BIOSAXS.proposal.onInitialized.attach(function() { - _this.stockSolutionGrid.refresh(BIOSAXS.proposal.getStockSolutionsByDewarId(_this.dewar.dewarId)); - _this.stockSolutionGrid.grid.setLoading(false); - }); - BIOSAXS.proposal.init(); - }); - adapter.onError.attach(function(sender, data) { - _this.stockSolutionGrid.grid.setLoading(false); - BUI.showError(data); - }); - stockSolution.boxId = _this.dewar.dewarId; - adapter.saveStockSolution(stockSolution); -}; - -CaseForm.prototype.fillStores = function() { - var _this = this; - this.panel.setLoading("Loading Labcontacts from database"); + - var proposal = BUI.getProposal(); - proposal.onDataRetrieved.attach(function(sender, data) { - _this.labContactForSendingStore.loadData(data, false); - _this.labContactForReturnStore.loadData(data, false); - _this.panel.setLoading(false); - }); - proposal.getLabContactsByProposalId(); +function ModelVisualizerForm(args){ + this.id =BUI.id(); + this.width = 600; + this.height = 400; + if (args!= null){ + if (args.width != null){ + this.width = args.width; + } + if (args.height != null){ + this.height = args.height; + } + } }; -CaseForm.prototype.refresh = function(dewar) { - this.setDewar(dewar); - this.stockSolutionGrid.refresh(BIOSAXS.proposal.getStockSolutionsByDewarId(dewar.dewarId)); +ModelVisualizerForm.prototype._getFirHTML = function(modelId, width, height, type, desc) { + var html = ""; + html = html + ''; + html = html + ''; + html = html + ''; + html = html + '
dammin.' + type + '
' + desc + '
'; + return html; }; -CaseForm.prototype.getDewar = function() { - this.dewar.code = Ext.getCmp("dewar_code").getValue(); - this.dewar.comments = Ext.getCmp("dewar_comments").getValue(); - this.dewar.trackingNumberFromSynchrotron = Ext.getCmp("dewar_trackingNumberFromSynchrotron").getValue(); - this.dewar.trackingNumberToSynchrotron = Ext.getCmp("dewar_trackingNumberToSynchrotron").getValue(); - this.dewar.transportValue = Ext.getCmp("dewar_transportValue").getValue(); - this.dewar.storageLocation = Ext.getCmp("dewar_storageLocation").getValue(); - this.dewar.firstExperimentId = this.macromoleculeCombo.getValue(); - return this.dewar; +ModelVisualizerForm.prototype.getItems = function(modelPanel){ + _this = this; + var height = _this.height/2 - 60; + var width = _this.width/2 - 10; + + return Ext.create('Ext.container.Container', { + layout: { + type: 'vbox', // Arrange child items vertically + }, + items: [ + modelPanel, + { + xtype : 'container', + layout: { + type: 'hbox', // Arrange child items vertically + }, + items : [{ + html : this._getFirHTML("id", width, height, "fir", "Fit of the simulated scattering curve versus a smoothed experimental data (spline interpolation)"), + height :height, + width : width, + padding: 2 + }, + { + html : this._getFirHTML("id", width, height, "fit", "Fit of the simulated scattering curve versus the experimental data."), + height : height, + width : width, + padding: 2 + }] + } + + ] + }); }; -CaseForm.prototype.setDewar = function(dewar) { - this.dewar = dewar; - Ext.getCmp("dewar_code").setValue(this.dewar.code); - Ext.getCmp("dewar_dewarStatus").setText(new String(this.dewar.dewarStatus).toUpperCase()); - Ext.getCmp("dewar_comments").setValue(this.dewar.comments); - Ext.getCmp("dewar_trackingNumberFromSynchrotron").setValue(this.dewar.trackingNumberFromSynchrotron); - Ext.getCmp("dewar_trackingNumberToSynchrotron").setValue(this.dewar.trackingNumberToSynchrotron); - Ext.getCmp("dewar_transportValue").setValue(this.dewar.transportValue); - Ext.getCmp("dewar_storageLocation").setValue(this.dewar.storageLocation); - if (dewar.sessionVO != null) { - this.macromoleculeCombo.setValue(dewar.sessionVO.sessionId); +ModelVisualizerForm.prototype.refresh = function(models){ + var input = []; +// var colors = ["008000", "F0A804", "0000FF", "800080", "C0C0C0"]; + for (var i = 0; i < models.length; i++) { + console.log(BUI.rainbow(models.length, i).replace("#", "0x")); + input.push({ + color: BUI.rainbow(models.length, i).replace("#", "0x"), + modelId: models[i].modelId, + opacity: 0.8, + radius: 3, + title: BUI.getFileNameByPath(models[i].pdbFile), + type: "SHAPEDETERMINATIONMODEL" + + }); } + + this.panel.removeAll(); + this.panel.add( + _this.getItems( + new PDBViewer({ + width : this.width - 10, + height : (this.height/2) - 10, + title : "" + }).draw(input) + ) + ); + + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, data) { + var splitted = data.toString().split("\n"); + var array = []; + for ( var i = 0; i < splitted.length; i++) { + var line = splitted[i].trim(); + var line_splited = line.split(/\s*[\s,]\s*/); + if (line_splited.length == 4) { + array.push([ Number(line_splited[0]), BUI.getStvArray(line_splited[1], line_splited[2]), BUI.getStvArray(line_splited[3], 0) ]); + } + } + + var id = (_this.id + "firid"); + var dygraphWidget = new StdDevDyGraph(id, { + width : (_this.width/2) - 10, + height :(_this.height/2) -110, + xlabel : 'q(nm-1)' + }); + dygraphWidget.draw(array, [ "#FF0000", "#0000FF", "#FF00FF" ], [ 's', 'I(exp)', 'I(sim)' ]); + }); + + adapter.getModelFile("FIR", models[0].modelId); + + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, data) { + var splitted = data.toString().split("\n"); + var array = []; + for ( var i = 0; i < splitted.length; i++) { + var line = splitted[i].trim(); + var line_splited = line.split(/\s*[\s,]\s*/); + if (line_splited.length == 3) { + array.push([ Number(line_splited[0]), BUI.getStvArray(line_splited[1], 0), BUI.getStvArray(line_splited[2], 0) ]); + } + } + + var id = (_this.id + "fitid"); + + var dygraphWidget = new StdDevDyGraph(id, { + width : (_this.width/2) - 10, + height : (_this.height/2) - 110, + xlabel : 'q(nm-1)' + }); + dygraphWidget.draw(array, [ "#FF0000", "#0000FF" ], [ "s", "I(exp)", "I(sim)" ]); + }); + adapter.getModelFile("FIT", models[0].modelId); }; -CaseForm.prototype.getSessionCombo = function() { - this.macromoleculeCombo = BIOSAXS_COMBOMANAGER.getComboSessions(BIOSAXS.proposal.getSessions(), { - labelWidth : 100, - margin : '5 0 00 0', - width : 250 +ModelVisualizerForm.prototype.getPanel = function(modelList){ + _this = this; + this.modelList = modelList; + this.panel = Ext.create('Ext.Panel', { + title: 'Results', + width: this.width, + height: this.height, + layout: { + type: 'vbox', // Arrange child items vertically +// align: 'stretch' // Each takes up full width + }, + items: [ + + ], + listeners : { + afterrender : function(grid, eOpts) { +// alert(_this.modelList) + } + } }); - return this.macromoleculeCombo; + + return this.panel; + }; -CaseForm.prototype.getInformationPanel = function() { - if (this.panel == null) { - this.informationPanel = Ext.create('Ext.form.Panel', { - width : this.width - 10, - border : 0, - items : [ { - xtype : 'container', - margin : "2 2 2 2", - collapsible : false, - defaultType : 'textfield', - layout : 'anchor', - items : [ { - xtype : 'container', - layout : 'hbox', - items : [ { - xtype : 'requiredtext', - fieldLabel : 'Code', - allowBlank : false, - name : 'code', - id : 'dewar_code', - anchor : '50%' - }, { - xtype : 'label', - margin : '0 0 0 20', - readOnly : true, - id : 'dewar_dewarStatus', - anchor : '50%' - } ] - }, this.getSessionCombo(), { - margin : '5 0 0 0', - xtype : 'textareafield', - name : 'comments', - id : 'dewar_comments', - width : this.width - 50, - fieldLabel : 'Comments' - } ] - }, { - xtype : 'fieldset', - title : 'Courier Accounts Details for Return', - collapsible : false, - layout : 'anchor', - defaults : { - anchor : '100%' - }, - items : [ { - xtype : 'container', - layout : 'hbox', - items : [ { - xtype : 'textfield', - labelWidth : 200, - width : 280, - fieldLabel : 'Track Number From Synchrotron', - id : 'dewar_trackingNumberFromSynchrotron' - }, { - xtype : 'numberfield', - width : 190, - labelWidth : 110, - margin : '0 0 0 30', - fieldLabel : 'Transport Value', - id : 'dewar_transportValue' - } - - ] - }, { - xtype : 'container', - layout : 'hbox', - margin : '10 0 0 0', - items : [ - - { - xtype : 'textfield', - labelWidth : 200, - width : 280, - fieldLabel : 'Track Number To Synchrotron', - id : 'dewar_trackingNumberToSynchrotron' - }, { - xtype : 'textfield', - margin : '0 0 0 30', - width : 190, - labelWidth : 110, - fieldLabel : 'Storage Location', - id : 'dewar_storageLocation' - } - - ] - } ] - } ] - }); - } - - return this.informationPanel; -}; - -CaseForm.prototype.getPanel = function(dewar) { - this.dewar = dewar; - this.panel = Ext.createWidget({ - xtype : 'container', - layout : 'vbox', - width : this.width, - border : 0, - items : [ { - items : { - xtype : "container", - layout : "vbox", - margin : "5 5 5 5", - items : [ this.getInformationPanel(dewar), this.stockSolutionGrid.getPanel() ] - - } - } ] - }); - - this.refresh(dewar); - return this.panel; - -}; - -CaseForm.prototype.input = function() { - return { - dewar : { - "dewarId" : 305861, - "code" : "ESRF-TEST", - "comments" : "comments", - "storageLocation" : "FRIDGE", - "dewarStatus" : "opened", - "timeStamp" : null, - "isStorageDewar" : null, - "barCode" : "ESRF305861", - "customsValue" : null, - "transportValue" : null, - "trackingNumberToSynchrotron" : "3333", - "trackingNumberFromSynchrotron" : "224466", - "type" : "Dewar", - "sessionVO" : { - "sessionId" : 31697, - "expSessionPk" : null, - "projectCode" : null, - "startDate" : "2012 07 21", - "endDate" : "2012 07 23", - "beamlineName" : "BM29", - "scheduled" : 1, - "nbShifts" : 2, - "comments" : null, - "beamlineOperator" : "PERNOT P", - "usedFlag" : null, - "sessionTitle" : null, - "structureDeterminations" : null, - "dewarTransport" : null, - "databackupFrance" : null, - "databackupEurope" : null, - "visit_number" : null, - "operatorSiteNumber" : "14061", - "timeStamp" : "2012 04 25" - } - }, - proposal : { - "assemblies" : [], - "sessions" : [ { - "sessionId" : 31697, - "expSessionPk" : null, - "projectCode" : null, - "startDate" : "2012 07 21", - "endDate" : "2012 07 23", - "beamlineName" : "BM29", - "scheduled" : 1, - "nbShifts" : 2, - "comments" : null, - "beamlineOperator" : "PERNOT P", - "usedFlag" : null, - "sessionTitle" : null, - "structureDeterminations" : null, - "dewarTransport" : null, - "databackupFrance" : null, - "databackupEurope" : null, - "visit_number" : null, - "operatorSiteNumber" : "14061", - "timeStamp" : "2012 04 25" - } ], - "labcontacts" : [ { - "labContactId" : 787, - "personVO" : { - "personId" : 304252, - "personUUID" : null, - "familyName" : "KIM", - "givenName" : "Henry", - "title" : null, - "emailAddress" : "henry-sung.kim@ibs.fr", - "phoneNumber" : "", - "login" : "", - "passwd" : "", - "faxNumber" : "" - }, - "cardName" : "KIM-Institut de Bio", - "defaultCourrierCompany" : "22", - "courierAccount" : "", - "billingReference" : "", - "dewarAvgCustomsValue" : 0, - "dewarAvgTransportValue" : 0 - } ], - "buffers" : [ { - "bufferId" : 811, - "proposalId" : 3124, - "safetyLevelId" : null, - "name" : "EDTA", - "acronym" : "EDTA", - "ph" : null, - "composition" : "", - "bufferhasadditive3VOs" : [], - "comments" : "" - }, { - "bufferId" : 810, - "proposalId" : 3124, - "safetyLevelId" : null, - "name" : "HEPES", - "acronym" : "HEPES", - "ph" : null, - "composition" : "", - "bufferhasadditive3VOs" : [], - "comments" : "" - } ], - "shippings" : [ { - "shippingId" : 304107, - "shippingName" : "TEST", - "deliveryAgentAgentName" : null, - "deliveryAgentShippingDate" : null, - "deliveryAgentDeliveryDate" : null, - "deliveryAgentAgentCode" : null, - "deliveryAgentFlightCode" : null, - "shippingStatus" : "opened", - "timeStamp" : "2013 09 25", - "laboratoryId" : null, - "isStorageShipping" : null, - "creationDate" : "2013 09 25", - "comments" : "test", - "sendingLabContactVO" : { - "labContactId" : 787, - "personVO" : { - "personId" : 304252, - "personUUID" : null, - "familyName" : "KIM", - "givenName" : "Henry", - "title" : null, - "emailAddress" : "henry-sung.kim@ibs.fr", - "phoneNumber" : "", - "login" : "", - "passwd" : "", - "faxNumber" : "" - }, - "cardName" : "KIM-Institut de Bio", - "defaultCourrierCompany" : "22", - "courierAccount" : "", - "billingReference" : "", - "dewarAvgCustomsValue" : 0, - "dewarAvgTransportValue" : 0 - }, - "returnLabContactVO" : { - "labContactId" : 787, - "personVO" : { - "personId" : 304252, - "personUUID" : null, - "familyName" : "KIM", - "givenName" : "Henry", - "title" : null, - "emailAddress" : "henry-sung.kim@ibs.fr", - "phoneNumber" : "", - "login" : "", - "passwd" : "", - "faxNumber" : "" - }, - "cardName" : "KIM-Institut de Bio", - "defaultCourrierCompany" : "22", - "courierAccount" : "", - "billingReference" : "", - "dewarAvgCustomsValue" : 0, - "dewarAvgTransportValue" : 0 - }, - "returnCourier" : null, - "dateOfShippingToUser" : null, - "shippingType" : "DewarTracking", - "dewarVOs" : [ { - "dewarId" : 305861, - "code" : "ESRF-TEST", - "comments" : "comments", - "storageLocation" : "FRIDGE", - "dewarStatus" : "opened", - "timeStamp" : null, - "isStorageDewar" : null, - "barCode" : "ESRF305861", - "customsValue" : null, - "transportValue" : null, - "trackingNumberToSynchrotron" : "3333", - "trackingNumberFromSynchrotron" : "224466", - "type" : "Dewar", - "sessionVO" : { - "sessionId" : 31697, - "expSessionPk" : null, - "projectCode" : null, - "startDate" : "2012 07 21", - "endDate" : "2012 07 23", - "beamlineName" : "BM29", - "scheduled" : 1, - "nbShifts" : 2, - "comments" : null, - "beamlineOperator" : "PERNOT P", - "usedFlag" : null, - "sessionTitle" : null, - "structureDeterminations" : null, - "dewarTransport" : null, - "databackupFrance" : null, - "databackupEurope" : null, - "visit_number" : null, - "operatorSiteNumber" : "14061", - "timeStamp" : "2012 04 25" - } - } ] - } ], - "macromolecules" : [ { - "macromoleculeId" : 5933, - "safetylevelId" : null, - "proposalId" : 3124, - "name" : "A", - "acronym" : "A", - "molecularMass" : "", - "extintionCoefficient" : "", - "sequence" : null, - "creationDate" : null, - "comments" : "", - "macromoleculeregion3VOs" : [], - "stoichiometry3VOsForHostMacromoleculeId" : [], - "structure3VOs" : [] - } ], - "stockSolutions" : [ { - "stockSolutionId" : 6, - "proposalId" : 3124, - "macromoleculeId" : 5933, - "bufferId" : 811, - "instructionSet3VO" : null, - "boxId" : 305861, - "storageTemperature" : "20", - "volume" : "300", - "concentration" : "1.2", - "comments" : "Buffer EDTA with A", - "name" : "A_EDTA_1.2", - "samples" : [] - } ] - } - - }; -}; - -CaseForm.prototype.test = function(targetId) { - var caseForm = new CaseForm(); - BIOSAXS.proposal = new Proposal(caseForm.input().proposal); - var panel = caseForm.getPanel(caseForm.input().dewar); - panel.render(targetId); -}; - -/** - * Example form - * - * @witdh - * @height - */ -function ExampleForm(args) { - this.id = BUI.id(); - this.width = 700; - this.height = 500; - - if (args != null) { - if (args.width != null) { - this.width = args.width; - } - if (args.height != null) { - this.height = args.height; - } - } -} - -ExampleForm.prototype._getItems = function() { - return [{ - fieldLabel : 'First Name', - name : 'first', - allowBlank : false - }, { - fieldLabel : 'Last Name', - name : 'last', - allowBlank : false - } ]; -}; - -ExampleForm.prototype._getItems = function() { - return [ ]; -}; - -ExampleForm.prototype._getButtons = function() { - return [ ]; -}; - -ExampleForm.prototype.getPanel = function() { - this.panel = Ext.create('Ext.form.Panel', { - width : this.width, - height : this.height, - margin : 10, - border : 1, - defaultType : 'textfield', - items : this._getItems(), - buttons : this._getButtons() - }); - return this.panel; -}; - -/** It populates the form **/ -ExampleForm.prototype.refresh = function(macromolecule) { -}; - -function ExperimentForm(args) { - this.id = BUI.id(); - - if (args != null) { - } - - this.onSaved = new Event(this); -} - -ExperimentForm.prototype._getItems = function(experiment) { - this.experiment = experiment; - var typeCombo = Ext.create('Ext.form.ComboBox', { - id : this.id + 'type', - fieldLabel : 'Type', - store : [ "STATIC", "CALIBRATION", "HPLC" ], - queryMode : 'local', - labelWidth : 120, - displayField : 'name', - valueField : 'name', - value : experiment.json.type, - disabled : (experiment.json.type == 'TEMPLATE') - }); - - var items = []; - - - if (experiment.json.type == "HPLC" ){ - var typeMacromolecule = BIOSAXS_COMBOMANAGER.getComboMacromoleculeByMacromolecules(BIOSAXS.proposal.getMacromolecules(), {labelWidth : 120, width: "120px"}); - if (experiment.getHPLCMacromolecule() != null){ - typeMacromolecule.setValue(experiment.getHPLCMacromolecule().macromoleculeId); - items.push(typeMacromolecule); - } - } - - - items.push(typeCombo, { - id : this.id + 'name', - xtype : 'textfield', - fieldLabel : 'Name', - labelWidth : 120, - width : '100%', - value : experiment.json.name - }, { - id : this.id + 'comments', - xtype : 'textareafield', - name : 'comments', - fieldLabel : 'Comments', - labelWidth : 120, - height : 120, - width : '100%', - value : experiment.json.comments - }); - return items; -}; -ExperimentForm.prototype.getPanel = function(experiment) { - var _this = this; - - this.panel = Ext.createWidget({ - xtype : 'container', - layout : 'vbox', - border : 0, - style : { - padding : '10px' - }, - fieldDefaults : { - labelAlign : 'left', - labelWidth : 50 - }, - items : this._getItems(experiment) - }); - return this.panel; -}; - -ExperimentForm.prototype.input = function() { - return new ExperimentHeaderForm().input(); -}; - -ExperimentForm.prototype.test = function(targetId) { - var experimentForm = new ExperimentForm(); - var panel = experimentForm.getPanel(new Experiment(experimentForm.input().experiment)); - panel.render(targetId); -}; - -/** - * Shows the header for the experiments changing the color and parameters depending on experiment type - * - */ -function ExperimentHeaderForm(args) { - this.id = BUI.id(); - this.backgroundColor = '#FFFFFF'; -} - -ExperimentHeaderForm.prototype.getHTMLSource = function(experiment) { - var html = BUI.createFormLabel("Name :", experiment.json.name, 75, 400, this.backgroundColor); - if (experiment.json.type == "HPLC") { - if (experiment.getHPLCMacromolecule() != null){ - html = html + BUI.createFormLabel("Molecule :", experiment.getHPLCMacromolecule().acronym, 75, 400, this.backgroundColor); - } - } - else{ - html = html + BUI.createFormLabel("Type :", experiment.json.type, 75, 400, this.backgroundColor); - } - - html = html + BUI.createFormLabel("Date :", experiment.json.creationDate, 75, 400, this.backgroundColor); - return html; -}; - -ExperimentHeaderForm.prototype.getHTMLDownload = function(experiment) { - var bgcolor = "background-color:" + this.backgroundColor + ";"; - var html = "
"; - if ((experiment.json.type == "CALIBATION") || (experiment.json.type == "STATIC")) { - html = html + " Download Source File
"; - html = html - + ""; - } - if (experiment.json.type == "TEMPLATE") { - html = html - + " Download Source File"; - } - - if (experiment.json.type == "HPLC") { - html = html + " Download h5 File"; - } - - return html; -}; - -ExperimentHeaderForm.prototype.getTopPanel = function(experiment) { - return { - xtype : 'container', - layout : 'hbox', - items : [ { - margin : "0 0 0 0", - width : 475, - border : 0, - html : this.getHTMLSource(experiment) - }, { - margin : "0 0 0 0", - width : 475, - border : 0, - html : this.getHTMLDownload(experiment) - } ] - }; -}; - -ExperimentHeaderForm.prototype.getButton = function(experiment) { - var _this = this; - return Ext.create('Ext.Button', { - text : 'EDIT', - minWidth : '100', - margin : '10 0 0 30', - handler : function() { - var experimentWindow = new ExperimentWindow(); - experimentWindow.onSaved.attach(function(sender, data) { - _this.experiment.json.name = data.name; - _this.experiment.json.type = data.type; - _this.experiment.json.comments = data.comments; - _this.panel.remove(_this.panel.items.items[0]); - _this.panel.remove(_this.panel.items.items[0]); - _this.panel.insert(_this.getTopPanel(_this.experiment)); - _this.panel.insert(_this.getBottomPanel(_this.experiment)); - }); - experimentWindow.show(experiment); - } - }); -}; - -ExperimentHeaderForm.prototype.getBottomPanel = function(experiment) { - return { - xtype : 'container', - layout : 'hbox', - margin : '10 0 0 0', - items : [ this.getComments(experiment), this.getButton(experiment) ] - }; -}; - -ExperimentHeaderForm.prototype.getComments = function(experiment) { - return { - xtype : 'textareafield', - labelStyle : 'font-weight: bold;', - name : 'comments', - fieldLabel : 'Comments', - labelWidth : 70, - height : 40, - minWidth : '450', - readOnly : true, - value : experiment.json.comments - }; -}; - -ExperimentHeaderForm.prototype.getPanel = function(experiment) { - this.experiment = experiment; - - if (experiment.json.type == 'CALIBRATION') { - this.backgroundColor = '#EFFBFB'; - } - if (experiment.json.type == 'TEMPLATE') { - this.backgroundColor = '#E0F8E6'; - } - - this.panel = Ext.create('Ext.container.Container', { - frame : false, - layout : 'vbox', - padding : 5, - bodyPadding : '5 5 0 0', - width : 890, - margin : '0 0 10 0', - height : 120, - style : { - borderColor : '#99bce8', - borderStyle : 'solid', - borderWidth : '1px', - 'background-color' : this.backgroundColor - }, - fieldDefaults : { - msgTarget : 'side', - labelWidth : 100 - }, - items : [ this.getTopPanel(experiment), this.getBottomPanel(experiment) ] - }); - return this.panel; -}; - -ExperimentHeaderForm.prototype.input = function() { - return { - experiment : DATADOC.getExperiment_10() - }; -}; - -ExperimentHeaderForm.prototype.test = function(targetId) { - var experimentHeaderForm = new ExperimentHeaderForm(); - var panel = experimentHeaderForm.getPanel(new Experiment(experimentHeaderForm.input().experiment)); - panel.render(targetId); - -}; - -/** - * Macromolecule form with the general parameters of a macromolecule - * - * @witdh - * @height - * - * #onSave button save has been clicked - * #onClose button close has been clicked - */ -function MacromoleculeForm(args) { - this.id = BUI.id(); - this.width = 700; - this.height = 500; - - if (args != null) { - if (args.width != null) { - this.width = args.width; - } - if (args.height != null) { - this.height = args.height; - } - } - - /** Events **/ - this.onSave = new Event(this); - this.onClose = new Event(this); -} - -/** Type : is the Ext type then requiredtext or textfield * */ -MacromoleculeForm.prototype._getFieldTextWithHelp = function(type, fieldLabel, fieldName, help) { - return Ext.create('Ext.container.Container', { - margin : "10 0 0 10", - items : [ { - xtype : type, - fieldLabel : fieldLabel, - name : fieldName, - id : this.id + fieldName - }, { - xtype : 'label', - forId : 'myFieldId', - text : help, - margin : "5 0 0 105", - cls : "inline-help" - } ] - }); -}; - -MacromoleculeForm.prototype._getNumericWithHelp = function(type, fieldLabel, fieldName, help) { - return Ext.create('Ext.container.Container', { - margin : "0 0 0 10", - items : [ { - xtype : type, - fieldLabel : fieldLabel, - name : fieldName, - id : this.id + fieldName, - decimalPrecision : 6, - width : 220 - }, { - xtype : 'label', - forId : 'myFieldId', - text : help, - margin : "5 0 0 10", - cls : "inline-help" - } ] - }); -}; - -MacromoleculeForm.prototype._getButtons = function() { - var _this = this; - return [ { - text : 'Save', - handler : function() { - _this._save(); - } - },{ - text : 'Close', - handler : function() { - _this.onClose.notify(); - - } - } ]; -}; - -/** It persits the macromolecule in the database **/ -MacromoleculeForm.prototype._persist = function(macromoleculeId, acronym, name, molecularMass, extintionCoefficient, comments, refractiveIndex, solventViscosity) { - - /** Checking not duplicated acronym **/ - if (((macromoleculeId == null) && (BIOSAXS.proposal.getMacromoleculeByAcronym(acronym) != null))== true){ - BUI.showError("Duplicated acronym"); - return; - } - - - if (macromoleculeId == null){ - /** new macromolecule **/ - this.macromolecule = {}; - this.macromolecule.macromoleculeId = null; - } - else{ - this.macromolecule.macromoleculeId = macromoleculeId; - } - - this.macromolecule["acronym"] = acronym; - this.macromolecule["name"] = name; - this.macromolecule["molecularMass"] = molecularMass; - this.macromolecule["extintionCoefficient"] = extintionCoefficient; - this.macromolecule["comments"] = comments; - this.macromolecule["symmetry"] = Ext.getCmp(this.id + 'comboSym').getValue(); - this.macromolecule["refractiveIndex"] = refractiveIndex; - this.macromolecule["solventViscosity"] = solventViscosity; - - var _this = this; - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, proposal) { - /** Updating the proposal global object **/ - BIOSAXS.proposal.setItems(proposal); - _this.panel.setLoading(false); - - var saved = BIOSAXS.proposal.getMacromoleculeByAcronym(acronym); - /** Refreshing to check that everything is ok **/ - _this.refresh(saved); - _this.onSave.notify(saved); - }); - - this.panel.setLoading("Saving Macromolecule") - adapter.saveMacromolecule(this.macromolecule); -}; - -/** Save the macromolecule in the DB **/ -MacromoleculeForm.prototype._save = function() { - - var _this = this; - - var acronym = this._getField("acronym"); - var name = this._getField("name"); - var molecularMass = this._getField("molecularMass"); - var extintionCoefficient = this._getField("extintionCoefficient"); - var comments = this._getField("comments"); - - var refractiveIndex = this._getField("refractiveIndex"); - var solventViscosity = this._getField("solventViscosity"); - - /** Checking required fields **/ - if (name == "") { - BUI.showError("Name field is mandatory"); - return; - } - if (acronym == "") { - BUI.showError("Acroynm field is mandatory"); - return; - } - - if (this.macromolecule != null){ - /** Checking if it is a new macromolecule **/ - if (this.macromolecule.macromoleculeId == null){ - /** Check if the acronym exists already **/ - this._persist(null, acronym, name, molecularMass, extintionCoefficient, comments, refractiveIndex, solventViscosity); - } - else{ - /** It is an update **/ - this._persist(this.macromolecule.macromoleculeId, acronym, name, molecularMass, extintionCoefficient, comments, refractiveIndex, solventViscosity); - } - } - else{ - /** It is a new macromolecule **/ - this._persist(null, acronym, name, molecularMass, extintionCoefficient, comments, refractiveIndex, solventViscosity); - } -}; - - - -MacromoleculeForm.prototype._getItems = function() { - var _this = this; - /** Symmetry combo box **/ - var symmetry = Ext.create('Ext.data.Store', { - fields : [ 's' ], - data : this._getSymmetries() - }); - - this.symmetryComboBox = Ext.create('Ext.form.ComboBox', { - fieldLabel : 'Symmetry', - store : symmetry, - id : this.id + 'comboSym', - queryMode : 'local', - displayField : 's', - valueField : 's', - value : "P1", - margin : "0 0 0 30", - width : 220 - }); - - return [this._getFieldTextWithHelp("requiredtext", "Name", "name", "Long name. i.e: Bovine serum albumin"), - this._getFieldTextWithHelp("requiredtext", "Acronym", "acronym", "Acronym will be used in the files and analisys. i.e: BSA"), - this._getFieldTextWithHelp("textfield", "Mol. Mass (Da)", "molecularMass", "Atomic mass estimation measured in Da"), - { - xtype : 'container', - layout : 'hbox', - margin : "10 0 0 0", - items :[ - this._getNumericWithHelp("numberfield", "Extinction coef.", "extintionCoefficient", ""), - this.symmetryComboBox - ] - }, - { - xtype : 'container', - layout : 'hbox', - margin : "5 0 0 0", - items :[ - this._getNumericWithHelp("numberfield", "Refractive Index", "refractiveIndex", "How radiation propagates through the medium"), - this._getNumericWithHelp("numberfield", "Solvent Viscosity", "solventViscosity", "") - ] - }, - { - id : this.id + "comments", - xtype : 'textareafield', - name : 'comments', - margin : '35 0 0 10', - fieldLabel : 'Comments', - width : this.width - 100 - } ]; -}; - -MacromoleculeForm.prototype._getSymmetries = function() { - return [ { - "s" : "P1" - }, { - "s" : "P2" - }, { - "s" : "P3" - }, { - "s" : "P4" - }, { - "s" : "P5" - }, { - "s" : "P6" - }, { - "s" : "P32" - }, { - "s" : "P42" - }, { - "s" : "P52" - }, { - "s" : "P62" - }, { - "s" : "P222" - } ] -}; - -MacromoleculeForm.prototype.getPanel = function() { - this.panel = Ext.create('Ext.form.Panel', { - width : this.width, - height : this.height, - margin : 10, - border : 1, - defaultType : 'textfield', - items : this._getItems(), - buttons : this._getButtons() - }); - return this.panel; -}; - - -/** Populates each text field by field name and value **/ -MacromoleculeForm.prototype._populateField = function(fieldName, value) { - if (value != null){ - Ext.getCmp(this.id + fieldName).setValue(value); - } -}; - -/** Gets the value of a textfield **/ -MacromoleculeForm.prototype._getField = function(fieldName) { - return Ext.getCmp(this.id + fieldName).getValue(); -}; - - -/** It populates the form **/ -MacromoleculeForm.prototype.refresh = function(macromolecule) { - this.macromolecule = macromolecule; - if (macromolecule != null){ - this._populateField("name", macromolecule.name); - this._populateField("acronym", macromolecule.acronym); - this._populateField("extintionCoefficient", macromolecule.extintionCoefficient); - this._populateField("molecularMass", macromolecule.molecularMass); - this._populateField("comments", macromolecule.comments); - this._populateField("refractiveIndex", macromolecule.refractiveIndex); - this._populateField("solventViscosity", macromolecule.solventViscosity); - if (macromolecule.symmetry != null){ - Ext.getCmp(this.id + 'comboSym').setValue(macromolecule.symmetry); - } - } -}; - - -MacromoleculeForm.prototype.input = function() { - return {}; -}; - - -/** It populates the form **/ -MacromoleculeForm.prototype.test = function(targetId) { - var macromoleculeForm = new MacromoleculeForm(); - macromoleculeForm.onClose.attach(function(sender){ - alert("Click on close"); - }); - var panel = macromoleculeForm.getPanel(); - panel.render(targetId); -}; - - - - - -function ModelVisualizerForm(args){ - this.id =BUI.id(); - this.width = 600; - this.height = 400; - if (args!= null){ - if (args.width != null){ - this.width = args.width; - } - if (args.height != null){ - this.height = args.height; - } - } -}; - -ModelVisualizerForm.prototype._getFirHTML = function(modelId, width, height, type, desc) { - var html = ""; - html = html + ''; - html = html + ''; - html = html + ''; - html = html + '
dammin.' + type + '
' + desc + '
'; - return html; -}; - -ModelVisualizerForm.prototype.getItems = function(modelPanel){ - _this = this; - var height = _this.height/2 - 60; - var width = _this.width/2 - 10; - - return Ext.create('Ext.container.Container', { - layout: { - type: 'vbox', // Arrange child items vertically - }, - items: [ - modelPanel, - { - xtype : 'container', - layout: { - type: 'hbox', // Arrange child items vertically - }, - items : [{ - html : this._getFirHTML("id", width, height, "fir", "Fit of the simulated scattering curve versus a smoothed experimental data (spline interpolation)"), - height :height, - width : width, - padding: 2 - }, - { - html : this._getFirHTML("id", width, height, "fit", "Fit of the simulated scattering curve versus the experimental data."), - height : height, - width : width, - padding: 2 - }] - } - - ] - }); -}; - -ModelVisualizerForm.prototype.refresh = function(models){ - var input = []; -// var colors = ["008000", "F0A804", "0000FF", "800080", "C0C0C0"]; - for (var i = 0; i < models.length; i++) { - console.log(BUI.rainbow(models.length, i).replace("#", "0x")); - input.push({ - color: BUI.rainbow(models.length, i).replace("#", "0x"), - modelId: models[i].modelId, - opacity: 0.8, - radius: 3, - title: BUI.getFileNameByPath(models[i].pdbFile), - type: "SHAPEDETERMINATIONMODEL" - - }); - } - - this.panel.removeAll(); - this.panel.add( - _this.getItems( - new PDBViewer({ - width : this.width - 10, - height : (this.height/2) - 10, - title : "" - }).draw(input) - ) - ); - - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, data) { - var splitted = data.toString().split("\n"); - var array = []; - for ( var i = 0; i < splitted.length; i++) { - var line = splitted[i].trim(); - var line_splited = line.split(/\s*[\s,]\s*/); - if (line_splited.length == 4) { - array.push([ Number(line_splited[0]), BUI.getStvArray(line_splited[1], line_splited[2]), BUI.getStvArray(line_splited[3], 0) ]); - } - } - - var id = (_this.id + "firid"); - var dygraphWidget = new StdDevDyGraph(id, { - width : (_this.width/2) - 10, - height :(_this.height/2) -110, - xlabel : 'q(nm-1)' - }); - dygraphWidget.draw(array, [ "#FF0000", "#0000FF", "#FF00FF" ], [ 's', 'I(exp)', 'I(sim)' ]); - }); - - adapter.getModelFile("FIR", models[0].modelId); - - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, data) { - var splitted = data.toString().split("\n"); - var array = []; - for ( var i = 0; i < splitted.length; i++) { - var line = splitted[i].trim(); - var line_splited = line.split(/\s*[\s,]\s*/); - if (line_splited.length == 3) { - array.push([ Number(line_splited[0]), BUI.getStvArray(line_splited[1], 0), BUI.getStvArray(line_splited[2], 0) ]); - } - } - - var id = (_this.id + "fitid"); - - var dygraphWidget = new StdDevDyGraph(id, { - width : (_this.width/2) - 10, - height : (_this.height/2) - 110, - xlabel : 'q(nm-1)' - }); - dygraphWidget.draw(array, [ "#FF0000", "#0000FF" ], [ "s", "I(exp)", "I(sim)" ]); - }); - adapter.getModelFile("FIT", models[0].modelId); -}; - -ModelVisualizerForm.prototype.getPanel = function(modelList){ - _this = this; - this.modelList = modelList; - this.panel = Ext.create('Ext.Panel', { - title: 'Results', - width: this.width, - height: this.height, - layout: { - type: 'vbox', // Arrange child items vertically -// align: 'stretch' // Each takes up full width - }, - items: [ - - ], - listeners : { - afterrender : function(grid, eOpts) { -// alert(_this.modelList) - } - } - }); - - return this.panel; - -}; - - -/** - * Example form - * - * @witdh - * @height - */ -function MolarityForm(args) { - this.id = BUI.id(); - this.width = 700; - this.height = 500; - - if (args != null) { - if (args.width != null) { - this.width = args.width; - } - if (args.height != null) { - this.height = args.height; - } - } - - this.onSave = new Event(this); - this.onClose = new Event(this); -} - - -MolarityForm.prototype._getNumericWithHelp = function(type, fieldLabel, fieldName, help) { - return Ext.create('Ext.container.Container', { - margin : "10 0 0 10", - items : [ { - xtype : type, - fieldLabel : fieldLabel, - name : fieldName, - id : this.id + fieldName, - decimalPrecision : 6 - }, { - xtype : 'label', - forId : 'myFieldId', - text : help, - margin : "5 0 0 105", - cls : "inline-help" - } ] - }); -}; - - -MolarityForm.prototype._getItems = function() { - this.macromoleculeCombo = BIOSAXS_COMBOMANAGER.getComboMacromoleculeByMacromolecules(this.getMacromuleculesCandidates(this.macromolecule), { - width : 250, - labelWidth : 100, - margin : 10 - }); - - return [ { - xtype : 'container', - flex : 1, - margin : '10 0 0 10', - border : 0, - layout : 'anchor', - defaultType : 'requiredtext', - items : [ this.macromoleculeCombo, - this._getNumericWithHelp("textfield", "Ratio", "ratio", "Number in assymmetric units") - ] - } ]; -}; - -MolarityForm.prototype._persist = function() { - var _this = this; - var macromoleculeId = this.macromoleculeCombo.getValue(); - var ratio = Ext.getCmp(this.id + "ratio").getValue(); - var comments = "Not used yet"; - var dataAdapter = new BiosaxsDataAdapter(); - this.panel.setLoading("Saving"); - dataAdapter.onSuccess.attach(function(sender, args) { - _this.onSave.notify(); - }); - dataAdapter.saveStoichiometry(this.macromolecule.macromoleculeId, macromoleculeId, ratio, comments); -}; - -MolarityForm.prototype._getButtons = function() { - var _this = this; - - function onClose() { - _this.onClose.notify(); - } - - return [ { - text : 'Save', - handler : function() { - _this._persist(); - } - }, { - text : 'Cancel', - handler : function() { - onClose(); - } - } ]; -}; - -MolarityForm.prototype.getPanel = function() { - this.panel = Ext.create('Ext.form.Panel', { -// width : null, - height : this.height, - margin : 2, - border : 1, - defaultType : 'requiredtext', - items : this._getItems(), - buttons : this._getButtons() - }); - return this.panel; -}; - -/** macromolecules contains all macromolecules except this one **/ -MolarityForm.prototype.getMacromuleculesCandidates = function(macromolecule) { - var macromolecules = []; - if ( BIOSAXS.proposal.macromolecules){ - for (var i = 0; i < BIOSAXS.proposal.macromolecules.length; i++) { - var m = BIOSAXS.proposal.macromolecules[i]; - if (this.macromolecule != null){ - if (m.macromoleculeId != this.macromolecule.macromoleculeId) { - macromolecules.push(m); - } - } - } - } - return macromolecules; -}; - - -/** It populates the form **/ -MolarityForm.prototype.refresh = function(macromolecule) { - this.macromolecule = macromolecule; - -}; - - -MolarityForm.prototype.input = function() { - return {}; -}; - - -/** It populates the form **/ -MolarityForm.prototype.test = function(targetId) { - var macromoleculeForm = new MolarityForm(); - var panel = macromoleculeForm.getPanel(); - panel.render(targetId); -}; - -///** -// * -// * @witdh -// * @height -// */ -//function MacromoleculeForm(args) { -// this.id = BUI.id(); -// this.width = 700; -// this.height = 500; -// -// if (args != null) { -// if (args.width != null) { -// this.width = args.width; -// } -// if (args.height != null) { -// this.height = args.height; -// } -// } -// -// this.onClose = new Event(this); -//} -// -//MacromoleculeForm.prototype.getMacromolecule = function() { -// this.macromolecule.name = Ext.getCmp(this.panel.getItemId()).getValues().name; -// this.macromolecule.acronym = Ext.getCmp(this.panel.getItemId()).getValues().acronym; -// this.macromolecule.comments = Ext.getCmp(this.panel.getItemId()).getValues().comments; -// this.macromolecule.extintionCoefficient = Ext.getCmp(this.panel.getItemId()).getValues().extintionCoefficient; -// this.macromolecule.molecularMass = Ext.getCmp(this.panel.getItemId()).getValues().molecularMass; -// return this.macromolecule; -//}; -// -//MacromoleculeForm.prototype.setMacromolecule = function(macromolecule) { -// this.pdbStore.loadData(macromolecule.structure3VOs); -// -//}; -// -//MacromoleculeForm.prototype.getForm = function(macromolecule) { -// this.panel = Ext.createWidget('form', { -// frame : false, -// border : 0, -// padding : 15, -// width : 550, -// height : 350, -// items : [ { -// xtype : 'container', -// layout : 'hbox', -// items : [ { -// xtype : 'container', -// flex : 1, -// border : false, -// layout : 'anchor', -// defaultType : 'requiredtext', -// items : [ { -// fieldLabel : 'Name', -// name : 'name', -// anchor : '95%', -// tooltip : "Name of the macromolecule", -// value : macromolecule.name -// }, { -// fieldLabel : 'Acronym', -// name : 'acronym', -// anchor : '95%', -// value : macromolecule.acronym -// } ] -// }, { -// xtype : 'container', -// flex : 1, -// layout : 'anchor', -// defaultType : 'textfield', -// items : [ { -// xtype : 'numberfield', -// fieldLabel : 'Mol. Mass (Da)', -// name : 'molecularMass', -// value : macromolecule.molecularMass, -// decimalPrecision : 6 -// }, { -// xtype : 'numberfield', -// fieldLabel : 'Extinction coef.', -// name : 'extintionCoefficient', -// value : macromolecule.extintionCoefficient, -// decimalPrecision : 6 -// } ] -// } ] -// }, { -// xtype : 'textareafield', -// name : 'comments', -// fieldLabel : 'Comments', -// value : macromolecule.comments, -// width : this.width - 10 -// }, -// -// { -// html : BUI.getWarningHTML("The macromolecule is unsaved. Save the macromolecule to get access to other tabs"), -// margin : "150 10 10 10", -// id : this.id + "unsavedWarning", -// hidden : !(!macromolecule.macromoleculeId) -// } -// -// ] -// }); -// return this.panel; -//}; -// -//MacromoleculeForm.prototype.save = function() { -// var _this = this; -// -// var adapter = new BiosaxsDataAdapter(); -// adapter.onSuccess.attach(function(sender, proposal) { -// BIOSAXS.proposal.setItems(proposal); -// _this.panel.setLoading(false); -// -// Ext.getCmp(_this.id + "assembly").enable() -// Ext.getCmp(_this.id + "advanced").enable(); -// Ext.getCmp(_this.id + "unsavedWarning").hide(); -// }); -// -// if (this.getMacromolecule().name == "") { -// BUI.showError("Name field is mandatory"); -// return; -// } -// if (this.getMacromolecule().acronym == "") { -// BUI.showError("Acroynm field is mandatory"); -// return; -// } -// -// /** Check if acronym is unique * */ -// if (this.getMacromolecule().macromoleculeId == null) { -// if (BIOSAXS.proposal.getMacromoleculeByAcronym(this.getMacromolecule().acronym) == null) { -// this.panel.setLoading("ISPyB: Saving Macromolecule") -// adapter.saveMacromolecule(this.getMacromolecule()); -// } else { -// alert("There is already an existing macromolecule with the same acronym"); -// -// } -// } else { -// this.panel.setLoading("ISPyB: Saving Macromolecule") -// adapter.saveMacromolecule(this.getMacromolecule()); -// } -// -//}; -// -//MacromoleculeForm.prototype.getPanel = function(macromolecule) { -// var _this = this; -// this.macromolecule = macromolecule; -// return Ext.createWidget('tabpanel', { -// height : this.height, -// margin : 5, -// plain : true, -// style : { -// padding : 5 -// }, -// items : [ { -// tabConfig : { -// title : "General", -// disabled : false -// }, -// items : [ this.getForm(macromolecule) ], -// bbar : [ "->", { -// text : 'Save', -// cls : 'btn-with-border', -// style : { -// -// border : 1 -// }, -// handler : function() { -// _this.save(); -// } -// }, { -// text : 'Close', -// cls : 'btn-with-border', -// handler : function() { -// _this.onClose.notify(); -// } -// } ] -// }, { -// tabConfig : { -// id : this.id + "assembly", -// title : "Assembly", -// tooltip : 'Description of subunits present in the macromolecule', -// // hidden : (!macromolecule.macromoleculeId), -// disabled : (!macromolecule.macromoleculeId) -// }, -// items : [ this.getMolarityGrid(macromolecule) ] -// }, { -// tabConfig : { -// id : this.id + "advanced", -// title : "Advanced Modeling", -// // hidden : (!macromolecule.macromoleculeId), -// tooltip : 'Definition of the description contacts and symetries', -// disabled : (!macromolecule.macromoleculeId) -// }, -// items : [ this.getRigidBodyForm(macromolecule) ] -// } ] -// }); -//}; -// -//MacromoleculeForm.prototype.getRigidBodyForm = function(macromolecule) { -// var _this = this; -// -// // [P1, P2, P3, P4 ,P5 ,P6 ,32, P42, P52, P62] + P222 -// var symmetry = Ext.create('Ext.data.Store', { -// fields : [ 's' ], -// data : [ { -// "s" : "P1" -// }, { -// "s" : "P2" -// }, { -// "s" : "P3" -// }, { -// "s" : "P4" -// }, { -// "s" : "P5" -// }, { -// "s" : "P6" -// }, { -// "s" : "P32" -// }, { -// "s" : "P42" -// }, { -// "s" : "P52" -// }, { -// "s" : "P62" -// }, { -// "s" : "P222" -// } ] -// }); -// -// if (macromolecule.symmetry == null) { -// macromolecule.symmetry = "P1"; -// } -// var comboBox = Ext.create('Ext.form.ComboBox', { -// fieldLabel : 'Symmetry', -// store : symmetry, -// id : 'comboSym', -// queryMode : 'local', -// displayField : 's', -// valueField : 's', -// value : macromolecule.symmetry, -// margin : "10 0 0 0", -// listeners : { -// change : function(combo, newValue, oldValue, eOpts) { -// macromolecule.symmetry = newValue; -// } -// } -// }); -// -// this.rigidBodyPanel = Ext.createWidget('form', { -// frame : false, -// border : 0, -// padding : 10, -// width : 550, -// height : 400, -// items : [ { -// xtype : 'container', -// layout : 'hbox', -// items : [ { -// xtype : 'container', -// border : false, -// layout : 'hbox', -// items : [ { -// xtype : 'label', -// forId : 'myFieldId', -// text : 'Contact Desc:', -// width : 105, -// margin : '0 0 0 0' -// }, { -// xtype : 'textfield', -// hideLabel : true, -// id : "contactsDescriptionFilePath", -// margin : '0 0 0 0', -// width : 300, -// value : macromolecule.contactsDescriptionFilePath -// }, { -// text : 'Upload', -// xtype : 'button', -// margin : "0 0 0 20", -// width : 100, -// handler : function() { -// _this._openUploadDialog(macromolecule.macromoleculeId, "CONTACTS", "Upload Contact Description File"); -// } -// } ] -// } ] -// }, -// -// comboBox, { -// xtype : 'checkbox', -// margin : '10 0 0 5', -// boxLabel : "I want rigid body modeling run on this stuff", -// checked : true, -// width : 300 -// }, _this.getPDBGrid(macromolecule) ] -// }); -// return this.rigidBodyPanel; -//}; -// -//MacromoleculeForm.prototype.update = function() { -// var _this = this; -// BIOSAXS.proposal.onInitialized.attach(function() { -// if (BIOSAXS.proposal != null) { -// var macromolecules = BIOSAXS.proposal.macromolecules; -// for (var i = 0; i < macromolecules.length; i++) { -// if (macromolecules[i].macromoleculeId == _this.macromolecule.macromoleculeId) { -// _this.macromolecule = macromolecules[i]; -// _this.setMacromolecule(_this.macromolecule); -// _this.molarityStore.loadData(_this.parseMolarityData(_this.macromolecule)); -// _this.pdbGrid.setLoading(false); -// Ext.getCmp("contactsDescriptionFilePath").setValue(_this.macromolecule.contactsDescriptionFilePath) -// _this.molarityGrid.setLoading(false); -// -// } -// } -// } -// }); -// this.molarityGrid.setLoading("Updating"); -// this.pdbGrid.setLoading("Updating"); -// BIOSAXS.proposal.init(); -//}; -// -///******************************************************************************* -// * MOLARITY GRID -// ******************************************************************************/ -// -//MacromoleculeForm.prototype.parseMolarityData = function(macromolecule) { -// var data = []; -// if (macromolecule.stoichiometry != null) { -// for (var i = 0; i < macromolecule.stoichiometry.length; i++) { -// data.push({ -// ratio : macromolecule.stoichiometry[i].ratio, -// acronym : macromolecule.stoichiometry[i].macromolecule3VO.acronym, -// comments : macromolecule.stoichiometry[i].macromolecule3VO.comments, -// stoichiometryId : macromolecule.stoichiometry[i].stoichiometryId, -// name : macromolecule.stoichiometry[i].macromolecule3VO.name -// }); -// } -// } -// return data; -//}; -// -//MacromoleculeForm.prototype.getMolarityGrid = function(macromolecule) { -// var _this = this; -// -// this.molarityStore = Ext.create('Ext.data.Store', { -// fields : [ 'acronym', 'ratio', 'comments', 'stoichiometryId', 'name' ], -// data : this.parseMolarityData(macromolecule), -// sorters : { -// property : 'ratio', -// direction : 'DESC' -// } -// }); -// -// this.molarityGrid = Ext.create('Ext.grid.Panel', { -// store : this.molarityStore, -// height : 350, -// padding : 5, -// columns : [ -// -// { -// text : 'Subunit', -// columns : [ { -// text : "Acronym", -// width : 100, -// hidden : false, -// dataIndex : 'acronym', -// sortable : true -// }, { -// text : "Name", -// width : 100, -// hidden : false, -// dataIndex : 'name', -// sortable : true -// }, { -// text : "Comments", -// width : 100, -// dataIndex : 'comments', -// sortable : true -// } ] -// }, { -// text : "Number
in assymmetric units", -// width : 150, -// dataIndex : 'ratio', -// tooltip : 'Number of times this subunit is present in the macromolecule', -// sortable : true -// }, { -// id : this.id + 'MOLARITY_REMOVE', -// flex : 0.1, -// sortable : false, -// renderer : function(value, metaData, record, rowIndex, colIndex, store) { -// return BUI.getRedButton('REMOVE'); -// } -// } ], -// listeners : { -// cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { -// if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'MOLARITY_REMOVE') { -// var dataAdapter = new BiosaxsDataAdapter(); -// dataAdapter.onSuccess.attach(function() { -// _this.molarityGrid.setLoading(false); -// _this.update(); -// }); -// dataAdapter.removeStoichiometry(record.data.stoichiometryId); -// _this.molarityGrid.setLoading("Removing Structure"); -// } -// } -// }, -// buttons : [ { -// text : 'Add molarity', -// handler : function() { -// -// function onClose() { -// w.destroy(); -// _this.update(); -// } -// var w = Ext.create('Ext.window.Window', { -// title : 'Molarity', -// height : 300, -// width : 500, -// modal : true, -// buttons : [ { -// text : 'Save', -// handler : function() { -// var macromoleculeId = (_this.macromoleculeCombo.getValue()); -// var ratio = Ext.getCmp(_this.id + "ratio").getValue(); -// var comments = ""; -// var dataAdapter = new BiosaxsDataAdapter(); -// dataAdapter.onSuccess.attach(function(sender, args) { -// _this.update(); -// w.destroy(); -// }); -// dataAdapter.saveStoichiometry(_this.macromolecule.macromoleculeId, macromoleculeId, ratio, comments); -// } -// }, { -// text : 'Cancel', -// handler : function() { -// onClose(); -// } -// } ], -// items : [ _this.getMolarityForm(macromolecule) ], -// listeners : { -// onEsc : function() { -// onClose(); -// }, -// close : function() { -// onClose(); -// } -// } -// }).show(); -// } -// } ] -// }); -// return this.molarityGrid; -//}; -// -///******************************************************************************* -// * PDB GRID -// ******************************************************************************/ -// -//MacromoleculeForm.prototype._openUploadDialog = function(macromoleculeId, type, title) { -// var _this = this; -// function onClose() { -// w.destroy(); -// _this.update(); -// } -// -// var w = Ext.create('Ext.window.Window', { -// title : title, -// height : 200, -// width : 400, -// modal : true, -// buttons : [ { -// text : 'Close', -// handler : function() { -// onClose(); -// } -// } ], -// layout : 'fit', -// items : { -// html : "" -// }, -// listeners : { -// onEsc : function() { -// onClose(); -// }, -// close : function() { -// onClose(); -// } -// } -// }).show(); -//}; -// -//MacromoleculeForm.prototype._getPlugins = function() { -// var _this = this; -// var plugins = []; -// // if (this.updateRowEnabled) { -// plugins.push(Ext.create('Ext.grid.plugin.RowEditing', { -// clicksToEdit : 1, -// listeners : { -// validateedit : function(grid, e) { -// /** Comments are always updatable* */ -// e.record.raw.symmetry = e.newValues.symmetry; -// e.record.raw.multiplicity = e.newValues.multiplicity; -// -// var adapter = new BiosaxsDataAdapter(); -// adapter.onSuccess.attach(function(sender, measurement) { -// // _this.grid.setLoading(false); -// }); -// adapter.onError.attach(function() { -// alert("Error"); -// // _this.grid.setLoading(false); -// }); -// -// // _this.grid.setLoading(); -// adapter.saveStructure(e.record.raw); -// } -// } -// })); -// // } -// return plugins; -//}; -// -//MacromoleculeForm.prototype.getPDBGrid = function(macromolecule) { -// var _this = this; -// -// var data = macromolecule.structure3VOs; -// -// // /** Getting PDB from subunits **/ -// // if (macromolecule != null){ -// // if (macromolecule.stoichiometry != null){ -// // for (var i =0; i < macromolecule.stoichiometry.length; i++){ -// // var stoichiometry = macromolecule.stoichiometry[i]; -// // if (stoichiometry.macromolecule3VO != null){ -// // if (stoichiometry.macromolecule3VO.structure3VOs != null){ -// // for (var j = 0; j < stoichiometry.macromolecule3VO.structure3VOs.length; -// // j++) { -// // var structure = stoichiometry.macromolecule3VO.structure3VOs[j]; -// // structure["isSubunit"] = stoichiometry.macromolecule3VO.acronym; -// // data.push(structure) -// // } -// // } -// // } -// // } -// // } -// // } -// -// this.pdbStore = Ext.create('Ext.data.Store', { -// fields : [ 'filePath', 'structureId', 'structureType', 'symmetry', 'structureId', 'name', 'multiplicity' ], -// data : macromolecule.structure3VOs, -// groupField : 'structureType', -// sorters : { -// property : 'structureId', -// direction : 'DESC' -// } -// }); -// -// var groupingFeature = Ext.create('Ext.grid.feature.Grouping', { -// groupHeaderTpl : Ext.create('Ext.XTemplate', -// "
{name:this.formatName}
", { -// formatName : function(name) { -// return name; -// } -// }), -// hideGroupedHeader : true, -// startCollapsed : false -// }); -// -// this.pdbGrid = Ext.create('Ext.grid.Panel', { -// margin : "15 0 0 5", -// height : 250, -// store : this.pdbStore, -// plugins : _this._getPlugins(), -// buttons : [ { -// // text : 'Add PDB file', -// text : 'Add Modeling Option', -// handler : function() { -// _this._openUploadDialog(macromolecule.macromoleculeId, "PDB", 'Upload PDB File'); -// } -// } -// -// ], -// columns : [ -// { -// text : "structureId", -// flex : 0.2, -// hidden : true, -// dataIndex : 'structureId', -// sortable : true -// }, -// { -// text : "File", -// flex : 0.5, -// dataIndex : 'filePath', -// sortable : true, -// hidden : true -// }, -// { -// text : "PDB", -// flex : 0.4, -// dataIndex : 'name', -// sortable : true -// }, -// { -// text : "Symmetry", -// flex : 0.4, -// dataIndex : 'symmetry', -// sortable : true, -// editor : { -// xtype : 'combobox', -// typeAhead : true, -// triggerAction : 'all', -// selectOnTab : true, -// store : [ [ "P1", "P1" ], [ "P2", "P2" ], [ "P3", "P3" ], [ "P4", "P4" ], [ "P5", "P5" ], [ "P6", "P6" ], [ "P32", "P32" ], [ "P42", "P42" ], -// [ "P52", "P52" ], [ "P62", "P62" ], [ "P222", "P222" ] ], -// } -// }, { -// text : "Multiplicity", -// flex : 0.4, -// dataIndex : 'multiplicity', -// sortable : true, -// editor : { -// xtype : 'textfield' -// } -// -// }, { -// text : "Subunit", -// flex : 0.2, -// dataIndex : 'isSubunit', -// sortable : true, -// hidden : true -// }, { -// text : "Type", -// flex : 0.2, -// dataIndex : 'structureType', -// sortable : true, -// hidden : true -// }, -// -// { -// id : this.id + 'REMOVE', -// flex : 0.2, -// hidden : true, -// sortable : false, -// renderer : function(value, metaData, record, rowIndex, colIndex, store) { -// return BUI.getRedButton('REMOVE'); -// } -// }, ], -// -// listeners : { -// itemdblclick : function(dataview, record, item, e) { -// _this._editExperiment(record.raw.experimentId); -// }, -// cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { -// -// if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'REMOVE') { -// var dataAdapter = new BiosaxsDataAdapter(); -// dataAdapter.onSuccess.attach(function() { -// _this.pdbGrid.setLoading(false); -// _this.update(); -// }); -// dataAdapter.removeStructure(record.data.structureId); -// _this.pdbGrid.setLoading("Removing PDB file"); -// } -// -// } -// }, -// viewConfig : { -// getRowClass : function(record, rowIdx, params, store) { -// if (record.raw.isSubunit != null) { -// return "blue-row"; -// } -// } -// } -// }); -// -// return this.pdbGrid; -//}; -// -//MacromoleculeForm.prototype.getMolarityForm = function(macromolecule) { -// var _this = this; -// var data = []; -// for (var i = 0; i < BIOSAXS.proposal.macromolecules.length; i++) { -// var m = BIOSAXS.proposal.macromolecules[i]; -// if (m.macromoleculeId != macromolecule.macromoleculeId) { -// data.push(m); -// } -// -// } -// this.macromoleculeCombo = BIOSAXS_COMBOMANAGER.getComboMacromoleculeByMacromolecules(data, { -// width : 250, -// labelWidth : 100, -// margin : 10 -// }); -// -// return Ext.createWidget('form', { -// -// frame : false, -// border : 0, -// // padding : 15, -// width : 550, -// height : 350, -// items : [ { -// xtype : 'container', -// flex : 1, -// border : false, -// layout : 'anchor', -// defaultType : 'requiredtext', -// items : [ this.macromoleculeCombo, { -// xtype : 'numberfield', -// name : 'Ratio', -// id : _this.id + "ratio", -// fieldLabel : 'Number in assymmetric units', -// value : 1, -// decimalPrecision : 6, -// margin : 10 -// }, { -// xtype : 'textareafield', -// name : 'comments', -// fieldLabel : 'Comments', -// margin : 10, -// width : 400, -// value : "" -// -// } ] -// } ] -// }); -// -//}; -// -///******************************************************************************* -// * JAVASCRIPT DOC -// ******************************************************************************/ -//MacromoleculeForm.prototype.input = function() { -// return { -// macromolecule : DATADOC.getMacromolecule_10() -// }; -//}; -// -//MacromoleculeForm.prototype.test = function(targetId) { -// var macromoleculeForm = new MacromoleculeForm(); -// var panel = macromoleculeForm.getPanel(macromoleculeForm.input().macromolecule); -// panel.render(targetId); -//}; - -function ResultSummaryForm() { - this.radiationDamageThreshold = BUI.getRadiationDamageThreshold(); - this.qualityThreshold = BUI.getQualityThreshold(); - - /** Data clusters with bufers **/ - this.clusterByBuffers = false; - - /** Visualization are groupeb by concenttrations. Ex: 4.5mg/ml and 4.7mg/ml will be clusterized together **/ - this.collapseConcentrations = true; - - this.summaryHeight = 350; - this.id = BUI.id(); - - var _this = this; - - this.qualityControlResultsWidget = new QualityControlResultsWidget({ - qualityThreshold : this.radiationDamageThreshold, - radiationDamageThreshold : this.qualityThreshold - - }); - this.qualityControlResultsWidget.onQualityChanged.attach(function(sender, value) { - _this.qualityThreshold = value; - _this.thresholdsChanged(); - }); - - this.qualityControlResultsWidget.onRadiationDatamageChanged.attach(function(sender, value) { - _this.radiationDamageThreshold = value; - _this.thresholdsChanged(); - - }); - this.concentrationHTMLTableWidget = new ConcentrationHTMLTableWidget({ - height : 250, - showBufferColumns : this.clusterByBuffers - }); - - this.plot = new BoxWhiskerGraph({ - targetId : _this.id + '_boxPlot', - height : 350, - width : 450, - maxBoxWidth : 25 - }); - - this.rangePlot = new RangeWhiskerGraph({ - targetId : _this.id + '_rangePlot', - height : 350, - width : 450, - maxBoxWidth : 25 - }); -} - -ResultSummaryForm.prototype.thresholdsChanged = function() { - var parsedData = this.prepareData(this.rawData); - - var filtered = JSON.parse(JSON.stringify(parsedData)); - filtered.concentration.concentrations = []; - for ( var conc in parsedData.concentration.concentrations) { - if (parsedData.concentration.concentrations[conc].calculation.rgGnom.validNumber > 0) { - filtered.concentration.concentrations.push(parsedData.concentration.concentrations[conc]); - } - } - - this.plotWhisker(filtered); - this.plotRange(filtered); - - this.concentrationHTMLTableWidget.refresh(parsedData); -}; - -/** Given a frame object (object returned by analyzeFrames()) and depending of the threshold indicates if a datacollection has radiation damage **/ -ResultSummaryForm.prototype.hasRadiationDamage = function(frameObject) { - var bb = frameObject.bufferBeforeFramesMerged / frameObject.framesCount; - var ba = frameObject.bufferAfterFramesMerged / frameObject.framesCount; - var mol = frameObject.framesMerge / frameObject.framesCount; - return !((bb >= this.radiationDamageThreshold) && (ba >= this.radiationDamageThreshold) && (mol >= this.radiationDamageThreshold)); -}; - -/** Return (frameObject) an object with the information about the frames for a data collection**/ -ResultSummaryForm.prototype.analyzeFrames = function(dataCollectionRow) { - return { - bufferAfterFramesMerged : dataCollectionRow.bufferAfterFramesMerged, - bufferBeforeFramesMerged : dataCollectionRow.bufferBeforeFramesMerged, - framesCount : dataCollectionRow.framesCount, - framesMerge : dataCollectionRow.framesMerge - }; -}; - -ResultSummaryForm.prototype.detectDataCollectionWarnings = function(dataCollectionRow) { - var frameObject = this.analyzeFrames(dataCollectionRow); - var warnings = { - count : 0, - type : [] - }; - - if (this.hasRadiationDamage(frameObject)) { - warnings.count = warnings.count + 1; - warnings.type.push("RADIATION DAMAGE"); - } - - if (Number(dataCollectionRow.quality) < this.qualityThreshold) { - warnings.count = 1;//warnings.count + 1; - warnings.type.push("Quality <" + this.qualityThreshold); - } - return warnings; -}; - -/** Return array composed by {concentration} objects **/ -ResultSummaryForm.prototype.getConcentrations = function(data) { - var concentrationsId = {}; - - for ( var i = 0; i < data.length; i++) { - var warning = this.detectDataCollectionWarnings(data[i]); - var id = data[i].conc;// + "_" + data[i].bufferId; - if (this.clusterByBuffers) { - id = data[i].conc + "_" + data[i].bufferId; - } - - if (concentrationsId[id] == null) { - concentrationsId[id] = { - id : id, - concentration : Number(data[i].conc).toFixed(2), - bufferId : data[i].bufferId, - bufferAcronym : data[i].bufferAcronym, - rgGuinier : [], - i0Guinier : [], - rgGnom : [], - dMax : [], - quality : [], - frames : [], - frames_warning : warning.count - }; - } else { - concentrationsId[id].frames_warning = concentrationsId[id].frames_warning + warning.count; - } - - concentrationsId[id].frames.push(data[i]); - - if (warning.count == 0) { - concentrationsId[id].rgGuinier.push(data[i].rgGuinier); - concentrationsId[id].i0Guinier.push(data[i].I0); - concentrationsId[id].quality.push(data[i].quality); - concentrationsId[id].rgGnom.push(data[i].rgGnom); - concentrationsId[id].dMax.push(data[i].dmax); - } - - } - var concentrations = []; - for ( var item in concentrationsId) { - if (concentrationsId.hasOwnProperty(item)) { - concentrations.push({ - concentration : concentrationsId[item].concentration, - id : item, - bufferId : Number(concentrationsId[item].bufferId).toFixed(2), - bufferAcronym : concentrationsId[item].bufferAcronym, - rgGuinier : concentrationsId[item].rgGuinier, - /** Frames **/ - frames : concentrationsId[item].frames, - frames_warning : concentrationsId[item].frames_warning, - /** Calculation **/ - calculation : { - rgGuinier : BUI.getStandardDeviation(concentrationsId[item].rgGuinier), - i0Guinier : BUI.getStandardDeviation(concentrationsId[item].i0Guinier), - quality : BUI.getStandardDeviation(concentrationsId[item].quality), - rgGnom : BUI.getStandardDeviation(concentrationsId[item].rgGnom), - dMax : BUI.getStandardDeviation(concentrationsId[item].dMax) - - } - }); - } - } - - return { - concentrations : concentrations - }; -}; - -ResultSummaryForm.prototype.prepareData = function(data) { - /** Return array composed by {acronym, bufferId} objects **/ - function getBuffers(data) { - var buffersId = {}; - for ( var i = 0; i < data.length; i++) { - buffersId[data[i].bufferId] = data[i].acronym; - } - var buffers = []; - for ( var id in buffersId) { - if (buffersId.hasOwnProperty(id)) { - buffers.push({ - acronym : buffersId[id], - bufferId : id - }); - } - } - return buffers; - } - - /** Get a string with all the concentrations **/ - function getConcentrationString(parseConcentrations) { - var concentrations = []; - for ( var i = 0; i < parseConcentrations.concentrations.length; i++) { - concentrations.push(parseConcentrations.concentrations[i].concentration + " mg/ml "); - } - return concentrations.toString(); - } - - var parseConcentrations = this.getConcentrations(data); - - return { - dataCollectionCount : data.length, - buffers : getBuffers(data), - concentration : parseConcentrations, - concentrationLabel : getConcentrationString(parseConcentrations) - }; -}; - -ResultSummaryForm.prototype.getDataForWhisker = function(data) { - var clusters = []; - - var concentrations = {}; - var i = 0; - var conc = 0; - if (this.collapseConcentrations) { - for (i = 0; i < data.concentration.concentrations.length; i++) { - conc = data.concentration.concentrations[i]; - var concentration = Number(conc.concentration).toFixed(0); - if (concentrations[concentration] == null) { - concentrations[concentration] = {}; - concentrations[concentration].concentration = concentration; - concentrations[concentration].calculation = {}; - concentrations[concentration].calculation.rgGuinier = {}; - concentrations[concentration].calculation.rgGuinier.values = []; - concentrations[concentration].calculation.rgGuinier.values = conc.calculation.rgGuinier.values; - concentrations[concentration].calculation.rgGnom = {}; - concentrations[concentration].calculation.rgGnom.values = []; - concentrations[concentration].calculation.rgGnom.values = conc.calculation.rgGnom.values; - } else { - concentrations[concentration].calculation.rgGuinier.values = concentrations[concentration].calculation.rgGuinier.values - .concat(conc.calculation.rgGuinier.values); - concentrations[concentration].calculation.rgGnom.values = concentrations[concentration].calculation.rgGnom.values - .concat(conc.calculation.rgGnom.values); - } - } - - /** From object to array **/ - var array = []; - for ( var key in concentrations) { - if (concentrations.hasOwnProperty(key)) { - array.push(concentrations[key]); - } - } - data.concentration.concentrations = array; - } - - for (i = 0; i < data.concentration.concentrations.length; i++) { - conc = data.concentration.concentrations[i]; - clusters.push({ - name : conc.concentration + "mg/ml", - concentration : Number(conc.concentration), - x : Number(conc.concentration), - classes : [] - }); - clusters[clusters.length - 1].classes.push({ - name : "Guinier", - color : '#9A2EFE', - values : conc.calculation.rgGuinier.values - - }); - clusters[clusters.length - 1].classes.push({ - name : "P(r)", - color : '#2E64FE', - values : conc.calculation.rgGnom.values - - }); - } - return { - clusters : clusters.sort(function(a, b) { - return a.concentration - b.concentration; - }) - }; -}; - -ResultSummaryForm.prototype.getDataForWhiskerQuality = function(data) { - var clusters = []; - - for ( var i = 0; i < data.concentration.concentrations.length; i++) { - var conc = data.concentration.concentrations[i]; - clusters.push({ - name : conc.concentration + "mg/ml", - classes : [] - }); - clusters[clusters.length - 1].classes.push({ - name : "Quality", - values : conc.calculation.quality.values - - }); - } - - return { - clusters : clusters - }; -}; - -ResultSummaryForm.prototype.plotQualityWhisker = function(parsedData) { - this.qualityPlot.refresh(this.getDataForWhiskerQuality(parsedData)); -}; - -ResultSummaryForm.prototype.plotWhisker = function(parsedData) { - this.plot.refresh(this.getDataForWhisker(parsedData)); -}; - -ResultSummaryForm.prototype.plotRange = function(parsedData) { - this.rangePlot.refresh(this.getDataForWhisker(parsedData)); -}; - -ResultSummaryForm.prototype.getPanel = function(data) { - var _this = this; - _this.rawData = data; - var parsedData = this.prepareData(data); - - this.panel = Ext - .createWidget( - 'form', - { - bodyPadding : 20, - frame : false, - border : 0, - width : this.width, - height : this.summaryHeight + 1000, - items : [ { - xtype : 'container', - layout : 'hbox', - items : [ { - xtype : 'container', - flex : 1, - border : false, - layout : 'anchor', - defaultType : 'textfield', - items : [ - _this.qualityControlResultsWidget.getPanel(), - { - xtype : 'fieldset', - width : 950, - margin : "20, 0 0 0", - height : this.summaryHeight + 500, - items : [ - { - html : "
Concentration Analysis
", - border : 0, - width : 900 - - }, - { - html : this.concentrationHTMLTableWidget.getPanel(parsedData), - border : 0, - width : 900, - margin : "10, 0 0 10" - - }, - { - xtype : 'container', - layout : 'vbox', - border : 5, - items : [ - { - html : "
Rg based on Guinier and Gnom
", - border : 8, - width : 900, - margin : "20 0 0 10" - - }, { - xtype : 'container', - layout : 'hbox', - items : [ { - xtype : 'container', - layout : 'vbox', - items : [ { - html : "
", - border : 0, - width : this.plot.width, - padding : "10 0 0 20", - listeners : { - afterrender : function() { - _this.plotWhisker(parsedData); - - } - } - }, { - html : "
Concentration (mg/ml)
", - width : 450, - border : 0, - margin : "-50 0 0 0" - - } ] - }, { - xtype : 'container', - layout : 'vbox', - items : [ { - html : "
", - border : 0, - width : this.rangePlot.width, - padding : "10 0 0 10", - listeners : { - afterrender : function() { - _this.plotRange(parsedData); - } - } - }, { - html : "
Concentration (mg/ml)
", - width : 450, - border : 0, - margin : "-50 0 0 0" - - } ] - } ] - } ] - } ] - } ] - } - - ] - } ] - }); - return this.panel; - -}; - -ResultSummaryForm.prototype.input = function() { - return { - data : DATADOC.getData_3367() - }; -}; - -ResultSummaryForm.prototype.test = function(targetId) { - var resultSummaryForm = new ResultSummaryForm(); - var panel = resultSummaryForm.getPanel(resultSummaryForm.input().data); - panel.render(targetId); - -}; - -/** - * Example form - * - * @witdh - * @height - */ -function RigibBodyModelingForm(args) { - this.id = BUI.id(); - this.width = 700; - this.height = 500; - - if (args != null) { - if (args.width != null) { - this.width = args.width; - } - if (args.height != null) { - this.height = args.height; - } - } - - var _this = this; - this.rigidBodyGrid = new AprioriRigidBodyGrid(); - - this.rigidBodyGrid.onUploadFile.attach(function(sender, type, title){ - _this._openUploadDialog(_this.macromolecule.macromoleculeId, type, title); - }); - - this.rigidBodyGrid.onRemove.attach(function(sender, type, title){ - _this._update(); - }); - - this.onSave = new Event(this); - -} - -RigibBodyModelingForm.prototype._getItems = function() { - var _this = this; - - - return [ { - xtype : 'label', - forId : 'myFieldId', - text : 'Information for model fit, mixture analysis and rigid body modeling', - margin : '15 0 20 10', - cls : "inline-help" - }, - this.rigidBodyGrid.getPanel(), - { - xtype : 'label', - forId : 'myFieldId', - text : 'Distance restraints may be imposed on the model using contacts conditions file (OPTIONAL)', - margin : '25 0 5 10', - cls : "inline-help" - }, - { - xtype : 'container', - layout : 'hbox', - items : [ { - xtype : 'container', - border : false, - layout : 'hbox', - items : [ { - xtype : 'label', - forId : 'myFieldId', - text : 'Contact Description File: (Optional)', - width : 150, - margin : '10 0 0 10' - }, { - id : this.id + "contactsDescriptionFilePath", - xtype : 'textfield', - hideLabel : true, - margin : '10 0 0 0', - width : 200, - disabled: true - }, { - text : 'Upload', - xtype : 'button', - margin : "10 0 0 10", - width : 80, - handler : function() { - - _this._openUploadDialog(_this.macromolecule.macromoleculeId, "CONTACTS", "Upload Contact Description File"); - } - }, { - text : 'Remove', - id : _this.id + "_remove", - xtype : 'button', - margin : "10 0 0 10", - width : 80, - handler : function() { - _this.macromolecule.contactsDescriptionFilePath = null; - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, proposal) { - /** Updating the proposal global object **/ - BIOSAXS.proposal.setItems(proposal); - _this.panel.setLoading(false); - - var saved = BIOSAXS.proposal.getMacromoleculeByAcronym(_this.macromolecule.acronym); - /** Refreshing to check that everything is ok **/ - _this.refresh(saved); - _this.onSave.notify(saved); - }); - - _this.panel.setLoading("Saving Macromolecule") - adapter.saveMacromolecule(_this.macromolecule); - } - } ] - } ] - }, { - xtype : 'panel', - html : "Go to SASREF manual for further information", - margin : "10 0 0 160", - border : 0 - - }, - { - xtype : 'checkbox', - margin : '10 0 0 5', - boxLabel : "I want rigid body modeling run on this stuff", - checked : true, - width : 300 - } ] - -}; - -/** Because update is a jsp page we don't know if the user has uploaded a file or not then we need to refresh **/ -RigibBodyModelingForm.prototype._update = function(macromoleculeId, type, title) { - var _this = this; - BIOSAXS.proposal.onInitialized.attach(function() { - if (BIOSAXS.proposal != null) { - _this.refresh(BIOSAXS.proposal.getMacromoleculeById(_this.macromolecule.macromoleculeId)); - _this.panel.setLoading(false); - } - }); - this.panel.setLoading(); - BIOSAXS.proposal.init(); -}; - -RigibBodyModelingForm.prototype._openUploadDialog = function(macromoleculeId, type, title) { - var _this = this; - function onClose() { - w.destroy(); - _this._update(); - - } - - var w = Ext.create('Ext.window.Window', { - title : title, - height : 200, - width : 400, - modal : true, - buttons : [ { - text : 'Close', - handler : function() { - onClose(); - } - } ], - layout : 'fit', - items : { - html : "" - }, - listeners : { - onEsc : function() { - onClose(); - }, - close : function() { - onClose(); - } - } - }).show(); -}; - -RigibBodyModelingForm.prototype._getButtons = function() { - return []; -}; - -RigibBodyModelingForm.prototype.getPanel = function() { - var _this = this; - this.panel = Ext.create('Ext.form.Panel', { - width : this.width, - height : this.height, - margin : 10, - border : 1, - defaultType : 'textfield', - items : this._getItems(), - buttons : this._getButtons(), - listeners : { - afterrender : function(){ - _this._populate(); - - } - } - }); - return this.panel; -}; - -/** Populates could be call when the DOM is not filled yet **/ -RigibBodyModelingForm.prototype._populate = function() { - if (this.macromolecule != null){ - if (Ext.getCmp(this.id + "contactsDescriptionFilePath") != null){ - if (this.macromolecule.contactsDescriptionFilePath != null){ - Ext.getCmp(this.id + "contactsDescriptionFilePath").setValue(this.macromolecule.contactsDescriptionFilePath); - Ext.getCmp(this.id + "_remove").enable(); - } - else{ - Ext.getCmp(this.id + "_remove").disable(); - Ext.getCmp(this.id + "contactsDescriptionFilePath").setValue(""); - - } - } - } -}; - -/** It populates the form * */ -RigibBodyModelingForm.prototype.refresh = function(macromolecule) { - this.macromolecule = macromolecule; - this.rigidBodyGrid.refresh(macromolecule); - this._populate(); -}; - -RigibBodyModelingForm.prototype.input = function() { - return {}; -}; - - -/** It populates the form **/ -RigibBodyModelingForm.prototype.test = function(targetId) { - var macromoleculeForm = new RigibBodyModelingForm(); - var panel = macromoleculeForm.getPanel(); - panel.render(targetId); -}; - - -/** - * Same form as MX part - * - * @creationMode if true a create button appears instead of save - * @showTitle true or false - */ -function ShipmentForm(args) { - this.id = BUI.id(); - - this.creationMode = false; - this.showTitle = true; - if (args != null) { - if (args.creationMode != null) { - this.creationMode = args.creationMode; - } - if (args.showTitle != null) { - this.showTitle = args.showTitle; - } - } - - this.onSaved = new Event(this); -} - -ShipmentForm.prototype.fillStores = function() { - this.panel.setLoading("Loading Labcontacts from database"); - this.labContactForSendingStore.loadData(BIOSAXS.proposal.getLabcontacts(), false); - this.labContactForReturnStore.loadData(BIOSAXS.proposal.getLabcontacts(), false); - this.panel.setLoading(false); - if (this.shipment != null) { - this.setShipment(this.shipment); - } -}; - -ShipmentForm.prototype.draw = function(targetId) { - this.getPanel().render(targetId); -}; - -ShipmentForm.prototype.setShipment = function(shipment) { - this.shipment = shipment; - var _this = this; - Ext.getCmp(_this.id + "shippingName").setValue(shipment.json.shippingName); - Ext.getCmp(_this.id + "shippingStatus").setValue(shipment.json.shippingStatus); - Ext.getCmp(_this.id + "comments").setValue(shipment.json.comments); - if (shipment.json.sendingLabContactVO != null) { - this.labContactsSendingCombo.setValue(shipment.json.sendingLabContactVO.labContactId); - } - if (shipment.json.returnLabContactVO != null) { - this.labContactsReturnCombo.setValue(shipment.json.returnLabContactVO.labContactId); - } - -}; - -ShipmentForm.prototype._saveShipment = function() { - var _this = this; - var shippingId = null; - if (this.shipment != null) { - shippingId = this.shipment.json.shippingId; - } - var json = { - shippingId : shippingId, - name : Ext.getCmp(_this.id + "shippingName").getValue(), - status : Ext.getCmp(_this.id + "shippingStatus").getValue(), - sendingLabContactId : Ext.getCmp(_this.id + "shipmentform_sendingLabContactId").getValue(), - returnLabContactId : Ext.getCmp(_this.id + "returnLabContactId").getValue(), - returnCourier : Ext.getCmp(_this.id + "returnCourier").getValue(), - courierAccount : Ext.getCmp(_this.id + "courierAccount").getValue(), - BillingReference : Ext.getCmp(_this.id + "BillingReference").getValue(), - dewarAvgCustomsValue : Ext.getCmp(_this.id + "dewarAvgCustomsValue").getValue(), - dewarAvgTransportValue : Ext.getCmp(_this.id + "dewarAvgTransportValue").getValue(), - comments : Ext.getCmp(_this.id + "comments").getValue() - }; - - var dataAdapter = new BiosaxsDataAdapter(); - dataAdapter.onSuccess.attach(function(sender, shipment) { - window.location = BUI.getShippingURL(shipment.shippingId); - }); - - dataAdapter.onError.attach(function(sender, error) { - _this.onError.notify(error); - }); - - /** Cheking params **/ - if (json.name == "") { - BUI.showError("Name field is mandatory"); - return; - } - - if (json.sendingLabContactId == null) { - BUI.showError("Lab contact for sending field is mandatory"); - return; - } - - if (json.returnLabContactId == null) { - BUI.showError("Lab contact for return field is mandatory"); - return; - } - - dataAdapter.createShipment(json.shippingId, json.name, json.status, json.comments, json.sendingLabContactId, json.returnLabContactId, - json.returnCourier, json.courierAccount, json.BillingReference, json.dewarAvgCustomsValue, json.dewarAvgTransportValue); - -}; - -ShipmentForm.prototype.getPanel = function(shipment) { - var _this = this; - this.shipment = shipment; - var required = '*'; - var buttons = []; - - if (_this.creationMode) { - buttons.push({ - text : 'Create', - scope : this, - handler : function() { - _this._saveShipment(); - } - }); - } else { - buttons.push({ - text : 'Save', - scope : this, - handler : function() { - _this._saveShipment(); - } - }); - - } - - this.labContactForSendingStore = Ext.create('Ext.data.Store', { - fields : [ 'cardName', 'labContactId' ] - }); - - this.labContactForReturnStore = Ext.create('Ext.data.Store', { - fields : [ 'cardName', 'labContactId' ] - }); - - // Create the combo box, attached to the states data store - this.labContactsSendingCombo = Ext.create('Ext.form.ComboBox', { - id : _this.id + "shipmentform_sendingLabContactId", - fieldLabel : 'Lab contact for sending', - afterLabelTextTpl : required, - store : this.labContactForSendingStore, - queryMode : 'local', - labelWidth : 200, - displayField : 'cardName', - valueField : 'labContactId' - }); - - this.labContactsReturnCombo = Ext.create('Ext.form.ComboBox', { - id : _this.id + "returnLabContactId", - fieldLabel : 'If No, Lab-Contact for Return', - afterLabelTextTpl : required, - store : this.labContactForReturnStore, - queryMode : 'local', - labelWidth : 200, - displayField : 'cardName', - valueField : 'labContactId', - listeners : { - change : function(x, newValue) { - for ( var i = 0; i < x.getStore().data.items.length; i++) { - if (x.getStore().data.items[i].raw.labContactId == newValue) { - Ext.getCmp(_this.id + "returnCourier").setValue(x.getStore().data.items[i].raw.defaultCourrierCompany); - Ext.getCmp(_this.id + "courierAccount").setValue(x.getStore().data.items[i].raw.courierAccount); - Ext.getCmp(_this.id + "BillingReference").setValue(x.getStore().data.items[i].raw.billingReference); - Ext.getCmp(_this.id + "dewarAvgCustomsValue").setValue(x.getStore().data.items[i].raw.dewarAvgCustomsValue); - Ext.getCmp(_this.id + "dewarAvgTransportValue").setValue(x.getStore().data.items[i].raw.dewarAvgTransportValue); - } - } - } - } - }); - - if (this.panel == null) { - this.panel = Ext.create('Ext.form.Panel', { - bodyPadding : 5, - width : 600, - border : 1, - items : [ { - xtype : 'fieldset', - title : 'Details', - collapsible : false, - defaultType : 'textfield', - layout : 'anchor', - defaults : { - anchor : '100%' - }, - items : [ { - xtype : 'requiredtext', - fieldLabel : 'Shipment Label', - allowBlank : false, - name : 'shippingName', - id : _this.id + 'shippingName', - value : '', - anchor : '50%' - }, { - - xtype : 'textareafield', - name : 'comments', - id : _this.id + 'comments', - fieldLabel : 'Comments', - value : '' - }, { - fieldLabel : 'Status', - readOnly : true, - id : _this.id + 'shippingStatus', - value : 'Opened', - anchor : '50%' - } ] - }, { - xtype : 'fieldset', - title : 'Lab-Contacts', - collapsible : false, - defaultType : 'textfield', - layout : 'anchor', - defaults : { - anchor : '100%' - }, - items : [ this.labContactsSendingCombo, this.labContactsReturnCombo ] - }, { - border : 0, - html : BUI.getWarningHTML("These informations are relevant for all shipments") - }, { - xtype : 'fieldset', - title : 'Courier accounts details for return', - collapsible : false, - layout : 'anchor', - defaults : { - anchor : '100%' - }, - items : [ { - xtype : 'textfield', - labelWidth : 400, - fieldLabel : 'Courier company for return (if ESRF sends a dewar back)', - id : _this.id + 'returnCourier', - value : '' - }, { - xtype : 'textfield', - labelWidth : 400, - fieldLabel : 'Courier account', - id : _this.id + 'courierAccount', - value : '' - }, { - xtype : 'textfield', - labelWidth : 400, - fieldLabel : 'Billing reference', - id : _this.id + 'BillingReference', - value : '' - }, { - xtype : 'numberfield', - labelWidth : 400, - fieldLabel : 'Average Customs value of a dewar (Euro)', - id : _this.id + 'dewarAvgCustomsValue', - value : '' - }, { - xtype : 'numberfield', - labelWidth : 400, - fieldLabel : 'Average Transport value of a dewar (Euro)', - id : _this.id + 'dewarAvgTransportValue', - value : '' - } ] - } ], - buttons : buttons - }); - } - this.fillStores(); - if (this.showTitle) { - this.panel.setTitle('Create a new Shipment'); - } - return this.panel; -}; - -ShipmentForm.prototype.input = function() { - return { - proposal : DATADOC.getProposal_10() - - }; -}; - -ShipmentForm.prototype.test = function(targetId) { - var shipmentForm = new ShipmentForm({ - creationMode : true - - }); - BIOSAXS.proposal = new Proposal(shipmentForm.input().proposal); - shipmentForm.getPanel().render(targetId); -}; - -/** - * #onSaved - */ -function StockSolutionForm(args) { - this.id = BUI.id(); - this.actions = []; - - if (args != null) { - if (args.actions != null) { - this.actions = args.actions; - } - } - this.onSaved = new Event(this); -} - -StockSolutionForm.prototype.getStockSolution = function() { - if (this.stockSolution != null) { - this.stockSolution.concentration = Ext.getCmp(this.id + "stockSolution_concentration").getValue(); - this.stockSolution.storageTemperature = Ext.getCmp(this.id + "stockSolution_storageTemperature").getValue(); - this.stockSolution.volume = Ext.getCmp(this.id + "stockSolution_volume").getValue(); - this.stockSolution.comments = Ext.getCmp(this.id + "stockSolution_comments").getValue(); - this.stockSolution.name = Ext.getCmp(this.id + "stockSolution_name").getValue(); - this.stockSolution.bufferId = this.bufferCombo.getValue(); - - if (this.macromoleculeCombo.getValue() != null) { - this.stockSolution.macromoleculeId = this.macromoleculeCombo.getValue(); - } else { - this.stockSolution.macromolecule3VO = null; - } - - } else { - return { - concentration : Ext.getCmp(this.id + "stockSolution_concentration").getValue(), - storageTemperature : Ext.getCmp(this.id + "stockSolution_storageTemperature").getValue(), - volume : Ext.getCmp(this.id + "stockSolution_volume").getValue(), - comments : Ext.getCmp(this.id + "stockSolution_comments").getValue(), - name : Ext.getCmp(this.id + "stockSolution_name").getValue(), - bufferId : this.bufferCombo.getValue(), - macromoleculeId : this.macromoleculeCombo.getValue() - }; - } - return this.stockSolution; -}; - -StockSolutionForm.prototype.setStockSolution = function(stockSolution) { - if (stockSolution != null) { - if (stockSolution.macromoleculeId != null) { - this.macromoleculeCombo.setValue(stockSolution.macromoleculeId); - } - this.bufferCombo.setValue(stockSolution.bufferId); - Ext.getCmp(this.id + "stockSolution_concentration").setValue(this.stockSolution.concentration); - Ext.getCmp(this.id + "stockSolution_storageTemperature").setValue(this.stockSolution.storageTemperature); - Ext.getCmp(this.id + "stockSolution_volume").setValue(this.stockSolution.volume); - Ext.getCmp(this.id + "stockSolution_name").setValue(this.stockSolution.name); - Ext.getCmp(this.id + "stockSolution_comments").setValue(this.stockSolution.comments); - } -}; - -StockSolutionForm.prototype.getBufferCombo = function() { - this.bufferCombo = BIOSAXS_COMBOMANAGER.getComboBuffers(BIOSAXS.proposal.getBuffers(), { - labelWidth : 120, - margin : '0 0 10 0', - width : 220 - - }); - return this.bufferCombo; -}; - -StockSolutionForm.prototype.getMacromoleculeCombo = function() { - this.macromoleculeCombo = BIOSAXS_COMBOMANAGER.getComboMacromoleculeByMacromolecules(BIOSAXS.proposal.getMacromolecules(), { - labelWidth : 120, - margin : '0 0 10 0', - width : 220 - - }); - return this.macromoleculeCombo; -}; - -StockSolutionForm.prototype.refresh = function() { -}; - -StockSolutionForm.prototype._getTopPanel = function() { - return { - xtype : 'container', - layout : 'hbox', - border : 0, - items : [ { - xtype : 'container', - layout : 'hbox', - items : [ { - xtype : 'container', - flex : 1, - border : false, - layout : 'anchor', - defaultType : 'textfield', - items : [ - - this.getMacromoleculeCombo(), { - xtype : 'requiredtext', - id : this.id + 'stockSolution_name', - fieldLabel : 'Acronym', - labelWidth : 120, - width : 250 - }, { - xtype : 'requiredtext', - id : this.id + 'stockSolution_concentration', - fieldLabel : 'Conc. (mg/ml)', - labelWidth : 120, - width : 250 - }, - - { - id : this.id + 'stockSolution_storageTemperature', - fieldLabel : 'Storage Temp.(C)', - labelWidth : 120, - width : 250 - }, { - xtype : 'requiredtext', - id : this.id + 'stockSolution_volume', - fieldLabel : 'Volume in Well (µl)', - labelWidth : 120, - width : 250 - } ] - } ] - }, { - xtype : 'container', - flex : 1, - layout : 'anchor', - defaultType : 'textfield', - margin : '0 0 0 10', - items : [ this.getBufferCombo() ] - } ] - }; - -}; - -StockSolutionForm.prototype.getPanel = function(stockSolution) { - this.stockSolution = stockSolution; - this.panel = Ext.createWidget({ - xtype : 'container', - layout : 'vbox', - border : 0, - style : { - padding : '10px' - }, - fieldDefaults : { - labelAlign : 'left', - labelWidth : 50 - }, - items : [ this._getTopPanel(stockSolution), { - id : this.id + 'stockSolution_comments', - xtype : 'textareafield', - name : 'comments', - fieldLabel : 'Comments', - labelWidth : 120, - width : '100%' - } ] - }); - - this.setStockSolution(stockSolution); - return this.panel; -}; - -StockSolutionForm.prototype.input = function() { - return { - stock : { - "stockSolutionId" : 6, - "proposalId" : 3124, - "macromoleculeId" : 5933, - "bufferId" : 811, - "instructionSet3VO" : null, - "boxId" : 305861, - "storageTemperature" : "20", - "volume" : "300", - "concentration" : "1.2", - "comments" : "Buffer EDTA with A", - "name" : "A_EDTA_1.2", - "samples" : [], - "buffer" : "EDTA", - "macromolecule" : "A" - }, - proposal : new MeasurementGrid().input().proposal - }; -}; - -StockSolutionForm.prototype.test = function(targetId) { - var stockSolutionForm = new StockSolutionForm(); - BIOSAXS.proposal = new Proposal(stockSolutionForm.input().proposal); - var panel = stockSolutionForm.getPanel(new Shipment(stockSolutionForm.input().stock)); - panel.render(targetId); -}; - - -BoxWhiskerGraph.prototype.cleanArray = GenericGraph.prototype.cleanArray; -BoxWhiskerGraph.prototype.plotAxes = GenericGraph.prototype.plotAxes; -BoxWhiskerGraph.prototype.plotRuler = GenericGraph.prototype.plotRuler; -BoxWhiskerGraph.prototype.drawSVGVerticalText = GenericGraph.prototype.drawSVGVerticalText; -BoxWhiskerGraph.prototype.getClassColor = GenericGraph.prototype.getClassColor; -BoxWhiskerGraph.prototype.getDimensions = GenericGraph.prototype.getDimensions; -BoxWhiskerGraph.prototype.calculate = GenericGraph.prototype.calculate; -BoxWhiskerGraph.prototype.getMedian = GenericGraph.prototype.getMedian; -BoxWhiskerGraph.prototype.pointToPixel = GenericGraph.prototype.pointToPixel; -BoxWhiskerGraph.prototype.maxValueWhisker = GenericGraph.prototype.maxValueWhisker; -BoxWhiskerGraph.prototype.refresh = GenericGraph.prototype.refresh; - -/** - * Subclass of GenericGraph - * - * @maxBoxWidth - */ -function BoxWhiskerGraph(args){ - this.maxBoxWidth = 25; - - if (args == null){ - args = new Object(); - } - args["plotHorizontalByCluster"] = true; - - GenericGraph.call(this, args); - - if (args.maxBoxWidth != null){ - this.maxBoxWidth = args.maxBoxWidth; - } -}; - - - -/** -There are several different methods for calculating quartiles.[1] This calculator uses a method described by Moore and McCabe to find quartile values. -The same method also used by The TI-83 to calculate quartile values. -With this method, the first quartile is the median of the numbers below the median, the third quartile is the median of the numbers above the median. - -http://www.miniwebtool.com/quartile-calculator/ -http://www.alcula.com/calculators/statistics/box-plot/ -**/ -BoxWhiskerGraph.prototype.getQ1 = function(array){ - array = array.slice(0, array.length/2); - return this.getMedian(array); -}; - -BoxWhiskerGraph.prototype.getQ3 = function(array){ - array = array.slice((array.length + 1)/2); - return this.getMedian(array); - -}; - -BoxWhiskerGraph.prototype.getQ2 = function(array){ - return this.getMedian(array); -}; - -BoxWhiskerGraph.prototype.getBelowOutliers = function(belowLimit, array){ - var points = new Array(); - for (var i = 0; i < array.length; i++){ - if (array[i] <= belowLimit){ - points.push(array[i]); - } - } - return points; -}; - -BoxWhiskerGraph.prototype.getAboveOutliers = function(aboveLimit, array){ - var points = new Array(); - for (var i = 0; i < array.length; i++){ - if (array[i] >= aboveLimit){ - points.push(array[i]); - } - } - return points; -}; - - -BoxWhiskerGraph.prototype.minValueWhisker = function(belowLimit, q1, array){ - var points = new Array(); - - for (var i = 0; i < array.length; i++){ - if ((array[i] < q1) && (array[i]) > belowLimit){ - points.push(array[i]); - } - } - - if (points.length > 0){ - points.sort(function(a, b){return a - b;}); - return points[0]; - } - return null; -}; - -BoxWhiskerGraph.prototype.isNumber = function(value){ - if (value=="") return false; - - var d = parseInt(value); - if (!isNaN(d)) return true; else return false; - -}; - -BoxWhiskerGraph.prototype.plotBoxWhisker = function(boxProperties){ - if (this.maxBoxWidth != null){ - if (boxProperties.width > this.maxBoxWidth){ - boxProperties.x = boxProperties.x + (boxProperties.width/2) - (this.maxBoxWidth/2); - boxProperties.width = this.maxBoxWidth; - } - - } - - if (this.plotPoints){ - for(var i = 0; i < boxProperties.values.length; i++){ - var value = boxProperties.values[i]; - var toPixel = this.pointToPixel(value, boxProperties); - SVG.drawCircle(boxProperties.x + (boxProperties.width/2) + 2, toPixel, this.pointRadius, this.svg, [["fill", "green"], ["fill-opacity", this.fillOpacityPoint],['stroke-opacity', this.strokeOpacityPoint], ["stroke", "black"]]); - } - } - - - var boxColor = this.getClassColor(boxProperties.name); - var result = this.calculate(boxProperties.values); - /** Q1 **/ - - if (this.isNumber(result.Q1)){ - SVG.drawLine(boxProperties.x, this.pointToPixel(result.Q1, boxProperties), boxProperties.x + boxProperties.width, this.pointToPixel(result.Q1, boxProperties), this.svg, [["stroke", boxColor]]); - } - /** Q2 **/ - if (this.isNumber(result.Q2)){ - SVG.drawLine(boxProperties.x, this.pointToPixel(result.Q2, boxProperties), boxProperties.x + boxProperties.width, this.pointToPixel(result.Q2, boxProperties), this.svg, [["stroke", "blue"], ["stroke-width", "2"]]); - } - - /** Q3 **/ - if (this.isNumber(result.Q3)){ - SVG.drawLine(boxProperties.x, this.pointToPixel(result.Q3, boxProperties), boxProperties.x + boxProperties.width, this.pointToPixel(result.Q3, boxProperties), this.svg, [["stroke", boxColor]]); - } - - /** Concenting Q1 and Q3 **/ - if (this.isNumber(result.Q1)&&this.isNumber(result.Q3)){ - SVG.drawLine(boxProperties.x, this.pointToPixel(result.Q1, boxProperties), boxProperties.x, this.pointToPixel(result.Q3, boxProperties), this.svg, [["stroke", boxColor]]); - SVG.drawLine(boxProperties.x + boxProperties.width, this.pointToPixel(result.Q1, boxProperties), boxProperties.x + boxProperties.width, this.pointToPixel(result.Q3, boxProperties), this.svg, [["stroke", boxColor]]); - } - - /** min-whisker **/ - if (result["min-whisker"] != null){ - if (this.isNumber(result.Q1)){ - SVG.drawLine(boxProperties.x + (boxProperties.width/2) + 2, this.pointToPixel(result.Q1, boxProperties), boxProperties.x + (boxProperties.width/2) + 2, this.pointToPixel(result["min-whisker"], boxProperties), this.svg, [["stroke", boxColor]]); - } - SVG.drawLine(boxProperties.x,this.pointToPixel(result["min-whisker"], boxProperties), boxProperties.x + boxProperties.width, this.pointToPixel(result["min-whisker"], boxProperties) , this.svg, [["stroke", boxColor]]); - - } - - /** max-whisker **/ - if (result["max-whisker"] != null){ - if (this.isNumber(result.Q3)){ - SVG.drawLine(boxProperties.x + (boxProperties.width/2) + 2, this.pointToPixel(result.Q3, boxProperties), boxProperties.x + (boxProperties.width/2) + 2, this.pointToPixel(result["max-whisker"], boxProperties), this.svg, [["stroke", boxColor]]); - } - SVG.drawLine(boxProperties.x , this.pointToPixel(result["max-whisker"], boxProperties), boxProperties.x + boxProperties.width, this.pointToPixel(result["max-whisker"], boxProperties), this.svg, [["stroke", boxColor]]); - - } - - /** outliners **/ - if (result["above-outliers"] != null){ - for (var point in result["above-outliers"]){ - SVG.drawCircle(boxProperties.x + (boxProperties.width/2) + 2, this.pointToPixel(result["above-outliers"][point], boxProperties), this.pointRadius, this.svg, [["fill", "red"], ["fill-opacity", "1"]]); - //SVG.drawText(boxProperties.x + (boxProperties.width/2) - 2, this.pointToPixel(result["above-outliers"][point], boxProperties) + 2, "x",this.svg, [["fill", "black"], ["font-size", "x-small"]]); - } - } - if (result["below-outliers"] != null){ - for (var point in result["below-outliers"]){ - SVG.drawCircle(boxProperties.x + (boxProperties.width/2) + 2, this.pointToPixel(result["below-outliers"][point], boxProperties), this.pointRadius, this.svg, [["fill", "red"], ["fill-opacity", "1"]]); - //SVG.drawText(boxProperties.x + (boxProperties.width/2) - 2, this.pointToPixel(result["below-outliers"][point], boxProperties) + 2, "x",this.svg, [["fill", "black"], ["font-size", "x-small"]]); - } - } -}; - - - -BoxWhiskerGraph.prototype.plotClusterTitles = function(properties){ - /** Cluster Titles **/ - var posX = this.left + this.rulerWidth; - var data = this.data; - for(var i = 0; i < data.clusters.length; i++ ){ - /** title for the clusters **/ - posX = posX + this.interClustersSpace; - - /** Drawing title of classes **/ - var cluster = data.clusters[i]; - var posXClasses = posX; - for(var j = 0; j < cluster.classes.length; j++){ - //SVG.drawRectangle(posXClasses, this.height - (this.bottom + this.clusterTitleHeight + this.rulerHeight), properties.classWidth, this.rulerHeight, this.svg, [["fill", "green"]]); -// SVG.drawText(posXClasses + 1/6*properties.classWidth, this.height - (this.bottom + this.clusterTitleHeight + (this.rulerHeight*1/4)), cluster.classes[j].name, this.svg, [["style", "font-size:xx-small"]]); - var color = cluster.classes[j].color; - this.drawSVGVerticalText(posXClasses + 1/2*(properties.classWidth), this.height - (this.bottom - this.clusterTitleHeight + (this.rulerHeight)), cluster.classes[j].name , [["style", "font-size:x-small;"], ["fill", color]]); - - // SVG.drawRectangle(posXClasses, this.height - (this.bottom + this.clusterTitleHeight), properties.classWidth, 2, this.svg, [["fill", "black"]]); - posXClasses = posXClasses + properties.classWidth + this.interClassesSpace; - } - - var clusterTitleSpace = (data.clusters[i].classes.length * properties.classWidth) + ((data.clusters[i].classes.length - 1) * this.interClassesSpace); - //SVG.drawRectangle(posX, this.height - (this.bottom + this.clusterTitleHeight), clusterTitleSpace, this.clusterTitleHeight, this.svg, [["fill", "pink"]]); - SVG.drawRectangle(posX, this.height - (this.clusterTitleHeight+this.bottom), clusterTitleSpace, 1, this.svg, []); - -// var transform = ["translate", "(" + posX + 1/3*clusterTitleSpace +", " + this.height - (this.bottom + (this.clusterTitleHeight*1/4)) +")"], ["transform", "rotate(180)"]; -// var transform = ["transform", "translate(" + (posX + 1/3*clusterTitleSpace) +", " + (this.height - (this.bottom + (this.clusterTitleHeight*1/4))) + "), rotate(-90) "]; -// this.drawSVGVerticalText(posX + 1/3*clusterTitleSpace, this.height - (this.bottom + (this.clusterTitleHeight*1/4)),data.clusters[i].name, [["style", "font-size:x-small"]]); - SVG.drawText(posX , this.height - (this.bottom + (this.clusterTitleHeight*1/4)),data.clusters[i].name ,this.svg, [["style", "font-size:x-small"]]); - posX = posX + clusterTitleSpace; - } -}; - -BoxWhiskerGraph.prototype.plotWhisters = function(data, properties){ - var colors = ["yellow", "orange", "green"]; - var posX = this.left + this.rulerWidth; - for(var i = 0; i < data.clusters.length; i++ ){ - var cluster = data.clusters[i]; - /** inter cluster space **/ - //SVG.drawRectangle(posX, this.top, this.interClustersSpace, this.height - this.top - this.bottom - this.clusterTitleHeight - this.rulerHeight, this.svg, [["fill", "black"]]); - posX = posX + this.interClustersSpace; - - for (var j = 0; j < cluster.classes.length; j ++){ - - //SVG.drawRectangle(posX, this.top, properties.classWidth, this.height - this.top - this.bottom - this.clusterTitleHeight - this.rulerHeight, this.svg, [["fill", colors[j]]]); - this.plotBoxWhisker( - { - name : cluster.classes[j].name, - values : cluster.classes[j].values, - minPoint : properties.minPoint, - maxPoint : properties.maxPoint, - x : posX, - y : this.top, - width : properties.classWidth, - height : this.height - this.top - this.bottom - this.clusterTitleHeight - this.rulerHeight - - - } - ); - posX = posX + properties.classWidth; - //SVG.drawRectangle(posX, this.top, this.interClassesSpace, this.height - this.top - this.bottom - this.clusterTitleHeight - this.rulerHeight, this.svg, [["fill", "black"]]); - if (j < cluster.classes.length - 1){ - posX = posX + this.interClassesSpace; - } - } - } -}; - -BoxWhiskerGraph.prototype.draw = function(targetId, data){ - this.targetId = targetId; - var properties = (this.getDimensions(data)); - this.svg = SVG.createSVGCanvas(document.getElementById(this.targetId), [["width", this.width], ["height", this.height]]); - this.plotAxes(properties); - this.plotClusterTitles(properties); - this.plotWhisters(data,properties); -}; - -BoxWhiskerGraph.prototype.input = function(){ - return DATADOC.getBoxWhikerData(); -}; - -BoxWhiskerGraph.prototype.test = function(targetId){ - var plot = new BoxWhiskerGraph( - { - targetId : targetId, - height : 350, - width : 450, - maxBoxWidth : 25 - } - ); - plot.refresh(this.input()); -}; - - - - - - - -function DataSet(){ - this.json = null; - -}; - - -DataSet.prototype.loadFromJSON = function(json){ - if(json != null) { - if(this.validate(json)) { - this.json = json; - } - } -}; - - -DataSet.prototype.toJSON = function(json){ - return this.json; -}; - - -/** Abstract method to be override on childs classes **/ -DataSet.prototype.validate = function(json){ - if (true){ - return true; - } - /*else{ - throw "Data validation failed"; - }*/ -}; - - - - -Edge.prototype.getName = GraphItem.prototype.getName; -Edge.prototype.setName = GraphItem.prototype.setName; -Edge.prototype.getId = GraphItem.prototype.getId; - -function Edge(id, name, nodeSource, nodeTarget, args){ - GraphItem.prototype.constructor.call(this, id, name, args); - - this.sourceNode = nodeSource; - this.targetNode = nodeTarget; - -}; - -Edge.prototype.toJSON = function(){ - return {"index": this.id,"sourceIndex":this.sourceNode.getId(),"targetIndex":this.targetNode.getId(),"args":this.args}; -}; - -Edge.prototype.getNodeSource = function(){ - return this.sourceNode; -}; - -Edge.prototype.getNodeTarget = function(){ - return this.targetNode; -}; - -Edge.prototype.remove = function(){ - //Remove edge object in the nodes - this.getNodeSource().removeEdge(this); - this.getNodeTarget().removeEdge(this); - - this.deleted.notify(this); -}; - - -EdgeGraphFormatter.prototype.setProperties = ItemGraphFormatter.prototype.setProperties; -EdgeGraphFormatter.prototype.getDefault = ItemGraphFormatter.prototype.getDefault; -EdgeGraphFormatter.prototype.getSelected = ItemGraphFormatter.prototype.getSelected; -EdgeGraphFormatter.prototype.getOver = ItemGraphFormatter.prototype.getOver; -EdgeGraphFormatter.prototype.getDragging = ItemGraphFormatter.prototype.getDragging; -EdgeGraphFormatter.prototype.getId = ItemGraphFormatter.prototype.getId; -EdgeGraphFormatter.prototype.toJSON = ItemGraphFormatter.prototype.toJSON; -EdgeGraphFormatter.prototype.loadFromJSON = ItemGraphFormatter.prototype.loadFromJSON; -EdgeGraphFormatter.prototype._setEvents = ItemGraphFormatter.prototype._setEvents; - -function EdgeGraphFormatter(id, defaultFormat, selectedFormat, overFormat, dragginFormat){ - ItemGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, dragginFormat); - - this.getDefault().args.type = "EdgeGraphFormatter"; -}; - - - - -LineEdgeGraphFormatter.prototype.setProperties = EdgeGraphFormatter.prototype.setProperties; -LineEdgeGraphFormatter.prototype.getDefault = EdgeGraphFormatter.prototype.getDefault; -LineEdgeGraphFormatter.prototype.getSelected = EdgeGraphFormatter.prototype.getSelected; -LineEdgeGraphFormatter.prototype.getOver = EdgeGraphFormatter.prototype.getOver; -LineEdgeGraphFormatter.prototype.getDragging = EdgeGraphFormatter.prototype.getDragging; -LineEdgeGraphFormatter.prototype.getId = EdgeGraphFormatter.prototype.getId; -LineEdgeGraphFormatter.prototype.toJSON = EdgeGraphFormatter.prototype.toJSON; -LineEdgeGraphFormatter.prototype.loadFromJSON = EdgeGraphFormatter.prototype.loadFromJSON; -LineEdgeGraphFormatter.prototype._setEvents = EdgeGraphFormatter.prototype._setEvents; - -function LineEdgeGraphFormatter(id, defaultFormat, selectedFormat, overFormat, dragginFormat){ - EdgeGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, dragginFormat); - this.args.type = "LineEdgeGraphFormatter"; -}; - -/** DIRECTED **/ -DirectedLineEdgeGraphFormatter.prototype.setProperties = EdgeGraphFormatter.prototype.setProperties; -DirectedLineEdgeGraphFormatter.prototype.getDefault = EdgeGraphFormatter.prototype.getDefault; -DirectedLineEdgeGraphFormatter.prototype.getSelected = EdgeGraphFormatter.prototype.getSelected; -DirectedLineEdgeGraphFormatter.prototype.getOver = EdgeGraphFormatter.prototype.getOver; -DirectedLineEdgeGraphFormatter.prototype.getDragging = EdgeGraphFormatter.prototype.getDragging; -DirectedLineEdgeGraphFormatter.prototype.getId = EdgeGraphFormatter.prototype.getId; -DirectedLineEdgeGraphFormatter.prototype.toJSON = EdgeGraphFormatter.prototype.toJSON; -DirectedLineEdgeGraphFormatter.prototype.loadFromJSON = EdgeGraphFormatter.prototype.loadFromJSON; -DirectedLineEdgeGraphFormatter.prototype._setEvents = EdgeGraphFormatter.prototype._setEvents; - -function DirectedLineEdgeGraphFormatter(id, defaultFormat, selectedFormat, overFormat, dragginFormat){ - EdgeGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, dragginFormat); - this.args.type = "DirectedLineEdgeGraphFormatter"; - this.args.arrowSize = "3"; -}; -DirectedLineEdgeGraphFormatter.prototype.getArrowSize = function(){ - return this.args.arrowSize; -}; - -OdirectedLineEdgeGraphFormatter.prototype.setProperties = EdgeGraphFormatter.prototype.setProperties; -OdirectedLineEdgeGraphFormatter.prototype.getDefault = EdgeGraphFormatter.prototype.getDefault; -OdirectedLineEdgeGraphFormatter.prototype.getSelected = EdgeGraphFormatter.prototype.getSelected; -OdirectedLineEdgeGraphFormatter.prototype.getOver = EdgeGraphFormatter.prototype.getOver; -OdirectedLineEdgeGraphFormatter.prototype.getDragging = EdgeGraphFormatter.prototype.getDragging; -OdirectedLineEdgeGraphFormatter.prototype.getId = EdgeGraphFormatter.prototype.getId; -OdirectedLineEdgeGraphFormatter.prototype.toJSON = EdgeGraphFormatter.prototype.toJSON; -OdirectedLineEdgeGraphFormatter.prototype.loadFromJSON = EdgeGraphFormatter.prototype.loadFromJSON; -OdirectedLineEdgeGraphFormatter.prototype._setEvents = EdgeGraphFormatter.prototype._setEvents; - -function OdirectedLineEdgeGraphFormatter(id, defaultFormat, selectedFormat, overFormat, dragginFormat){ - EdgeGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, dragginFormat); - this.args.type = "DirectedLineEdgeGraphFormatter"; - this.args.arrowSize = "3"; -}; - -OdirectedLineEdgeGraphFormatter.prototype.getArrowSize = function(){ - return this.args.arrowSize; -}; - - -/** CUT (inhibition) **/ -CutDirectedLineEdgeGraphFormatter.prototype.setProperties = EdgeGraphFormatter.prototype.setProperties; -CutDirectedLineEdgeGraphFormatter.prototype.getDefault = EdgeGraphFormatter.prototype.getDefault; -CutDirectedLineEdgeGraphFormatter.prototype.getSelected = EdgeGraphFormatter.prototype.getSelected; -CutDirectedLineEdgeGraphFormatter.prototype.getOver = EdgeGraphFormatter.prototype.getOver; -CutDirectedLineEdgeGraphFormatter.prototype.getDragging = EdgeGraphFormatter.prototype.getDragging; -CutDirectedLineEdgeGraphFormatter.prototype.getId = EdgeGraphFormatter.prototype.getId; -CutDirectedLineEdgeGraphFormatter.prototype.toJSON = EdgeGraphFormatter.prototype.toJSON; -CutDirectedLineEdgeGraphFormatter.prototype.loadFromJSON = EdgeGraphFormatter.prototype.loadFromJSON; -CutDirectedLineEdgeGraphFormatter.prototype._setEvents = EdgeGraphFormatter.prototype._setEvents; - -function CutDirectedLineEdgeGraphFormatter(id, defaultFormat, selectedFormat, overFormat, dragginFormat){ - EdgeGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, dragginFormat); - this.args.type = "CutDirectedLineEdgeGraphFormatter"; -}; - - - - -BezierEdgeGraphFormatter.prototype.setProperties = EdgeGraphFormatter.prototype.setProperties; -BezierEdgeGraphFormatter.prototype.setProperties = EdgeGraphFormatter.prototype.setProperties; -BezierEdgeGraphFormatter.prototype.getDefault = EdgeGraphFormatter.prototype.getDefault; -BezierEdgeGraphFormatter.prototype.getSelected = EdgeGraphFormatter.prototype.getSelected; -BezierEdgeGraphFormatter.prototype.getOver = EdgeGraphFormatter.prototype.getOver; -BezierEdgeGraphFormatter.prototype.getDragging = EdgeGraphFormatter.prototype.getDragging; -BezierEdgeGraphFormatter.prototype.getId = EdgeGraphFormatter.prototype.getId; -BezierEdgeGraphFormatter.prototype.toJSON = EdgeGraphFormatter.prototype.toJSON; -BezierEdgeGraphFormatter.prototype.loadFromJSON = EdgeGraphFormatter.prototype.loadFromJSON; -BezierEdgeGraphFormatter.prototype._setEvents = EdgeGraphFormatter.prototype._setEvents; - -function BezierEdgeGraphFormatter(id, defaultFormat, selectedFormat, overFormat, dragginFormat){ - EdgeGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, dragginFormat); - this.args.type = "BezierEdgeGraphFormatter"; -}; - - - -DotDirectedLineEdgeGraphFormatter.prototype.setProperties = EdgeGraphFormatter.prototype.setProperties; -DotDirectedLineEdgeGraphFormatter.prototype.getDefault = EdgeGraphFormatter.prototype.getDefault; -DotDirectedLineEdgeGraphFormatter.prototype.getSelected = EdgeGraphFormatter.prototype.getSelected; -DotDirectedLineEdgeGraphFormatter.prototype.getOver = EdgeGraphFormatter.prototype.getOver; -DotDirectedLineEdgeGraphFormatter.prototype.getDragging = EdgeGraphFormatter.prototype.getDragging; -DotDirectedLineEdgeGraphFormatter.prototype.getId = EdgeGraphFormatter.prototype.getId; -DotDirectedLineEdgeGraphFormatter.prototype.toJSON = EdgeGraphFormatter.prototype.toJSON; -DotDirectedLineEdgeGraphFormatter.prototype.loadFromJSON = EdgeGraphFormatter.prototype.loadFromJSON; -DotDirectedLineEdgeGraphFormatter.prototype._setEvents = EdgeGraphFormatter.prototype._setEvents; - -function DotDirectedLineEdgeGraphFormatter(id, defaultFormat, selectedFormat, overFormat, dragginFormat){ - EdgeGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, dragginFormat); - this.args.type = "DotDirectedLineEdgeGraphFormatter"; -}; - - -OdotDirectedLineEdgeGraphFormatter.prototype.setProperties = EdgeGraphFormatter.prototype.setProperties; -OdotDirectedLineEdgeGraphFormatter.prototype.getDefault = EdgeGraphFormatter.prototype.getDefault; -OdotDirectedLineEdgeGraphFormatter.prototype.getSelected = EdgeGraphFormatter.prototype.getSelected; -OdotDirectedLineEdgeGraphFormatter.prototype.getOver = EdgeGraphFormatter.prototype.getOver; -OdotDirectedLineEdgeGraphFormatter.prototype.getDragging = EdgeGraphFormatter.prototype.getDragging; -OdotDirectedLineEdgeGraphFormatter.prototype.getId = EdgeGraphFormatter.prototype.getId; -OdotDirectedLineEdgeGraphFormatter.prototype.toJSON = EdgeGraphFormatter.prototype.toJSON; -OdotDirectedLineEdgeGraphFormatter.prototype.loadFromJSON = EdgeGraphFormatter.prototype.loadFromJSON; -OdotDirectedLineEdgeGraphFormatter.prototype._setEvents = EdgeGraphFormatter.prototype._setEvents; - -function OdotDirectedLineEdgeGraphFormatter(id, defaultFormat, selectedFormat, overFormat, dragginFormat){ - EdgeGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, dragginFormat); - this.args.type = "OdotDirectedLineEdgeGraphFormatter"; -}; - - - -function GraphDataset(){ - DataSet.prototype.constructor.call(this); - this.edges = new Object(); - this.vertices = new Object(); - this.verticesIndex = new Object(); - - //Events - this.newVertex = new Event(this); - this.vertexNameChanged = new Event(this); - this.vertexDeleted = new Event(this); - - this.newEdge = new Event(this); - this.edgeNameChanged = new Event(this); - this.edgeDeleted = new Event(this); - - this.json = new Object(); - this.json.vertices = new Array(); - this.json.edges = new Array(); - this.json.relations = new Array(); -}; - -GraphDataset.prototype.loadFromJSON = DataSet.prototype.loadFromJSON; -GraphDataset.prototype.toJSON = DataSet.prototype.toJSON; -GraphDataset.prototype.validate = DataSet.prototype.validate; - -/** Devuelve el numero de edges incidentes sobre el nodo con mas edges **/ -GraphDataset.prototype.getMaxClass = function(){ - var maxClassNode = 0; - for ( var node in this.vertices) { - if (this.vertices[node].getEdgesCount() > maxClassNode){ - maxClassNode = this.vertices[node].getEdgesCount(); - } - } - return maxClassNode; -}; - -/** Devuelve el numero de edges incidentes sobre el nodo con mas edges **/ -GraphDataset.prototype.getMinClass = function(){ - var minClassNode = Math.min(); - for ( var node in this.vertices) { - if (this.vertices[node].getEdgesCount() < minClassNode){ - minClassNode = this.vertices[node].getEdgesCount(); - } - } - return minClassNode; -}; - -GraphDataset.prototype.getVertexByName = function(nodeName){ - var results = new Array(); - - for (var vertexId in this.verticesIndex[nodeName]){ - var vertexByid = this.getVertexById(this.verticesIndex[nodeName][vertexId]); - results.push(vertexByid); - //* añadido nuevo porque fallaba el anterior codigo - return vertexByid - } - - if (results <= 1){ - return this.getVertexById(this.verticesIndex[nodeName]); - } - else{ - return results; - } -}; - -GraphDataset.prototype.getVertexById = function(id){ - return this.vertices[id]; -}; - -GraphDataset.prototype.toSIF = function(){ - var sifDataAdapter = new SifFileDataAdapter(); - return sifDataAdapter.toSIF(this); -}; - -GraphDataset.prototype.toSIFID = function(){ - var sifDataAdapter = new SifFileDataAdapter(); - return sifDataAdapter.toSIFID(this); -}; - -GraphDataset.prototype.toDOT = function(){ - var dotFileDataAdapter = new DotFileDataAdapter(); - return dotFileDataAdapter.toDOT(this); -}; - -GraphDataset.prototype.toDOTID = function(){ - var dotFileDataAdapter = new DotFileDataAdapter(); - return dotFileDataAdapter.toDOTID(this); -}; - -GraphDataset.prototype._addNode = function(nodeName, args){ - return new Vertex(this._getVerticesCount()-1, nodeName, args); -}; - -GraphDataset.prototype.addNode = function(nodeName, args){ - this.json.vertices.push(nodeName); - this._addVerticesIndex(nodeName, this._getVerticesCount() - 1); - var vertex = this._addNode(nodeName, args); - this.vertices[this._getVerticesCount()-1] = vertex; - this._setNodeEvents(vertex); - this.newVertex.notify(vertex); -}; - -GraphDataset.prototype._addVerticesIndex = function(nodeName, id){ - if (this.verticesIndex[nodeName] == null){ - this.verticesIndex[nodeName] = new Array(); - } - this.verticesIndex[nodeName].push(id); -}; - -GraphDataset.prototype.addEdge = function(edgeName, nodeSourceId, nodeTargetId, args){ - this.json.edges.push(edgeName); - var nodeSource = this.getVertexById(nodeSourceId); - var nodeTarget = this.getVertexById(nodeTargetId); - var index = this.getEdgesCount() - 1; - this.edges[index] = new Edge(index, edgeName, nodeSource, nodeTarget, args); - this.json.relations.push({"index": index, "sourceIndex": nodeSourceId, "targetIndex": nodeTargetId, "args": args }); - - nodeSource.addEdge(this.edges[index]); - nodeTarget.addEdge(this.edges[index]); - this._setEdgeEvents(this.edges[index]); - this.newEdge.notify(this.edges[index]); -}; - -GraphDataset.prototype.getVertices = function(){ - return this.vertices; -}; - -GraphDataset.prototype.getEdges = function(){ - return this.edges; -}; - -GraphDataset.prototype.getEdgeById = function(edgeId){ - return this.edges[edgeId]; -}; - -GraphDataset.prototype._getVerticesCount = function(){ - return this.json.vertices.length; -}; - - -GraphDataset.prototype.getVerticesCount = function(){ - var count = 0; - for ( var vertex in this.getVertices()) { - count ++; - } - return count; -}; - - -GraphDataset.prototype.getEdgesCount = function(){ - return this.json.edges.length; -}; - -GraphDataset.prototype.init = function(){ - this.edges = new Object(); - this.vertices = new Object(); -}; - -GraphDataset.prototype._setNodeEvents = function(node){ - var _this = this; - //NODE EVENTS - node.deleted.attach(function (sender, node){ - _this._removeNode(node); - }); - - node.nameChanged.attach(function (sender, args){ - var item = args.item; - var newName = item.name; - var indexes = _this.verticesIndex[args.previousName]; - for(var i = 0; i < indexes.length; i++){ - if(indexes[i] == item.id) - indexes.splice(i,1); - } - if(indexes.length == 0){ - delete _this.verticesIndex[args.previousName]; - } - _this._addVerticesIndex(newName, item.id); - _this.json.vertices[item.id] = newName; - _this.vertexNameChanged.notify(args); - }); -}; - -GraphDataset.prototype._setEdgeEvents = function(edge){ - var _this = this; - //EDGE EVENTS - edge.nameChanged.attach(function (sender, edge){ - _this.edgeNameChanged.notify(edge); - - }); - - edge.deleted.attach(function (sender, edge){ - _this._removeEdge(edge); - }); -}; - -GraphDataset.prototype._connectVerticesByName = function(nodeNameSource, nodeNameTarget){ - var source = this.getVertexByName(nodeNameSource); - var target = this.getVertexByName(nodeNameTarget); - - if ((source != null)&&(target!=null)){ - this.addEdge(source.getName() +"_" + target.getName(), source.getId(), target.getId(), {}); - } - else{ - if (source == null){ - console.log("No encontrado: " + nodeNameSource) - } - if (target == null){ - console.log("No encontrado: " + nodeNameTarget) - } - } -}; - -GraphDataset.prototype.loadFromJSON = function(json){ - var json = json; - this.init(); - this.json = new Object(); - this.json.vertices = new Array(); - this.json.edges = new Array(); - this.json.relations = new Array(); - - for ( var i = 0; i < json.nodes.length; i++) { - if (json.nodes[i] != null){ - var name = json.nodes[i]; - this.addNode(name); - } - else{ - this.json.vertices.push(null); - } - } - - for ( var i = 0; i < json.edges.length; i++) { - if (json.edges[i] != null){ - if (json.relations[i] != null){ - var name = json.edges[i]; - this.addEdge(name, json.relations[i].sourceIndex, json.relations[i].targetIndex, json.relations[i].args); - } - } - else{ - this.json.edges.push(null); - this.json.relations.push(null); - } - } -}; - -GraphDataset.prototype.prettyPrint = function(){ - for ( var node in this.vertices) { - console.log(this.vertices[node].getName() ); - for ( var j = 0; j < this.vertices[node].getEdgesIn().length; j++) { - console.log(" --> " + this.vertices[node].getEdgesIn()[j].getNodeTarget().getName() ); - } - } -}; - -GraphDataset.prototype._removeEdge = function(edge){ - this.json.edges[edge.getId()] = null; - this.json.relations[edge.getId()] = null; - - delete this.edges[edge.getId()]; - this.edgeDeleted.notify(edge); - - -}; - -GraphDataset.prototype._removeNode = function(node){ - this.json.vertices[node.getId()] = null; - delete this.vertices[node.getId()]; - this.vertexDeleted.notify(node); -}; - -GraphDataset.prototype.toJSON = function(){ - var json = new Object(); - var nodes = new Array(); - json.nodes = this.json.vertices; //nodes; - json.edges = this.json.edges; //edges; - json.relations = this.json.relations; - return json; -}; - -GraphDataset.prototype.clone = function(){ - var dsDataset = new GraphDataset(); - dsDataset.loadFromJSON(this.toJSON()); - return dsDataset; -}; -//GraphDataset.prototype.test = function(){ -// this.loadFromJSON(this.toJSON()); -//}; - -function labels(){ - var names = new Array(); - - var dataset = interactomeViewer.graphEditorWidget.dataset; - var layout = interactomeViewer.graphEditorWidget.layout; - - for ( var vertexId in interactomeViewer.graphEditorWidget.dataset.getVertices()) { - names.push(interactomeViewer.graphEditorWidget.dataset.getVertexById(vertexId).getName()); - } - - var sorted = (names.sort()); - console.log(sorted) - var distance = 0.01; - var altura = 0.6; - for ( var i = 0; i < names.length; i++) { - var id =dataset.getVertexByName(names[i]).getId(); - - layout.getNodeById(id).setCoordenates(distance, altura); - - - distance = parseFloat(distance) + parseFloat(0.03); - - altura = parseFloat(altura) + parseFloat(0.02); - - if (parseFloat(altura) == 0.9800000000000003){ - - altura = 0.6; - distance = distance - 0.51; - } - - } - - -}; - -function GraphItem(id, name, args){ - this.id = id; - this.name = name; - this.type = "NONE"; - - this.args = new Object(); - - - if (args!=null){ - this.args = args; - if (args.type !=null){ - this.type = args.type; - } - } - - //Events - this.nameChanged = new Event(this); - this.deleted = new Event(this); -} - -GraphItem.prototype.getName = function(){ - return this.name; -}; - -GraphItem.prototype.getId = function(){ - return this.id; -}; - -GraphItem.prototype.setName = function(name){ - var oldName = this.getName(); - this.name = name; - this.nameChanged.notify({"item": this, "previousName" : oldName}); -}; - - - - - -function LayoutDataset(){ - this.dataset = null; - this.vertices = new Object(); - this.changed = new Event(this); - - - this.args = new Object(); - - //RANDOM, CIRCLE - this.args.type = "CIRCLE"; -}; - -LayoutDataset.prototype.loadFromJSON = function(dataset, json){ - var _this = this; - this.vertices = new Object(); - this.dataset = dataset; //new GraphDataset(); - for ( var vertex in json) { - this.vertices[vertex] = new NodeLayout(vertex, json[vertex].x, json[vertex].y); - this.vertices[vertex].changed.attach(function (sender, item){ - _this.changed.notify(item); - }); - } - this._attachDatasetEvents(); -}; - - -LayoutDataset.prototype.toJSON = function(){ - var serialize = new Object(); - for ( var vertex in this.vertices) { - serialize[vertex] = new Object(); - serialize[vertex].x = this.vertices[vertex].x; - serialize[vertex].y = this.vertices[vertex].y; - } - serialize.dataset = new Object(); - serialize.dataset =this.dataset.toJSON(); - return serialize; -}; - -LayoutDataset.prototype.dataBind = function(graphDataset){ - this.dataset = graphDataset; - this._attachDatasetEvents(); - this._calculateLayout(); -}; - -LayoutDataset.prototype._removeVertex = function(vertexId){ - delete this.vertices[vertexId]; -}; - -LayoutDataset.prototype._attachDatasetEvents = function(){ - var _this = this; - - this.dataset.vertexDeleted.attach(function (sender, item){ - _this._removeVertex(item.getId()); - }); - - this.dataset.newVertex.attach(function (sender, item){ - _this.vertices[item.getId()] = new NodeLayout(item.getId(), 0.5, 0.5); - _this.vertices[item.getId()].changed.attach(function (sender, item){ - _this.changed.notify(item); - }); - }); -}; - -LayoutDataset.prototype.getType = function(){ - return this.args.type; -}; - -LayoutDataset.prototype._calculateLayoutVertices = function(type, count){ - - if (type == "CIRCLE"){ - var radius = 0.4; - var centerX = 0.5; - var centerY = 0.5; - var verticesCoordinates = new Array(); - for(var i = 0; i < count; i++){ - x = centerX + radius * Math.sin(i * 2 * Math.PI/count); - y = centerY + radius * Math.cos(i * 2 * Math.PI/count); - verticesCoordinates.push({'x':x,'y':y}); - } - return verticesCoordinates; - } -}; - - -LayoutDataset.prototype._calculateLayout = function(){ - var _this = this; - if (this.getType() == "RANDOM"){ - for ( var vertex in this.dataset.getVertices()) { - if (this.vertices[vertex] == null){ - this.vertices[vertex] = new NodeLayout(vertex, 0, 0); - } - this.vertices[vertex].setCoordinates(Math.random(), Math.random()); - this.vertices[vertex].changed.attach(function (sender, item){ - _this.changed.notify(item); - }); - } - } - - if ( this.getType() == "CIRCLE"){ - - var count = this.dataset._getVerticesCount(); - var verticesCoordinates = this._calculateLayoutVertices(this.getType(), count); - - var aux = 0; - for ( var vertex in this.dataset.getVertices()) { - if (this.vertices[vertex] == null){ - this.vertices[vertex] = new NodeLayout(vertex, 0, 0); - } - this.vertices[vertex].setCoordinates(verticesCoordinates[aux].x, verticesCoordinates[aux].y);//{"x":, "y":}; - aux++; - this.vertices[vertex].changed.attach(function (sender, item){ - _this.changed.notify(item); - }); - } - } - - - if (this.getType() == "SQUARE"){ - - var count = this.dataset._getVerticesCount(); - var xMin = 0.1; - var xMax = 0.9; - var yMin = 0.1; - var yMax = 0.9; - - var rows = Math.sqrt(count); - var step = (xMax - xMin) / rows; - - var verticesCoordinates = new Array(); - for(var i = 0; i < rows; i ++){ - for ( var j = 0; j < rows; j++) { - x = i * step + xMin; - y = j * step + yMin; - verticesCoordinates.push({'x':x,'y':y}); - } - } - - var aux = 0; - for ( var vertex in this.dataset.getVertices()) { - if (this.vertices[vertex] == null){ - this.vertices[vertex] = new NodeLayout(vertex, 0, 0); - } - this.vertices[vertex].setCoordinates(verticesCoordinates[aux].x, verticesCoordinates[aux].y);//{"x":, "y":}; - aux++; - this.vertices[vertex].changed.attach(function (sender, item){ - _this.changed.notify(item); - }); - } - } - -}; - -LayoutDataset.prototype.getNodeById = function(id){ - return this.vertices[id]; -}; - -LayoutDataset.prototype.getVerticesByArea = function(x1, y1, x2, y2){ - var vertices = new Array(); - for ( var vertex in this.dataset.getVertices()) { - if ((this.vertices[vertex].x >= x1)&&(this.vertices[vertex].x <= x2)){ - if ((this.vertices[vertex].y >= y1)&&(this.vertices[vertex].y <= y2)){ - vertices.push(this.vertices[vertex]); - } - } - } - return vertices; -}; - - - - -LayoutDataset.prototype.getLayout = function(type){ - - if (type == "CIRCLE"){ - this.args.type = "CIRCLE"; - this._calculateLayout(); - return; - } - - if (type == "SQUARE"){ - this.args.type = "SQUARE"; - this._calculateLayout(); - return; - } - - if (type == "RANDOM"){ - this.args.type = "RANDOM"; - this._calculateLayout(); - return; - } - - - var dotText = this.dataset.toDOTID(); - var url = "http://bioinfo.cipf.es/utils/ws/rest/network/layout/"+type+".coords"; - var _this = this; - - $.ajax({ - async: true, - type: "POST", - url: url, - dataType: "text", - data: { - dot :dotText - }, - cache: false, - success: function(data){ - var response = JSON.parse(data); - for ( var vertexId in response) { - _this.vertices[vertexId].setCoordinates(response[vertexId].x, response[vertexId].y); - } - } - }); - -// $.ajax({ -// async: true, -// type: "POST", -// url: url, -// dataType: "script", -// data: { -// dot :dotText -// }, -// cache: false, -// success: function(data){ -// var response = JSON.parse(data); -// for ( var vertexId in response) { -// _this.vertices[vertexId].setCoordinates(response[vertexId].x, response[vertexId].y); -// } -// } -// }); - -}; - -function NodeLayout(id, x, y, args){ - this.id = id; - this.x = x; - this.y = y; - this.changed = new Event(this); -}; - -NodeLayout.prototype.getId = function(id){ - return this.id; -}; - -NodeLayout.prototype.setCoordinates = function(x, y){ - this.x = x; - this.y = y; - this.changed.notify(this); -}; - - - -function NetworkDataSetFormatter(vertexFormatProperties, defaultEdgeProperties, args){ - DataSet.prototype.constructor.call(this); - - this.args = new Object(); - - this.vertices = new Object(); - this.edges = new Object(); - this.dataset = null; - this.maxClass = 0; - this.minClass = 0; - - /** Coordenates with default Setting */ - this.args.width = 1100; - this.args.height = 500; - - /** Optional parameters */ - this.args.backgroundColor = "#FFFFFF"; - this.args.backgroundImage = null; - this.args.backgroundImageHeight = null; - this.args.backgroundImageWidth = null; - this.args.backgroundImageX = null; - this.args.backgroundImageY = null; - - - this.args.balanceNodes = false; - this.args.nodesMaxSize = 3; - this.args.nodesMinSize = 0.5; - - - /** Zoom **/ - this.args.zoomScale = 1; - this.args.zoomScaleStepFactor = 0.4; - - if (args != null){ - if (args.backgroundImage != null){ - this.args.backgroundImage = args.backgroundImage; - } - - if (args.width != null){ - this.args.width = args.width; - } - - if (args.height != null){ - this.args.height = args.height; - this.args.svgHeight = args.height; - } - - if (args.svgHeight != null){ - this.args.svgHeight = args.svgHeight; - } - - if (args.backgroundColor != null){ - this.args.backgroundColor = args.backgroundColor; - } - - if(args.balanceNodes != null){ - this.args.balanceNodes = args.balanceNodes; - } - - if(args.nodesMaxSize != null){ - this.args.nodesMaxSize = args.nodesMaxSize; - } - if(args.nodesMinSize != null){ - this.args.nodesMinSize = args.nodesMinSize; - } - } - - this.args.defaultEdgeProperties = {"fill":"#000000", "radius":"1", "stroke":"#000000", "size":1, "title":{"fontSize":10, "fontColor":"#000000"}}; - this.args.vertexFormatProperties = { "fill":"#000000", "stroke":"#000000", "stroke-opacity":"#000000"}; - - if (vertexFormatProperties!= null){ - this.args.vertexFormatProperties = vertexFormatProperties; - } - if (defaultEdgeProperties!= null){ - this.args.defaultEdgeProperties = defaultEdgeProperties; - } - - /** Events **/ - this.changed = new Event(this); - this.edgeChanged = new Event(this); - this.resized = new Event(this); - this.backgroundImageChanged= new Event(this); - this.backgroundColorChanged= new Event(this); -}; - -NetworkDataSetFormatter.prototype.loadFromJSON = function(dataset, json){ - this.args = new Object(); - this.vertices = new Object(); - this.args = json; - this._setDataset(dataset); - var _this = this; - - for ( var vertex in json.vertices) { - this.addVertex(vertex, json.vertices[vertex]); - } - - for ( var edgeId in json.edges) { - this.addEdge(edgeId, json.edges[edgeId]); - } -}; - - -NetworkDataSetFormatter.prototype.toJSON = function(){ - - -// this.args.vertices = new Object(); -// this.args.edges = new Object(); -// -// for ( var vertex in this.vertices) { -// this.args.vertices[vertex] = this.getVertexById(vertex).toJSON(); -// } -// for ( var edge in this.edges) { -// this.args.edges[edge] = this.getEdgeById(edge).toJSON(); -// } -// -// return (this.args); - - - var serialize = new Object(); - serialize = JSON.parse(JSON.stringify(this.args)); - serialize.vertices = new Object(); - serialize.edges = new Object(); - - for ( var vertex in this.vertices) { - serialize.vertices[vertex] = this.getVertexById(vertex).toJSON(); - } - for ( var edge in this.edges) { - serialize.edges[edge] = this.getEdgeById(edge).toJSON(); - } - - return (serialize); -}; - - - -NetworkDataSetFormatter.prototype._getNodeSize = function(nodeId){ - if (this.isVerticesBalanced()){ - var total = this.maxClass - this.minClass; - if (total == 0){ total = 1;} - var nodeConnection = this.dataset.getVertexById(nodeId).getEdges().length; - return ((nodeConnection*this.args.nodesMaxSize)/total) + this.args.nodesMinSize; - } - return this.getVertexById(nodeId).getDefault().getSize(); -}; - -NetworkDataSetFormatter.prototype._recalculateSize = function(){ - if (this.isVerticesBalanced()){ - this.maxClass = this.dataset.getMaxClass(); - this.minClass = this.dataset.getMinClass(); - for ( var vertexIdAll in this.vertices) { - var size = this._getNodeSize(vertexIdAll); - this.vertices[vertexIdAll].getDefault().setSize(size); - this.vertices[vertexIdAll].getSelected().setSize(size); - this.vertices[vertexIdAll].getOver().setSize(size); - } - } -}; - - -NetworkDataSetFormatter.prototype.addVertex = function(vertexId, json){ - - - if (json == null){ - this.vertices[vertexId] = new CircleVertexGraphFormatter(vertexId, this.args.vertexFormatProperties.defaultFormat, this.args.vertexFormatProperties.selected, this.args.vertexFormatProperties.over, this.args.vertexFormatProperties.dragging); - } - else{ - - /** Cargo los attributos desde el json **/ - if (json.type == null){ - this.vertices[vertexId] = new CircleVertexGraphFormatter(vertexId, this.args.vertexFormatProperties.defaultFormat, this.args.vertexFormatProperties.selected, this.args.vertexFormatProperties.over, this.args.vertexFormatProperties.dragging); - } - - if ((json.type == "SquareVertexGraphFormatter")||(json.type == "SquareVertexNetworkFormatter")){ - this.vertices[vertexId] = new SquareVertexGraphFormatter(vertexId); - this.vertices[vertexId].loadFromJSON(json); - } - - if ((json.type == "CircleVertexGraphFormatter")||(json.type == "CircleVertexNetworkFormatter")){ - this.vertices[vertexId] = new CircleVertexGraphFormatter(vertexId); - this.vertices[vertexId].loadFromJSON(json); - } - - if ((json.type == "EllipseVertexGraphFormatter")||(json.type == "EllipseVertexNetworkFormatter")){ - this.vertices[vertexId] = new EllipseVertexGraphFormatter(vertexId); - this.vertices[vertexId].loadFromJSON(json); - } - - if ((json.type == "RectangleVertexGraphFormatter")||(json.type == "RectangleVertexNetworkFormatter")){ - this.vertices[vertexId] = new RectangleVertexGraphFormatter(vertexId); - this.vertices[vertexId].loadFromJSON(json); - } - - if ((json.type == "RoundedVertexGraphFormatter")||(json.type == "RoundedVertexNetworkFormatter")){ - this.vertices[vertexId] = new RoundedVertexGraphFormatter(vertexId); - this.vertices[vertexId].loadFromJSON(json); - } - - } - - - var _this = this; - this.vertices[vertexId].stateChanged.attach(function (sender, item){ - _this.changed.notify(item); - }); - - var size = this._getNodeSize(vertexId); - this.vertices[vertexId].defaultFormat.args.size = size; - this.vertices[vertexId].selected.args.size = size; - this.vertices[vertexId].over.args.size = size; - -}; - -NetworkDataSetFormatter.prototype.addEdge = function(edgeId, json){ - - /** Es un edge nuevo que le doy los atributos por defecto **/ - if (json == null){ - if (this.dataset.getEdgeById(edgeId).getNodeSource().getId() == this.dataset.getEdgeById(edgeId).getNodeTarget().getId()){ - this.edges[edgeId] = new BezierEdgeGraphFormatter(edgeId, this.args.defaultEdgeProperties.defaultFormat, this.args.defaultEdgeProperties.selected, this.args.defaultEdgeProperties.over, this.args.defaultEdgeProperties.dragging); - }else{ - this.edges[edgeId] = new DirectedLineEdgeGraphFormatter(edgeId, this.args.defaultEdgeProperties.defaultFormat, this.args.defaultEdgeProperties.selected, this.args.defaultEdgeProperties.over, this.args.defaultEdgeProperties.dragging); - } - } - else{ - /** Cargo los attributos desde el json **/ - if (json.type == null){ - this.edges[edgeId] = new LineEdgeGraphFormatter(edgeId, this.args.defaultEdgeProperties.defaultFormat, this.args.defaultEdgeProperties.selected, this.args.defaultEdgeProperties.over, this.args.defaultEdgeProperties.dragging); - } - - if ((json.type == "LineEdgeGraphFormatter")||(json.type == "LineEdgeNetworkFormatter")){ - this.edges[edgeId] = new LineEdgeGraphFormatter(edgeId); - this.edges[edgeId].loadFromJSON(json); - } - if ((json.type == "DirectedLineEdgeGraphFormatter")||(json.type == "DirectedLineEdgeNetworkFormatter")){ - this.edges[edgeId] = new DirectedLineEdgeGraphFormatter(edgeId); - this.edges[edgeId].loadFromJSON(json); - } - - if ((json.type == "BezierEdgeGraphFormatter")||(json.type == "BezierEdgeNetworkFormatter")){ - this.edges[edgeId] = new BezierEdgeGraphFormatter(edgeId); - this.edges[edgeId].loadFromJSON(json); - } - - - if ((json.type == "CutDirectedLineEdgeGraphFormatter")||(json.type == "CutDirectedLineEdgeNetworkFormatter")){ - this.edges[edgeId] = new CutDirectedLineEdgeGraphFormatter(edgeId); - this.edges[edgeId].loadFromJSON(json); - } - - if ((json.type == "DotDirectedLineEdgeGraphFormatter")||(json.type == "DotDirectedLineEdgeNetworkFormatter")){ - this.edges[edgeId] = new DotDirectedLineEdgeGraphFormatter(edgeId); - this.edges[edgeId].loadFromJSON(json); - } - if ((json.type == "OdotDirectedLineEdgeGraphFormatter")||(json.type == "OdotDirectedLineEdgeNetworkFormatter")){ - this.edges[edgeId] = new OdotDirectedLineEdgeGraphFormatter(edgeId); - this.edges[edgeId].loadFromJSON(json); - } - - if ((json.type == "OdirectedLineEdgeGraphFormatter")||(json.type == "OdirectedLineEdgeNetworkFormatter")){ - this.edges[edgeId] = new OdirectedLineEdgeGraphFormatter(edgeId); - this.edges[edgeId].loadFromJSON(json); - } - } - - var _this = this; - this.edges[edgeId].stateChanged.attach(function (sender, item){ - _this.edgeChanged.notify(item); - }); - - this._recalculateSize(); -}; - -NetworkDataSetFormatter.prototype._setDataset = function(dataset){ - this.dataset = dataset; - this.maxClass = dataset.getMaxClass(); - this.minClass = dataset.getMinClass(); - this._attachDatasetEvents(); -}; - -NetworkDataSetFormatter.prototype.changeEdgeType = function(edgeId, type){ - if ((type == "LineEdgeGraphFormatter")||(type == "LineEdgeNetworkFormatter")){ - this.edges[edgeId] = new LineEdgeGraphFormatter(edgeId, this.args.defaultEdgeProperties.defaultFormat, this.args.defaultEdgeProperties.selected, this.args.defaultEdgeProperties.over, this.args.defaultEdgeProperties.dragging); - - } - if ((type == "DirectedLineEdgeGraphFormatter")||(type == "DirectedLineEdgeNetworkFormatter")){ - this.edges[edgeId] = new DirectedLineEdgeGraphFormatter(edgeId, this.args.defaultEdgeProperties.defaultFormat, this.args.defaultEdgeProperties.selected, this.args.defaultEdgeProperties.over, this.args.defaultEdgeProperties.dragging); - } - - if ((type == "CutDirectedLineEdgeGraphFormatter")||(type == "CutDirectedLineEdgeNetworkFormatter")){ - this.edges[edgeId] = new CutDirectedLineEdgeGraphFormatter(edgeId, this.args.defaultEdgeProperties.defaultFormat, this.args.defaultEdgeProperties.selected, this.args.defaultEdgeProperties.over, this.args.defaultEdgeProperties.dragging); - } - - if ((type == "DotDirectedLineEdgeGraphFormatter")||(type == "DotDirectedLineEdgeNetworkFormatter")){ - this.edges[edgeId] = new DotDirectedLineEdgeGraphFormatter(edgeId, this.args.defaultEdgeProperties.defaultFormat, this.args.defaultEdgeProperties.selected, this.args.defaultEdgeProperties.over, this.args.defaultEdgeProperties.dragging); - } - - if ((type == "OdotDirectedLineEdgeGraphFormatter")||(type == "OdotDirectedLineEdgeNetworkFormatter")){ - this.edges[edgeId] = new OdotDirectedLineEdgeGraphFormatter(edgeId, this.args.defaultEdgeProperties.defaultFormat, this.args.defaultEdgeProperties.selected, this.args.defaultEdgeProperties.over, this.args.defaultEdgeProperties.dragging); - } - - if ((type == "OdirectedLineEdgeGraphFormatter")||(type == "OdirectedLineEdgeNetworkFormatter")){ - this.edges[edgeId] = new OdirectedLineEdgeGraphFormatter(edgeId, this.args.defaultEdgeProperties.defaultFormat, this.args.defaultEdgeProperties.selected, this.args.defaultEdgeProperties.over, this.args.defaultEdgeProperties.dragging); - } - - - - var _this = this; - this.edges[edgeId].stateChanged.attach(function (sender, item){ - _this.edgeChanged.notify(item); - }); - _this.edgeChanged.notify(this.edges[edgeId]); -}; -/* -classe = "SquareNetworkNodeFormatter"; -} -if (value == "circle"){ - classe = "CircleNetworkNodeFormatter"; - **/ -NetworkDataSetFormatter.prototype.changeNodeType = function(vertexId, type){ - var defaultFormat = JSON.parse(JSON.stringify(this.vertices[vertexId].getDefault())); - var selectedFormat = JSON.parse(JSON.stringify(this.vertices[vertexId].getSelected())); - var defaultFormat = JSON.parse(JSON.stringify(this.vertices[vertexId].getDefault())); - var defaultFormat = JSON.parse(JSON.stringify(this.vertices[vertexId].getDefault())); - - if ((type == "SquareVertexGraphFormatter")||(type == "SquareVertexNetworkFormatter")){ - this.vertices[vertexId] = new SquareVertexGraphFormatter(vertexId,defaultFormat, this.args.vertexFormatProperties.selected, this.args.vertexFormatProperties.over, this.args.vertexFormatProperties.dragging); - } - - if ((type == "CircleVertexGraphFormatter")||(type == "CircleVertexNetworkFormatter")){ - this.vertices[vertexId] = new CircleVertexGraphFormatter(vertexId, defaultFormat, this.args.vertexFormatProperties.selected, this.args.vertexFormatProperties.over, this.args.vertexFormatProperties.dragging); - } - - if ((type == "EllipseVertexGraphFormatter")||(type == "EllipseVertexNetworkFormatter")){ - this.vertices[vertexId] = new EllipseVertexGraphFormatter(vertexId, defaultFormat, this.args.vertexFormatProperties.selected, this.args.vertexFormatProperties.over, this.args.vertexFormatProperties.dragging); - } - - if ((type == "RectangleVertexGraphFormatter")||(type == "RectangleVertexNetworkFormatter")){ - this.vertices[vertexId] = new RectangleVertexGraphFormatter(vertexId, defaultFormat, this.args.vertexFormatProperties.selected, this.args.vertexFormatProperties.over, this.args.vertexFormatProperties.dragging); - } - - if ((type == "RoundedVertexGraphFormatter")||(type == "RoundedVertexNetworkFormatter")){ - this.vertices[vertexId] = new RoundedVertexGraphFormatter(vertexId, defaultFormat, this.args.vertexFormatProperties.selected, this.args.vertexFormatProperties.over, this.args.vertexFormatProperties.dragging); - } - - if ((type == "OctagonVertexGraphFormatter")||(type == "OctagonVertexNetworkhFormatter")){ - this.vertices[vertexId] = new OctagonVertexGraphFormatter(vertexId, defaultFormat, this.args.vertexFormatProperties.selected, this.args.vertexFormatProperties.over, this.args.vertexFormatProperties.dragging); - } - - - var _this = this; - this.vertices[vertexId].stateChanged.attach(function (sender, item){ - _this.changed.notify(item); - }); - _this.changed.notify(this.vertices[vertexId]); -}; - - - -NetworkDataSetFormatter.prototype.dataBind = function(networkDataSet){ - var _this = this; - this._setDataset(networkDataSet); - - for ( var vertex in this.dataset.getVertices()) { - this.addVertex(vertex); - } - - for ( var edge in this.dataset.getEdges()) { - this.addEdge(edge); - } -}; - -NetworkDataSetFormatter.prototype._removeEdge = function(edgeId){ - delete this.edges[edgeId]; -}; - -NetworkDataSetFormatter.prototype._removeVertex = function(vertexId){ - delete this.vertices[vertexId]; -}; - -NetworkDataSetFormatter.prototype._attachDatasetEvents = function(id){ - var _this = this; - this.dataset.vertexDeleted.attach(function (sender, item){ - _this._removeVertex(item.getId()); - }); - - this.dataset.edgeDeleted.attach(function (sender, item){ - _this._removeEdge(item.getId()); - }); - - this.dataset.newVertex.attach(function (sender, item){ - _this.addVertex(item.getId()); - }); - - this.dataset.newEdge.attach(function (sender, item){ - _this.addEdge(item.getId()); - }); -}; - - -NetworkDataSetFormatter.prototype.getVertexById = function(id){ - return this.vertices[id]; -}; - -NetworkDataSetFormatter.prototype.getEdgeById = function(id){ - return this.edges[id]; -}; - -NetworkDataSetFormatter.prototype.makeLabelsBigger = function(){ -for ( var vertexId in this.vertices) { - var fontSize = this.vertices[vertexId].getDefault().getFontSize() + 2; - this.vertices[vertexId].getDefault().setFontSize(fontSize); - } -}; - -NetworkDataSetFormatter.prototype.makeLabelsSmaller = function(){ - for ( var vertexId in this.vertices) { - var fontSize = this.vertices[vertexId].getDefault().getFontSize() - 2; - this.vertices[vertexId].getDefault().setFontSize(fontSize); - } -}; - - -NetworkDataSetFormatter.prototype.resize = function(width, height){ - this.args.width = width; - this.args.height = height; - this.resized.notify(); -}; - -/** ZOOM GETTERS **/ -NetworkDataSetFormatter.prototype.getZoomScaleStepFactor = function(){return this.args.zoomScaleStepFactor;}; -NetworkDataSetFormatter.prototype.setZoomScaleStepFactor = function(scaleFactor){this.args.zoomScaleStepFactor = scaleFactor;}; -NetworkDataSetFormatter.prototype.getZoomScale = function(){return this.args.zoomScale;}; -NetworkDataSetFormatter.prototype.setZoomScale = function(scale){this.args.zoomScale = scale;}; - -NetworkDataSetFormatter.prototype.getNodesMaxSize = function(){return this.args.nodesMaxSize;}; -NetworkDataSetFormatter.prototype.getNodesMinSize = function(){return this.args.nodesMinSize;}; - -/** SIZE SETTERS AND GETTERS **/ -NetworkDataSetFormatter.prototype.isVerticesBalanced = function(){return this.args.balanceNodes;}; -NetworkDataSetFormatter.prototype.getWidth = function(){return this.args.width;}; -NetworkDataSetFormatter.prototype.getHeight = function(){return this.args.height;}; - -/** OPTIONAL PARAMETERS GETTERS AND SETTERS **/ -NetworkDataSetFormatter.prototype.getBackgroundImage = function(){return this.args.backgroundImage;}; -NetworkDataSetFormatter.prototype.setBackgroundImage = function(value){this.args.backgroundImage = value; this.backgroundImageChanged.notify(this);}; - - -NetworkDataSetFormatter.prototype.getBackgroundImageWidth = function(){return this.args.backgroundImageWidth;}; -NetworkDataSetFormatter.prototype.setBackgroundImageWidth = function(value){this.args.backgroundImageWidth = value; this.backgroundImageChanged.notify(this);}; - -NetworkDataSetFormatter.prototype.getBackgroundImageHeight = function(){return this.args.backgroundImageHeight;}; -NetworkDataSetFormatter.prototype.setBackgroundImageHeight = function(value){this.args.backgroundImageHeight = value; this.backgroundImageChanged.notify(this);}; - -NetworkDataSetFormatter.prototype.getBackgroundImageX = function(){return this.args.backgroundImageX;}; -NetworkDataSetFormatter.prototype.setBackgroundImageX = function(value){this.args.backgroundImageX = value; this.backgroundImageChanged.notify(this);}; - -NetworkDataSetFormatter.prototype.getBackgroundImageY = function(){return this.args.backgroundImageX;}; -NetworkDataSetFormatter.prototype.setBackgroundImageY = function(value){this.args.backgroundImageY = value; this.backgroundImageChanged.notify(this);}; - - - -NetworkDataSetFormatter.prototype.getBackgroundColor = function(){return this.args.backgroundColor;}; -NetworkDataSetFormatter.prototype.setBackgroundColor = function(value){this.args.backgroundColor = value; this.backgroundColorChanged.notify(this);}; - -NetworkDataSetFormatter.prototype.getWidth = function(){return this.args.width;}; -NetworkDataSetFormatter.prototype.setWidth = function(value){this.args.width = value;}; - -NetworkDataSetFormatter.prototype.getHeight = function(){return this.args.height;}; -NetworkDataSetFormatter.prototype.setHeight = function(value){this.args.height = value;}; - - - - -Vertex.prototype.getName = GraphItem.prototype.getName; -Vertex.prototype.setName = GraphItem.prototype.setName; -Vertex.prototype.getId = GraphItem.prototype.getId; - -function Vertex(id, name, args){ - GraphItem.prototype.constructor.call(this, id, name, args); - this.edgesIn= new Array(); - this.edgesOut= new Array(); -}; - -Vertex.prototype.getEdges = function(){ - return this.edgesIn.concat(this.edgesOut); -}; - -Vertex.prototype.getEdgesCount = function(){ - return this.getEdges().length; -}; - -Vertex.prototype.getEdgesIn = function(){ - return this.edgesIn; -}; - -Vertex.prototype.getEdgesOut = function(){ - return this.edgesOut; -}; - -Vertex.prototype.addEdge = function(edge){ - if (edge.getNodeSource().getId() == this.id){ - this.edgesIn.push(edge); - } - else{ - this.edgesOut.push(edge); - } -}; - -Vertex.prototype.removeEdge = function(edge){ - for ( var i = 0; i < this.getEdgesIn().length; i++) { - var edgeIn = this.edgesIn[i]; - if (edgeIn.getId() == edge.getId()){ - this.edgesIn.splice(i, 1); - } - } - for ( var i = 0; i < this.getEdgesOut().length; i++) { - var edgeIn = this.edgesOut[i]; - if (edgeIn.getId() == edge.getId()){ - this.edgesOut.splice(i, 1); - } - } -}; - -Vertex.prototype.remove = function(){ - var edges = this.getEdges(); - for ( var i = 0; i < edges.length; i++) { - var edge = edges[i]; - edge.remove(); - } - this.deleted.notify(this); -}; - - - - - -VertexGraphFormatter.prototype.getDefault = ItemGraphFormatter.prototype.getDefault; -VertexGraphFormatter.prototype.getSelected = ItemGraphFormatter.prototype.getSelected; -VertexGraphFormatter.prototype.getOver = ItemGraphFormatter.prototype.getOver; -VertexGraphFormatter.prototype.getDragging = ItemGraphFormatter.prototype.getDragging; -VertexGraphFormatter.prototype.getId = ItemGraphFormatter.prototype.getId; -VertexGraphFormatter.prototype.toJSON = ItemGraphFormatter.prototype.toJSON; -VertexGraphFormatter.prototype.loadFromJSON = ItemGraphFormatter.prototype.loadFromJSON; -VertexGraphFormatter.prototype._setEvents = ItemGraphFormatter.prototype._setEvents; - - -function VertexGraphFormatter(defaultFormat, selectedFormat, overFormat, draggingFormat){ - ItemGraphFormatter.prototype.constructor.call(this, defaultFormat, selectedFormat, overFormat, draggingFormat); -}; - - -CircleVertexGraphFormatter.prototype.getDefault = VertexGraphFormatter.prototype.getDefault; -CircleVertexGraphFormatter.prototype.getSelected = VertexGraphFormatter.prototype.getSelected; -CircleVertexGraphFormatter.prototype.getOver = VertexGraphFormatter.prototype.getOver; -CircleVertexGraphFormatter.prototype.getDragging = VertexGraphFormatter.prototype.getDragging; -CircleVertexGraphFormatter.prototype.getId = VertexGraphFormatter.prototype.getId; -CircleVertexGraphFormatter.prototype.toJSON = VertexGraphFormatter.prototype.toJSON; -CircleVertexGraphFormatter.prototype.loadFromJSON = VertexGraphFormatter.prototype.loadFromJSON; -CircleVertexGraphFormatter.prototype._setEvents = VertexGraphFormatter.prototype._setEvents; - -function CircleVertexGraphFormatter(id, defaultFormat, selectedFormat, overFormat, draggingFormat){ - VertexGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, draggingFormat); - this.args.type = "CircleVertexGraphFormatter"; - if (defaultFormat != null){ - if (defaultFormat.radius != null){ - this.defaultFormat.args.radius = defaultFormat.radius; - } - } - - if (selectedFormat != null){ - if (selectedFormat.radius != null){ - this.selected.args.radius = selectedFormat.radius; - } - } - - if (overFormat != null){ - if (overFormat.radius != null){ - this.over.args.radius = overFormat.radius; - } - } - - if (draggingFormat != null){ - if (draggingFormat.radius != null){ - this.dragging.args.draggingFormat = draggingFormat.radius; - } - } -}; - - - -SquareVertexGraphFormatter.prototype.getDefault = VertexGraphFormatter.prototype.getDefault; -SquareVertexGraphFormatter.prototype.getSelected = VertexGraphFormatter.prototype.getSelected; -SquareVertexGraphFormatter.prototype.getOver = VertexGraphFormatter.prototype.getOver; -SquareVertexGraphFormatter.prototype.getDragging = VertexGraphFormatter.prototype.getDragging; -SquareVertexGraphFormatter.prototype.getId = VertexGraphFormatter.prototype.getId; -SquareVertexGraphFormatter.prototype.toJSON = VertexGraphFormatter.prototype.toJSON; -SquareVertexGraphFormatter.prototype.loadFromJSON = VertexGraphFormatter.prototype.loadFromJSON; -SquareVertexGraphFormatter.prototype._setEvents = VertexGraphFormatter.prototype._setEvents; - -function SquareVertexGraphFormatter(id, defaultFormat, selectedFormat, overFormat, draggingFormat){ - VertexGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, draggingFormat); - this.args.type = "SquareVertexGraphFormatter"; - if (defaultFormat != null){ - if (defaultFormat.radius != null){ - this.defaultFormat.args.radius = defaultFormat.radius; - } - } - - if (selectedFormat != null){ - if (selectedFormat.radius != null){ - this.selected.args.radius = selectedFormat.radius; - } - } - - if (overFormat != null){ - if (overFormat.radius != null){ - this.over.args.radius = overFormat.radius; - } - } - - if (draggingFormat != null){ - if (draggingFormat.radius != null){ - this.dragging.args.draggingFormat = draggingFormat.radius; - } - } -}; - - -EllipseVertexGraphFormatter.prototype.getDefault = VertexGraphFormatter.prototype.getDefault; -EllipseVertexGraphFormatter.prototype.getSelected = VertexGraphFormatter.prototype.getSelected; -EllipseVertexGraphFormatter.prototype.getOver = VertexGraphFormatter.prototype.getOver; -EllipseVertexGraphFormatter.prototype.getDragging = VertexGraphFormatter.prototype.getDragging; -EllipseVertexGraphFormatter.prototype.getId = VertexGraphFormatter.prototype.getId; -EllipseVertexGraphFormatter.prototype.toJSON = VertexGraphFormatter.prototype.toJSON; -EllipseVertexGraphFormatter.prototype.loadFromJSON = VertexGraphFormatter.prototype.loadFromJSON; -EllipseVertexGraphFormatter.prototype._setEvents = VertexGraphFormatter.prototype._setEvents; - -function EllipseVertexGraphFormatter(id, defaultFormat, selectedFormat, overFormat, draggingFormat){ - VertexGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, draggingFormat); - this.args.type = "EllipseVertexGraphFormatter"; - if (defaultFormat != null){ - if (defaultFormat.radius != null){ - this.defaultFormat.args.radius = defaultFormat.radius; - } - } - - if (selectedFormat != null){ - if (selectedFormat.radius != null){ - this.selected.args.radius = selectedFormat.radius; - } - } - - if (overFormat != null){ - if (overFormat.radius != null){ - this.over.args.radius = overFormat.radius; - } - } - - if (draggingFormat != null){ - if (draggingFormat.radius != null){ - this.dragging.args.draggingFormat = draggingFormat.radius; - } - } -}; - - - -RectangleVertexGraphFormatter.prototype.getDefault = ItemGraphFormatter.prototype.getDefault; -RectangleVertexGraphFormatter.prototype.getSelected = ItemGraphFormatter.prototype.getSelected; -RectangleVertexGraphFormatter.prototype.getOver = ItemGraphFormatter.prototype.getOver; -RectangleVertexGraphFormatter.prototype.getDragging = ItemGraphFormatter.prototype.getDragging; -RectangleVertexGraphFormatter.prototype.getId = ItemGraphFormatter.prototype.getId; -RectangleVertexGraphFormatter.prototype.toJSON = ItemGraphFormatter.prototype.toJSON; -RectangleVertexGraphFormatter.prototype.loadFromJSON = ItemGraphFormatter.prototype.loadFromJSON; -RectangleVertexGraphFormatter.prototype._setEvents = ItemGraphFormatter.prototype._setEvents; - -function RectangleVertexGraphFormatter(id, defaultFormat, selectedFormat, overFormat, draggingFormat){ - VertexGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, draggingFormat); - this.args.type = "RectangleVertexGraphFormatter"; - if (defaultFormat != null){ - if (defaultFormat.radius != null){ - this.defaultFormat.args.radius = defaultFormat.radius; - } - } - - if (selectedFormat != null){ - if (selectedFormat.radius != null){ - this.selected.args.radius = selectedFormat.radius; - } - } - - if (overFormat != null){ - if (overFormat.radius != null){ - this.over.args.radius = overFormat.radius; - } - } - - if (draggingFormat != null){ - if (draggingFormat.radius != null){ - this.dragging.args.draggingFormat = draggingFormat.radius; - } - } -}; - - - -RoundedVertexGraphFormatter.prototype.getDefault = ItemGraphFormatter.prototype.getDefault; -RoundedVertexGraphFormatter.prototype.getSelected = ItemGraphFormatter.prototype.getSelected; -RoundedVertexGraphFormatter.prototype.getOver = ItemGraphFormatter.prototype.getOver; -RoundedVertexGraphFormatter.prototype.getDragging = ItemGraphFormatter.prototype.getDragging; -RoundedVertexGraphFormatter.prototype.getId = ItemGraphFormatter.prototype.getId; -RoundedVertexGraphFormatter.prototype.toJSON = ItemGraphFormatter.prototype.toJSON; -RoundedVertexGraphFormatter.prototype.loadFromJSON = ItemGraphFormatter.prototype.loadFromJSON; -RoundedVertexGraphFormatter.prototype._setEvents = ItemGraphFormatter.prototype._setEvents; - -function RoundedVertexGraphFormatter(id, defaultFormat, selectedFormat, overFormat, draggingFormat){ - VertexGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, draggingFormat); - this.args.type = "RoundedVertexGraphFormatter"; - if (defaultFormat != null){ - if (defaultFormat.radius != null){ - this.defaultFormat.args.radius = defaultFormat.radius; - } - } - - if (selectedFormat != null){ - if (selectedFormat.radius != null){ - this.selected.args.radius = selectedFormat.radius; - } - } - - if (overFormat != null){ - if (overFormat.radius != null){ - this.over.args.radius = overFormat.radius; - } - } - - if (draggingFormat != null){ - if (draggingFormat.radius != null){ - this.dragging.args.draggingFormat = draggingFormat.radius; - } - } -}; - - - -OctagonVertexGraphFormatter.prototype.getDefault = ItemGraphFormatter.prototype.getDefault; -OctagonVertexGraphFormatter.prototype.getSelected = ItemGraphFormatter.prototype.getSelected; -OctagonVertexGraphFormatter.prototype.getOver = ItemGraphFormatter.prototype.getOver; -OctagonVertexGraphFormatter.prototype.getDragging = ItemGraphFormatter.prototype.getDragging; -OctagonVertexGraphFormatter.prototype.getId = ItemGraphFormatter.prototype.getId; -OctagonVertexGraphFormatter.prototype.toJSON = ItemGraphFormatter.prototype.toJSON; -OctagonVertexGraphFormatter.prototype.loadFromJSON = ItemGraphFormatter.prototype.loadFromJSON; -OctagonVertexGraphFormatter.prototype._setEvents = ItemGraphFormatter.prototype._setEvents; - -function OctagonVertexGraphFormatter(id, defaultFormat, selectedFormat, overFormat, draggingFormat){ - VertexGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, draggingFormat); - this.args.type = "OctagonVertexGraphFormatter"; - if (defaultFormat != null){ - if (defaultFormat.radius != null){ - this.defaultFormat.args.radius = defaultFormat.radius; - } - } - - if (selectedFormat != null){ - if (selectedFormat.radius != null){ - this.selected.args.radius = selectedFormat.radius; - } - } - - if (overFormat != null){ - if (overFormat.radius != null){ - this.over.args.radius = overFormat.radius; - } - } - - if (draggingFormat != null){ - if (draggingFormat.radius != null){ - this.dragging.args.draggingFormat = draggingFormat.radius; - } - } -}; - - - -var Colors = new function() -{ - this.hashColor = []; - this.getColorByScoreArrayValue = function (arrayScore) - { - var array = new Array(); - - for (var i = 0; i< arrayScore.length; i++) - { - - var color = this.getColorByScoreValue(arrayScore[i]) - array.push( color); - - } - return array; - }; - - this.getHexStringByScoreArrayValue = function (arrayScore) - { - var arrayColor = this.getColorByScoreArrayValue(arrayScore); - var arrayHex = new Array(); - for (var i = 0; i< arrayColor.length; i++) - { - arrayHex.push( arrayColor[i].HexString()); - } - return arrayHex; - }; - - this.getColorByScoreValue = function (score) - { - - var truncate = score.toString().substr(0,4); - if (this.hashColor[truncate]!=null) - { - return this.hashColor[truncate]; - } - - - if(isNaN(score)) { - return Colors.ColorFromRGB(0,0,0); - } - var value; - - var from, to; - if(score < 0.5) { - from = Colors.ColorFromRGB(0,0,255); - to = Colors.ColorFromRGB(255,255,255); - value = (score * 2); - } else { - from = Colors.ColorFromRGB(255,255,255); - to = Colors.ColorFromRGB(255,0,0); - value = (score * 2) - 1; - } - - var x = value; - var y = 1.0 - value; - var color = Colors.ColorFromRGB(y * from.Red() + x * to.Red(), y * from.Green() + x * to.Green(), y * from.Blue() + x * to.Blue()); - - this.hashColor[truncate] = color; - - return color; - }; - - this.ColorFromHSV = function(hue, sat, val) - { - var color = new Color(); - color.SetHSV(hue,sat,val); - return color; - }; - - this.ColorFromRGB = function(r, g, b) - { - var color = new Color(); - color.SetRGB(r,g,b); - return color; - }; - - this.ColorFromHex = function(hexStr) - { - var color = new Color(); - color.SetHexString(hexStr); - return color; - }; - - function Color() { - //Stored as values between 0 and 1 - var red = 0; - var green = 0; - var blue = 0; - - //Stored as values between 0 and 360 - var hue = 0; - - //Strored as values between 0 and 1 - var saturation = 0; - var value = 0; - - this.SetRGB = function(r, g, b) - { - red = r/255.0; - green = g/255.0; - blue = b/255.0; - calculateHSV(); - }; - - this.Red = function() { return Math.round(red*255); }; - - this.Green = function() { return Math.round(green*255); }; - - this.Blue = function() { return Math.round(blue*255); }; - - this.SetHSV = function(h, s, v) - { - hue = h; - saturation = s; - value = v; - calculateRGB(); - }; - - this.Hue = function() - { return hue; }; - - this.Saturation = function() - { return saturation; }; - - this.Value = function() - { return value; }; - - this.SetHexString = function(hexString) - { - if(hexString == null || typeof(hexString) != "string") - { - this.SetRGB(0,0,0); - return; - } - - if (hexString.substr(0, 1) == '#') - hexString = hexString.substr(1); - - if(hexString.length != 6) - { - this.SetRGB(0,0,0); - return; - } - - var r = parseInt(hexString.substr(0, 2), 16); - var g = parseInt(hexString.substr(2, 2), 16); - var b = parseInt(hexString.substr(4, 2), 16); - if (isNaN(r) || isNaN(g) || isNaN(b)) - { - this.SetRGB(0,0,0); - return; - } - - this.SetRGB(r,g,b); - }; - - this.HexString = function() - { - - var rStr = this.Red().toString(16); - if (rStr.length == 1) - rStr = '0' + rStr; - var gStr = this.Green().toString(16); - if (gStr.length == 1) - gStr = '0' + gStr; - var bStr = this.Blue().toString(16); - if (bStr.length == 1) - bStr = '0' + bStr; - return ('#' + rStr + gStr + bStr).toUpperCase(); - }; - - this.Complement = function() - { - var newHue = (hue >= 180) ? hue - 180 : hue + 180; - var newVal = (value * (saturation - 1) + 1); - var newSat = (value*saturation) / newVal; - var newColor = new Color(); - newColor.SetHSV(newHue, newSat, newVal); - return newColor; - } ; - - function calculateHSV() - { - var max = Math.max(Math.max(red, green), blue); - var min = Math.min(Math.min(red, green), blue); - - value = max; - - saturation = 0; - if(max != 0) - saturation = 1 - min/max; - - hue = 0; - if(min == max) - return; - - var delta = (max - min); - if (red == max) - hue = (green - blue) / delta; - else if (green == max) - hue = 2 + ((blue - red) / delta); - else - hue = 4 + ((red - green) / delta); - hue = hue * 60; - if(hue < 0) - hue += 360; - } - - function calculateRGB() - { - red = value; - green = value; - blue = value; - - if(value == 0 || saturation == 0) - return; - - var tHue = (hue / 60); - var i = Math.floor(tHue); - var f = tHue - i; - var p = value * (1 - saturation); - var q = value * (1 - saturation * f); - var t = value * (1 - saturation * (1 - f)); - switch(i) - { - case 0: - red = value; green = t; blue = p; - break; - case 1: - red = q; green = value; blue = p; - break; - case 2: - red = p; green = value; blue = t; - break; - case 3: - red = p; green = q; blue = value; - break; - case 4: - red = t; green = p; blue = value; - break; - default: - red = value; green = p; blue = q; - break; - } - } - } -} -(); -/* - * Clase gestiona la insercción, eliminación de los elementos en el DOM - * - * Vital hacerla SIEMPRE compatible hacia atrás - * - * Last update: 28-10-2010 - * - */ - - -var DOM = {}; - -DOM.createNewElement = function(type, nodeParent, attributes) -{ - - var node = document.createElement(type); - for (var i=0; i0) - { - parent.removeChild(parent.childNodes[0]); - - } -}; - -DOM.select = function(targetID) -{ - return document.getElementById(targetID); -// return $("#"+targetID); -}; -var Geometry = -{ - - /** From tow points obtains the angles formed with the cartesian side **/ - getAngleBetweenTwoPoints : function(x1, y1, x2, y2){ - //var m = (y1 - y2)/ (x1 - x2); - return Math.atan2(y2-y1, x2-x1); - }, - - getAdjacentSideOfRectangleRight : function(angle, hypotenuse){ - return Math.abs(Math.cos(angle)*hypotenuse); - }, - - getOppositeSideOfRectangleRight : function(angle, hypotenuse){ - return Math.abs(Math.sin(angle)*hypotenuse); - }, - - toDegree: function(radian){ - return radian*180/Math.PI; - - } - - -}; - -var SVG = -{ - svgns : 'http://www.w3.org/2000/svg', - xlinkns : "http://www.w3.org/1999/xlink", - - createSVGCanvas: function(parentNode, attributes) - { - - attributes.push( ['xmlns', SVG.svgns], ['xmlns:xlink', 'http://www.w3.org/1999/xlink']); - var svg = document.createElementNS("http://www.w3.org/2000/svg", "svg"); - this._setProperties(svg, attributes); - parentNode.appendChild( svg); - return svg; - - }, - - createRectangle : function (x, y, width, height, attributes){ - var rect = document.createElementNS(this.svgns, "rect"); - rect.setAttribute("x",x); - rect.setAttribute("y",y); - rect.setAttribute("width",width); - rect.setAttribute("height",height); - SVG._setProperties(rect, attributes); - return rect; - }, - - drawImage64 : function (x, y, width, height, base64, svgNode, attributes) { - var node = SVG.createImage64(x, y, width, height, base64, attributes); - svgNode.appendChild(node); - return node; - }, - - createImage64 : function (x, y, width, height, base64, attributes) { - var img = document.createElementNS(this.svgns, "image"); - img.setAttribute("x",x); - img.setAttribute("y",y); - img.setAttribute("width",width); - img.setAttribute("height",height); - img.setAttribute("xlink:href",base64); - SVG._setProperties(img, attributes); - return img; - }, - - createLine: function (x1, y1, x2, y2, attributes){ - var line = document.createElementNS(this.svgns,"line"); - line.setAttribute("x1",x1); - line.setAttribute("y1",y1); - line.setAttribute("x2", x2); - line.setAttribute("y2", y2); - SVG._setProperties(line, attributes); - return line; - }, - - createClip: function (id, nodeToClip, attributes){ - var clip = document.createElementNS(this.svgns,"clipPath"); - clip.setAttribute("id",id); - clip.appendChild(nodeToClip); - return clip; - }, - - drawClip : function (id, nodeToClip, svgNode) { - var node = SVG.createClip(id, nodeToClip); - svgNode.appendChild(node); - return node; - }, - - drawRectangle : function (cx, cy, width, height, svgNode, attributes) { - try{ - var node = SVG.createRectangle(cx, cy, width, height, attributes); - svgNode.appendChild(node); - } - catch(e){ - - console.log("-------------------- "); - console.log("Error on drawRectangle " + e); - console.log(attributes); - console.log("-------------------- "); - } - return node; - }, - - createEllipse : function (x, y, rx, ry, attributes){ - var rect = document.createElementNS(this.svgns, "ellipse"); - rect.setAttribute("cx",x); - rect.setAttribute("cy",y); - rect.setAttribute("rx",rx); - rect.setAttribute("ry",ry); - SVG._setProperties(rect, attributes); - return rect; - }, - - drawEllipse : function (cx, cy, rx, ry, svgNode, attributes) { - var node = SVG.createEllipse(cx, cy, rx, ry, attributes); - svgNode.appendChild(node); - return node; - }, - - drawImage : function (x, y, canvasSVG, attributes) { - var image = document.createElementNS(this.svgns, "image"); - image.setAttribute("x",x); - image.setAttribute("y",y); - canvasSVG.appendChild(image); - SVG._setProperties(image, attributes); - }, - - drawLine : function (x1, y1, x2, y2, nodeSVG, attributes) { - try{ - var line = SVG.createLine(x1, y1, x2, y2, attributes); - nodeSVG.appendChild(line); - }catch(e){ - } - return line; - }, - - - drawPath: function (d, nodeSVG, attributes) { - var path = SVG.createPath(d, attributes); - nodeSVG.appendChild(path); - return path; - }, - - createPoligon : function (points, attributes){ - var poligon = document.createElementNS(this.svgns, "polygon"); - poligon.setAttribute("points",points); - SVG._setProperties(poligon, attributes); - return poligon; - }, - - drawPoligon : function (points, canvasSVG, attributes){ - var poligon = SVG.createPoligon(points, attributes); - canvasSVG.appendChild(poligon); - return poligon; - }, - // - - createPath : function (d, attributes){ - var path = document.createElementNS(this.svgns, "path"); - path.setAttribute("d",d); - SVG._setProperties(path, attributes); - return path; - }, - - drawCircle : function (x, y, radio, canvasSVG, attributes) { - - var newText = document.createElementNS(this.svgns,"circle"); - newText.setAttribute("cx",x); - newText.setAttribute("cy",y); - newText.setAttribute("r",radio); - - canvasSVG.appendChild(newText); - attributes["cursor"] = "pointer"; - this._setProperties(newText, attributes); - return newText; - }, - - - _setProperties: function(node, attributes) - { - if (attributes instanceof Array){ - for (var i=0; i< attributes.length; i++) - { - node.setAttribute(attributes[i][0], attributes[i][1]); - } - } - else{ - for ( var key in attributes){ - node.setAttribute(key, attributes[key]); - } - } - }, - -/* drawPath: function(pointsArray, canvasSVG, attributes){ - var path = document.createElementNS(this.svgns,"polyline"); - path.setAttribute ('id', id); - - var d= pointsArray[0].x+ " "+ pointsArray[0].y; - for (var i=1; i< pointsArray.length; i++) - { - d=d+" "+pointsArray[i].x+" "+pointsArray[i].y; - } - path.setAttribute ('points', d); - canvasSVG.appendChild(path); - },*/ - - createText : function (x, y, text, attributes) { - var node = document.createElementNS(this.svgns,"text"); - node.setAttributeNS(null , "x",x); - node.setAttributeNS(null, "y",y); - - var textNode = document.createTextNode(text); - node.appendChild(textNode); - - this._setProperties(node, attributes); - return node; - }, - - drawText : function (x, y, text, canvasSVG, attributes) { - var text = SVG.createText(x, y, text, attributes); - canvasSVG.appendChild(text); - return text; - }, - - - - drawGroup: function(svgNode, attributes) - { - var group = SVG.createGroup(attributes); - svgNode.appendChild(group); - return group; - }, - - createGroup: function(attributes){ - var group = document.createElementNS(this.svgns,"g"); - this._setProperties(group, attributes); - return group; - } - -}; - - - -var CanvasToSVG = { - - convert: function(sourceCanvas, targetSVG, x, y, id, attributes) { - - var img = this._convert(sourceCanvas, targetSVG, x, y, id); - - for (var i=0; i< attributes.length; i++) - { - img.setAttribute(attributes[i][0], attributes[i][1]); - } - }, - - _convert: function(sourceCanvas, targetSVG, x, y, id) { - var svgNS = "http://www.w3.org/2000/svg"; - var xlinkNS = "http://www.w3.org/1999/xlink"; - // get base64 encoded png from Canvas - var image = sourceCanvas.toDataURL(); - - // must be careful with the namespaces - var svgimg = document.createElementNS(svgNS, "image"); - - svgimg.setAttribute('id', id); - - //svgimg.setAttribute('class', class); - //svgimg.setAttribute('xlink:href', image); - svgimg.setAttributeNS(xlinkNS, 'xlink:href', image); - - - - - svgimg.setAttribute('x', x ? x : 0); - svgimg.setAttribute('y', y ? y : 0); - svgimg.setAttribute('width', sourceCanvas.width); - svgimg.setAttribute('height', sourceCanvas.height); - //svgimg.setAttribute('cursor', 'pointer'); - svgimg.imageData = image; - - targetSVG.appendChild(svgimg); - return svgimg; - }, - - importSVG: function(sourceSVG, targetCanvas) { - svg_xml = sourceSVG;//(new XMLSerializer()).serializeToString(sourceSVG); - var ctx = targetCanvas.getContext('2d'); - - var img = new Image(); - img.src = "data:image/svg+xml;base64," + btoa(svg_xml); -// img.onload = function() { - ctx.drawImage(img, 0, 0); -// }; - } - -}; -/* -Graph.prototype.importSVG = function(sourceSVG, targetCanvas) { - sourceSVG = this._svg; - targetCanvas = document.createElementNS('canvas'); - // https://developer.mozilla.org/en/XMLSerializer - svg_xml = (new XMLSerializer()).serializeToString(sourceSVG); - var ctx = targetCanvas.getContext('2d'); - // this is just a JavaScript (HTML) image - var img = new Image(); - // http://en.wikipedia.org/wiki/SVG#Native_support - // https://developer.mozilla.org/en/DOM/window.btoa - img.src = "data:image/svg+xml;base64," + btoa(svg_xml); - img.onload = function() { - // after this, Canvas’ origin-clean is DIRTY - ctx.drawImage(img, 0, 0); - } -}; -*/ - -/* - -Normalizacion de datos para dibujar colores -Issues: - No sé como debería llamarse esta libreria - No sé si ya existe una funciçon en javascript que lo haga - - -*/ - - -var Normalizer = new function() -{ - this.normalizeArray = function (arrayData) - { - - return this.standardizeArray(this.normal(arrayData)); - -// var result = this._getMaxAndMin(arrayData); -// var min =result[0]; -// var max = result[1]; -// -// -// //los hacemos todos positivos -// for (var i = 0; i< arrayData.length; i++) -// { -// arrayData[i]= Math.abs(min) + parseFloat(arrayData[i]); -// } -// -// var result = this._getMaxAndMin(arrayData); -// var min =result[0]; -// var max = result[1]; -// -// -// var resultArray = new Array(); -// for (var i = 0; i< arrayData.length; i++) -// { -// resultArray.push(arrayData[i]*1/max); -// } -// return resultArray; - }; - - this.normal = function(arrayData){ - var mean = this._getMean(arrayData); - var deviation = this._getStdDeviation(arrayData); - var result = this._getMaxAndMin(arrayData); - var min = result[0]; - var max = result[1]; - - var resultArray = new Array(); - for (var i = 0; i< arrayData.length; i++) { - if (deviation!=0){ - resultArray.push((arrayData[i]-mean)/deviation); - }else{ - resultArray.push(arrayData[i]); - } - } - return resultArray; - }; - - this.standardizeArray = function(arrayData) - { - var result = this._getMaxAndMin(arrayData); - var min = result[0]; - var max = result[1]; - - var offset = ( min <= 0 ) ? Math.abs(min) : (-1 * min); - var resultArray = new Array(); - for (var i = 0; i< arrayData.length; i++) { - if(max + offset!=0){ - resultArray.push((arrayData[i] + offset) / (max + offset)); - }else{ - resultArray.push(arrayData[i]+offset); - } - } - return resultArray; - }; - - - this._getMean = function(arrayData) { - var sum = 0; - for (var i = 0; i< arrayData.length; i++) { - sum = sum + parseFloat(arrayData[i]); - } - return sum/arrayData.length; - }; - - this._getStdDeviation = function(arrayData) { - var mean = this._getMean(arrayData); - var acum = 0.0; - for(var i=0; i max) max = parseFloat(arrayData[i]); - } - - return [min, max]; - }; -}; -function GraphCanvas(componentID, targetNode, args) { - this.args = {}; - /** target */ - this.targetID = targetNode.id; - - /** id manage */ - this.id = componentID; - this.args.idGraph = this.id + "main"; - this.args.idBackgroundNode = this.id + "background"; - - this.args.idEdgesGraph = this.id + "edges"; - this.args.idNodesGraph = this.id + "vertices"; - this.args.idLabelGraph = this.id + "label"; - this.args.idBackground = this.id + "background"; - - /** Objects Graph **/ - this.dataset = null; - this.formatter = null; - this.layout = null; - - /** Drawing **/ - this.circleDefaultRadius = 2; - this.squareDefaultSide = this.circleDefaultRadius * 1.5; - - /** Directed Arrow **/ - this.arrowDefaultSize = this.circleDefaultRadius; - - /** Groups **/ - this.GraphGroup = null; - this.GraphNodeGroup = null; - this.GraphLabelGroup = null; - this.GraphBackground = null; - - /** SETTINGS FLAGS **/ - this.args.draggingCanvasEnabled = false; //Flag to set if the canvas can be dragged - this.args.multipleSelectionEnabled = false; - this.args.interactive = false; - this.args.labeled = false; - this.args.linkEnabled = false; - - /** If numberEdge > maxNumberEdgesMoving then only it will move edges when mouse up **/ - this.args.maxNumberEdgesMoving = 3; - this.args.maxNumberEdgesFiringEvents = 50; - - /** Linking edges **/ - this.args.linking = false; - this.linkStartX = 0; - this.linkStartY = 0; - this.linkSVGNode = null; - this.linkNodeSource = null; - this.linkNodeTarget = null; - - /** Dragging Control **/ - this.draggingElement = null; - this.dragging = false; - this.nMouseOffsetX = 0; - this.nMouseOffsetY = 0; - this.dragStartX = 0; - this.dragStartY = 0; - this.desplazamientoX = 0; - this.desplazamientoY = 0; - - /** Selection Control **/ - this.selecting = false; - this.selectorX = null; - this.selectorY = null; - this.selectorSVGNode = null; - - /** Node status **/ - this.args.isVertexSelected = {}; - this.args.selectedVertices = []; - - /** Edges status **/ - this.args.isEdgeSelected = {}; - //this.args.selectedEdges = []; - - if (args != null) { - if (args.multipleSelectionEnabled != null) { - this.args.multipleSelectionEnabled = args.multipleSelectionEnabled; - this.args.draggingCanvasEnabled = !(this.args.multipleSelectionEnabled); - } - if (args.draggingCanvasEnabled != null) { - this.args.draggingCanvasEnabled = args.draggingCanvasEnabled; - this.args.multipleSelectionEnabled = !(this.args.draggingCanvasEnabled); - } - if (args.interactive != null) { - this.args.interactive = args.interactive; - } - if (args.labeled != null) { - this.args.labeled = args.labeled; - } - - } - - /** Hashmap with the svg node labels **/ - this.svgLabels = {}; - - /** EVENTS **/ - this.onVertexOut = new Event(this); - this.onVertexOver = new Event(this); - this.onVertexSelect = new Event(this); - this.onEdgeSelect = new Event(this); - this.onCanvasClicked = new Event(this); - this.onVertexUp = new Event(this); -} - -GraphCanvas.prototype.showLabels = function(value) { - this.args.labeled = value; - this.removeLabels(); - if (value) { - this.renderLabels(); - } -}; - -GraphCanvas.prototype.getSelectedVertices = function() { - return this.args.selectedVertices; -}; - -GraphCanvas.prototype.getSelectedEdges = function() { - var selected = []; - for ( var selectedEdge in this.args.isEdgeSelected) { - selected.push(selectedEdge); - } - return selected; -}; - -GraphCanvas.prototype.createSVGDom = function(targetID, id, width, height, backgroundColor) { - var container = document.getElementById(targetID); - this._svg = SVG.createSVGCanvas(container, [ - [ "style", "background-color:" + backgroundColor + ";" ], [ "id", id ], [ "dragx", 0 ], [ "dragy", 0 ], - [ "height", this.getFormatter().getHeight() ], [ "width", this.getFormatter().getWidth() ] ]); - return this._svg; -}; - -/** MULTIPLE SELECTION **/ -GraphCanvas.prototype.isMultipleSelectionEnabled = function() { - return this.args.multipleSelectionEnabled; -}; - -GraphCanvas.prototype.setMultipleSelection = function(value) { - this.args.multipleSelectionEnabled = value; - this.args.draggingCanvasEnabled = (!value); -}; - -GraphCanvas.prototype.setSelecting = function(value) { - this.selecting = value; -}; - -/** linking **/ -GraphCanvas.prototype.setLinking = function(value) { - this.args.linkEnabled = value; - this.selecting = !value; - this.dragging = !value; -}; - -/** CANVAS MOVING **/ -GraphCanvas.prototype.setDraggingCanvas = function(value) { - this.args.draggingCanvasEnabled = value; - this.args.multipleSelectionEnabled = !value; -}; - -GraphCanvas.prototype.isDraggingCanvasEnabled = function() { - return this.args.draggingCanvasEnabled; -}; -/** ZOOM **/ -GraphCanvas.prototype.getScale = function() { - return this.getFormatter().getZoomScale(); -}; - -GraphCanvas.prototype.setScale = function(scale) { - var graphNode = document.getElementById(this.args.idGraph); - graphNode.setAttribute("transform", graphNode.getAttribute("transform").replace("scale(" + this.getScale() + ")", "scale(" + scale + ")")); - this.getFormatter().setZoomScale(scale); -}; - -GraphCanvas.prototype.zoomIn = function() { - this.setScale(this.getScale() + this.getFormatter().getZoomScaleStepFactor()); -}; - -GraphCanvas.prototype.zoomOut = function() { - this.setScale(this.getScale() - this.getFormatter().getZoomScaleStepFactor()); - -}; - -/** SVG COORDENATES **/ -GraphCanvas.prototype.getSVGCoordenates = function(evt) { - var p = this._svg.createSVGPoint(); - p.x = evt.clientX; - p.y = evt.clientY; - - var m = this._svg.getScreenCTM(document.documentElement); - p = p.matrixTransform(m.inverse()); - return p; -}; - -/** SVG EVENTS **/ -GraphCanvas.prototype.mouseClick = function(event) { - if (event.button == 0) { - if (!this.args.interactive) { - return; - } - - if (this.isVertex(event.target)) { - this.clickNode(this.getVertexIdFromSVGId(event.target.id)); - } - /** Como el evento mouseClick viene despues del mouse up es aqui donde manejo el tema de deseccionar los elementos que estoy dragging **/ - if (this.dragging) { - this.dragging = false; - } - } -}; - -GraphCanvas.prototype.mouseMove = function(evt) { - if (this.selecting) { - this.clearLabels(); - - var width = (this.getSVGCoordenates(evt).x - this.selectorX); - var height = (this.getSVGCoordenates(evt).y - this.selectorY); - if ((width > 0) && (height > 0)) { - this.displaySelection(this.selectorX, this.selectorY, width, height); - } - if ((width > 0) && (height < 0)) { - this.displaySelection(this.selectorX, this.getSVGCoordenates(evt).y, width, Math.abs(height)); - } - if ((width < 0) && (height < 0)) { - this.displaySelection(this.getSVGCoordenates(evt).x, this.getSVGCoordenates(evt).y, Math.abs(width), Math.abs(height)); - } - if ((width < 0) && (height > 0)) { - this.displaySelection(this.selectorX + width, this.selectorY, Math.abs(width), Math.abs(height)); - } - - var x1 = (parseFloat(this.selectorSVGNode.getAttribute("x")) - DOM.select(this.id).getAttribute("dragx")) / this.getFormatter().getWidth(); - var y1 = (parseFloat(this.selectorSVGNode.getAttribute("y")) - DOM.select(this.id).getAttribute("dragy")) / this.getFormatter().getHeight(); - var x2 = (x1 + parseFloat(this.selectorSVGNode.getAttribute("width") / this.getFormatter().getWidth())); - var y2 = (y1 + parseFloat(this.selectorSVGNode.getAttribute("height") / this.getFormatter().getHeight())); - - this.deselectNodes(this.getLayout()); - var verticesSelected = this.getLayout().getVerticesByArea(x1 / this.getFormatter().getZoomScale(), y1 / this.getFormatter().getZoomScale(), - x2 / this.getFormatter().getZoomScale(), y2 / this.getFormatter().getZoomScale()); - for ( var i = 0; i < verticesSelected.length; i++) { - this.selectNode(verticesSelected[i].getId()); - this.renderLabel(verticesSelected[i].getId()); - } - - } - var p = null; - if (this.args.linking) { - p = this.getSVGCoordenates(evt); - if (this.linkSVGNode != null) { - this.linkSVGNode.setAttribute("x2", p.x - 2); - this.linkSVGNode.setAttribute("y2", p.y - 2); - } - } - - if (this.dragging) { - p = this.getSVGCoordenates(evt); - p.x -= this.nMouseOffsetX; - p.y -= this.nMouseOffsetY; - this.desplazamientoX = (this.getSVGCoordenates(evt).x - this.dragStartX);// + parseFloat(DOM.select(this.id).getAttribute("dragx")); - this.desplazamientoY = (this.getSVGCoordenates(evt).y - this.dragStartY);// + parseFloat(DOM.select(this.id).getAttribute("dragy")); - - if (this.draggingElement != null) { - /** Click sobre el recct del banground que provoca que mueva todo el canvas **/ - if (this.isNodeCanvas(this.draggingElement)) { - - p = this.getSVGCoordenates(evt); - p.x = this.desplazamientoX; - p.y = this.desplazamientoY; - - this.draggingElement.setAttribute("dragx", p.x); - this.draggingElement.setAttribute("dragy", p.y); - this.draggingElement = document.getElementById(this.args.idGraph); - this.draggingElement.setAttribute("transform", "translate(" + p.x + "," + p.y + "), scale(" + this.getScale() + ")"); - - DOM.select(this.id).setAttribute("dragx", p.x); - DOM.select(this.id).setAttribute("dragy", p.y); - - if (this.NodeSVGbackgroundImage != null) { - this.NodeSVGbackgroundImage.setAttribute("dragx", p.x); - this.NodeSVGbackgroundImage.setAttribute("dragy", p.y); - } - } else { - if (this.isVertex(this.draggingElement)) { - this.selectNode(this.getVertexIdFromSVGId(this.draggingElement.id)); - this.desplazamientoX = this.desplazamientoX / this.getFormatter().getZoomScale(); - this.desplazamientoY = this.desplazamientoY / this.getFormatter().getZoomScale(); - this.moveSelectedNodes(this.desplazamientoX, this.desplazamientoY); - - this.dragStartX = this.getSVGCoordenates(evt).x; - this.dragStartY = this.getSVGCoordenates(evt).y; - } else { - if (this.isNodeBackground(this.draggingElement)) { - - this.draggingElement.setAttribute("dragx", p.x); - this.draggingElement.setAttribute("dragy", p.y); - this.draggingElement = document.getElementById(this.args.idGraph); - this.draggingElement.setAttribute("transform", "translate(" + p.x + "," + p.y + "), scale(" + this.getScale() + ")"); - } else { - this.draggingElement.setAttribute("transform", "translate(" + p.x + "," + p.y + ")"); - } - } - } - } - } -}; - -GraphCanvas.prototype.moveSelectedNodes = function(offsetX, offsetY) { - for ( var i = 0; i < this.getSelectedVertices().length; i++) { - - var nodeId = this.getSelectedVertices()[i]; - var svgNodeId = this.getSVGNodeId(nodeId); - - var x = parseFloat(DOM.select(svgNodeId).getAttribute("dragx")) + parseFloat(offsetX);// - parseFloat(DOM.select(this.id).getAttribute("dragx")); - var y = parseFloat(DOM.select(svgNodeId).getAttribute("dragy")) + parseFloat(offsetY);// + parseFloat(DOM.select(this.id).getAttribute("dragy")); - - this._movingNode(DOM.select(svgNodeId), x, y); - } -}; - -GraphCanvas.prototype.mouseDown = function(evt) { - if (event.button == 0) { - - /** if !no interactive mouse events do anything **/ - if (!this.args.interactive) { - return; - } - - var p = this.getSVGCoordenates(evt); - - /** When click on canvas or background deselect all **/ - if (this.isNodeCanvas(evt.target) || this.isNodeBackground(evt.target)) { - this.deselectNodes(); - this.deselectEdges(); - this.onCanvasClicked.notify(); - } - - /** if I am linking vertices **/ - if (this.args.linkEnabled) { - - if (!this.args.linking) { - this.args.linking = true; - if (this.isVertex(evt.target)) { - this.linkStartX = p.x; - this.linkStartY = p.y; - this.linkSVGNode = SVG.drawLine(p.x, p.y, p.x, p.y, this._svg, { - "stroke" : "#FF0000" - }); - this.linkNodeSource = this.getVertexIdFromSVGId(evt.target.id); - } - } else { - this.linkNodeTarget = this.getVertexIdFromSVGId(evt.target.id); - this.args.linking = false; - this.args.linkEnabled = false; - if (this.isVertex(evt.target)) { - this.getDataset().addEdge(this.linkNodeSource + "_" + this.linkNodeTarget, this.linkNodeSource, this.linkNodeTarget, {}); - } - this.linkSVGNode.parentNode.removeChild(this.linkSVGNode); - } - return; - } - - /** Id is a vertex or the canvas **/ - if (this.isVertex(evt.target) || this.isNodeCanvas(evt.target) || this.isNodeBackground(evt.target)) { - this._startDragging(evt); - } - /** if i is edge **/ - if (this.isEdge(evt.target)) { - this.selectEdge(this.getEdgeIdFromSVGId(evt.target.getAttribute("id"))); - } - - if (this.args.multipleSelectionEnabled) { - if (!this.dragging) { - this.setSelecting(true); - this.selectorX = p.x; - this.selectorY = p.y; - this.displaySelection(p.x, p.y, 1, 1); - } - } - - } - if (event.button == 1) { - this.setLinking(false); - this.setMultipleSelection(false); - this.selecting = false; - - /** Id is a vertex or the canvas **/ - if (this.isVertex(evt.target) || this.isNodeCanvas(evt.target) || this.isNodeBackground(evt.target)) { - this._startDragging(evt); - } - } -}; - -GraphCanvas.prototype.mouseUp = function(event) { - if (!this.args.interactive) { - return; - } - - if (this.dragging) { - this._stopDragging(event); - if (this.isVertex(event.target)) { - var vertexId = this.getVertexIdFromSVGId(event.target.id); - if (this.getDataset().getVertexById(vertexId).getEdges().length >= this.args.maxNumberEdgesMoving) { - this.moveEdge(vertexId); - } - } - } - - if (this.selecting) { - this.setSelecting(false); - - var x1 = (parseFloat(this.selectorSVGNode.getAttribute("x")) - DOM.select(this.id).getAttribute("dragx")) / this.getFormatter().getWidth(); - var y1 = (parseFloat(this.selectorSVGNode.getAttribute("y")) - DOM.select(this.id).getAttribute("dragy")) / this.getFormatter().getHeight(); - var x2 = (x1 + parseFloat(this.selectorSVGNode.getAttribute("width") / this.formatter.getWidth())); - var y2 = (y1 + parseFloat(this.selectorSVGNode.getAttribute("height") / this.formatter.getHeight())); - - var verticesSelected = this.getLayout().getVerticesByArea(x1 / this.getFormatter().getZoomScale, y1 / this.getFormatter().getZoomScale, - x2 / this.getFormatter().getZoomScale, y2 / this.getFormatter().getZoomScale); - - for ( var i = 0; i < verticesSelected.length; i++) { - this.selectNode(verticesSelected[i].getId()); - } - - if (this.selectorSVGNode != null) { - this._svg.removeChild(this.selectorSVGNode); - } - - if (this.args.labeled) { - this.clearLabels(); - this.renderLabels(); - } - - this.selectorSVGNode = null; - // this.renderLabels(); - } -}; - -/** SELECTION **/ -GraphCanvas.prototype.displaySelection = function(x, y, width, height) { - if (this.selectorSVGNode != null) { - this.selectorSVGNode.setAttribute("x", x); - this.selectorSVGNode.setAttribute("y", y); - this.selectorSVGNode.setAttribute("width", width); - this.selectorSVGNode.setAttribute("height", height); - } else { - this.selectorSVGNode = SVG.drawRectangle(x, y, width, height, this._svg, { - "fill" : "red", - "stroke" : "black", - "opacity" : "0.2", - "stroke-opacity" : "1" - }); - } -}; - -/** DRAGGING **/ -GraphCanvas.prototype._startDragging = function(evt) { - if (!this.isDraggingCanvasEnabled()) { - if (this.isNodeCanvas(evt.target)) { - this.draggingElement = null; - } - } - - if (this.isVertex(evt.target) || (this.isNodeBackground(evt.target) && (this.isDraggingCanvasEnabled()))|| (this.isNodeCanvas(evt.target) && (this.isDraggingCanvasEnabled()))) { - this.clearLabels(); - this.draggingElement = evt.target; - this.dragging = true; - var p = this.getSVGCoordenates(evt); - - this.nMouseOffsetX = p.x - parseInt(evt.target.getAttribute("dragx")); - this.nMouseOffsetY = p.y - parseInt(evt.target.getAttribute("dragy")); - - if (this.isVertex(evt.target)) { - this.dragStartX = parseInt(this.draggingElement.getAttribute("dragx")) * this.getFormatter().getZoomScale() - + parseFloat(DOM.select(this.id).getAttribute("dragx")); - this.dragStartY = parseInt(this.draggingElement.getAttribute("dragy")) * this.getFormatter().getZoomScale() - + parseFloat(DOM.select(this.id).getAttribute("dragy")); - } else { - this.dragStartX = p.x - parseInt(this.draggingElement.getAttribute("dragx"));// + parseFloat(DOM.select(this.id).getAttribute("dragx")); - this.dragStartY = p.y - parseInt(this.draggingElement.getAttribute("dragy"));// + parseFloat(DOM.select(this.id).getAttribute("dragy")); - } - } -}; - -GraphCanvas.prototype._stopDragging = function(event) { - this.nMouseOffsetX = 0; - this.nMouseOffsetX = 0; - /** despues del evento up viene el evento click entonces acabo el dragging en el mouseclick **/ - this.dragging = false; - this.draggingElement = null; - this.renderLabels(); - - this.setLinking(false); - this.setMultipleSelection(true); - this.selecting = false; - -}; - -/** Move the edges of the vertex with the vertexId indicado **/ -GraphCanvas.prototype.moveEdge = function(vertexId) { - var x = this.getLayout().getNodeById(vertexId).x * this.getFormatter().getWidth(); - var y = this.getLayout().getNodeById(vertexId).y * this.getFormatter().getHeight(); - - /** Moving edges out **/ - for ( var i = 0; i < this.getDataset().getVertexById(vertexId).getEdgesOut().length; i++) { - var edgeId = this.getDataset().getVertexById(vertexId).getEdgesOut()[i].getId(); - var svgEdgeId = this.getSVGEdgeId(edgeId); - var edgeFormatter = this.getFormatter().getEdgeById(edgeId); - if (edgeFormatter instanceof LineEdgeGraphFormatter) { - DOM.select(svgEdgeId + "_shadow").setAttribute("x2", x); - DOM.select(svgEdgeId + "_shadow").setAttribute("y2", y); - DOM.select(svgEdgeId).setAttribute("x2", x); - DOM.select(svgEdgeId).setAttribute("y2", y); - } - - if ((edgeFormatter instanceof DirectedLineEdgeGraphFormatter) || (edgeFormatter instanceof OdirectedLineEdgeGraphFormatter) - || (edgeFormatter instanceof OdotDirectedLineEdgeGraphFormatter) || (edgeFormatter instanceof DotDirectedLineEdgeGraphFormatter) - || (edgeFormatter instanceof CutDirectedLineEdgeGraphFormatter)) { - this.removeEdge(edgeId); - this.renderEdge(edgeId); - } - } - - /** Moving edges in **/ - for ( var i = 0; i < this.getDataset().getVertexById(vertexId).getEdgesIn().length; i++) { - var edgeId = this.getDataset().getVertexById(vertexId).getEdgesIn()[i].getId(); - var svgEdgeId = this.getSVGEdgeId(edgeId); - var edgeFormatter = this.getFormatter().getEdgeById(edgeId); - if (edgeFormatter instanceof LineEdgeGraphFormatter) { - DOM.select(svgEdgeId).setAttribute("x1", x); - DOM.select(svgEdgeId).setAttribute("y1", y); - DOM.select(svgEdgeId + "_shadow").setAttribute("x1", x); - DOM.select(svgEdgeId + "_shadow").setAttribute("y1", y); - } - - if ((edgeFormatter instanceof DirectedLineEdgeGraphFormatter) || (edgeFormatter instanceof OdirectedLineEdgeGraphFormatter) - || (edgeFormatter instanceof OdotDirectedLineEdgeGraphFormatter) || (edgeFormatter instanceof DotDirectedLineEdgeGraphFormatter) - || (edgeFormatter instanceof CutDirectedLineEdgeGraphFormatter)) { - this.removeEdge(edgeId); - this.renderEdge(edgeId); - } - - if (edgeFormatter instanceof BezierEdgeGraphFormatter) { - var radius = this.getFormatter().getVertexById(vertexId).getDefault().getSize() * this.getFormatter().getNodesMaxSize(); - var d = this.calculateCoordenatesBezier(radius, x, y); - DOM.select(svgEdgeId).setAttribute("d", d); - } - } -}; - -GraphCanvas.prototype.moveNode = function(vertexId) { - var x = this.getLayout().getNodeById(vertexId).x * this.getFormatter().getWidth(); - var y = this.getLayout().getNodeById(vertexId).y * this.getFormatter().getHeight(); - var svgNodeElement = DOM.select(this.getSVGNodeId(vertexId)); - - svgNodeElement.setAttribute("dragx", x); - svgNodeElement.setAttribute("dragy", y); - svgNodeElement.setAttribute("transform", "translate(" + x + "," + y + ")"); - - if (this.getDataset().getVertexById(vertexId).getEdges().length < this.args.maxNumberEdgesMoving) { - this.moveEdge(vertexId); - } -}; - -GraphCanvas.prototype._movingNode = function(svgNodeElement, x, y) { - var vertexId = this.getVertexIdFromSVGId(svgNodeElement.getAttribute("id")); - this.getLayout().getNodeById(vertexId).setCoordinates(x / this.getFormatter().getWidth(), y / this.getFormatter().getHeight()); - this.desplazamientoX = 0; - this.desplazamientoY = 0; - this.removeLabel(vertexId); - this.renderLabel(vertexId); -}; - -/** INIT **/ -GraphCanvas.prototype.init = function() { - - this._svg = this.createSVGDom(this.targetID, this.id, this.getFormatter().getWidth(), this.getFormatter().getHeight(), this.getFormatter() - .getBackgroundColor()); - this.GraphGroup = SVG.drawGroup(this._svg, [ [ "id", this.args.idGraph ], [ "transform", "translate(0,0), scale(1)" ] ]); - this.GraphBackground = SVG.drawGroup(this.GraphGroup, [ [ "id", this.args.idBackground ] ]); - this.GraphEdgeGroup = SVG.drawGroup(this.GraphGroup, [ [ "id", this.args.idEdgesGraph ] ]); - this.GraphNodeGroup = SVG.drawGroup(this.GraphGroup, [ [ "id", this.args.idNodesGraph ] ]); - this.GraphLabelGroup = SVG.drawGroup(this.GraphGroup, [ [ "id", this.args.idLabelGraph ] ]); - - if ((this.getFormatter().getBackgroundImage() != null) && (this.getFormatter().getBackgroundImage() != "")) { - this.setBackgroundImage(this.getFormatter().getBackgroundImage()); - } - /** SVG Events listener */ - var _this = this; - this._svg.addEventListener("click", function(event) { - _this.mouseClick(event); - }, false); - this._svg.addEventListener("mousemove", function(event) { - _this.mouseMove(event, _this); - }, false); - this._svg.addEventListener("mousedown", function(event) { - _this.mouseDown(event, _this); - }, false); - this._svg.addEventListener("mouseup", function(event) { - _this.mouseUp(event, _this); - }, false); -}; - -/* - GraphCanvas.prototype.backgroungToSVG = function(){ - var _this = this; - var canvas = document.createElement('canvas'); - canvas.setAttribute("id", "canvas"); - canvas.width = this.formatter.getWidth(); - canvas.height = this.formatter.getHeight(); - - this._svg.parentNode.parentNode.appendChild(canvas); - var ctx = document.getElementById('canvas').getContext('2d'); - var img = new Image(); - - img.src = this.formatter.getBackgroundImage(); - ctx.drawImage(img,0,0 ,_this.formatter.getWidth(), _this.formatter.getHeight()); - - - img.onload = function() { - canvas.parentNode.removeChild(canvas); - } - - this.NodeSVGbackgroundImage.setAttribute("xlink:href", document.getElementById("canvas").toDataURL()); - this.NodeSVGbackgroundImage.removeAttribute("href"); - - // - - };*/ - -GraphCanvas.prototype.setBackgroundImage = function() { - if (this.NodeSVGbackgroundImage != null) { - this.NodeSVGbackgroundImage.parentNode.removeChild(this.NodeSVGbackgroundImage); - } - $('#' + this.targetID).svg(); - $('#' + this.targetID).svg("get"); - - $('#' + this.targetID).svg("get")._svg = document.getElementById(this.id); - - var svg = $('#' + this.targetID).svg("get"); - this.NodeSVGbackgroundImage = svg.image(0, 0, this.getFormatter().getWidth(), this.getFormatter().getHeight(), this.getFormatter() - .getBackgroundImage()); - this.NodeSVGbackgroundImage.setAttribute("id", this.args.idBackgroundNode); - - this.NodeSVGbackgroundImage.setAttribute("x", 0); - this.NodeSVGbackgroundImage.setAttribute("y", 0); - - this.NodeSVGbackgroundImage.setAttribute("dragx", 0); - this.NodeSVGbackgroundImage.setAttribute("dragy", 0); - - if (this.getFormatter().args.backgroundImageHeight != null) { - this.NodeSVGbackgroundImage.setAttribute("height", this.getFormatter().args.backgroundImageHeight); - } - if (this.getFormatter().args.backgroundImageWidth != null) { - this.NodeSVGbackgroundImage.setAttribute("width", this.getFormatter().args.backgroundImageWidth); - } - - if (this.getFormatter().args.backgroundImageX != null) { - this.NodeSVGbackgroundImage.setAttribute("x", this.getFormatter().args.backgroundImageX); - } - if (this.getFormatter().args.backgroundImageY != null) { - this.NodeSVGbackgroundImage.setAttribute("y", this.getFormatter().args.backgroundImageY); - } - - this.GraphBackground.appendChild(this.NodeSVGbackgroundImage); - this.NodeSVGbackgroundImage.removeAttribute("href"); - this.NodeSVGbackgroundImage.setAttribute("xlink:href", this.getFormatter().getBackgroundImage()); -}; - -GraphCanvas.prototype.removeBackgroundImage = function() { - if (this.NodeSVGbackgroundImage != null) { - this.NodeSVGbackgroundImage.parentNode.removeChild(this.NodeSVGbackgroundImage); - } -}; - -GraphCanvas.prototype._setBackgroundColor = function(color) { - var attributes = [ [ "fill", color ] ]; - SVG.drawRectangle(0, 0, this.getFormatter().getWidth(), this.getFormatter().getHeight(), this.GraphBackground, attributes); -}; - -/** Serialize **/ -GraphCanvas.prototype.toJSON = function() { - var json = {}; - json.dataset = {}; - json.formatter = {}; - json.layout = {}; - json.dataset = this.getDataset().toJSON(); - json.formatter = this.getFormatter().toJSON(); - json.layout = this.getLayout().toJSON(); - return json; -}; - -GraphCanvas.prototype.toHTML = function() { - //this.backgroungToSVG(); - var html = this._svg.parentElement.innerHTML; - - var start = html.indexOf("") + 6; - - return html.substr(start, end); -}; - -/** DRAW **/ -GraphCanvas.prototype.draw = function(graphdataset, graphformatter, graphlayout) { - this.setDataset(graphdataset); - this.setFormatter(graphformatter); - this.setLayout(graphlayout); - - var _this = this; - this.getFormatter().changed.attach(function(sender, item) { - _this.removeNode(item.getId()); - _this.renderNode(item.getId()); - if (_this.args.labeled) { - _this.removeLabel(item.getId()); - _this.renderLabel(item.getId()); - } - - }); - //TODO - this.getFormatter().edgeChanged.attach(function(sender, item) { - _this.removeEdge(item.getId()); - _this.renderEdge(item.getId()); - }); - - this.getFormatter().resized.attach(function(sender, item) { - _this.resize(_this.getFormatter().getWidth(), _this.getFormatter().getHeight()); - }); - - this.getFormatter().backgroundImageChanged.attach(function(sender, item) { - _this.setBackgroundImage(_this.getFormatter().getBackgroundImage()); - }); - - this.getFormatter().backgroundColorChanged.attach(function(sender, item) { - _this._setBackgroundColor(_this.getFormatter().getBackgroundColor()); - }); - - this.getLayout().changed.attach(function(sender, item) { - _this.moveNode(item.getId()); - _this.moveEdge(item.getId()); - if (_this.args.labeled) { - _this.removeLabel(item.getId()); - _this.renderLabel(item.getId()); - } - }); - - this.getDataset().newVertex.attach(function(sender, item) { - - _this.renderNode(item.getId()); - if (_this.args.labeled) { - _this.renderLabel(item.getId()); - } - }); - - this.getDataset().newEdge.attach(function(sender, item) { - _this.renderEdge(item.getId()); - }); - - this.getDataset().vertexDeleted.attach(function(sender, item) { - _this.removeNode(item.getId()); - if (_this.args.labeled) { - _this.removeLabel(item.getId()); - } - }); - - this.getDataset().edgeDeleted.attach(function(sender, item) { - _this.removeEdge(item.getId()); - }); - - this.getDataset().vertexNameChanged.attach(function(sender, args) { - if (_this.args.labeled) { - _this.removeLabel(args.item.getId()); - _this.removeLabel(args.item.getId()); - _this.renderLabel(args.item.getId()); - } - }); - this.init(); - this.render(); -}; - -GraphCanvas.prototype.render = function() { - for ( var id in this.getDataset().getVertices()) { - this.renderNode(id); - } - this.renderLabels(); - this.renderEdges(); -}; - -GraphCanvas.prototype.renderLabels = function() { - if (this.args.labeled) { - for ( var id in this.getDataset().getVertices()) { - this.renderLabel(id); - } - } -}; - -GraphCanvas.prototype.removeLabels = function() { - for ( var id in this.getDataset().getVertices()) { - this.removeLabel(id); - } -}; - -/** Utilities method for nodes **/ -GraphCanvas.prototype.isNodeCanvas = function(node) { - return ((node.id == this.args.idGraph) || (node.id == this.id)); -}; - -GraphCanvas.prototype.isNodeBackground = function(node) { - return ((node.id == this.args.idBackgroundNode)); -}; - -GraphCanvas.prototype.isVertex = function(node) { - if (node.getAttribute("id") != null) { - if (node.getAttribute("id").indexOf("_v_") != -1) { - return true; - } - } - return false; -}; - -GraphCanvas.prototype.isLabel = function(node) { - if (node.getAttribute("id") != null) { - if (node.getAttribute("id").indexOf("_l_") != -1) { - return true; - } - } - return false; -}; - -GraphCanvas.prototype.isEdge = function(node) { - if (node.getAttribute("id") != null) { - if (node.getAttribute("id").indexOf("_e_") != -1) { - return true; - } - } - return false; -}; - -/** Resize **/ -GraphCanvas.prototype.resize = function(width, height) { - // this._svg.setAttribute("width", width); - // this._svg.setAttribute("height", height); - if (this.NodeSVGbackgroundImage != null) { - this.NodeSVGbackgroundImage.setAttribute("width", width); - this.NodeSVGbackgroundImage.setAttribute("height", height); - } - - this._svg.setAttribute("width", width); - this._svg.setAttribute("height", height); - - this.clearCanvas(); - this.render(); -}; - -GraphCanvas.prototype.clearCanvas = function() { - DOM.removeChilds(this.GraphEdgeGroup.getAttribute("id")); - DOM.removeChilds(this.GraphNodeGroup.getAttribute("id")); - this.clearLabels(); -}; - -GraphCanvas.prototype.clearLabels = function() { - DOM.removeChilds(this.GraphLabelGroup.getAttribute("id")); -}; - -/** ID'S converter **/ -GraphCanvas.prototype.getSVGNodeId = function(nodeId) { - return this.id + "_v_" + nodeId; -}; - -GraphCanvas.prototype.getSVGEdgeId = function(edgeId) { - return this.id + "_e_" + edgeId; -}; - -GraphCanvas.prototype.getSVGArrowEdgeId = function(edgeId) { - return this.id + "_arrow_" + edgeId; -}; - -GraphCanvas.prototype.getSVGLabelId = function(edgeId) { - return this.id + "_l_" + edgeId; -}; - -GraphCanvas.prototype.blinkVertexById = function(vertexId) { - $("#" + this.getSVGNodeId(vertexId)).fadeIn().fadeOut().fadeIn().fadeOut().fadeIn().fadeOut(); -}; - -GraphCanvas.prototype.getVertexIdFromSVGId = function(svgVertexId) { - return svgVertexId.replace(this.id, "").replace("_v_", ""); -}; - -GraphCanvas.prototype.getEdgeIdFromSVGId = function(svgEdgeId) { - return svgEdgeId.replace(this.id, "").replace("_e_", ""); -}; - -/** VERTEX **/ -GraphCanvas.prototype.getVertexById = function(id) { - return document.getElementById(this.getSVGNodeId(id)); -}; - -GraphCanvas.prototype.renderNodes = function() { - for ( var id in this.getDataset().getVertices()) { - this.renderNode(id); - } -}; - -GraphCanvas.prototype.overNode = function(nodeId) { - if (!this.args.interactive) { - return; - } - /** If selected we don't change the format **/ - if (this.args.isVertexSelected[nodeId] == null) { - var args = this.getFormatter().getVertexById(nodeId).getOver(); - args.args["cursor"] = 'pointer'; - this.changeVertexFormat(nodeId, args); - } -}; - -GraphCanvas.prototype.outNode = function(nodeId) { - if (!this.args.interactive) { - return; - } - - /** If selected we don't change the format **/ - if (this.args.isVertexSelected[nodeId] == null) { - this.changeVertexFormat(nodeId, this.getFormatter().getVertexById(nodeId).getDefault()); - } -}; - -GraphCanvas.prototype.overLabel = function(nodeId) { - this.overNode(nodeId); - // this.svgLabels[nodeId].setAttribute("cursor", "pointer"); -}; - -GraphCanvas.prototype.outLabel = function(nodeId) { - this.outNode(nodeId); - // this.svgLabels[nodeId].setAttribute("cursor", ""); -}; - -GraphCanvas.prototype.clickNode = function(nodeId) { - if (!this.args.interactive) { - return; - } - - /** si el evento se dispara oprque estaba dragging entonces no activo nada **/ - if (this.args.isVertexSelected[nodeId] == null) { - this.selectNode(nodeId); - } else { - this.deselectNode(nodeId); - } -}; - -GraphCanvas.prototype.selectNode = function(nodeId) { - for ( var i = 0; i < this.args.selectedVertices.length; i++) { - var format = this.getFormatter().getVertexById(nodeId).getSelected(); - format.opacity = 0.2; - this.changeVertexFormat(nodeId, this.getFormatter().getVertexById(nodeId).getSelected()); - } - - if (this.args.isVertexSelected[nodeId] == null) { - var format = this.getFormatter().getVertexById(nodeId).getSelected(); - format.opacity = 1; - this.changeVertexFormat(nodeId, this.getFormatter().getVertexById(nodeId).getSelected()); - this.args.selectedVertices.push(nodeId); - this.args.isVertexSelected[nodeId] = this.args.selectedVertices.length - 1; - this.onVertexSelect.notify(nodeId); - } -}; - -GraphCanvas.prototype.selectAllEdges = function() { - this.deselectNodes(); - this.deselectEdges(); - - for ( var edgesId in this.getDataset().edges) { - this.selectEdge(edgesId); - } -}; - -GraphCanvas.prototype.selectAllNodes = function() { - this.deselectNodes(); - this.deselectEdges(); - - for ( var vertexId in this.getDataset().vertices) { - this.selectNode(vertexId); - } -}; - -GraphCanvas.prototype.selectAll = function() { - this.deselectNodes(); - this.deselectEdges(); - - for ( var vertexId in this.getDataset().vertices) { - this.selectNode(vertexId); - } - - for ( var edgesId in this.getDataset().edges) { - this.selectEdge(edgesId); - } -}; - -GraphCanvas.prototype.selectEdge = function(edgeId) { - if (this.args.isEdgeSelected[edgeId] == null) { - this.changeEdgeFormat(edgeId, this.getFormatter().getEdgeById(edgeId).getSelected()); - //this.args.selectedEdges.push(edgeId); - this.args.isEdgeSelected[edgeId] = true; //this.args.selectedEdges.length - 1; - this.onEdgeSelect.notify(edgeId); - } -}; - -GraphCanvas.prototype.selectEdges = function(edges) { - - for ( var i = 0; i < edges.length; i++) { - this.selectEdge(edges[i]); - } -}; - -GraphCanvas.prototype.deselectNode = function(nodeId) { - if (this.args.isVertexSelected[nodeId] != null) { - this.changeVertexFormat(nodeId, this.getFormatter().getVertexById(nodeId).getDefault()); - this.args.selectedVertices.splice(this.args.isVertexSelected[nodeId], 1); - var index = this.args.isVertexSelected[nodeId]; - delete this.args.isVertexSelected[nodeId]; - - for ( var vertex in this.args.isVertexSelected) { - if (this.args.isVertexSelected[vertex] > index) { - this.args.isVertexSelected[vertex] = this.args.isVertexSelected[vertex] - 1; - } - } - } -}; - -GraphCanvas.prototype.deselectNodes = function() { - var selected = JSON.parse(JSON.stringify(this.getSelectedVertices())); - for ( var i = 0; i < selected.length; i++) { - this.deselectNode(selected[i]); - } -}; -GraphCanvas.prototype.selectNodes = function(idNodes) { - - for ( var i = 0; i < idNodes.length; i++) { - this.selectNode(idNodes[i]); - } - - // for ( var vertex in this.args.isVertexSelected) { - // if (this.args.isVertexSelected[vertex] > index){ - // this.args.isVertexSelected[vertex] = this.args.isVertexSelected[vertex] - 1; - // } - // } - -}; - -GraphCanvas.prototype.changeVertexFormat = function(nodeId, format) { - var svgNode = DOM.select(this.getSVGNodeId(nodeId)); - if (svgNode != null) { - var properties = format.toJSON(); - for ( var item in properties) { - svgNode.setAttribute(item, properties[item]); - } - - if (this.getFormatter().getVertexById(nodeId) instanceof CircleVertexGraphFormatter) { - var transform = "translate(" + svgNode.getAttribute("dragx") + "," + svgNode.getAttribute("dragy") + "), scale(" + format.getSize() + ")"; - svgNode.setAttribute("transform", transform); - } - } -}; - -GraphCanvas.prototype.renderLabel = function(nodeId) { - var x = Math.ceil(this.getLayout().getNodeById(nodeId).x * this.getFormatter().getWidth()); - var y = Math.ceil(this.getLayout().getNodeById(nodeId).y * this.getFormatter().getHeight()); - - var svgAttributesNode = JSON.parse(JSON.stringify(this.getFormatter().getVertexById(nodeId).getDefault().toJSON().title)); - svgAttributesNode.id = this.getSVGLabelId(this.getDataset().getVertexById(nodeId).getId()); - svgAttributesNode.dx = (-1) * (this.getDataset().getVertexById(nodeId).getName().length * svgAttributesNode["font-size"]) / 4 - 4; - - svgAttributesNode.dy = parseFloat((this.getFormatter().getVertexById(nodeId).getDefault().getSize())) - + parseFloat(svgAttributesNode["font-size"]) + parseFloat(this.getFormatter().getVertexById(nodeId).getDefault().getStrokeWidth()) - 4; - svgAttributesNode.dragx = Math.ceil(this.getLayout().getNodeById(nodeId).x * this.getFormatter().getWidth()); - - var gragy = parseFloat(this.getFormatter().getVertexById(nodeId).getDefault().getSize()) - + Math.ceil(this.getLayout().getNodeById(nodeId).y * this.getFormatter().getHeight()); - svgAttributesNode.dragy = gragy; - svgAttributesNode.transform = "translate(" + svgAttributesNode.dragx + "," + svgAttributesNode.dragy + ")";//, scale("+this.formatter.getVertexById(nodeId).getDefault().getSize()+")"; - - var nodeSVG = SVG.drawText(0, 0, this.getDataset().getVertexById(nodeId).getName(), this.GraphLabelGroup, svgAttributesNode); - - this.svgLabels[nodeId] = nodeSVG; - - /** Events for the SVG node **/ - var _this = this; - if (nodeSVG != null) { - nodeSVG.addEventListener("mouseover", function() { - _this.overLabel(nodeId); - }, false); - nodeSVG.addEventListener("mouseout", function() { - _this.outLabel(nodeId); - }, false); - } - -}; - -GraphCanvas.prototype.removeLabel = function(labelId) { - if (DOM.select(this.getSVGLabelId(labelId)) != null) { - DOM.select(this.getSVGLabelId(labelId)).parentNode.removeChild(DOM.select(this.getSVGLabelId(labelId))); - } -}; - -GraphCanvas.prototype.renderNode = function(nodeId) { - var svgAttributesNode = JSON.parse(JSON.stringify(this.getFormatter().getVertexById(nodeId).getDefault().toJSON())); - svgAttributesNode.dragx = Math.ceil(this.getLayout().getNodeById(nodeId).x * this.getFormatter().getWidth()); - svgAttributesNode.dragy = Math.ceil(this.getLayout().getNodeById(nodeId).y * this.getFormatter().getHeight()); - svgAttributesNode.transform = "translate(" + svgAttributesNode.dragx + "," + svgAttributesNode.dragy + ")"; - svgAttributesNode.id = this.getSVGNodeId(nodeId); - /*svgAttributesNode["stroke-width"] = 3 ; - svgAttributesNode["stroke-opacity"] = 1 ; - svgAttributesNode["fill-opacity"] = svgAttributesNode["opacity"] ; - svgAttributesNode["opacity"] = 1 ;*/ - this.circleDefaultRadius = this.getFormatter().getVertexById(nodeId).getDefault().getSize(); - var nodeSVG; - - if (this.getFormatter().getVertexById(nodeId) instanceof CircleVertexGraphFormatter) { - nodeSVG = SVG.drawCircle(0, 0, this.circleDefaultRadius, this.GraphNodeGroup, svgAttributesNode); - } - - if (this.getFormatter().getVertexById(nodeId) instanceof SquareVertexGraphFormatter) { - //nodeSVG = SVG.drawRectangle(0 - (this.circleDefaultRadius) ,0 - (this.formatter.getVertexById(nodeId).getDefault().getSize()) , (this.getFormatter().getVertexById(nodeId).getDefault().getSize()*2), (this.getFormatter().getVertexById(nodeId).getDefault().getSize()*2), this.GraphNodeGroup, svgAttributesNode); - nodeSVG = SVG.drawRectangle(0 - (this.circleDefaultRadius), 0 - (this.circleDefaultRadius), (this.circleDefaultRadius * 2), - (this.circleDefaultRadius * 2), this.GraphNodeGroup, svgAttributesNode); - } - - if (this.getFormatter().getVertexById(nodeId) instanceof EllipseVertexGraphFormatter) { - nodeSVG = SVG.drawEllipse(0, 0, this.circleDefaultRadius * 1.5, this.circleDefaultRadius, this.GraphNodeGroup, svgAttributesNode); - } - - if (this.getFormatter().getVertexById(nodeId) instanceof RectangleVertexGraphFormatter) { - //nodeSVG = SVG.drawRectangle(0 - (this.circleDefaultRadius) ,0 - ((this.circleDefaultRadius*2)/2) , (this.circleDefaultRadius*2), (this.circleDefaultRadius), this.GraphNodeGroup, svgAttributesNode); - nodeSVG = SVG.drawRectangle(0 - (this.circleDefaultRadius * 1.5), 0 - (this.circleDefaultRadius), (this.circleDefaultRadius * 2 * 1.5), - (this.circleDefaultRadius * 2), this.GraphNodeGroup, svgAttributesNode); - - } - - if (this.getFormatter().getVertexById(nodeId) instanceof RoundedVertexGraphFormatter) { - svgAttributesNode.ry = 2;// this.formatter.getVertexById(nodeId).getDefault().getSize()/4; - svgAttributesNode.rx = 2;// this.formatter.getVertexById(nodeId).getDefault().getSize()/4; - nodeSVG = SVG.drawRectangle(0 - (this.circleDefaultRadius * 1.5), 0 - (this.circleDefaultRadius), (this.circleDefaultRadius * 2 * 1.5), - (this.circleDefaultRadius * 2), this.GraphNodeGroup, svgAttributesNode); - } - - // - - if (this.getFormatter().getVertexById(nodeId) instanceof OctagonVertexGraphFormatter) { - svgAttributesNode.ry = 2; - svgAttributesNode.rx = 2; - nodeSVG = SVG.drawRectangle(0 - (this.circleDefaultRadius * 1.5), 0 - (this.circleDefaultRadius), (this.circleDefaultRadius * 2 * 1.5), - (this.circleDefaultRadius * 2), this.GraphNodeGroup, svgAttributesNode); - } - - nodeSVG.internalId = nodeId; - // - var _this = this; - - /** Events for the SVG node **/ - if (nodeSVG != null) { - nodeSVG.addEventListener("mouseover", function() { - _this.onVertexOver.notify(nodeId); - _this.overNode(nodeId); - }, false); - nodeSVG.addEventListener("mouseout", function() { - _this.onVertexOut.notify(nodeId); - _this.outNode(nodeId); - }, false); - //nodeSVG.addEventListener("click", function(){_this.clickNode(nodeId);}, false); - // - nodeSVG.addEventListener("mouseup", function() { - _this.onVertexUp.notify(nodeId); - }, false); - } -}; - -GraphCanvas.prototype.removeNode = function(nodeId) { - DOM.select(this.getSVGNodeId(nodeId)).parentNode.removeChild(DOM.select(this.getSVGNodeId(nodeId))); - if (this.args.labeled) { - this.removeLabel(nodeId); - } -}; - -/** REMOVING **/ -GraphCanvas.prototype.removeSelected = function() { - /** El orden importa **/ - this.removeSelectedEdges(); - this.removeSelectedNode(); - -}; - -GraphCanvas.prototype.removeSelectedNode = function() { - var selected = JSON.parse(JSON.stringify(this.getSelectedVertices())); - this.deselectNodes(); - var sorted = selected.sort(function(a, b) { - return a - b - }); - for ( var i = 0; i < sorted.length; i++) { - if (this.getDataset().getVertexById(sorted[i]) != null) { - this.getDataset().getVertexById(sorted[i]).remove(); - } - } -}; - -/** EDGES **/ -GraphCanvas.prototype.removeEdge = function(edgeId) { - if (DOM.select(this.getSVGEdgeId(edgeId)) != null) { - DOM.select(this.getSVGEdgeId(edgeId)).parentNode.removeChild(DOM.select(this.getSVGEdgeId(edgeId))); - } - - if (DOM.select(this.getSVGEdgeId(edgeId) + "_shadow") != null) { - DOM.select(this.getSVGEdgeId(edgeId) + "_shadow").parentNode.removeChild(DOM.select(this.getSVGEdgeId(edgeId) + "_shadow")); - } - - if (DOM.select(this.getSVGArrowEdgeId(edgeId)) != null) { - DOM.select(this.getSVGArrowEdgeId(edgeId)).parentNode.removeChild(DOM.select(this.getSVGArrowEdgeId(edgeId))); - } -}; - -GraphCanvas.prototype.overEdge = function(edgeId) { - if ((!this.args.interactive) || this.dragging || this.selecting) { - return; - } - - /** If selected we don't change the format **/ - if (this.args.isEdgeSelected[edgeId] == null) { - var format = this.getFormatter().getEdgeById(edgeId).getOver(); - format.args["cursor"] = "pointer"; - this.changeEdgeFormat(edgeId, format); - } -}; - -GraphCanvas.prototype.outEdge = function(edgeId) { - if (!this.args.interactive) { - return; - } - - /** If selected we don't change the format **/ - if (this.args.isEdgeSelected[edgeId] == null) { - this.changeEdgeFormat(edgeId, this.getFormatter().getEdgeById(edgeId).getDefault()); - } -}; - -GraphCanvas.prototype.changeEdgeFormat = function(edgeId, format) { - var svgEdge = DOM.select(this.getSVGEdgeId(edgeId) + "_shadow"); - if (svgEdge != null) { - var properties = format.toJSON(); - for ( var item in properties) { - svgEdge.setAttribute(item, properties[item]); - } - } -}; - -GraphCanvas.prototype.deselectEdge = function(edgeID) { - if (this.args.isEdgeSelected[edgeID] != null) { - this.changeEdgeFormat(edgeID, this.getFormatter().getEdgeById(edgeID).getDefault()); - var index = this.args.isEdgeSelected[edgeID]; - delete this.args.isEdgeSelected[edgeID]; - } -}; - -GraphCanvas.prototype.deselectEdges = function() { - var selected = JSON.parse(JSON.stringify(this.getSelectedEdges())); - for ( var i = 0; i < selected.length; i++) { - this.deselectEdge(selected[i]); - } -}; - -GraphCanvas.prototype.removeSelectedEdges = function() { - var selected = JSON.parse(JSON.stringify(this.getSelectedEdges())); - this.deselectEdges(); - for ( var i = 0; i < selected.length; i++) { - if (this.getDataset().getEdgeById(selected[i]) != null) { - this.getDataset().getEdgeById(selected[i]).remove(); - } - } -}; - -GraphCanvas.prototype.renderEdge = function(edgeId) { - var svgAttributesEdge = this.getFormatter().getEdgeById(edgeId).getDefault().toJSON(); - var edge = this.getDataset().getEdgeById(edgeId); - - var svgNodeTarget = this.getVertexById(edge.getNodeTarget().getId()); - var svgNodeSource = this.getVertexById(edge.getNodeSource().getId()); - svgAttributesEdge.id = this.getSVGEdgeId(edge.getId()) + "_shadow"; - - var svgEdge = null; - - if (this.getFormatter().getEdgeById(edgeId) instanceof LineEdgeGraphFormatter) { - var coordenateSourceX = svgNodeSource.getAttribute("dragx"); - var coordenateSourceY = svgNodeSource.getAttribute("dragy"); - var coordenateTargetX = svgNodeTarget.getAttribute("dragx"); - var coordenateTargetY = svgNodeTarget.getAttribute("dragy"); - - SVG.drawLine(coordenateSourceX, coordenateSourceY, coordenateTargetX, coordenateTargetY, this.GraphEdgeGroup, svgAttributesEdge); - var attributesShadow = {}; - attributesShadow.id = this.getSVGEdgeId(edge.getId()); - attributesShadow["stroke-opacity"] = 0; - attributesShadow["stroke-width"] = 4; - attributesShadow["stroke"] = "black"; - svgEdge = SVG.drawLine(svgNodeSource.getAttribute("dragx"), svgNodeSource.getAttribute("dragy"), svgNodeTarget.getAttribute("dragx"), - svgNodeTarget.getAttribute("dragy"), this.GraphEdgeGroup, attributesShadow); - } - - if (this.getFormatter().getEdgeById(edgeId) instanceof BezierEdgeGraphFormatter) { - var nodeId = edge.getNodeTarget().getId(); - var nodeSize = this.formatter.getVertexById(nodeId).getDefault().getSize() * this.getFormatter().getNodesMaxSize(); - svgAttributesEdge.fill = "none"; - svgAttributesEdge.id = this.getSVGEdgeId(edgeId); - var d = this.calculateCoordenatesBezier(nodeSize, svgNodeSource.getAttribute("dragx"), svgNodeSource.getAttribute("dragy")); - svgEdge = SVG.drawPath(d, this.GraphEdgeGroup, svgAttributesEdge); - } - ; - - if ((this.getFormatter().getEdgeById(edgeId) instanceof DirectedLineEdgeGraphFormatter) - || (this.getFormatter().getEdgeById(edgeId) instanceof CutDirectedLineEdgeGraphFormatter) - || (this.getFormatter().getEdgeById(edgeId) instanceof DotDirectedLineEdgeGraphFormatter) - || (this.getFormatter().getEdgeById(edgeId) instanceof OdotDirectedLineEdgeGraphFormatter) - || (this.getFormatter().getEdgeById(edgeId) instanceof OdirectedLineEdgeGraphFormatter)) { - var coordenateSourceX = svgNodeSource.getAttribute("dragx"); - var coordenateSourceY = svgNodeSource.getAttribute("dragy"); - var coordenateTargetX = svgNodeTarget.getAttribute("dragx"); - var coordenateTargetY = svgNodeTarget.getAttribute("dragy"); - - var offset = parseFloat(this.getFormatter().getVertexById(this.getDataset().getEdgeById(edgeId).getNodeTarget().getId()).getDefault() - .getSize() - * this.circleDefaultRadius); - var point = this._calculateEdgePointerPosition(coordenateSourceX, coordenateSourceY, coordenateTargetX, coordenateTargetY, offset); - coordenateTargetX = point.x; - coordenateTargetY = point.y; - SVG.drawLine(coordenateSourceX, coordenateSourceY, coordenateTargetX, coordenateTargetY, this.GraphEdgeGroup, svgAttributesEdge); - - var attributesShadow = {}; - attributesShadow.id = this.getSVGEdgeId(edge.getId()); - attributesShadow["stroke-opacity"] = 0; - attributesShadow["stroke-width"] = 4; - attributesShadow["stroke"] = "black"; - svgEdge = SVG.drawLine(coordenateSourceX, coordenateSourceY, coordenateTargetX, coordenateTargetY, this.GraphEdgeGroup, attributesShadow); - } - - if (this.getFormatter().getEdgeById(edgeId) instanceof DirectedLineEdgeGraphFormatter - || (this.getFormatter().getEdgeById(edgeId) instanceof OdirectedLineEdgeGraphFormatter)) { - var coordenateSourceX = svgNodeSource.getAttribute("dragx"); - var coordenateSourceY = svgNodeSource.getAttribute("dragy"); - var coordenateTargetX = svgNodeTarget.getAttribute("dragx"); - var coordenateTargetY = svgNodeTarget.getAttribute("dragy"); - - var point = this._calculateEdgePointerPosition(coordenateSourceX, coordenateSourceY, coordenateTargetX, coordenateTargetY, offset); - coordenateTargetX = point.x; - coordenateTargetY = point.y; - - var angle = Geometry.toDegree(point.angle) + 90; - this.arrowDefaultSize = this.getFormatter().getEdgeById(edgeId).getArrowSize(); //getDefault().getArrowSize(); - var d = "-" + this.arrowDefaultSize + ",0 0,-" + parseFloat(this.arrowDefaultSize) * 2 + " " + this.arrowDefaultSize + ",0"; - - var attributes; - - if (this.getFormatter().getEdgeById(edgeId) instanceof DirectedLineEdgeGraphFormatter) { - attributes = [ - [ "fill", this.getFormatter().getEdgeById(edgeId).getDefault().getStroke() ], - [ "stroke", this.getFormatter().getEdgeById(edgeId).getDefault().getStroke() ], [ "id", this.getSVGArrowEdgeId(edgeId) ] ]; - } else { - attributes = [ - [ "fill", "#FFFFFF" ], [ "stroke", this.getFormatter().getEdgeById(edgeId).getDefault().getStroke() ], - [ "id", this.getSVGArrowEdgeId(edgeId) ] ]; - } - - var flechaSVGNode = SVG.drawPoligon(d, this.GraphEdgeGroup, attributes);//, ["transform", "rotate("+angle+"), translate(0,0)"]]); - flechaSVGNode.setAttribute("transform", " translate(" + coordenateTargetX + ", " + coordenateTargetY + "), rotate(" + angle + ")"); - } - ; - - if (this.getFormatter().getEdgeById(edgeId) instanceof CutDirectedLineEdgeGraphFormatter) { - var coordenateSourceX = svgNodeSource.getAttribute("dragx"); - var coordenateSourceY = svgNodeSource.getAttribute("dragy"); - var coordenateTargetX = svgNodeTarget.getAttribute("dragx"); - var coordenateTargetY = svgNodeTarget.getAttribute("dragy"); - - var point = this._calculateEdgePointerPosition(coordenateSourceX, coordenateSourceY, coordenateTargetX, coordenateTargetY, offset); - coordenateTargetX = point.x; - coordenateTargetY = point.y; - - var angle = Geometry.toDegree(point.angle) + 90; - - //this.arrowDefaultSize = 2; //getDefault().getArrowSize(); - var d = "-4,0 4,0 4,-2 -4,-2"; - - var flechaSVGNode = SVG.drawPoligon(d, this.GraphEdgeGroup, [ - [ "fill", this.getFormatter().getEdgeById(edgeId).getDefault().getStroke() ], - [ "stroke", this.getFormatter().getEdgeById(edgeId).getDefault().getStroke() ], [ "id", this.getSVGArrowEdgeId(edgeId) ] ]);//, ["transform", "rotate("+angle+"), translate(0,0)"]]); - flechaSVGNode.setAttribute("transform", " translate(" + coordenateTargetX + ", " + coordenateTargetY + "), rotate(" + angle + ")"); - } - ; - - if ((this.getFormatter().getEdgeById(edgeId) instanceof DotDirectedLineEdgeGraphFormatter) - || (this.getFormatter().getEdgeById(edgeId) instanceof OdotDirectedLineEdgeGraphFormatter)) { - var coordenateSourceX = svgNodeSource.getAttribute("dragx"); - var coordenateSourceY = svgNodeSource.getAttribute("dragy"); - var coordenateTargetX = svgNodeTarget.getAttribute("dragx"); - var coordenateTargetY = svgNodeTarget.getAttribute("dragy"); - var point = this._calculateEdgePointerPosition(coordenateSourceX, coordenateSourceY, coordenateTargetX, coordenateTargetY, offset); - coordenateTargetX = point.x; - coordenateTargetY = point.y; - var angle = Geometry.toDegree(point.angle) + 90; - // this.arrowDefaultSize = this.formatter.getEdgeById(edgeId).getArrowSize(); //getDefault().getArrowSize(); - var attributes = []; - if (this.getFormatter().getEdgeById(edgeId) instanceof OdotDirectedLineEdgeGraphFormatter) { - attributes = [ - [ "fill", "#FFFFFF" ], [ "stroke", this.getFormatter().getEdgeById(edgeId).getDefault().getStroke() ], - [ "id", this.getSVGArrowEdgeId(edgeId) ] ]; - } else { - attributes = [ - [ "fill", this.getFormatter().getEdgeById(edgeId).getDefault().getStroke() ], - [ "stroke", this.getFormatter().getEdgeById(edgeId).getDefault().getStroke() ], [ "id", this.getSVGArrowEdgeId(edgeId) ] ]; - } - var flechaSVGNode = SVG.drawCircle(0, 0, 4, this.GraphEdgeGroup, attributes); - flechaSVGNode.setAttribute("transform", " translate(" + coordenateTargetX + ", " + coordenateTargetY + "), rotate(" + angle + ")"); - } - ; - - var _this = this; - /** Events for the SVG edge **/ - if (svgEdge != null) { - if (this.getDataset().getEdgesCount() < this.args.maxNumberEdgesFiringEvents) { - svgEdge.addEventListener("mouseover", function() { - _this.overEdge(edgeId); - }, false); - svgEdge.addEventListener("mouseout", function() { - _this.outEdge(edgeId); - }, false); - } - } -}; - -GraphCanvas.prototype._calculateEdgePointerPosition = function(sourceX, sourceY, targetX, targetY, radius) { - var angle = Geometry.getAngleBetweenTwoPoints(sourceX, sourceY, targetX, targetY); - - /** Suponiendo el node source que este a la derecha **/ - if ((targetX - sourceX) < 0) { - var b = Geometry.getAdjacentSideOfRectangleRight(angle, radius); - targetX = parseFloat(targetX) + parseFloat(b); - arrowX = parseFloat(targetX) + parseFloat(b) + this.arrowDefaultSize / 2; - } else { - var b = Geometry.getAdjacentSideOfRectangleRight(angle, radius); - targetX = parseFloat(targetX) - parseFloat(b); - arrowX = parseFloat(targetX) - parseFloat(b) - this.arrowDefaultSize / 2; - } - - /** Suponiendo el node source que este a la arriba **/ - if ((targetY - sourceY) > 0) { - var a = Geometry.getOppositeSideOfRectangleRight(angle, radius); - targetY = parseFloat(targetY) - parseFloat(a); - arrowY = parseFloat(targetY) - parseFloat(a) - this.arrowDefaultSize / 2; - } else { - var a = Geometry.getOppositeSideOfRectangleRight(angle, radius); - targetY = parseFloat(targetY) + parseFloat(a); - arrowY = parseFloat(targetY) + parseFloat(a) - this.arrowDefaultSize / 2; - - } - - return { - "x" : arrowX, - "y" : arrowY, - "angle" : angle - }; -}; - -GraphCanvas.prototype.calculateCoordenatesBezier = function(nodeSize, x1, y1) { - var x11 = x1 - (nodeSize / 2); - var y11 = y1 - (nodeSize / 2); - - var x12 = parseFloat(x1) + parseFloat(nodeSize / 2); - var y12 = y1 - (nodeSize / 2); - - var curvePointX = (x12 - x11) / 2 + x11; - var curvePointY = y1 - (nodeSize * 2); - var d = "M" + x11 + "," + y11 + " T" + curvePointX + "," + curvePointY + " " + x12 + "," + y12; - return d; - -}; - -GraphCanvas.prototype.renderEdges = function() { - for ( var edge in this.getDataset().getEdges()) { - this.renderEdge(this.getDataset().getEdgeById(edge).getId()); - - } -}; - -GraphCanvas.prototype.getLastSelectedNode = function() { - var node = null; - if (this.getSelectedVertices().length > 0) { - var nodeId = this.getSelectedVertices()[this.getSelectedVertices().length - 1]; - node = this.getDataset().getVertexById(nodeId); - } - return node; -}; -/* - GraphCanvas.prototype.getNodeByNameAndIndex = function(node, index){ - var nodeId = this.getDataset().verticesIndex[node][index]; - var nodeItem = this.getDataset().getVertexById(nodeId); - return nodeItem; - }; - */ - -GraphCanvas.prototype.setDataset = function(dataset) { - this.dataset = dataset; -}; - -GraphCanvas.prototype.setFormatter = function(formatter) { - this.formatter = formatter; -}; - -GraphCanvas.prototype.setLayout = function(layout) { - this.layout = layout; -}; - -/** API **/ -GraphCanvas.prototype.getDataset = function() { - return this.dataset; -}; - -GraphCanvas.prototype.getFormatter = function() { - return this.formatter; -}; - -GraphCanvas.prototype.getLayout = function() { - return this.layout; -}; - -/** API DATASET **/ -GraphCanvas.prototype.addVertex = function(name, args) { - this.getDataset().addNode(name, args); -}; - -GraphCanvas.prototype.removeVertex = function(vertexId) { - this.getDataset().getVertexById(vertexId).remove(); -}; - -GraphCanvas.prototype.addEdge = function(edgeName, nodeSourceId, nodeTargetId, args) { - this.getDataset().addEdge(edgeName, nodeSourceId, nodeTargetId, args); -}; -/* - GraphCanvas.prototype.removeEdge = function(edgeId){ - this.getDataset().getEdgeById(edgeId).remove(); - }; - */ - -/** API FORMATTER **/ -GraphCanvas.prototype.getWidth = function() { - return this.getFormatter().getWidth(); -}; - -GraphCanvas.prototype.getHeight = function() { - return this.getFormatter().getHeight(); -}; - -GraphCanvas.prototype.getBackgroundImage = function() { - return this.getFormatter().getBackgroundImage(); -}; - -//GraphCanvas.prototype.setBackgroundImage = function(value){ -// this.getFormatter().setBackgroundImage(value); -//}; - -GraphCanvas.prototype.getBackgroundColor = function() { - return this.getFormatter().getBackgroundColor(); -}; - -GraphCanvas.prototype.setBackgroundColor = function() { - this.getFormatter().setBackgroundColor(value); -}; - -//GraphCanvas.prototype.setEdgeFill = function(edgeId, value){ -// this.getFormatter().getEdgeById(edgeId).getDefault().setFill(value); -//}; -// -//GraphCanvas.prototype.getEdgeFill = function(edgeId){ -// return this.getFormatter().getEdgeById(edgeId).getDefault().getFill(); -//}; - -/** VERTICES FORMATTER **/ -GraphCanvas.prototype.setVertexSize = function(vertexId, value) { - this.getFormatter().getVertexById(vertexId).getDefault().setSize(value); -}; - -GraphCanvas.prototype.getVertexSize = function(vertexId) { - return this.getFormatter().getVertexById(vertexId).getDefault().getSize(); -}; - -GraphCanvas.prototype.setVertexStroke = function(vertexId, value) { - this.getFormatter().getVertexById(vertexId).getDefault().setStroke(value); -}; - -GraphCanvas.prototype.getVertexStroke = function(vertexId) { - return this.getFormatter().getVertexById(vertexId).getDefault().getStroke(); -}; - -GraphCanvas.prototype.setVertexStrokeOpacity = function(vertexId, value) { - this.getFormatter().getVertexById(vertexId).getDefault().setStrokeOpacity(value); -}; - -GraphCanvas.prototype.getVertexStrokeOpacity = function(vertexId) { - return this.getFormatter().getVertexById(vertexId).getDefault().getStrokeOpacity(); -}; - -GraphCanvas.prototype.setVertexOpacity = function(vertexId, value) { - this.getFormatter().getVertexById(vertexId).getDefault().setOpacity(value); -}; - -GraphCanvas.prototype.getVertexOpacity = function(vertexId) { - return this.getFormatter().getVertexById(vertexId).getDefault().getOpacity(); -}; - -GraphCanvas.prototype.setVertexFill = function(vertexId, color) { - this.getFormatter().getVertexById(vertexId).getDefault().setFill(color); -}; - -GraphCanvas.prototype.getVertexFill = function(vertexId) { - return this.getFormatter().getVertexById(vertexId).getDefault().getFill(); -}; - -/** EDGES FORMATTER **/ -GraphCanvas.prototype.setEdgeSize = function(edgeId, value) { - this.getFormatter().getEdgeById(edgeId).getDefault().setSize(value); -}; - -GraphCanvas.prototype.getEdgeSize = function(edgeId) { - return this.getFormatter().getEdgeById(edgeId).getDefault().getSize(); -}; - -GraphCanvas.prototype.setEdgeStroke = function(edgeId, value) { - this.getFormatter().getEdgeById(edgeId).getDefault().setStroke(value); -}; - -GraphCanvas.prototype.getEdgeStroke = function(edgeId) { - return this.getFormatter().getEdgeById(edgeId).getDefault().getStroke(); -}; - -GraphCanvas.prototype.setEdgeStrokeOpacity = function(edgeId, value) { - this.getFormatter().getEdgeById(edgeId).getDefault().setStrokeOpacity(value); -}; - -GraphCanvas.prototype.getEdgeStrokeOpacity = function(edgeId) { - return this.getFormatter().getEdgeById(edgeId).getDefault().getStrokeOpacity(); -}; - -GraphCanvas.prototype.setEdgeFill = function(edgeId, color) { - this.getFormatter().getEdgeById(edgeId).getDefault().setFill(color); -}; - -GraphCanvas.prototype.getEdgeFill = function(edgeId) { - return this.getFormatter().getEdgeById(edgeId).getDefault().getFill(); -}; - -/** API LAYOUT **/ -GraphCanvas.prototype.setCoordinates = function(vertexId, x, y) { - return this.getLayout().getEdgeById(vertexId).setCoordinates(x, y); -}; - - - -function HPLCGraph(args) { - this.width = 600; - this.height = 600; - this.title = ''; - this.bbar = false; - this.plotInnerPanelPadding = 10; - this.plotPanelPadding = 5; - this.id = BUI.id(); - - this.hidePlots = null; - this.xlabel = ""; - this.scaled = false; - this.xParam = null; - this.showRangeSelector = true; - this.interactionModel = null; - - /** for each stat the max and minimum value when it is scaled in order to show correctly in the legend **/ - this.ranges = {}; - if (args != null) { - if (args.interactionModel != null) { - this.interactionModel = args.interactionModel; - } - if (args.width != null) { - this.width = args.width; - } - if (args.height != null) { - this.height = args.height; - } - if (args.bbar != null) { - this.bbar = args.bbar; - } - if (args.title != null) { - this.title = args.title; - } - if (args.plots != null) { - this.plots = args.plots; - } - - if (args.scaled != null) { - this.scaled = args.scaled; - } - if (args.xlabel != null) { - this.xlabel = args.xlabel; - } - if (args.xParam != null) { - this.xParam = args.xParam; - } - if (args.showRangeSelector != null) { - this.showRangeSelector = args.showRangeSelector; - } - } - - this.onZoomX = new Event(this); - this.onResetZoom = new Event(this); - this.dblclick = new Event(this); -} - -HPLCGraph.prototype.getMenu = function () { - var _this = this; - /** Actions buttons **/ - var actions = []; - - function toggle(item, pressed) { - if (pressed) { - _this.plots[item.param] = true; - } else { - delete _this.plots[item.param]; - } - _this.reloadData(this.hplcData); - } - - for (var i = 0; i < this.hplcData.length; i++) { - if (this.hplcData[i].showOnMenu != false) { - var param = this.hplcData[i].param; - var style = "style='padding:0 0px 0 5px;'"; - actions.push({ - text : "
" + BUI.getRectangleColorDIV(this.hplcData[i].color, 10, 10) + " " + this.hplcData[i].label + "
", - id : _this.id + param, - param : param, - enableToggle : true, - scope : this, - toggleHandler : toggle, - pressed : (_this.plots[param] != null) - }); - } - } - actions.push("-"); - - actions.push({ - text : "Scale", - enableToggle : true, - scope : this, - pressed : this.scaled, - icon : '../images/icon_graph.png', - toggleHandler : function (item, pressed) { - _this.scaled = pressed; - _this.reloadData(this.hplcData); - } - }); - - actions.push("->"); - actions.push({ - text : "Save", - scope : this, - icon : '../images/save.gif', - handler : function (item, pressed) { - var largeImage = document.createElement("img"); - largeImage.style.display = 'block'; - largeImage.style.width = 200 + "px"; - largeImage.style.height = 200 + "px"; - largeImage.setAttribute('src', Dygraph.Export.asCanvas(this.dygraphObject.dygraph).toDataURL()); - window.open(Dygraph.Export.asCanvas(this.dygraphObject.dygraph).toDataURL(), 'Image', ''); - } - }); - - return actions; -}; - -/** Looks for the maximum value and then divide everything but that value **/ -HPLCGraph.prototype.scaledData = function (data) { - for (var i = 0; i < data.length; i++) { - var values = this.getMaxAndMinValue(data[i]); - data[i] = this.divideValuesByMax(data[i], values.max); - this.ranges[data[i].label] = values; - } - return data; -}; - - -/** Given a stat float[] and a max number it will divide each value by max **/ -HPLCGraph.prototype.divideValuesByMax = function (stat, max) { - for (var j = 0; j < stat.data.length; j++) { - if (max != 0) { - stat.data[j] = Number(stat.data[j]) / max; - stat.std[j] = Number(stat.std[j]) / max; - } - } - return stat; -}; - -/** returns max value of a stat **/ -HPLCGraph.prototype.getMaxAndMinValue = function (stat) { - var max = 0; - var min = stat.data[0]; - for (var j = 0; j < stat.data.length; j++) { - if (Number(stat.data[j]) > max) { - max = Number(stat.data[j]); - } - if (Number(stat.std[j]) > max) { - max = Number(stat.std[j]); - } - if (Number(stat.data[j]) < min) { - min = Number(stat.data[j]); - } - } - return { - max : Number(max), - min : Number(min) - }; -}; - -HPLCGraph.prototype.getPoint = function (data, i) { - var point = [ 10, 10, 10 ]; - var y = parseFloat(data.data[i]); - var error = parseFloat(data.std[i]); - if (data.fdata == null) { - return [ y - error, y, y + error ]; - } else { - if (data.fstd != null) { - return [ data.fstd(y - error), data.fdata(y), data.fstd(y + error) ]; - } - return [ data.fdata(y) - error, data.fdata(y), data.fdata(y) + error ]; - } - return point; -}; - -HPLCGraph.prototype.reloadData = function(hplcData) { - this.panel.setLoading(false); - this.hplcData = hplcData; - - var data = hplcData; - - - /** In case of having peaks **/ - if (this.peaks != null) { - for (var peak in this.peaks) { - var values = []; - var std = []; - for (var i = 0; i < this.peaks[peak].length; i++) { - values.push(this.peaks[peak][i][1]); - std.push(this.peaks[peak][i][2]); - } - data.push({ - param : peak, - data : values, - showOnMenu : false, - fdata : function (a) { - var value = (Math.log(parseFloat(a))); - if (isNumber(value)) { - return value; - } - }, - fstd : function (a) { - var value = Math.log(Math.abs(parseFloat(a))); - if (isNumber(value)) - return value; - }, - std : std, - color : this.colorPeak[peak], - label : peak - }); - - } - } - - - if (this.scaled) { - data = this.scaledData(JSON.parse(JSON.stringify(hplcData))); - } - - var paramIndex = {}; - var parsed = []; - var j = 0; - for (var i = 0; i < data[0].data.length - 1; i++) { - var aux = []; - for (j = 0; j < data.length; j++) { - if (this.plots != null) { - if (this.plots[data[j].param] != null) { - aux.push(this.getPoint(data[j], i)); - paramIndex[data[j].param] = aux.length - 1; - } - } else { - aux.push([ data[j].data[i] - data[j].std[i], data[j].data[i], data[j].data[i] + data[j].std[i] ]); - } - } - parsed.push([]); - - var index = i; - if (this.xParam != null) { - index = parseFloat(data[this.xParam].data[i]); - } - - parsed[parsed.length - 1].push(index); - - for (j = 0; j < data.length; j++) { - if (this.plots != null) { - if (this.plots[data[j].param] != null) { - parsed[parsed.length - 1].push(aux[paramIndex[data[j].param]]); - } - } else { - parsed[parsed.length - 1].push(aux[j]); - } - } - } - - var colors = []; - var labels = [ "" ]; - for (j = 0; j < data.length; j++) { - if (this.plots != null) { - if (this.plots[data[j].param] != null) { - colors.push(data[j].color); - labels.push(data[j].label); - } - } else { - parsed[parsed.length - 1].push(aux[j]); - } - } - - this._renderDygraph(parsed, colors, labels); -}; - -HPLCGraph.prototype._renderDygraph = function (parsed, colors, labels) { - this.dygraphObject = new StdDevDyGraph(this.id, { - width : this.width, - height : this.height - 10, - xlabel : this.xlabel, - showRangeSelector : this.showRangeSelector, - interactionModel : this.interactionModel, - scaled : this.scaled, - ranges : this.ranges - }); - this.dygraphObject.draw(parsed, colors, labels); - - var _this = this; - this.dygraphObject.onZoomX.attach(function (sender, args) { - try { - _this.onZoomX.notify(args); - } catch (e) { - } - }); - - this.dygraphObject.onResetZoom.attach(function (sender, args) { - try { - _this.onResetZoom.notify(args); - } catch (e) { - } - }); - - this.dygraphObject.dblclick.attach(function (sender, args) { - try { - _this.dblclick.notify(args); - } catch (e) { - } - }); - -}; - -HPLCGraph.prototype.loadData = function (data) { - var _this = this; - this.reloadData(data); - this.panel.addDocked({ - xtype : 'toolbar', - items : this.getMenu() - }); - - - if (this.bbar == true){ - this.panel.addDocked({ - xtype : 'toolbar', - dock: 'bottom', - items : [ - { - xtype: 'numberfield', - id: 'main_field_start', - fieldLabel: 'Range from', - width: 170, - labelWidth : 70, - value: 0, - minValue: 0 - }, - { - xtype: 'numberfield', - id: 'main_field_end', - fieldLabel: 'to', - width: 130, - labelWidth : 30, - value: 0, - minValue: 0 - }, - { - xtype: 'button', - text: 'Go', - handler: function () { - var start = parseFloat(Ext.getCmp("main_field_start").getValue()); - var end = parseFloat(Ext.getCmp("main_field_end").getValue()); - - if (start < 0) { - start = 0; - } - if (end < 0) { - end = 0; - } - if (start > end) { - var aux = end; - end = start; - start = aux; - } - - _this.dygraphObject.dygraph.updateOptions({ isZoomedIgnoreProgrammaticZoom: true, dateWindow: [start, end] }); - } - } - ] - }); - } -}; - -HPLCGraph.prototype.getPanel = function () { - this.panel = Ext.create('Ext.panel.Panel', { - padding : this.plotPanelPadding, - width : this.width + 4 * this.plotInnerPanelPadding, - height : this.height + 4 * this.plotInnerPanelPadding, - items : [ { - html : "", - id : this.id, - width : this.width, - height : this.height - } ] - }); - - return this.panel; -}; - -HPLCGraph.prototype.input = function () { - return DATADOC.getHPLCData(); -}; - -HPLCGraph.prototype.getDataByFrameNumber = function (frameNumber) { - var data = {}; - data.frameNumber = frameNumber; - for (var key in this.hplcData){ - data[this.hplcData[key].label] = this.hplcData[key].data[frameNumber]; - } - console.log(data); - return data; -}; - - -HPLCGraph.prototype.test = function (targetId) { - var mainPlotPanel = new HPLCGraph({ - title : 'I0', - width : 800, - height : 400, - plots : { - "I0" : true, - "Rg" : true, - "Mass" : true - }, - xlabel : "HPLC Frames", - scaled : this.scaled, - interactionModel : { - 'dblclick' : function (event, g, context) { - } - } - }); - mainPlotPanel.getPanel().render(targetId); - mainPlotPanel.loadData(mainPlotPanel.input()); - -}; - - -function MergesHPLCGraph(args) { - HPLCGraph.prototype.constructor.call(this, args); - -// this.peakColors = ["#00FB42", "#00BA31", "#007C21", "#003E10"]; - this.peakColors = ["#DEBD00", "#6D9100", "#872900", "#0092CC"]; -} - - -MergesHPLCGraph.prototype.scaledData = HPLCGraph.prototype.scaledData; -MergesHPLCGraph.prototype.divideValuesByMax = HPLCGraph.prototype.divideValuesByMax; -MergesHPLCGraph.prototype.getMaxAndMinValue = HPLCGraph.prototype.getMaxAndMinValue; -MergesHPLCGraph.prototype.getPoint = HPLCGraph.prototype.getPoint; -MergesHPLCGraph.prototype.reloadData = HPLCGraph.prototype.reloadData; -MergesHPLCGraph.prototype._renderDygraph = HPLCGraph.prototype._renderDygraph; -MergesHPLCGraph.prototype.loadData = HPLCGraph.prototype.loadData; -MergesHPLCGraph.prototype.getPanel = HPLCGraph.prototype.getPanel; -MergesHPLCGraph.prototype.getDataByFrameNumber = HPLCGraph.prototype.getDataByFrameNumber; - - -MergesHPLCGraph.prototype.setPeaks = function (data) { - this.peaks = data; - /** get size of peaks **/ - this.peakKeys = []; - this.colorPeak = {}; - var colorCount = 1; - for (var key in this.peaks) { - if (this.peaks.hasOwnProperty(key)) { - var color = this.peakColors[colorCount % this.peakColors.length]; - colorCount = colorCount + 1; - this.peakKeys.push(key); - this.colorPeak[key] = color; - } - } - this.peakKeys.sort(); -}; - - -MergesHPLCGraph.prototype.getMenu = function () { - var _this = this; - /** Actions buttons **/ - var actions = []; - - function toggle(item, pressed) { - if (pressed) { - _this.plots[item.param] = true; - } else { - delete _this.plots[item.param]; - } - _this.reloadData(_this.hplcData); - } - - - /** Toolbar for peaks Average **/ - if (this.peaks != null) { - var items = []; - for (var i = 0; i < this.peakKeys.length; i++) { - var color = this.colorPeak[this.peakKeys[i]]; - items.push({ - text: "Peak #" + i + " " + this.peakKeys[i].replace("- ", " to #").replace(".0", "").replace(".0", "") + "", - peakid : this.peakKeys[i], - checked: false, - checkHandler: function (sender, pressed) { - var item = new Object(); - item.param = sender.peakid; - toggle(item, pressed); - } - }); - } - - var menu = Ext.create('Ext.menu.Menu', { - id: 'mainMenu', - style: { - overflow: 'visible' - }, - items: items - }); - var tb = Ext.create('Ext.toolbar.Toolbar'); - tb.add({ - text: 'Peaks Avg.', - menu: menu - }); - actions.push(tb); - } - - - - for (var i = 0; i < this.hplcData.length; i++) { - if (this.hplcData[i].showOnMenu != false) { - var param = this.hplcData[i].param; - var style = "style='padding:0 0px 0 5px;'"; - actions.push({ - text : "
" + BUI.getRectangleColorDIV(this.hplcData[i].color, 10, 10) + " " + this.hplcData[i].label + "
", - id : _this.id + param, - param : param, - enableToggle : true, - scope : this, - margin : 5, - toggleHandler : toggle, - pressed : (_this.plots[param] != null) - }); - } - } - - actions.push("->"); - actions.push({ - text : "Save", - scope : this, - icon : '../images/save.gif', - handler : function (item, pressed) { - var largeImage = document.createElement("img"); - largeImage.style.display = 'block'; - largeImage.style.width = 200 + "px"; - largeImage.style.height = 200 + "px"; - largeImage.setAttribute('src', Dygraph.Export.asCanvas(this.dygraphObject.dygraph).toDataURL()); - window.open(Dygraph.Export.asCanvas(this.dygraphObject.dygraph).toDataURL(), 'Image', ''); - } - }); - - return actions; -}; - - -MergesHPLCGraph.prototype.input = function () { - return DATADOC.getScatteringHPLCFrameData(); -}; - -MergesHPLCGraph.prototype.test = function (targetId) { - var mainPlotPanel = new MergesHPLCGraph({ - title : 'Scattering', - width : this.plotWidth, - height : 500, - showRangeSelector : false, - xParam : 0, - xlabel : "scattering_I", - plots : { - "scattering_I" : true, - "subtracted_I" : true, - "buffer_I" : true - } - }); - mainPlotPanel.getPanel().render(targetId); - mainPlotPanel.loadData(mainPlotPanel.input()); -}; - - -function NetworkWidget(args) { - this.id = "NetworkViewer_" + Math.random().toString().replace(".", "_"); - - this.label = true; - if (args != null) { - if (args.targetId != null) { - this.targetId = args.targetId; - } - if (args.label != null) { - this.label = args.label; - } - } - - this.onVertexOver = new Event(this); - this.onVertexOut = new Event(this); -} - -NetworkWidget.prototype.draw = function(dataset, formatter, layout) { - - this.graphCanvas = new GraphCanvas(this.id, document.getElementById(this.targetId), { - "labeled" : this.label, - "multipleSelectionEnabled" : false, - "draggingCanvasEnabled" : false - }); - this.graphCanvas.draw(dataset, formatter, layout); - - var _this = this; - this.graphCanvas.onVertexOver.attach(function(sender, nodeId) { - _this.onVertexOver.notify(nodeId); - }); - - this.graphCanvas.onVertexOut.attach(function(sender, nodeId) { - _this.onVertexOut.notify(nodeId); - }); -}; - -/** SELECT VERTICES BY NAME * */ -NetworkWidget.prototype.selectVertexByName = function(vertexName) { - var vertices = this.getDataset().getVertexByName(vertexName); - if (vertices != null) { - for ( var nodeId in vertices) { - if (vertices.hasOwnProperty(nodeId)) { - var vertexId = vertices[nodeId].getId(); - this.selectVertexById(vertexId); - } - } - } -}; - -NetworkWidget.prototype.selectVerticesByName = function(verticesName) { - for ( var i = 0; i < verticesName.length; i++) { - this.selectVertexByName(verticesName[i]); - } -}; - -/** SELECT VERTICES BY ID * */ -NetworkWidget.prototype.selectVertexById = function(vertexId) { - this.graphCanvas.selectNode(vertexId); - this.blinkVertexById(vertexId); -}; - -NetworkWidget.prototype.selectVerticesById = function(verticesId) { - for ( var i = 0; i < verticesId.length; i++) { - this.selectVertexById(verticesId[i]); - } -}; - -/** VECINDARIO * */ -NetworkWidget.prototype.selectNeighbourhood = function() { - this.selectEdgesFromVertices(); - this.selectAdjacent(); -}; - -/** DESELECT * */ -NetworkWidget.prototype.deselectNodes = function() { - this.graphCanvas.deselectNodes(); -}; - -/** SELECT ALL NODES * */ -NetworkWidget.prototype.selectAllNodes = function() { - this.getGraphCanvas().selectAllNodes(); -}; - -/** SELECT EVERYTHING * */ -NetworkWidget.prototype.selectAll = function() { - this.getGraphCanvas().selectAll(); -}; - -/** SELECT ALL EDGES * */ -NetworkWidget.prototype.selectAllEdges = function() { - this.getGraphCanvas().selectAllEdges(); -}; - -/** ZOOM * */ -NetworkWidget.prototype.setScale = function(value) { - this.graphCanvas.setScale(value); -}; - -NetworkWidget.prototype.getScale = function() { - return this.graphCanvas.getScale(value); -}; - -/** SELECT ADJACENT VERTICES FROM SELECTED VERTICES * */ -NetworkWidget.prototype.selectAdjacent = function() { - var selectedVertices = this.getGraphCanvas().getSelectedVertices(); - var edges = []; - for ( var i = 0; i < selectedVertices.length; i++) { - edges = edges.concat(this.getGraphCanvas().getDataset().getVertexById(selectedVertices[i]).getEdges()); - } - var vertices = []; - for ( i = 0; i < edges.length; i++) { - vertices.push(edges[i].getNodeSource().getId()); - vertices.push(edges[i].getNodeTarget().getId()); - } - - this.selectVerticesById(vertices); -}; - -/** SELECT EDGES FROM SELECTED VERTICES * */ -NetworkWidget.prototype.selectEdgesFromVertices = function() { - var selectedVertices = this.getGraphCanvas().getSelectedVertices(); - var edges = []; - for ( var i = 0; i < selectedVertices.length; i++) { - edges = edges.concat(this.getGraphCanvas().getDataset().getVertexById(selectedVertices[i]).getEdges()); - } - var edgesId = []; - for ( i = 0; i < edges.length; i++) { - edgesId.push(edges[i].getId()); - } - this.getGraphCanvas().selectEdges(edgesId); -}; - -/** BLINKING * */ -NetworkWidget.prototype.blinkVertexById = function(vertexId) { - this.graphCanvas.blinkVertexById(vertexId); -}; - -/** COMPONENTE CONEXA DE LOS NODOS SELECCIONADOS * */ -NetworkWidget.prototype.selectConnectedComponent = function() { - var elements = this.getConnectedComponent(); - this.selectVerticesById(elements.nodes); - this.graphCanvas.selectEdges(elements.edges); -}; - -NetworkWidget.prototype.getConnectedComponent = function() { - var nodosVisitados = {}; - var aristasVisitadas = {}; - var arrNodos = []; - var arrAristas = []; - var selectedGraphNodesId = this.getGraphCanvas().getSelectedVertices(); - for ( var i = 0; i < selectedGraphNodesId.length; i++) { - this.visitNode(selectedGraphNodesId[i], nodosVisitados, aristasVisitadas, arrNodos, arrAristas); - } - return { - nodes : arrNodos, - edges : arrAristas - }; -}; - -NetworkWidget.prototype.visitNode = function(nodeId, nodosVisitados, aristasVisitadas, arrNodos, arrAristas) { - nodosVisitados[nodeId] = true; - arrNodos.push(nodeId); - var nodeEdges = this.getDataset().getVertexById(nodeId).getEdges(); - for ( var j = 0; j < nodeEdges.length; j++) { - var edge = nodeEdges[j]; - var edgeId = edge.getId(); - if (aristasVisitadas[edgeId] == null) { - aristasVisitadas[edgeId] = true; - arrAristas.push(edgeId); - var nodeTargetId = edge.getNodeTarget().getId(); - if (nodosVisitados[nodeTargetId] == null) { - this.visitNode(nodeTargetId, nodosVisitados, aristasVisitadas, arrNodos, arrAristas); - } - var nodeSourceId = edge.getNodeSource().getId(); - if (nodosVisitados[nodeSourceId] == null) { - this.visitNode(nodeSourceId, nodosVisitados, aristasVisitadas, arrNodos, arrAristas); - } - } - } -}; - -/** COLLAPSE SELECTED VERTICES * */ -NetworkWidget.prototype.collapse = function() { - var selectedVertices = this.getGraphCanvas().getSelectedVertices(); - var xMin = -Infinity; - var xMax = Infinity; - var yMin = -Infinity; - var yMax = Infinity; - - for ( var i = 0; i < selectedVertices.length; i++) { - var vertex = this.getGraphCanvas().getLayout().getNodeById(selectedVertices[i]); - if (xMin < vertex.x) { - xMin = vertex.x; - } - if (xMax > vertex.x) { - xMax = vertex.x; - } - if (yMin < vertex.y) { - yMin = vertex.y; - } - if (yMax > vertex.y) { - yMax = vertex.y; - } - } - - var centerX = xMin - xMax; - var centerY = yMin - yMax; - var radius = (xMax - xMin) / 4; - - var count = selectedVertices.length; - var verticesCoordinates = []; - - for ( i = 0; i < selectedVertices.length; i++) { - x = centerX + radius * Math.sin(i * 2 * Math.PI / count); - y = centerY + radius * Math.cos(i * 2 * Math.PI / count); - verticesCoordinates.push({ - 'x' : x, - 'y' : y - }); - } - - for ( i = 0; i < selectedVertices.length; i++) { - this.getGraphCanvas().getLayout().getNodeById(selectedVertices[i]).setCoordinates(verticesCoordinates[i].x, verticesCoordinates[i].y); - } -}; - -/** SETTER FONT SIZE * */ -NetworkWidget.prototype.setVerticesFontSize = function(value) { - for ( var nodeId in this.getDataset().getVertices()) { - if (this.getDataset().getVertices().hasOwnProperty(nodeId)) { - this.getFormatter().getVertexById(nodeId).getDefault().setFontSize(value); - } - } -}; - -/** GETTERS * */ -NetworkWidget.prototype.getFormatter = function() { - return this.getGraphCanvas().getFormatter(); -}; -NetworkWidget.prototype.getLayout = function() { - return this.getGraphCanvas().getLayout(); -}; - -NetworkWidget.prototype.getDataset = function() { - return this.getGraphCanvas().getDataset(); -}; - -NetworkWidget.prototype.getGraphCanvas = function() { - return this.graphCanvas; -}; - -RangeWhiskerGraph.prototype.cleanArray = GenericGraph.prototype.cleanArray; -RangeWhiskerGraph.prototype.plotAxes = GenericGraph.prototype.plotAxes; -RangeWhiskerGraph.prototype.plotRuler = GenericGraph.prototype.plotRuler; -RangeWhiskerGraph.prototype.drawSVGVerticalText = GenericGraph.prototype.drawSVGVerticalText; -RangeWhiskerGraph.prototype.getClassColor = GenericGraph.prototype.getClassColor; -RangeWhiskerGraph.prototype.getDimensions = GenericGraph.prototype.getDimensions; -RangeWhiskerGraph.prototype.calculate = GenericGraph.prototype.calculate; -RangeWhiskerGraph.prototype.getMedian = GenericGraph.prototype.getMedian; -RangeWhiskerGraph.prototype.pointToPixel = GenericGraph.prototype.pointToPixel; -RangeWhiskerGraph.prototype.maxValueWhisker = GenericGraph.prototype.maxValueWhisker; -RangeWhiskerGraph.prototype.refresh = GenericGraph.prototype.refresh; - -/** - * Subclass of GenericGraph - * - * @plotHorizontalByCluster - */ -function RangeWhiskerGraph(args) { - this.maxBoxWidth = 25; - - if (args == null) { - args = {}; - } - args.plotHorizontalByCluster = false; - - GenericGraph.call(this, args); - - if (args.maxBoxWidth != null) { - this.maxBoxWidth = args.maxBoxWidth; - } -} - -RangeWhiskerGraph.prototype.refresh = function(data) { - document.getElementById(this.targetId).innerHTML = ""; - this.draw(this.targetId, data); -}; - -RangeWhiskerGraph.prototype.isNumber = function(value) { - if (value == "") - return false; - - var d = parseInt(value); - if (!isNaN(d)) - return true; - else - return false; - -}; - -/** - There are several different methods for calculating quartiles.[1] This calculator uses a method described by Moore and McCabe to find quartile values. - The same method also used by The TI-83 to calculate quartile values. - With this method, the first quartile is the median of the numbers below the median, the third quartile is the median of the numbers above the median. - - http://www.miniwebtool.com/quartile-calculator/ - http://www.alcula.com/calculators/statistics/box-plot/ - - **/ -RangeWhiskerGraph.prototype.getQ1 = function(array) { - array = array.slice(0, array.length / 2); - return this.getMedian(array); -}; - -RangeWhiskerGraph.prototype.getQ3 = function(array) { - array = array.slice((array.length + 1) / 2); - return this.getMedian(array); - -}; - -RangeWhiskerGraph.prototype.getQ2 = function(array) { - return this.getMedian(array); -}; - -RangeWhiskerGraph.prototype.getBelowOutliers = function(belowLimit, array) { - var points = []; - for ( var i = 0; i < array.length; i++) { - if (array[i] <= belowLimit) { - points.push(array[i]); - } - } - return points; -}; - -RangeWhiskerGraph.prototype.getAboveOutliers = function(aboveLimit, array) { - var points = []; - for ( var i = 0; i < array.length; i++) { - if (array[i] >= aboveLimit) { - points.push(array[i]); - } - } - return points; -}; - -RangeWhiskerGraph.prototype.minValueWhisker = function(belowLimit, q1, array) { - var points = []; - - for ( var i = 0; i < array.length; i++) { - if ((array[i] < q1) && (array[i]) > belowLimit) { - points.push(array[i]); - } - } - - if (points.length > 0) { - points.sort(function(a, b) { - return a - b; - }); - return points[0]; - } - return null; -}; - -//RangeWhiskerGraph.prototype.maxValueWhisker = function(aboveLimit, q3, array){ -// var points = []; -// for (var i = 0; i < array.length; i++){ -// if ((array[i] > q3) && (array[i]) <= aboveLimit){ -// points.push(array[i]); -// } -// } -// if (points.length > 0){ -// points.sort(function(a, b){return a - b;}); -// return points[points.length - 1]; -// } -// return null; -//}; - -RangeWhiskerGraph.prototype.drawPoints = function(boxProperties) { - if (this.plotPoints) { - for ( var i = 0; i < boxProperties.values.length; i++) { - var value = boxProperties.values[i]; - var toPixel = this.pointToPixel(value, boxProperties); - SVG.drawCircle(boxProperties.x, toPixel, this.pointRadius, this.svg, - [ - [ "fill", "green" ], [ "fill-opacity", this.fillOpacityPoint ], [ 'stroke-opacity', this.strokeOpacityPoint ], - [ "stroke", "black" ] ]); - } - } -}; - -RangeWhiskerGraph.prototype.plotRangeQ1Q3 = function(data, properties) { - var posX = this.left + this.rulerWidth; - - var boxBordersPointsQ1 = []; - var boxBordersPointsQ3 = []; - var Q2 = []; - - for ( var i = 0; i < data.clusters.length; i++) { - var cluster = data.clusters[i]; - /** inter cluster space **/ - posX = posX + this.interClustersSpace; - - for ( var j = 0; j < cluster.classes.length; j++) { - var ratio = properties.width / (properties.xValues.range); - var coorX = (cluster.x - properties.xValues.min) * ratio + this.rulerWidth; - var boxProperties = { - name : cluster.classes[j].name, - values : cluster.classes[j].values, - minPoint : properties.minPoint, - maxPoint : properties.maxPoint, - x : coorX + this.left, - y : this.top, - width : properties.classWidth, - height : this.height - this.top - this.bottom - this.clusterTitleHeight - this.rulerHeight - }; - - var result = this.calculate(boxProperties.values); - var boxColor = this.getClassColor(boxProperties.name); - - if (this.isNumber(result.Q1) && this.isNumber(result.Q3)) { - var x = boxProperties.x; - var y = this.pointToPixel(result.Q1, boxProperties); - if (boxBordersPointsQ1[cluster.classes[j].name] == null) { - boxBordersPointsQ1[cluster.classes[j].name] = []; - } - boxBordersPointsQ1[cluster.classes[j].name].push({ - x : x, - y : y - }); - - x = boxProperties.x; - y = this.pointToPixel(result.Q3, boxProperties); - if (boxBordersPointsQ3[cluster.classes[j].name] == null) { - boxBordersPointsQ3[cluster.classes[j].name] = []; - } - boxBordersPointsQ3[cluster.classes[j].name].push({ - x : x, - y : y - }); - } else { - - if (this.isNumber(result.Q2)) { - - if (boxBordersPointsQ1[cluster.classes[j].name] == null) { - boxBordersPointsQ1[cluster.classes[j].name] = []; - } - - if (boxBordersPointsQ3[cluster.classes[j].name] == null) { - boxBordersPointsQ3[cluster.classes[j].name] = []; - } - - boxBordersPointsQ1[cluster.classes[j].name].push({ - x : boxProperties.x, - y : this.pointToPixel(result.Q2, boxProperties) - }); - boxBordersPointsQ3[cluster.classes[j].name].push({ - x : boxProperties.x, - y : this.pointToPixel(result.Q2, boxProperties) - }); - } - } - } - } - for ( var classe in boxBordersPointsQ1) { - var points = boxBordersPointsQ1[classe]; - var pointsSVG = ""; - for (var k = 0; k < points.length; k++) { - pointsSVG = Number(points[k].x).toFixed(1) + "," + Number(points[k].y).toFixed(1) + " " + pointsSVG; - } - - points = boxBordersPointsQ3[classe]; - for (var z = points.length - 1; z >= 0; z--) { - pointsSVG = Number(points[z].x).toFixed(1) + "," + Number(points[z].y).toFixed(1) + " " + pointsSVG; - } - - SVG.drawPoligon(pointsSVG, this.svg, [ - [ "fill", this.getClassColor(classe) ], [ "opacity", "0.3" ], [ "stroke", "black" ], [ "stroke-width", 1 ] ]); - } -}; - -RangeWhiskerGraph.prototype.plotWhisters = function(data, properties) { - var colors = [ "yellow", "orange", "green" ]; - - this.plotRangeQ1Q3(data, properties); - var Q2 = {}; - - for ( var i = 0; i < data.clusters.length; i++) { - var cluster = data.clusters[i]; - for ( var j = 0; j < cluster.classes.length; j++) { - var ratio = properties.width / (properties.xValues.range); - var coorX = (cluster.x - properties.xValues.min) * ratio + this.left + this.rulerWidth - this.rulerStroke; - var boxProperties = { - name : cluster.classes[j].name, - values : cluster.classes[j].values, - minPoint : properties.minPoint, - maxPoint : properties.maxPoint, - x : coorX, - y : this.top, - width : properties.classWidth, - height : this.height - this.top - this.bottom - this.clusterTitleHeight - this.rulerHeight - }; - - this.drawPoints(boxProperties); - - /** PLOTTING Q2 **/ - var result = this.calculate(boxProperties.values); - var boxColor = this.getClassColor(boxProperties.name); - if (this.isNumber(result.Q2)) { - if (Q2[boxProperties.name] != null) { - SVG.drawLine(boxProperties.x, this.pointToPixel(result.Q2, boxProperties), Q2[boxProperties.name].x, Q2[boxProperties.name].y, - this.svg, [ [ "stroke", boxColor ], [ "stroke-width", "2" ] ]); - } - Q2[boxProperties.name] = { - x : boxProperties.x, - y : this.pointToPixel(result.Q2, boxProperties) - } - } - } - } - -}; - -RangeWhiskerGraph.prototype.draw = function(targetId, data) { - //this.calculate(data); - this.targetId = targetId; - var properties = (this.getDimensions(data)); - this.svg = SVG.createSVGCanvas(document.getElementById(this.targetId), [ [ "width", this.width ], [ "height", this.height ] ]); - this.plotAxes(properties); - this.plotWhisters(data, properties); -}; - -RangeWhiskerGraph.prototype.input = function() { - return DATADOC.getBoxWhikerData(); -}; - -RangeWhiskerGraph.prototype.test = function(targetId) { - var plot = new RangeWhiskerGraph({ - targetId : targetId, - height : 350, - width : 450, - maxBoxWidth : 25 - }); - plot.refresh(this.input()); -}; - -StdDevDyGraph.prototype.dblclick = DygraphWidget.prototype.dblclick; -StdDevDyGraph.prototype._createHTLMWrapper = DygraphWidget.prototype._createHTLMWrapper; -StdDevDyGraph.prototype.draw = DygraphWidget.prototype.draw; - -function StdDevDyGraph(targetId, args) { - this.scaled = false; - if (args == null) { - args = {}; - } - args.customBars = true; - DygraphWidget.prototype.constructor.call(this, targetId, args); -} - -StdDevDyGraph.prototype.input = function () { - return { - data : [ [ 1, [ 2, 3, 3.5 ], [ 4, 4.2, 5 ] ], [ 2, [ 5, 5.5, 5.7 ], [ 4, 4.2, 5 ] ] ], - colors : [ "blue", "red" ], - labels : [ "", 'data1', 'data2' ] - }; -}; - -StdDevDyGraph.prototype.test = function (targetId) { - var dygraphObject = new StdDevDyGraph(targetId, { - width : 500, - height : 400, - xlabel : "xLabel", - showRangeSelector : false - }); - - dygraphObject.draw(dygraphObject.input().data, dygraphObject.input().colors, dygraphObject.input().labels); -}; - - - -function AbinitioGrid(args) { - this.height = null; - this.width = null; - this.id = BUI.id(); - - if (args != null) { - if (args.height != null) { - this.height = args.height; - - if (args.width != null) { - this.width = args.width; - } - } - } - - this.onSelected = new Event(this); -}; - - -AbinitioGrid.prototype.refresh = function(subtractions){ - this.store.loadData(this._prepareData(subtractions)); -}; - -AbinitioGrid.prototype._prepareData = function(subtractions){ - /** Parsing data * */ - var models = []; - for (var l = 0; l < subtractions.length; l++) { - var subtraction = subtractions[l]; - for (var k = 0; k < subtraction.substractionToAbInitioModel3VOs.length; k++) { - var data = subtraction.substractionToAbInitioModel3VOs[k].abinitiomodel3VO; - if (data.averagedModel != null) { - models.push(data.averagedModel); - models[models.length - 1].type = "Reference"; - } - - if (data.shapeDeterminationModel != null) { - models.push(data.shapeDeterminationModel); - models[models.length - 1].type = "Refined"; - } - - if (data.modelList3VO != null) { - if (data.modelList3VO.modeltolist3VOs != null) { - for (var i = 0; i < data.modelList3VO.modeltolist3VOs.length; i++) { - models.push(data.modelList3VO.modeltolist3VOs[i].model3VO); - models[models.length - 1].type = "Model"; - } - } - } - } - } - return models; -}; - -AbinitioGrid.prototype.getPanel = function(){ - var _this = this; - - - var modelFields = [ "modelId", "type", "chiSqrt", "dmax", "firFile", "logFile", "fitFile", "pdbFile", "rfactor", "rg", "volume" ]; - Ext.define('AbinitioModel', { - extend : 'Ext.data.Model', - fields : modelFields - - }); - - /** - * Store in Memory - */ - this.store = Ext.create('Ext.data.Store', { - model : 'AbinitioModel', - autoload : true, - groupField : 'type' - }); - - - var groupingFeature = Ext.create('Ext.grid.feature.Grouping',{ - groupHeaderTpl: '{name} ({rows.length} model{[values.rows.length > 1 ? "s" : ""]})', - startCollapsed: true, - collapsible : true - }); - - this.grid = Ext.create('Ext.grid.Panel', { - collapsible : false, - resizable : true, - features: [groupingFeature], - autoscroll : true, - multiSelect : true, - store : this.store, - height : this.height, - width : this.width, - margin : 10, - columns : [ { - text : "Type", - dataindex : "type", - hidden : true, - renderer : function(a, b, record) { - return record.data.type; - }, - flex : 1 - }, - { - text : "ModelId", - dataindex : "modelId", - hidden : true, - renderer : function(a, b, record) { - return record.data.modelId; - - }, - flex : 1 - }, - - { - text : "chiSqrt", - dataindex : "chiSqrt", - renderer : function(a, b, record) { - if (record.data.dmax != null) { - return BUI.formatValuesUnits(record.data.chiSqrt, "", 12, this.decimals); - } - - }, - flex : 1 - }, - { - text : "Dmax", - dataindex : "dmax", - renderer : function(a, b, record) { - if (record.data.dmax != null) { - return BUI.formatValuesUnits(record.data.dmax, "nm", 12, this.decimals); - } - - }, - flex : 1 - }, { - text : "rFactor", - dataindex : "rfactor", - hidden : true, - renderer : function(a, b, record) { - if (record.data.rfactor != null) { - return record.data.rfactor; - } - }, - flex : 1 - }, { - text : "Rg", - dataindex : "rg", - renderer : function(a, b, record) { - if (record.data.rg != null) { - return BUI.formatValuesUnits(record.data.rg, "nm", 12, this.decimals); - } - - }, - flex : 1 - }, - { - text : "Volume", - dataindex : "volume", - renderer : function(a, b, record) { - if (record.raw.volume != null){ - return BUI.formatValuesUnits(record.raw.volume, '') + " nm3"; - } - }, - flex : 1 - }, - { - text : "PDB", - dataindex : "pdbFile", - renderer : function(a, b, record) { - if (record.data.pdbFile != null){ - return record.data.pdbFile.split("/")[record.data.pdbFile.split("/").length - 1]; - } - }, - flex : 1 - }, { - text : "Fir", - dataindex : "firFile", - renderer : function(a, b, record) { - if (record.data.firFile != null){ - return record.data.firFile.split("/")[record.data.firFile.split("/").length - 1]; - } - }, - flex : 1 - }, { - text : "LOG", - dataindex : "logFile", - hidden : true, - renderer : function(a, b, record) { - if (record.data.logFile != null){ - return record.data.logFile.split("/")[record.data.logFile.split("/").length - 1]; - } - }, - flex : 1 - } - ], - viewConfig : { - enableTextSelection : true, - preserveScrollOnRefresh : true, - stripeRows : true, - listeners : { - 'celldblclick' : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { - }, - 'cellclick' : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { - var models = []; - for (var i = 0; i < grid.getSelectionModel().selected.items.length; i++) { - models.push(grid.getSelectionModel().selected.items[i].raw); - } - _this.onSelected.notify(models); - } - } - } - }); - return this.grid; - -}; - - -function AdditiveGrid(args) { - this.onRemoveButtonClicked = new Event(this); -} - -AdditiveGrid.prototype.getBuffer = function() { - return this.buffer; -}; - -AdditiveGrid.prototype._getActions = function() { - var _this = this; - /** Actions buttons **/ - var actions = []; - - actions.push(Ext.create('Ext.Action', { - icon : '../images/add.png', - text : 'Add', - disabled : true, - alwaysEnabled : true, - handler : function(widget, event) { - _this.buffer.bufferhasadditive3VOs.push(BIOSAXS_BEANS.getBufferhasAdditive3VO()); - _this.refresh(_this.buffer, _this.experiment); - } - })); - - return actions; -}; - -AdditiveGrid.prototype.refresh = function(buffer, experiment) { - this.buffer = buffer; - this.experiment = experiment; - if (buffer) { - if (buffer.bufferhasadditive3VOs) { - this.features = buffer.bufferhasadditive3VOs; - } - } - this.experiment = experiment; - this.store.loadData(this._prepareData(), false); -}; - -AdditiveGrid.prototype.getAdditives = function() { - var additives = []; - for ( var i = 0; i < this.store.getCount(); i++) { - var bufferHasAdditive = BIOSAXS_BEANS.getBufferhasAdditive3VO(); - var data = this.store.getAt(i).getData(); - - bufferHasAdditive.additive3VO.name = data.name; - bufferHasAdditive.additive3VO.comments = data.comments; - bufferHasAdditive.additive3VO.additiveType = data.additiveType; - - bufferHasAdditive.bufferId = this.buffer.bufferId; - bufferHasAdditive.bufferHasAdditiveId = data.bufferHasAdditiveId; - bufferHasAdditive.quantity = data.quantity; - additives.push(bufferHasAdditive); - } - - return additives; -}; - -AdditiveGrid.prototype._getFields = function(buffers) { - var columns = [ - - { - header : 'Name', - dataIndex : 'name', - type : 'string', - editor : { - allowBlank : true - }, - flex : 1 - }, - { - header : 'Type', - name : 'additiveType', - dataIndex : 'additiveType', - editor : { - xtype : 'combobox', - typeAhead : true, - triggerAction : 'all', - selectOnTab : true, - store : BIOSAXS.proposal.getAdditiveTypes(), - lazyRender : true, - listClass : 'x-combo-list-small' - }, - flex : 0.6 - }, - { - header : 'Quantity', - dataIndex : 'quantity', - name : 'quantity', - editor : { - allowBlank : true - }, - type : 'string', - flex : 1 - }, - { - xtype : 'actioncolumn', - items : [ { - icon : '../images/cancel.png', - tooltip : 'Delete additive', - scope : this, - handler : function(grid, rowIndex, colIndex) { - if ((grid.getStore().getAt(rowIndex).data.bufferHasAdditiveId == null)|| (grid.getStore().getAt(rowIndex).data.bufferHasAdditiveId == "")) { - grid.getStore().removeAt(rowIndex); - } else { - this.onRemoveButtonClicked.notify({ - 'bufferId' : this.buffer.bufferId, - 'bufferHasAdditiveId' : this.store.getAt(rowIndex).data.bufferHasAdditiveId - }); - } - } - } ] - } ]; - - return columns; -}; - -AdditiveGrid.prototype._prepareData = function() { - var data = []; - if (this.features == null) { - this.features = []; - } - for (var i = 0; i < this.features.length; i++) { - var object = this.features[i]; - object.name = this.features[i].additive3VO.name; - object.additiveType = this.features[i].additive3VO.additiveType; - object.comments = this.features[i].additive3VO.comments; - object.additiveId = this.features[i].additive3VO.additiveId; - data.push(object); - } - return data; -}; - -AdditiveGrid.prototype.getPanel = function(buffer, experiment) { - this.buffer = buffer; - this.features = buffer.bufferhasadditive3VOs; - this.experiment = experiment; - return this._renderGrid(); -}; - -AdditiveGrid.prototype.getStore = function() { - var _this = this; - var store = Ext.create('Ext.data.Store', { - fields : [ "name", "additiveType", "comments", "additiveId", "bufferHasAdditiveId", "quantity" ], - autoload : false, - data : this._prepareData(), - listeners : { - update : function(store, record) { - record.index = _this.grid.getSelectionModel().getCurrentPosition().row; - _this.buffer.bufferhasadditive3VOs[record.index].additive3VO.name = record.data.name; - _this.buffer.bufferhasadditive3VOs[record.index].additive3VO.additiveType = record.data.additiveType; - _this.buffer.bufferhasadditive3VOs[record.index].additive3VO.comments = record.data.comments; - _this.buffer.bufferhasadditive3VOs[record.index].quantity = record.data.quantity; - } - } - }); - - // store.sort('bufferHasAdditiveId', 'ASC'); - store.loadData(this._prepareData(), false); - return store; -}; - -AdditiveGrid.prototype._renderGrid = function() { - var _this = this; - this.store = this.getStore(); - - var cellEditing = Ext.create('Ext.grid.plugin.CellEditing', { - clicksToEdit : 1 - }); - - this.grid = Ext.create('Ext.grid.Panel', { - dockedItems : [ { - xtype : 'toolbar', - items : this._getActions() - } ], - store : this.store, - height : 230, - title : "Additives", - width : "100%", - columns : _this._getFields(), - plugins : [ cellEditing ], - viewConfig : { - stripeRows : true, - listeners : { - itemcontextmenu : function(view, rec, node, index, e) { - e.stopEvent(); - contextMenu.showAt(e.getXY()); - return false; - } - } - }, - selModel : { - mode : 'SINGLE' - } - }); - - return this.grid; -}; - -AdditiveGrid.prototype.input = function() { -}; - -AdditiveGrid.prototype.test = function(targetId) { - var grid = new AdditiveGrid(); - var panel = grid.getPanel([]); - panel.render(targetId); - -}; - -/** AnalysisGrid **/ -function AnalysisGrid(args) { - var _this = this; - - this.id = BUI.id(); - if (Ext.get("mainPanel")){ - this.width = Ext.get("mainPanel").getWidth(); - } - else{ - this.width = this.maxWidth; - } - this.maxWidth = 1500; - - /** Visibles **/ - this.isGuinierTabVisible = true; - this.isGnomTabVisible = true; - this.isPorodTabVisible = true; - this.isScatteringPlotVisible = true; - this.isAbinitioTabVisible = true; - this.showButtonsVisible = true; - this.isI0Visible = true; - - this.margin = null; - this.grouped = true; - - this.hideNulls = false; - this.decimals = 4; - this.height = null; - this.sorters = [ { - property : 'conc', - direction : 'ASC' - } ]; - - if (args != null) { - if (args.grouped != null) { - this.grouped = args.grouped; - } - - if (args.showButtonsVisible != null) { - this.showButtonsVisible = args.showButtonsVisible; - } - if (args.isI0Visible != null) { - this.isI0Visible = args.isI0Visible; - } - if (args.sorters != null) { - this.sorters = args.sorters; - } - if (args.isScatteringPlotVisible != null) { - this.isScatteringPlotVisible = args.isScatteringPlotVisible; - } - if (args.isGuinierTabVisible != null) { - this.isGuinierTabVisible = args.isGuinierTabVisible; - } - if (args.isGnomTabVisible != null) { - this.isGnomTabVisible = args.isGnomTabVisible; - } - if (args.isPorodTabVisible != null) { - this.isPorodTabVisible = args.isPorodTabVisible; - } - if (args.hideNulls != null) { - this.hideNulls = args.hideNulls; - } - if (args.height != null) { - this.height = args.height; - } - if (args.width != null) { - this.width = args.width; - } - if (args.maxWidth != null) { - this.maxWidth = args.maxWidth; - } - } -} - -AnalysisGrid.prototype.refresh = function(data, args) { - this.store.loadData(this._prepareData(data), false); - if (args != null){ - if (args.experiment != null){ - this.experiment = args.experiment; - } - } -}; - -AnalysisGrid.prototype._getPorod = function() { - return { - text : 'Porod', - name : 'Porod', - columns : [ - { - text : 'Volume', - dataIndex : 'volumeEdna', - width : 75, - sortable : true, - hidden : !this.isPorodTabVisible, - renderer : function(val, y, sample) { - if (sample.raw.volume != null) - return BUI.formatValuesUnits(sample.raw.volume, '') + " nm3"; - } - }, - { - text : 'MM Vol. est.', - dataIndex : 'volumeEdna', - tooltip : '[Volume/2 - Volume/1.5] (Guinier)', - sortable : true, - width : 95, - hidden : !this.isPorodTabVisible, - renderer : function(val, y, sample) { - if (sample.raw.volume != null) - return Number(sample.raw.volume / 2).toFixed(1) + " - " + Number(sample.raw.volume / 1.5).toFixed(1)+ "kD"; - } - } ] - }; -}; - - -AnalysisGrid.prototype._getFramesColumn = function() { - var _this = this; - return { - text : 'Frames (Averaged/Total)', - dataIndex : 'datacollection', - name : 'datacollection', - sortable : true, - width : 150, - renderer : function(dataCollections, y, data) { - /** Bug of Webservices: frames count were swapped with frames averages **/ - if (data.raw.bufferAfterFramesCount){ - if (data.raw.bufferAfterFramesMerged){ - if (parseInt(data.raw.bufferAfterFramesMerged) > parseInt(data.raw.bufferAfterFramesCount)){ - var aux = parseInt(data.raw.bufferAfterFramesCount); - data.raw.bufferAfterFramesCount= data.raw.bufferAfterFramesMerged; - data.raw.bufferAfterFramesMerged = aux; - } - } - } - - if (data.raw.bufferBeforeFramesCount){ - if (data.raw.bufferBeforeFramesMerged){ - if (parseInt(data.raw.bufferBeforeFramesMerged) > parseInt(data.raw.bufferBeforeFramesCount)){ - var aux = parseInt(data.raw.bufferBeforeFramesCount); - data.raw.bufferBeforeFramesCount= data.raw.bufferBeforeFramesMerged; - data.raw.bufferBeforeFramesMerged = aux; - } - } - - } - - var bufferAcronym = data.raw.bufferAcronym; - var macromoleculeAcronym = data.raw.macromoleculeAcronym; - var bbmerges = data.raw.bufferBeforeFramesMerged; - var molmerges = data.raw.framesMerge; - var bamerges = data.raw.bufferAfterFramesMerged; - var totalframes = data.raw.framesCount; - var bufferId = data.raw.bufferId; - var macromoleculeId = data.raw.macromoleculeId; - var macromoleculeColor = null; - if (_this.experiment != null){ - macromoleculeColor = _this.experiment.macromoleculeColors[data.raw.macromoleculeId]; - } - - /** BUG in the database to be fixed **/ - try{ - if (totalframes != null){ - if(molmerges != null){ - if (parseFloat(totalframes) < parseFloat(molmerges)){ - var aux = totalframes; - totalframes = molmerges; - molmerges = aux; - } - } - } - } - catch(e){ - - } - - return BUI.getHTMLTableForFrameAveraged(bufferAcronym, - macromoleculeAcronym, - bbmerges, - molmerges, - bamerges, - totalframes, - bufferId, - macromoleculeId, - macromoleculeColor); - } - }; -}; - -AnalysisGrid.prototype._getColumns = function() { - var _this = this; - return [ - { - name : 'groupeField', - dataIndex : 'groupeField', - hidden : true - - }, - { - "header" : "subtractionId", - hidden : true, - "dataIndex" : "subtractionId", - "name" : "subtractionId" - }, - { - header : "Macromolecule", - dataIndex : "macromoleculeAcronym", - name : "macromoleculeAcronym", - renderer : function(val, y, sample) { - return '
' + val + '
' + - BUI.formatValuesUnits(sample.raw.conc, "mg/ml", 10, this.decimals) + '
' + - BUI.formatValuesUnits(sample.raw.exposureTemperature, "C", 10, this.decimals) + '
' - } - }, - { - header : "Order", - dataIndex : "priorityLevelId", - name : "priorityLevelId", - hidden : true - }, - { - header : "Code", - dataIndex : "code", - name : "code", - hidden : true - }, - { - header : "Conc.", - dataIndex : "conc", - width : 80, - name : "conc", - type : "number", - hidden : true, - renderer : function(val, y, sample) { - return BUI.formatValuesUnits(val, "mg/ml", 10, this.decimals); - } - }, - { - text : 'Scattering', - width : 100, - dataIndex : 'subtractionId', - name : 'subtractionId', - hidden : !this.isScatteringPlotVisible, - renderer : function(val, y, sample) { - var url = BUI.getURL() + '&type=scattering&subtractionId=' + sample.raw.subtractionId; - var event = "OnClick= window.open('" + url + "')"; - return ''; - } - }, - { - text : 'Kratky', - width : 100, - dataIndex : 'subtractionId', - hidden : true, - name : 'subtractionId', - renderer : function(val, y, sample) { - var url = BUI.getURL() + '&type=kratky&subtractionId=' + sample.raw.subtractionId; - var event = "OnClick= window.open('" + url + "')"; - return ''; - } - }, - this._getFramesColumn(), - { - text : 'File Name', - dataIndex : 'averageFilePath', - name : 'averageFilePath', - sortable : true, - width : 150, - hidden : true, - renderer : function(dataCollections, y, data) { - return BUI.getHTMLTableForPrefixes(data.raw.bufferBeforeAverageFilePath, data.raw.averageFilePath, - data.raw.bufferAfterAverageFilePath); - } - }, - - { - text : 'Guinier', - name : 'Guinier', - columns : [ - { - text : 'Rg', - dataIndex : 'rgGuinier', - name : 'rgGuinier', - hidden : !this.isGuinierTabVisible, - width : 75, - tooltip : 'In polymer physics, the radius of gyration is used to describe the dimensions of a polymer chain.', - sortable : true, - renderer : function(val, y, sample) { - if (sample.raw.rgGuinier != null) { - /** Show warning if rgGuinier and rgGnom differ more than 10% **/ - if (Math.abs(sample.raw.rgGuinier - sample.raw.rgGnom) > (sample.raw.rgGuinier * 0.1)) { - return "" + BUI.formatValuesUnits(sample.raw.rgGuinier, "") + ""; - - } - return BUI.formatValuesUnits(sample.raw.rgGuinier, "nm", 12, this.decimals); - } - } - }, - { - text : 'Points', - dataIndex : 'points', - sortable : true, - width : 100, - type : 'string', - hidden : !this.isGuinierTabVisible, - renderer : function(val, y, sample) { - if ((sample.raw.firstPointUsed == "") || (sample.raw.firstPointUsed == null)) - return; - return "" + sample.raw.firstPointUsed + " - " + sample.raw.lastPointUsed + " (" + - (sample.raw.lastPointUsed - sample.raw.firstPointUsed) + " )"; - } - }, - { - text : 'Quality', - dataIndex : 'quality', - hidden : !this.isGuinierTabVisible, - tooltip : 'Estimated data quality. 1.0 - means ideal quality, 0.0 - unusable data. In table format it is given in percent (100% - ideal quality, 0% - unusable data). Please note that this estimation is based only on the Guinier interval (very low angles).', - width : 60, - sortable : true, - renderer : function(val, y, sample) { - if (sample.raw.quality != null) { - val = sample.raw.quality; - if ((val != null) && (val != "")) { - return "" + (Number(val) * 100).toFixed(2) + " %"; - } - } - } - }, { - text : 'I(0)', - dataIndex : 'I0', - sortable : true, - hidden : !this.isI0Visible, - tooltip : 'Extrapolated scattering intensity at zero angle I(0) (forward scattering)', - width : 75, - type : 'string', - renderer : function(val, y, sample) { - if (sample.raw.I0 != null) { - return BUI.formatValuesErrorUnitsScientificFormat(sample.raw.I0, sample.raw.i0stdev, ""); - } - } - }, { - text : 'Aggregated', - tooltip : "If aggregation was detected from the slope of the data curve at low angles the value is '1', otherwise '0'.", - dataIndex : 'isagregated', - hidden : true, - width : 75, - renderer : function(val, y, sample) { - if ((sample.raw.isagregated != null)) { - if (val == true) { - return "Yes"; - } else { - return "No"; - } - } - } - }, { - text : 'Guinier', - sortable : true, - dataIndex : 'subtractionId', - type : 'string', - width : 100, - hidden : true, - renderer : function(val, y, sample) { - - if (sample.raw.subtractionId != null) { - var url = BUI.getURL() + '&type=guinier&subtractionId=' + sample.raw.subtractionId; - var event = "OnClick= window.open('" + url + "')"; - return ''; - } - } - } ] - }, - { - text : 'Gnom', - name : 'Gnom', - - columns : [ { - text : 'Rg', - dataIndex : 'rgGnom', - type : 'string', - width : 65, - hidden : !this.isGnomTabVisible, - sortable : true, - renderer : function(val, y, sample) { - /** Show warning if rgGuinier and rgGnom differ more than 10% **/ - if (sample.raw.rgGnom != null) { - if (Math.abs(sample.raw.rgGuinier - sample.raw.rgGnom) > (sample.raw.rgGuinier * 0.1)) { - return "" + BUI.formatValuesUnits(sample.raw.rgGnom, "") + ""; - - } - return BUI.formatValuesUnits(sample.raw.rgGnom, "nm"); - } - } - }, { - text : 'Total', - dataIndex : 'total', - width : 65, - hidden : !this.isGnomTabVisible, - sortable : true, - renderer : function(val, y, sample) { - if (sample.raw.total != null) - return BUI.formatValuesUnits(sample.raw.total, ''); - } - }, { - text : 'Dmax', - dataIndex : 'dmax', - sortable : true, - width : 75, - hidden : !this.isGnomTabVisible, - renderer : function(val, y, sample) { - if (sample.raw.dmax != null) - return BUI.formatValuesUnits(sample.raw.dmax, "") + " nm"; - } - }, { - text : 'P(r)', - sortable : true, - hidden : true, - width : 100, - dataIndex : 'subtractionId', - type : 'string', - renderer : function(val, y, sample) { - var url = BUI.getURL() + '&type=gnom&subtractionId=' + sample.raw.subtractionId; - var event = "OnClick= window.open('" + url + "')"; - return ''; - } - } ] - }, - this._getPorod(), - { - text : 'AbInitio Modeling', - name : 'AbInitio_modeling', - - columns : [ - { - text : 'NSD', - dataIndex : 'volumeEdna', - width : 100, - sortable : true, - hidden : true, - renderer : function(val, y, sample) { - var url = BUI.getNSDImageURL(sample.raw.modelListId); - var event = "OnClick= window.open('" + url + "')"; - return ''; - } - }, - { - text : 'Chi2', - dataIndex : 'volumeEdna', - sortable : true, - hidden : true, - width : 100, - renderer : function(val, y, sample) { - var url = BUI.getCHI2ImageURL(sample.raw.modelListId); - var event = "OnClick= window.open('" + url + "')"; - return ''; - } - }, - { - text : 'Pdb', - dataIndex : 'volumeEdna', - tooltip : 'Damaver: The program suite DAMAVER is a set of programs to align ab initio models, select the most typical one and build an averaged model. Dammif : Rapid ab initio shape determination by simulated annealing using a single phase dummy atom model. Dammin :Ab initio shape determination by simulated annealing using a single phase dummy atom model', - sortable : true, - width : 125, - hidden : !this.isAbinitioTabVisible, - renderer : function(val, y, sample) { - var html = new String(); - var split = null; - var url = null; - var file = sample.raw.averagedModel; - if (file != null) { - split = file.split("/"); - if (split.length > 0) { - url = BUI.getPdbURL() + '&type=AVERAGED&abInitioModelId=' + sample.raw.abInitioModelId; - html = html + ''+ split[split.length - 1] + '

'; - - } - } - - file = sample.raw.rapidShapeDeterminationModel; - if (file != null) { - split = file.split("/"); - if (split.length > 0) { - url = BUI.getPdbURL() + '&type=RAPIDSHAPEDETERMINATIONMODEL&abInitioModelId=' + sample.raw.abInitioModelId; - html = html + '' + split[split.length - 1] + '

'; - } - } - - file = sample.raw.shapeDeterminationModel; - if (file != null) { - split = file.split("/"); - if (split.length > 0) { - url = BUI.getPdbURL() + '&type=SHAPEDETERMINATIONMODEL&abInitioModelId=' + sample.raw.abInitioModelId; - html = html + ''+ split[split.length - 1] + ''; - } - } - return html; - } - }, - { - text : 'Damaver', - dataIndex : 'volumeEdna', - hidden : true, - tooltip : 'Damaver: The program suite DAMAVER is a set of programs to align ab initio models, select the most typical one and build an averaged model. ', - sortable : true, - width : 125, - renderer : function(val, y, sample) { - var file = sample.raw.averagedModel; - if (file != null) { - var split = file.split("/"); - if (split.length > 0) { - var url = BUI.getPdbURL() + '&type=AVERAGED&abInitioModelId=' + sample.raw.abInitioModelId; - return '' + split[split.length - 1]+ ''; - } - } - return sample.raw.averagedModel; - } - }, - { - text : 'Dammif', - dataIndex : 'volumeEdna', - hidden : true, - tooltip : 'Dammif : Rapid ab initio shape determination by simulated annealing using a single phase dummy atom model', - sortable : true, - width : 125, - renderer : function(val, y, sample) { - var file = sample.raw.rapidShapeDeterminationModel; - if (file != null) { - var split = file.split("/"); - if (split.length > 0) { - var url = BUI.getPdbURL() + '&type=RAPIDSHAPEDETERMINATIONMODEL&abInitioModelId=' + sample.raw.abInitioModelId; - return '' + split[split.length - 1] + ''; - } - } - return sample.raw.averagedModel; - } - }, - { - text : 'Dammin', - dataIndex : 'volumeEdna', - hidden : true, - tooltip : 'Dammin :Ab initio shape determination by simulated annealing using a single phase dummy atom model', - sortable : true, - width : 125, - renderer : function(val, y, sample) { - var file = sample.raw.shapeDeterminationModel; - if (file != null) { - var split = file.split("/"); - if (split.length > 0) { - var url = BUI.getPdbURL() + '&type=SHAPEDETERMINATIONMODEL&abInitioModelId=' + sample.raw.abInitioModelId; - return '' + split[split.length - 1]+ ''; - } - } - return sample.raw.averagedModel; - } - } ] - }, { - text : 'Time', - dataIndex : 'substractionCreationTime', - name : 'substractionCreationTime', - hidden : true, - width : 80, - sortable : true, - renderer : function(value, metaData, record, rowIndex, colIndex, store) { - try { - if (record.raw.substractionCreationTime != null) { - return moment(record.raw.substractionCreationTime).format('h:mm:ss a'); - } - } catch (e) { - return "NA"; - } - } - }, { - text : 'Date', - dataIndex : 'substractionCreationTime', - name : 'substractionCreationTime', - hidden : true, - width : 80, - sortable : true, - renderer : function(value, metaData, record, rowIndex, colIndex, store) { - try { - if (record.raw.substractionCreationTime != null) { - return moment(record.raw.substractionCreationTime).format("LL"); - } - } catch (e) { - return "NA"; - } - } - }, - this._getButtons()]; -}; - -AnalysisGrid.prototype._getButtons = function() { - return { - id : this.id + 'buttonPlot', - name : 'buttonPlot', - hidden : !this.showButtonsVisible, - width : 110, - sortable : false, - renderer : function(value, metaData, sample, rowIndex, colIndex, store) { - var html = ""; - - if (sample.raw.subtractionId) { - html = html + ""; - if (sample.raw.rapidShapeDeterminationModelId != null) { - html = html + ""; - } - } - return html + "
" + BUI.getGreenButton('Calibration', { - height : 20, - width : 100 - }) + "
" + BUI.getGreenButton('Primary Data Proc.', { - height : 20, - width : 100 - }) + "
" + BUI.getGreenButton('AbInitio Modeling', { - height : 20, - width : 100 - }) + "
"; - } - }; -}; -AnalysisGrid.prototype._prepareData = function(data) { - if (this.hideNulls) { - var result = []; - for ( var i = 0; i < data.length; i++) { - if (data[i].subtractionId != null) { - data[i].groupeField = data[i].macromoleculeAcronym + " " + data[i].bufferAcronym; - result.push(data[i]); - } - } - return result; - } else { - return data; - } -}; - -AnalysisGrid.prototype.getPanel = function(data) { - var _this = this; - var columns = this._getColumns(); - - var fields = JSON.parse(JSON.stringify(columns)); - fields.push({ - name : 'experimentId', - dataIndex : 'experimentId' - - }); - this.store = Ext.create('Ext.data.Store', { - fields : fields, - autoload : true, - data : this._prepareData(data), - groupField : 'groupeField' - }); - - this.store.sort(this.sorters); - - var features = []; - - if (this.grouped) { - features.push({ - ftype : 'grouping', - groupHeaderTpl : '{name}', - hideGroupedHeader : false, - startCollapsed : false - }); - } - - this.grid = Ext.create('Ext.grid.Panel', { - style : { - padding : 5 - }, - maxWidth : this.maxWidth, - width : this.width, - height : this.height, - store : this.store, - columns : columns, - resizable : true, - features : features, - viewConfig : { - preserveScrollOnRefresh : true, - stripeRows : true, - listeners : { - cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { - - if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonPlot') { - if (e.target.defaultValue == 'AbInitio Modeling') { - var url = BUI.getPDBVisualizerURL(record.raw.averagedModelId, record.raw.subtractionId, record.raw.experimentId); - window.open(url, "_blank"); - } - - if (e.target.defaultValue == 'Primary Data Proc.') { - _this._edit(record.raw); - } - - if (e.target.defaultValue == 'Calibration') { - _this._showCalibration(record.raw); - } - } - } - } - }, - selModel : { - mode : 'SINGLE' - } - }); - - return this.grid; - -}; - -AnalysisGrid.prototype._openVisualizarBySubstractionId = function(record) { - //experimentId, subtractionId) { - var experimentId = record.experimentId; - var subtractionId = record.subtractionId; - - var _this = this; - var adapter = new BiosaxsDataAdapter(); - this.subtractionId = subtractionId; - - adapter.onSuccess.attach(function(sender, data) { - var experiment = new Experiment(data); - var experimentList = new ExperimentList([ experiment ]); - var subtraction = experiment.getSubtractionById(_this.subtractionId); - _this.grid.setLoading(false); - _this._openVisualizer(experimentList, subtraction, record); - }); - this.grid.setLoading("ISPyB: Fetching experiment"); - adapter.getExperimentById(experimentId, "MEDIUM"); -}; - -AnalysisGrid.prototype._edit = function(record) { - var experimentId = record.experimentId; - var _this = this; - - if (BIOSAXS.proposal.macromolecules == null) { - this.grid.setLoading("ISPyB: Fetching proposal"); - BIOSAXS.proposal.onInitialized.attach(function(sender, data) { - _this._openVisualizarBySubstractionId(record); - }); - BIOSAXS.proposal.init(); - } else { - this._openVisualizarBySubstractionId(record); - } -}; - - -AnalysisGrid.prototype._showCalibration = function(row) { - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, data) { - Ext.create('Ext.window.Window', { - title : 'Calibration', - width : 900, - resizable : true, - height : 400, - modal : true, - frame : false, - draggable : true, - closable : true, - autoscroll : true, - paddin : 5, - layout : { - type : 'vbox', - align : 'stretch' - }, - items : [ new AnalysisGrid({ - isGuinierTabVisible : false, - isGnomTabVisible : false, - isPorodTabVisible : false, - isAbinitioTabVisible : false, - isI0Visible : true, - showButtonsVisible : false, - height : 350, - sorters : [ { - property : 'experimentId', - direction : 'DESC' - } ] - }).getPanel(data) ] - }).show(); - }); - adapter.getAnalysisCalibrationByProposalId(); -}; - -AnalysisGrid.prototype._openVisualizer = function(experimentList, subtraction, record) { - var merges = experimentList.getMergesByDataCollectionId(subtraction.dataCollectionId); - var mergeIdList = []; - for ( var i = 0; i < merges.length; i++) { - mergeIdList.push(merges[i].mergeId); - } - - var dataAdapter = new BiosaxsDataAdapter(); - dataAdapter.onSuccess.attach(function(sender, merges) { - var dataCollectionCurveVisualizer = new DataCollectionCurveVisualizer(); - dataCollectionCurveVisualizer.draw(); - dataCollectionCurveVisualizer.refresh(merges, experimentList, subtraction.dataCollectionId, record); - }); - - dataAdapter.onError.attach(function(sender, error) { - }); - - dataAdapter.getMergesByIdsList(mergeIdList); -}; - -AnalysisGrid.prototype.input = function() { - return { - data : DATADOC.getData_3367().slice(20, 30),//[{"total":"0.447505552082","bufferBeforeMeasurementId":22150,"sampleMergeId":12373,"averagedModelId":43636,"I0":"32.50776","proposalCode":"OPD","averagedModel":"/data/pyarch/bm29/opd29/1137/1d/damaver.pdb","bufferAfterMergeId":12374,"framesCount":"10","bufferBeforeMergeId":12372,"averageFilePath":" /data/pyarch/bm29/opd29/1137/1d/MG386_016_ave.dat","bufferAfterMeasurementId":22152,"rgGuinier":"2.96883","subtractedFilePath":" /data/pyarch/bm29/opd29/1137/1d/MG386_016_sub.dat","firstPointUsed":"30","chi2RgFilePath":"/data/pyarch/bm29/opd29/1137/1d/chi2_R.png","abInitioModelId":272,"macromoleculeId":112,"code":"_20.0_1.25","transmission":"100.0","timeStart":"2013-07-17 17:10:25.743734","bufferAcronym":"MES","quality":"0.87091","shapeDeterminationModelId":43638,"expermientComments":"[BsxCube] Generated from BsxCube","bufferId":707,"isagregated":"False","dmax":"10.390905","bufferBeforeAverageFilePath":" /data/pyarch/bm29/opd29/1137/1d/MG386_015_ave.dat","exposureTemperature":"20.0","subtractionId":5165,"conc":"1.25","rapidShapeDeterminationModel":"/data/pyarch/bm29/opd29/1137/1d/damfilt.pdb","experimentCreationDate":"Jul 17, 2013 5:08:37 PM","lastPointUsed":"92","modelListId":272,"framesMerge":"10","rapidShapeDeterminationModelId":43637,"bufferAfterAverageFilePath":" /data/pyarch/bm29/opd29/1137/1d/MG386_017_ave.dat","nsdFilePath":"/data/pyarch/bm29/opd29/1137/1d/nsd.png","bufferAfterFramesMerged":"10","experimentId":1137,"macromoleculeAcronym":"MG386","i0stdev":"0.05726128","sampleMeasurementId":22151,"measurementComments":"[1] MES","priorityLevelId":2,"bufferBeforeFramesMerged":"10","substractionCreationTime":"Jul 17, 2013 5:12:32 PM","proposalNumber":"29","rgGnom":"3.05242714339","volume":"44.1406","shapeDeterminationModel":"/data/pyarch/bm29/opd29/1137/1d/dammin.pdb","comments":null},{"total":"0.582641941147","bufferBeforeMeasurementId":22152,"sampleMergeId":12375,"averagedModelId":43809,"I0":"31.4366153846","proposalCode":"OPD","averagedModel":"/data/pyarch/bm29/opd29/1137/1d/damaver.pdb","bufferAfterMergeId":12376,"framesCount":"10","bufferBeforeMergeId":12374,"averageFilePath":" /data/pyarch/bm29/opd29/1137/1d/MG386_018_ave.dat","bufferAfterMeasurementId":22155,"rgGuinier":"2.95541","subtractedFilePath":" /data/pyarch/bm29/opd29/1137/1d/MG386_018_sub.dat","firstPointUsed":"21","chi2RgFilePath":"/data/pyarch/bm29/opd29/1137/1d/chi2_R.png","abInitioModelId":273,"macromoleculeId":112,"code":"_20.0_0.65000000000000002","transmission":"100.0","timeStart":"2013-07-17 17:12:55.837462","bufferAcronym":"MES","quality":"0.870164","shapeDeterminationModelId":43811,"expermientComments":"[BsxCube] Generated from BsxCube","bufferId":707,"isagregated":"False","dmax":"10.343935","bufferBeforeAverageFilePath":" /data/pyarch/bm29/opd29/1137/1d/MG386_017_ave.dat","exposureTemperature":"20.0","subtractionId":5166,"conc":"0.65000000000000002","rapidShapeDeterminationModel":"/data/pyarch/bm29/opd29/1137/1d/damfilt.pdb","experimentCreationDate":"Jul 17, 2013 5:08:37 PM","lastPointUsed":"80","modelListId":273,"framesMerge":"10","rapidShapeDeterminationModelId":43810,"bufferAfterAverageFilePath":" /data/pyarch/bm29/opd29/1137/1d/MG386_019_ave.dat","nsdFilePath":"/data/pyarch/bm29/opd29/1137/1d/nsd.png","bufferAfterFramesMerged":"10","experimentId":1137,"macromoleculeAcronym":"MG386","i0stdev":"0.100250461538","sampleMeasurementId":22154,"measurementComments":"[2] MES","priorityLevelId":4,"bufferBeforeFramesMerged":"10","substractionCreationTime":"Jul 17, 2013 5:15:02 PM","proposalNumber":"29","rgGnom":"2.9963404542","volume":"42.2255","shapeDeterminationModel":"/data/pyarch/bm29/opd29/1137/1d/dammin.pdb","comments":null}], - proposal : new ResultsAssemblyGrid().input().proposal - }; -}; - -AnalysisGrid.prototype.test = function(targetId) { - var analysisGrid = new AnalysisGrid(); - BIOSAXS.proposal = new Proposal(analysisGrid.input().proposal); - var panel = analysisGrid.getPanel(analysisGrid.input().data); - panel.render(targetId); -}; - - -function HPLCAnalysisGrid(args) { - AnalysisGrid.prototype.constructor.call(this, args); -} - -HPLCAnalysisGrid.prototype._edit = AnalysisGrid.prototype._edit; -HPLCAnalysisGrid.prototype._getColumns = AnalysisGrid.prototype._getColumns; -HPLCAnalysisGrid.prototype._openVisualizarBySubstractionId = AnalysisGrid.prototype._openVisualizarBySubstractionId; -HPLCAnalysisGrid.prototype._openVisualizer = AnalysisGrid.prototype._openVisualizer; -HPLCAnalysisGrid.prototype._prepareData = AnalysisGrid.prototype._prepareData; -HPLCAnalysisGrid.prototype._showCalibration = AnalysisGrid.prototype._showCalibration; -HPLCAnalysisGrid.prototype.getPanel = AnalysisGrid.prototype.getPanel; -HPLCAnalysisGrid.prototype.input = AnalysisGrid.prototype.input; -HPLCAnalysisGrid.prototype.test = AnalysisGrid.prototype.test; -HPLCAnalysisGrid.prototype.refresh = AnalysisGrid.prototype.refresh; -HPLCAnalysisGrid.prototype._getPorod = AnalysisGrid.prototype._getPorod; - -HPLCAnalysisGrid.prototype._getButtons = function() { - return { - id : this.id + 'buttonPlot', - name : 'buttonPlot', - hidden : !this.showButtonsVisible, - width : 110, - sortable : false, - renderer : function(value, metaData, sample, rowIndex, colIndex, store) { - var html = ""; - - if (sample.raw.subtractionId) { - if (sample.raw.rapidShapeDeterminationModelId != null) { - html = html + ""; - } - } - return html + "
" + BUI.getGreenButton('AbInitio Modeling', { - height : 20, - width : 100 - }) + "
"; - } - }; -}; - -HPLCAnalysisGrid.prototype._getFramesColumn = function() { - return { - text : 'Frames', - dataIndex : 'datacollection', - name : 'datacollection', - sortable : true, - width : 150, - renderer : function(dataCollections, y, data) { - molmerges = data.raw.framesMerge; - totalframes = data.raw.framesCount; - return molmerges +" - "+ totalframes; - } - }; -}; - - - - -/** - * Rigid body grid to show PDB, symmetry and multiplicity - * - * - * #onUploadFile click on upload file - */ -function AprioriRigidBodyGrid(args) { - - this.height = 250; - this.btnEditVisible = true; - this.btnRemoveVisible = true; - - if (args != null) { - if (args.height != null) { - this.height = args.height; - } - if (args.btnEditVisible != null) { - this.btnEditVisible = args.btnEditVisible; - } - if (args.btnRemoveVisible != null) { - this.btnRemoveVisible = args.btnRemoveVisible; - } - } - - /** Events **/ - this.onUploadFile = new Event(this); - this.onRemove = new Event(this); -} - -AprioriRigidBodyGrid.prototype._getColumns = function() { -}; - -AprioriRigidBodyGrid.prototype._getTopButtons = function() { - var _this = this; - /** Actions buttons **/ - var actions = []; - - /** ADD BUTTON **/ - actions.push(Ext.create('Ext.Action', { - icon : '../images/add.png', - text : 'Add', - disabled : false, - handler : function(widget, event) { - _this.onAddButtonClicked.notify(); - } - })); - - return actions; -}; - -AprioriRigidBodyGrid.prototype.refresh = function(macromolecule) { - this.macromolecule = macromolecule; - if (macromolecule != null){ - this.pdbStore.loadData(macromolecule.structure3VOs); - } -}; - -AprioriRigidBodyGrid.prototype._prepareData = function() { - var data = []; - for ( var i = 0; i < this.features.length; i++) { - data.push(this.features[i]); - } - return data; -}; - -AprioriRigidBodyGrid.prototype._getPlugins = function() { - var _this = this; - var plugins = []; - plugins.push(Ext.create('Ext.grid.plugin.RowEditing', { - clicksToEdit : 1, - listeners : { - validateedit : function(grid, e) { - /** Comments are always updatable* */ - e.record.raw.symmetry = e.newValues.symmetry; - e.record.raw.multiplicity = e.newValues.multiplicity; - - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, proposal) { - BIOSAXS.proposal.setItems(proposal); - _this.refresh(BIOSAXS.proposal.getMacromoleculeById(_this.macromolecule.macromoleculeId)); - _this.panel.setLoading(false); - }); - adapter.onError.attach(function() { - alert("Error"); - }); - - _this.panel.setLoading(); - adapter.saveStructure(e.record.raw); - } - } - })); - return plugins; -}; - -AprioriRigidBodyGrid.prototype.getPanel = function() { - var _this = this; - - this.pdbStore = Ext.create('Ext.data.Store', { - fields : [ 'filePath', 'structureId', 'structureType', 'symmetry', 'structureId', 'name', 'multiplicity' ], - groupField : 'structureType', - sorters : { - property : 'structureId', - direction : 'DESC' - } - }); - - var groupingFeature = Ext.create('Ext.grid.feature.Grouping', { - groupHeaderTpl : Ext.create('Ext.XTemplate', - "
{name:this.formatName}
", { - formatName : function(name) { - return name; - } - }), - hideGroupedHeader : true, - startCollapsed : false - }); - - this.panel = Ext.create('Ext.grid.Panel', { - margin : "15 10 0 10", - height : this.height, - store : this.pdbStore, - plugins : _this._getPlugins(), - tbar : [ { - text : 'Add Modeling Option (PDB)', - icon : '../images/add.png', - handler : function() { - _this.onUploadFile.notify('PDB', 'Upload PDB File'); - } - } - - ], - columns : [ - { - text : "structureId", - flex : 0.2, - hidden : true, - dataIndex : 'structureId', - sortable : true - }, - { - text : "File", - flex : 0.5, - dataIndex : 'filePath', - sortable : true, - hidden : true - }, - { - text : "PDB", - flex : 0.4, - dataIndex : 'name', - sortable : true - }, - { - text : "Symmetry", - flex : 0.2, - dataIndex : 'symmetry', - sortable : true, - editor : { - xtype : 'combobox', - typeAhead : true, - triggerAction : 'all', - selectOnTab : true, - store : [ [ "P1", "P1" ], [ "P2", "P2" ], [ "P3", "P3" ], [ "P4", "P4" ], [ "P5", "P5" ], [ "P6", "P6" ], [ "P32", "P32" ], [ "P42", "P42" ], - [ "P52", "P52" ], [ "P62", "P62" ], [ "P222", "P222" ] ], - } - }, { - text : "Multiplicity", - flex : 0.2, - dataIndex : 'multiplicity', - sortable : true, - editor : { - xtype : 'textfield' - } - - }, { - text : "Subunit", - flex : 0.2, - dataIndex : 'isSubunit', - sortable : true, - hidden : true - }, { - text : "Type", - flex : 0.2, - dataIndex : 'structureType', - sortable : true, - hidden : true - }, - - { - id : this.id + 'REMOVE', - flex : 0.2, - sortable : false, - renderer : function(value, metaData, record, rowIndex, colIndex, store) { - return BUI.getRedButton('REMOVE'); - } - }, ], - - listeners : { - itemdblclick : function(dataview, record, item, e) { - _this._editExperiment(record.raw.experimentId); - }, - cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { - - if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'REMOVE') { - var dataAdapter = new BiosaxsDataAdapter(); - dataAdapter.onSuccess.attach(function() { - _this.panel.setLoading(false); - _this.onRemove.notify(); - }); - _this.panel.setLoading("Removing PDB file"); - dataAdapter.removeStructure(record.data.structureId); - } - - } - }, - viewConfig : { - getRowClass : function(record, rowIdx, params, store) { - if (record.raw.isSubunit != null) { - return "blue-row"; - } - } - } - }); - - return this.panel; -}; - - - - -AprioriRigidBodyGrid.prototype.input = function() { - return { - proposal : DATADOC.getProposal_10(), - dewars : DATADOC.getDewars_10() - - }; -}; - -AprioriRigidBodyGrid.prototype.test = function(targetId) { - var AprioriRigidBodyGrid = new AprioriRigidBodyGrid({ - height : 150 - }); - BIOSAXS.proposal = new Proposal(AprioriRigidBodyGrid.input().proposal); - var panel = AprioriRigidBodyGrid.getPanel(AprioriRigidBodyGrid.input().dewars); - panel.render(targetId); - -}; - -/** - * It shows buffer grid with a top bar with "Add" button - * - * @height - * @searchBar - * @collapsed - * @width - */ -function BufferGrid(args) { - this.height = 500; - this.searchBar = false; - this.tbar = false; - this.collapsed = false; - this.id = BUI.id(); - - if (args != null) { - if (args.height != null) { - this.height = args.height; - } - if (args.searchBar != null) { - this.searchBar = args.searchBar; - } - - if (args.tbar != null) { - this.tbar = args.tbar; - } - - if (args.collapsed != null) { - this.collapsed = args.collapsed; - } - - if (args.width != null) { - this.width = args.width; - } - } -} - -BufferGrid.prototype._edit = function(bufferId) { - var _this = this; - var window = new BufferWindow(); - window.onSuccess.attach(function(sender, proposal) { - _this.store.loadData(BIOSAXS.proposal.getBuffers()); - }); - window.draw(BIOSAXS.proposal.getBufferById(bufferId)); -}; - -BufferGrid.prototype.refresh = function(buffers) { - this.store.loadData(this._prepareData(buffers), false); -}; - -BufferGrid.prototype._prepareData = function(buffers) { - return buffers; -}; - -BufferGrid.prototype._getTbar = function() { - var _this = this; - var actions = []; - - actions.push(Ext.create('Ext.Action', { - icon : '../images/add.png', - text : 'Add buffer', - disabled : false, - handler : function(widget, event) { - var window = new BufferWindow(); - window.onSuccess.attach(function(sender) { - _this.store.loadData(BIOSAXS.proposal.getBuffers()); - }); - window.draw({}); - } - })); - return actions; -}; - -BufferGrid.prototype.getPanel = function(buffers) { - var _this = this; - - this.store = Ext.create('Ext.data.Store', { - fields : [ 'bufferId', 'acronym', 'name', 'composition' ], - data : buffers - }); - - this.store.sort('acronym'); - - var type = 'Ext.grid.Panel'; - if (this.searchBar == true) { - type = 'Ext.ux.LiveSearchGridPanel'; - } - - this.grid = Ext.create(type, { - title : 'Buffers', - collapsible : true, - collapsed : this.collapsed, - store : this.store, - height : this.height, - width : this.width, - columns : [ - /*{ - text : '', - dataIndex : 'bufferId', - width : 20, - renderer : function(val, y, sample) { - return BUI.getRectangleColorDIV(BIOSAXS.proposal.bufferColors[val], 10, 10); - } - },*/ - { - text : 'Acronym', - dataIndex : 'acronym', - flex : 1 - }, { - text : 'Name', - dataIndex : 'name', - flex : 1, - hidden : true - }, { - text : 'Composition', - dataIndex : 'composition', - flex : 1, - hidden : true - }, { - id : _this.id + 'buttonEditBuffer', - width : 80, - sortable : false, - renderer : function(value, metaData, record, rowIndex, colIndex, store) { - return BUI.getGreenButton('EDIT'); - } - }, { - id : _this.id + 'buttonRemoveBuffer', - width : 85, - hidden : true, - sortable : false, - renderer : function(value, metaData, record, rowIndex, colIndex, store) { - return BUI.getRedButton('REMOVE'); - } - } ], - flex : 1, - viewConfig : { - stripeRows : true, - listeners : { - 'celldblclick' : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { - _this._edit(record.data.bufferId); - }, - 'cellclick' : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { - if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonEditBuffer') { - _this._edit(record.data.bufferId); - } - - if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonRemoveBuffer') { - BUI.showBetaWarning(); - } - } - - } - } - }); - - /** Adding the tbar **/ - if (this.tbar) { - this.grid.addDocked({ - xtype : 'toolbar', - items : this._getTbar() - }); - } - return this.grid; -}; - -BufferGrid.prototype.input = function() { - return new MacromoleculeGrid().input(); -}; - -BufferGrid.prototype.test = function(targetId) { - var bufferGrid = new BufferGrid({ - width : 800, - height : 350, - collapsed : false, - tbar : true - }); - - BIOSAXS.proposal = new Proposal(bufferGrid.input().proposal); - var panel = bufferGrid.getPanel(BIOSAXS.proposal.macromolecules); - panel.render(targetId); -}; - -/** - * A shipment may contains one or more cases where stock solutions and sample plates are stored - * - * @height - * @btnEditVisible - * @btnRemoveVisible - * - * #onEditButtonClicked - * #onAddButtonClicked - * #onRemoveButtonClicked - * #onDuplicateButtonClicked - */ -function CaseGrid(args) { - - this.height = 100; - this.btnEditVisible = true; - this.btnRemoveVisible = true; - - if (args != null) { - if (args.height != null) { - this.height = args.height; - } - if (args.btnEditVisible != null) { - this.btnEditVisible = args.btnEditVisible; - } - if (args.btnRemoveVisible != null) { - this.btnRemoveVisible = args.btnRemoveVisible; - } - } - - /** Events **/ - this.onEditButtonClicked = new Event(this); - this.onAddButtonClicked = new Event(this); - this.onRemoveButtonClicked = new Event(this); - this.onDuplicateButtonClicked = new Event(this); -} - -CaseGrid.prototype._getColumns = function() { - var _this = this; - - function actionItemRenderer(value, meta, record, rowIx, ColIx, store) { - /*if (record.raw.specimen3VOs.length > 0){ - return 'x-hide-display'; - }*/ - } - - var columns = [ - { - header : 'Code', - dataIndex : 'code', - name : 'code', - type : 'string', - flex : 1 - }, - { - header : 'Bar Code', - dataIndex : 'barCode', - name : 'barCode', - type : 'string', - flex : 1, - hidden : true - }, - { - header : 'BeamLine', - dataIndex : 'barCode', - type : 'string', - flex : 1, - renderer : function(comp, val, record) { - if (record.raw.sessionVO != null) { - return "" + record.raw.sessionVO.beamlineName + ""; - } - } - }, - { - header : 'Session', - dataIndex : 'barCode', - type : 'string', - flex : 1, - renderer : function(comp, val, record) { - if (record.raw.sessionVO != null) { - return "" + moment(record.raw.sessionVO.startDate).format("MMM Do YY") + ""; - } - } - }, - { - header : 'Status', - dataIndex : 'dewarStatus', - name : 'dewarStatus', - type : 'string', - flex : 1, - renderer : function(comp, val, record) { - if (new String(record.raw.dewarStatus).toUpperCase() == 'OPENED') { - return "" + new String(record.raw.dewarStatus).toUpperCase() + ""; - } - return "" + new String(record.raw.dewarStatus).toUpperCase() + ""; - } - }, - { - header : 'Type', - dataIndex : 'type', - name : 'type', - type : 'string', - flex : 1, - hidden : true - }, - { - header : 'Sample Plates', - dataIndex : 'plates', - name : 'plates', - type : 'string', - flex : 1, - hidden : true - }, - { - header : 'Stock Solutions', - dataIndex : 'plates', - name : 'plates', - type : 'string', - width : 100, - renderer : function(comp, val, record) { - var html = "
"; - var stockSolutions = BIOSAXS.proposal.getStockSolutionsByDewarId(record.raw.dewarId); - html = html + "" + stockSolutions.length + " x"; - return html + "
"; - } - }, { - header : 'isStorageDewar', - dataIndex : 'isStorageDewar', - name : 'isStorageDewar', - type : 'string', - flex : 1, - hidden : true - }, { - header : 'Tracking Number From Synchrotron', - dataIndex : 'trackingNumberFromSynchrotron', - name : 'trackingNumberFromSynchrotron', - type : 'string', - flex : 1, - hidden : true - }, { - header : 'Tracking Number To Synchrotron', - dataIndex : 'trackingNumberToSynchrotron', - name : 'trackingNumberToSynchrotron', - type : 'string', - flex : 1, - hidden : true - }, { - header : 'Storage Location', - dataIndex : 'storageLocation', - name : 'storageLocation', - type : 'string', - flex : 1, - hidden : false - }, { - header : 'Comments', - dataIndex : 'comments', - name : 'comments', - type : 'string', - flex : 1, - hidden : false - } - ]; - - if (this.btnEditVisible) { - columns.push({ - id : _this.id + 'buttonEdit', - text : '', - hidden : !_this.btnEditVisible, - width : 85, - sortable : false, - renderer : function(value, metaData, record, rowIndex, colIndex, store) { - return BUI.getGreenButton('EDIT'); - } - }); - } - - if (this.btnRemoveVisible) { - columns.push({ - id : _this.id + 'buttonRemove', - width : 85, - sortable : false, - renderer : function(value, metaData, record, rowIndex, colIndex, store) { - if (_this.btnRemoveVisible) { - return BUI.getRedButton('REMOVE'); - } - } - }); - } - - columns.push({ - dataIndex : 'comments', - type : 'string', - width : 85, - hidden : false, - renderer : function(comp, val, record) { - return BUI.getBlueButton("LABELS", { - href : BUI.getPrintcomponentURL(record.raw.dewarId) - }); - } - }); - - return columns; -}; - -CaseGrid.prototype._getTopButtons = function() { - var _this = this; - /** Actions buttons **/ - var actions = []; - - /** ADD BUTTON **/ - actions.push(Ext.create('Ext.Action', { - icon : '../images/add.png', - text : 'Add', - disabled : false, - handler : function(widget, event) { - _this.onAddButtonClicked.notify(); - } - })); - - return actions; -}; - -CaseGrid.prototype.refresh = function(dewars) { - this.features = dewars; - this.store.loadData(this._prepareData(dewars), false); -}; - -CaseGrid.prototype._sort = function(store) { - store.sort('dewarId', 'DESC'); -}; - -CaseGrid.prototype._prepareData = function() { - var data = []; - for ( var i = 0; i < this.features.length; i++) { - data.push(this.features[i]); - } - return data; -}; - -CaseGrid.prototype.getPanel = function(dewars, plates) { - this.features = dewars; - this.plates = plates; - return this._renderGrid(); -}; - -CaseGrid.prototype._edit = function(dewar) { - var _this = this; - var caseWindow = new CaseWindow(); - /**SAVED **/ - caseWindow.onSaved.attach(function(sender, dewar) { - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, shipment) { - _this.refresh(shipment.dewarVOs); - }); - adapter.saveCase(BIOSAXS.proposal.getShipmentByDewarId(dewar.dewarId).shippingId, dewar); - }); - caseWindow.draw(dewar); -}; - -CaseGrid.prototype._getStoreFields = function(data) { - return [ { - name : 'dewarId', - type : 'string' - }, { - name : 'barCode', - type : 'string' - }, { - name : 'code', - type : 'string' - }, { - name : 'comments', - type : 'string' - }, { - name : 'dewarStatus', - type : 'string' - }, { - name : 'isStorageDewar', - type : 'string' - }, { - name : 'plates', - type : 'string' - }, { - name : 'transportValue', - type : 'string' - }, { - name : 'trackingNumberFromSynchrotron', - type : 'string' - }, { - name : 'trackingNumberToSynchrotron', - type : 'string' - }, { - name : 'timeStamp', - type : 'string' - }, { - name : 'storageLocation', - type : 'string' - }, { - name : 'type', - type : 'string' - } - - ]; -}; - -CaseGrid.prototype._renderGrid = function() { - var _this = this; - - /** Store **/ - var data = this._prepareData(); - this.store = Ext.create('Ext.data.Store', { - fields : this._getStoreFields(data), - autoload : true, - data : data - }); - this._sort(this.store); - - this.store.loadData(data, false); - - this.grid = Ext.create('Ext.grid.Panel', { - style : { - padding : 5 - }, - height : this.height, - store : this.store, - columns : this._getColumns(), - viewConfig : { - stripeRows : true, - listeners : { - itemdblclick : function(dataview, record, item, e) { - _this._edit(record.raw); - }, - cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { - if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonEdit') { - _this._edit(record.raw); - } - - if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonRemove') { - _this.onRemoveButtonClicked.notify(_this.store.getAt(rowIndex).raw.dewarId); - } - } - } - }, - selModel : { - mode : 'SINGLE' - } - }); - - var actions = _this._getTopButtons(); - this.grid.addDocked({ - xtype : 'toolbar', - items : actions - }); - - this.grid.getSelectionModel().on({ - selectionchange : function(sm, selections) { - if (selections.length) { - for ( var i = 0; i < actions.length; i++) { - if (actions[i].enable) { - actions[i].enable(); - } - } - } else { - for ( var i = 0; i < actions.length; i++) { - if (actions[i].alwaysEnabled == false) { - if (actions[i].disable) { - actions[i].disable(); - } - } - } - } - } - }); - return this.grid; -}; - -CaseGrid.prototype.input = function() { - return { - proposal : DATADOC.getProposal_10(), - dewars : DATADOC.getDewars_10() - - }; -}; - -CaseGrid.prototype.test = function(targetId) { - var CaseGrid = new CaseGrid({ - height : 150 - }); - BIOSAXS.proposal = new Proposal(CaseGrid.input().proposal); - var panel = CaseGrid.getPanel(CaseGrid.input().dewars); - panel.render(targetId); - -}; - -/** - * A shipment may contains one or more cases where stock solutions and sample plates are stored - * - * @height - * @btnEditVisible - * @btnRemoveVisible - * - * #onEditButtonClicked - * #onAddButtonClicked - * #onRemoveButtonClicked - * #onDuplicateButtonClicked - */ -function ExampleGrid(args) { - - this.height = 100; - this.btnEditVisible = true; - this.btnRemoveVisible = true; - - if (args != null) { - if (args.height != null) { - this.height = args.height; - } - if (args.btnEditVisible != null) { - this.btnEditVisible = args.btnEditVisible; - } - if (args.btnRemoveVisible != null) { - this.btnRemoveVisible = args.btnRemoveVisible; - } - } - - /** Events **/ - this.onEditButtonClicked = new Event(this); - this.onAddButtonClicked = new Event(this); - this.onRemoveButtonClicked = new Event(this); - this.onDuplicateButtonClicked = new Event(this); -} - -ExampleGrid.prototype._getColumns = function() { - var _this = this; - - function actionItemRenderer(value, meta, record, rowIx, ColIx, store) { - /*if (record.raw.specimen3VOs.length > 0){ - return 'x-hide-display'; - }*/ - } - - var columns = [ - { - header : 'Code', - dataIndex : 'code', - name : 'code', - type : 'string', - flex : 1 - }, - { - header : 'Bar Code', - dataIndex : 'barCode', - name : 'barCode', - type : 'string', - flex : 1, - hidden : true - }, - { - header : 'BeamLine', - dataIndex : 'barCode', - type : 'string', - flex : 1, - renderer : function(comp, val, record) { - if (record.raw.sessionVO != null) { - return "" + record.raw.sessionVO.beamlineName + ""; - } - } - }, - { - header : 'Session', - dataIndex : 'barCode', - type : 'string', - flex : 1, - renderer : function(comp, val, record) { - if (record.raw.sessionVO != null) { - return "" + moment(record.raw.sessionVO.startDate).format("MMM Do YY") + ""; - } - } - }, - { - header : 'Status', - dataIndex : 'dewarStatus', - name : 'dewarStatus', - type : 'string', - flex : 1, - renderer : function(comp, val, record) { - if (new String(record.raw.dewarStatus).toUpperCase() == 'OPENED') { - return "" + new String(record.raw.dewarStatus).toUpperCase() + ""; - } - return "" + new String(record.raw.dewarStatus).toUpperCase() + ""; - } - }, - { - header : 'Type', - dataIndex : 'type', - name : 'type', - type : 'string', - flex : 1, - hidden : true - }, - { - header : 'Sample Plates', - dataIndex : 'plates', - name : 'plates', - type : 'string', - flex : 1, - hidden : true - }, - { - header : 'Stock Solutions', - dataIndex : 'plates', - name : 'plates', - type : 'string', - width : 100, - renderer : function(comp, val, record) { - var html = "
"; - var stockSolutions = BIOSAXS.proposal.getStockSolutionsByDewarId(record.raw.dewarId); - html = html + "" + stockSolutions.length + " x"; - return html + "
"; - } - }, { - header : 'isStorageDewar', - dataIndex : 'isStorageDewar', - name : 'isStorageDewar', - type : 'string', - flex : 1, - hidden : true - }, { - header : 'Tracking Number From Synchrotron', - dataIndex : 'trackingNumberFromSynchrotron', - name : 'trackingNumberFromSynchrotron', - type : 'string', - flex : 1, - hidden : true - }, { - header : 'Tracking Number To Synchrotron', - dataIndex : 'trackingNumberToSynchrotron', - name : 'trackingNumberToSynchrotron', - type : 'string', - flex : 1, - hidden : true - }, { - header : 'Storage Location', - dataIndex : 'storageLocation', - name : 'storageLocation', - type : 'string', - flex : 1, - hidden : false - }, { - header : 'Comments', - dataIndex : 'comments', - name : 'comments', - type : 'string', - flex : 1, - hidden : false - } - ]; - - if (this.btnEditVisible) { - columns.push({ - id : _this.id + 'buttonEdit', - text : '', - hidden : !_this.btnEditVisible, - width : 85, - sortable : false, - renderer : function(value, metaData, record, rowIndex, colIndex, store) { - return BUI.getGreenButton('EDIT'); - } - }); - } - - if (this.btnRemoveVisible) { - columns.push({ - id : _this.id + 'buttonRemove', - width : 85, - sortable : false, - renderer : function(value, metaData, record, rowIndex, colIndex, store) { - if (_this.btnRemoveVisible) { - return BUI.getRedButton('REMOVE'); - } - } - }); - } - - columns.push({ - dataIndex : 'comments', - type : 'string', - width : 85, - hidden : false, - renderer : function(comp, val, record) { - return BUI.getBlueButton("LABELS", { - href : BUI.getPrintcomponentURL(record.raw.dewarId) - }); - } - }); - - return columns; -}; - -ExampleGrid.prototype._getTopButtons = function() { - var _this = this; - /** Actions buttons **/ - var actions = []; - - /** ADD BUTTON **/ - actions.push(Ext.create('Ext.Action', { - icon : '../images/add.png', - text : 'Add', - disabled : false, - handler : function(widget, event) { - _this.onAddButtonClicked.notify(); - } - })); - - return actions; -}; - -ExampleGrid.prototype.refresh = function(dewars) { - this.features = dewars; - this.store.loadData(this._prepareData(dewars), false); -}; - -ExampleGrid.prototype._sort = function(store) { - store.sort('dewarId', 'DESC'); -}; - -ExampleGrid.prototype._prepareData = function() { - var data = []; - for ( var i = 0; i < this.features.length; i++) { - data.push(this.features[i]); - } - return data; -}; - -ExampleGrid.prototype.getPanel = function() { - this.grid = Ext.create('Ext.grid.Panel', { - style : { - padding : 5 - }, - height : this.height, - store : this.store, - columns : this._getColumns(), - viewConfig : { - stripeRows : true, - listeners : { - itemdblclick : function(dataview, record, item, e) { - _this._edit(record.raw); - }, - cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { - if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonEdit') { - _this._edit(record.raw); - } - - if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonRemove') { - _this.onRemoveButtonClicked.notify(_this.store.getAt(rowIndex).raw.dewarId); - } - } - } - }, - selModel : { - mode : 'SINGLE' - } - }); - - var actions = _this._getTopButtons(); - this.grid.addDocked({ - xtype : 'toolbar', - items : actions - }); - - this.grid.getSelectionModel().on({ - selectionchange : function(sm, selections) { - if (selections.length) { - for ( var i = 0; i < actions.length; i++) { - if (actions[i].enable) { - actions[i].enable(); - } - } - } else { - for ( var i = 0; i < actions.length; i++) { - if (actions[i].alwaysEnabled == false) { - if (actions[i].disable) { - actions[i].disable(); - } - } - } - } - } - }); - return this.grid; -}; - - -ExampleGrid.prototype._getStoreFields = function(data) { - return [ { - name : 'dewarId', - type : 'string' - }, { - name : 'barCode', - type : 'string' - }, { - name : 'code', - type : 'string' - }, { - name : 'comments', - type : 'string' - }, { - name : 'dewarStatus', - type : 'string' - }, { - name : 'isStorageDewar', - type : 'string' - }, { - name : 'plates', - type : 'string' - }, { - name : 'transportValue', - type : 'string' - }, { - name : 'trackingNumberFromSynchrotron', - type : 'string' - }, { - name : 'trackingNumberToSynchrotron', - type : 'string' - }, { - name : 'timeStamp', - type : 'string' - }, { - name : 'storageLocation', - type : 'string' - }, { - name : 'type', - type : 'string' - } - - ]; -}; - -ExampleGrid.prototype._renderGrid = function() { - var _this = this; - - /** Store **/ - var data = this._prepareData(); - this.store = Ext.create('Ext.data.Store', { - fields : this._getStoreFields(data), - autoload : true, - data : data - }); - this._sort(this.store); - - this.store.loadData(data, false); - - this.grid = Ext.create('Ext.grid.Panel', { - style : { - padding : 5 - }, - height : this.height, - store : this.store, - columns : this._getColumns(), - viewConfig : { - stripeRows : true, - listeners : { - itemdblclick : function(dataview, record, item, e) { - _this._edit(record.raw); - }, - cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { - if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonEdit') { - _this._edit(record.raw); - } - - if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonRemove') { - _this.onRemoveButtonClicked.notify(_this.store.getAt(rowIndex).raw.dewarId); - } - } - } - }, - selModel : { - mode : 'SINGLE' - } - }); - - var actions = _this._getTopButtons(); - this.grid.addDocked({ - xtype : 'toolbar', - items : actions - }); - - this.grid.getSelectionModel().on({ - selectionchange : function(sm, selections) { - if (selections.length) { - for ( var i = 0; i < actions.length; i++) { - if (actions[i].enable) { - actions[i].enable(); - } - } - } else { - for ( var i = 0; i < actions.length; i++) { - if (actions[i].alwaysEnabled == false) { - if (actions[i].disable) { - actions[i].disable(); - } - } - } - } - } - }); - return this.grid; -}; - -ExampleGrid.prototype.input = function() { - return { - proposal : DATADOC.getProposal_10(), - dewars : DATADOC.getDewars_10() - - }; -}; - -ExampleGrid.prototype.test = function(targetId) { - var ExampleGrid = new ExampleGrid({ - height : 150 - }); - BIOSAXS.proposal = new Proposal(ExampleGrid.input().proposal); - var panel = ExampleGrid.getPanel(ExampleGrid.input().dewars); - panel.render(targetId); - -}; - - -/** - * Shows a list of experiment AKA data acquisitions - * @height - * @sorters - * @minHeight - * @gridType: Ext.ux.LiveSearchGridPanel or Ext.grid.Panel - * @tbar true or false - * @grouping true or false - * @width - * @title - * #onEditButtonClicked - */ -function ExperimentGrid(args) { - this.width = "100%"; - this.height = 700; - this.minHeight = 500; - - this.id = BUI.id(); - this.gridType = 'Ext.grid.Panel'; //'Ext.ux.LiveSearchGridPanel'; - this.tbar = false; - this.hideHeaders = false; - this.grouping = true; - this.title = null; - - this.filtered = null; - - /** maximum row count **/ - this.limit = 100; - - this.sessionIdFilter = null; - - /** if not null filtered by date **/ - this.date = null; - - this.sorters = [ { - property : 'date', - direction : 'DESC' - }, { - property : 'time', - direction : 'DESC' - } ]; - - this.dates = {}; - if (args != null) { - if (args.height != null) { - this.height = args.height; - } - if (args.limit != null) { - this.limit = args.limit; - } - if (args.sorters != null) { - this.sorters = args.sorters; - } - if (args.sessionId != null){ - this.sessionIdFilter = args.sessionId; - } - if (args.filtered != null) { - this.filtered = args.filtered; - } - if (args.minHeight != null) { - this.minHeight = args.minHeight; - } - if (args.gridType != null) { - this.gridType = args.gridType; - } - if (args.tbar != null) { - this.tbar = args.tbar; - } - if (args.grouping != null) { - this.grouping = args.grouping; - } - if (args.width != null) { - this.width = args.width; - } - if (args.title != null) { - this.title = args.title; - } - - } - /** Events **/ - this.onEditButtonClicked = new Event(this); -} - -ExperimentGrid.prototype._getFilterTypes = function() { - return []; -}; - -ExperimentGrid.prototype._prepareData = function(rows) { - var data = []; - var count = 0; - - rows.sort(function(a,b){return b.experimentId - a.experimentId;}); - for ( var i = 0; i < rows.length; i++) { - var row = rows[i]; - this.dates[moment(row.creationDate).format("YYYYMMDD")] = moment(row.creationDate).format("MMM Do YY"); - if ( - ( this.filtered == null || row.experimentType == this.filtered ) && (count < this.limit || this.limit == null ) && (this.sessionIdFilter == null || this.date != null || (this.date == null && this.sessionIdFilter == row.sessionId)) - ){ - - data.push({ - experimentId : row.experimentId, - status : row.status, - dataAcquisitionFilePath : row.dataAcquisitionFilePath, - type : row.experimentType, - name : row.name, - macromolecules_names : row.macromolecules, - percentageAnalysed : { - value : (row.dataCollectionDoneCount / row.dataCollectionCount) * 100, - text : row.dataCollectionDoneCount + " of " + row.dataCollectionCount - }, - percentageCollected : { - value : (row.measurementDoneCount / row.measurementCount) * 100, - text : row.measurementDoneCount + " of " + row.measurementCount - }, - percentageMerged : { - value : (row.measurementAveragedCount / row.measurementCount) * 100, - text : row.measurementAveragedCount + " of " + row.measurementCount - }, - date : moment(row.creationDate).format("YYYYMMDD"), - time : moment(row.creationDate).format("YYYYMMDDHHmmss"), - creationDate : row.creationDate - }); - count ++; - } - } - return data; -}; - -ExperimentGrid.prototype.getPanel = function(experiments) { - this.features = experiments; - return this._renderGrid(experiments); -}; - -ExperimentGrid.prototype.refresh = function(experiments) { - this.experiments = experiments; - var filtered = []; - for ( var i = 0; i < experiments.length; i++) { - if (experiments[i].experimentType != "TEMPLATE") { - filtered.push(experiments[i]); - } - } - - this.parsedData = this._prepareData(filtered); - - var day = {}; - var dates = []; - for ( var i = 0; i < this.experiments.length; i++) { - var date = moment(this.experiments[i].creationDate).format("MMM Do YYYY"); - if (day[date] == null){ - dates.push({ - date : date, - value : moment(this.experiments[i].creationDate) - }); - day[date] = true; - } - } - - this.storeDate.loadData(dates, false); - this.store.loadData(this.parsedData, false); - - /** If it has already been filtered by date we keep the filter **/ - if (this.date != null){ - this._filterByDate(this.date); - } -}; - -ExperimentGrid.prototype._getTopButtons = function() { - var _this = this; - /** Actions buttons **/ - var actions = []; - - actions.push(Ext.create('Ext.Action', { - icon : '../images/calendar_icon.png', - text : 'Show Calendar', - disabled : false, - handler : function(widget, event) { - var window = Ext.create('Ext.window.Window', { - title : 'Calendar', - width : 600, - height : 600, - modal : true, - closable : true, - layout : { - type : 'vbox', - align : 'stretch' - }, - items : [ { - xtype : 'label', - html : 'Click on a data acquisition to select:', - margin : '5 5 5 5' - }, { - html : '
', - margin : '5 5 5 5' - } - - ] - }).show(); - - var calendarWidget = new CalendarWidget({ - height : 450 - }); - var aux = _this.limit; - /** we remove the limit temporarily **/ - _this.limit = null; - _this.sessionIdFilter = null; - calendarWidget.loadData(_this._prepareData(_this.experiments)); - _this.limit = aux; - calendarWidget.draw('calendar'); - calendarWidget.onClick.attach(function(sender, date) { - date = moment(date, "YYYY-MM-DD"); - _this._filterByDate(date); - window.close(); - - }); - - } - })); - this.storeDate = Ext.create('Ext.data.ArrayStore', { - fields: ['date', 'value'], - data : [] - }); - - this.dateMenu = Ext.create('Ext.form.field.ComboBox', { - hideLabel: true, - store: this.storeDate, - displayField: 'date', - typeAhead: true, - queryMode: 'local', - margin : '0 0 0 30', - triggerAction: 'all', - emptyText:'Select a date...', - selectOnFocus:true, - width:135, - listeners:{ - scope: this, - 'select': function (a,b,c){ - _this.limit = null; - _this._filterByDate(moment(b[0].raw.value, "YYYY-MM-DD")); - } - } - }); - - actions.push(this.dateMenu); - - actions.push("->"); - if (_this.filtered != null){ - actions.push({ - html : "Experiment Type: " + _this.filtered +"" - }); - } - else{ - actions.push({ - html : "Experiment Type: ALL" - }); - } - return actions; -}; - -/** - * Date format: "YYYY-MM-DD" - */ -ExperimentGrid.prototype._filterByDate = function(date) { - var experimentsFiltered = []; - /** Getting all the experiments of date**/ - for ( var i = 0; i < this.experiments.length; i++) { - var experiment = this.experiments[i]; - if (experiment.creationDate != null) { - var experimentDate = moment(experiment.creationDate); - if (experimentDate.year() == date.year()) { - if (experimentDate.month() == date.month()) { - if (experimentDate.date() == date.date()) { - experimentsFiltered.push(experiment); - } - } - } - } - } - var parsedData = this._prepareData(experimentsFiltered); - this.store.loadData(parsedData); - this.date = date; -}; - -/** Only for templates **/ -ExperimentGrid.prototype._removeExperimentById = function(experimentId) { - var _this = this; - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(evt, args) { - _this.grid.setLoading(false); - document.getElementById(BIOSAXS.targetId).innerHTML = ""; - BIOSAXS.start(BIOSAXS.targetId); - }); - this.grid.setLoading("Removing experiment "); - adapter.removeExperimentById(experimentId); -}; - -ExperimentGrid.prototype._renderGrid = function() { - var _this = this; - - /** Store **/ - this.store = Ext.create('Ext.data.Store', { - fields : this._getColumns(), - groupField : 'date', - autoload : true, - data : [], - remoteSort: false, - sorters : this.sorters - }); - - - - - var groupingFeature = Ext.create('Ext.grid.feature.Grouping', { - groupHeaderTpl : Ext.create('Ext.XTemplate', - "
{name:this.formatName}
", { - formatName : function(name) { - return _this.dates[name]; - } - }), - hideGroupedHeader : true, - startCollapsed : false - }); - - this.features = []; - if (this.grouping) { - this.features.push(groupingFeature); - } - - /** Grid **/ - this.grid = Ext.create(this.gridType, { - hideHeaders : this.hideHeaders, - resizable : true, - title : this.title, - width : this.width, - minHeight : this.minHeight, - height : this.height, - features : this.features, - store : this.store, - columns : this._getColumns(), - selModel : { - mode : 'SINGLE' - }, - viewConfig : { - stripeRows : true, - getRowClass : function(record, rowIdx, params, store) { - if (record.raw.type == "TEMPLATE") { - return "template-color-row"; - } - if ((record.raw.type == "CALIBRATION") && (record.raw.status == "FINISHED")) { - return "blue-row"; - } - }, - listeners : { - itemdblclick : function(dataview, record, item, e) { - _this._editExperiment(record.raw.experimentId); - }, - cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { - if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'GO') { - _this._editExperiment(record.raw.experimentId); - } - - if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'REMOVE') { - _this._removeExperimentById(record.raw.experimentId); - } - - } - } - } - }); - - var actions = _this._getTopButtons(); - - if (this.tbar) { - this.grid.addDocked({ - xtype : 'toolbar', - items : actions - }); - this.grid.getSelectionModel().on({ - selectionchange : function(sm, selections) { - if (selections.length) { - for ( var i = 0; i < actions.length; i++) { - if (actions[i].enable) { - actions[i].enable(); - } - } - } else { - for ( var i = 0; i < actions.length; i++) { - if (actions[i].alwaysEnabled == false) { - if (actions[i].disable) { - actions[i].disable(); - } - } - } - } - } - }); - } - - return this.grid; -}; - -ExperimentGrid.prototype._getColumns = function() { - var _this = this; - function actionItemRenderer(value, meta, record, rowIx, ColIx, store) { - if (record.raw.buffer3VOs != null) { - if (record.raw.buffer3VOs.length > 0) { - return 'x-hide-display'; - } - } - - if (record.data.platesCount > 0) { - return 'x-hide-display'; - } - } - - return [ - { - text : 'experimentId', - dataIndex : 'experimentId', - name : 'experimentId', - type : 'string', - hidden : true - }, - { - xtype : 'rownumberer', - width : 40 - }, - { - text : 'Name', - dataIndex : 'name', - name : 'name', - type : 'string', - flex : 1 - }, - - { - text : 'Type', - dataIndex : 'type', - name : 'type', - type : 'string', - flex : 1, - renderer : function(val) { - if (val == "CALIBRATION") { - return "" + val + ""; - } - - return val; - } - }, - { - text : 'Macromolecules', - name : 'macromolecules_names', - dataIndex : 'macromolecules_names', - flex : 1, - renderer : function(val) { - if (val != null) { - return " " + val + ""; - } - return " Information not available"; - } - }, - { - text : 'Buffers', - dataIndex : 'buffer_names', - name : 'buffer_names', - flex : 1, - hidden : true, - renderer : function(val) { - return "Buffer/s: " + val + ""; - } - }, - { - text : 'Status', - dataIndex : 'status', - name : 'status', - type : 'string', - flex : 1, - renderer : function(val, x, sample) { - if (sample.raw.type == "TEMPLATE") { - return "READY"; - } - if (sample.raw.status == "ABORTED") { - return "" + val + ""; - } - return "" + val + ""; - } - }, - { - text : 'Download', - dataIndex : 'creationDate', - name : 'creationDate', - renderer : function(val, x, sample) { - if (sample != null) { - if (sample.raw.type == "HPLC") { - return; - } - return BUI.getZipHTMLByExperimentId(sample.raw.experimentId, sample.raw.name); - } - }, - width : 100 - - }, - { - header : 'Measurements', - dataIndex : 'percentageCollected', - name : 'percentageCollected', - type : 'string', - renderer : function(val, y, sample) { - if ((sample.raw.type == "TEMPLATE") || (sample.raw.type == "HPLC")) { - return; - } - return "
" + BUI.getProgessBar(sample.raw.percentageCollected.value, sample.raw.percentageCollected.text) + "
"; - }, - width : 100 - }, - { - header : 'Averaged', - dataIndex : 'percentageMerged', - name : 'percentageMerged', - type : 'string', - renderer : function(val, y, sample) { - if ((sample.raw.type == "TEMPLATE") || (sample.raw.type == "HPLC")) { - return; - } - return "
" + BUI.getProgessBar(sample.raw.percentageMerged.value, sample.raw.percentageMerged.text) + "
"; - }, - width : 100 - }, - { - header : 'Subtractions', - dataIndex : 'percentageAnalysed', - name : 'percentageAnalysed', - type : 'string', - renderer : function(val, y, sample) { - if ((sample.raw.type == "TEMPLATE") || (sample.raw.type == "HPLC")) { - return; - } - return "
" + BUI.getProgessBar(sample.raw.percentageAnalysed.value, sample.raw.percentageAnalysed.text) + "
"; - }, - width : 100 - }, - - { - text : 'time', - dataIndex : 'time', - name : 'time', - hidden : true, - renderer : function(val) { - return val; - }, - width : 100 - - }, { - text : 'Date', - dataIndex : 'date', - name : 'date', - renderer : function(val) { - return val; - }, - width : 100 - - }, - - { - text : 'Time', - dataIndex : 'creationDate', - name : 'creationDate', - renderer : function(val) { - return moment(val).format(" HH:mm:ss"); - }, - width : 100 - - }, { - id : _this.id + 'GO', - width : 80, - sortable : false, - renderer : function(value, metaData, record, rowIndex, colIndex, store) { - return BUI.getGreenButton('GO'); - } - } ]; -}; - -/** Changes location.href in order to edit the experiment **/ -ExperimentGrid.prototype._editExperiment = function(experimentId) { - if (Ext.urlDecode(window.location.href).sessionId != null) { - location.href = 'viewProjectList.do?reqCode=display&experimentId=' + experimentId + '&sessionId='+ Ext.urlDecode(window.location.href).sessionId; - } else { - location.href = 'viewProjectList.do?reqCode=display&experimentId=' + experimentId; - } -}; - -ExperimentGrid.prototype.input = function() { - var experiments = DATADOC.getExperimentList_10(); - return { - experiments : experiments, - proposal : new MeasurementGrid().input().proposal - - }; -}; - -ExperimentGrid.prototype.test = function(targetId) { - var experimentGrid = new ExperimentGrid({ - height : 350, - minHeight : 350, - width : 1000 - - }); - BIOSAXS.proposal = new Proposal(experimentGrid.input().proposal); - var panel = experimentGrid.getPanel(experimentGrid.input().experiments); - experimentGrid.refresh(experimentGrid.input().experiments); - panel.render(targetId); -}; - -function FitStructureToExperimentDataGrid(args) { - this.height = null; - this.width = null; - this.id = BUI.id(); - - if (args != null) { - if (args.height != null) { - this.height = args.height; - - if (args.width != null) { - this.width = args.width; - } - } - } - - this.onSelected = new Event(this); -} - -FitStructureToExperimentDataGrid.prototype.getStructuresByMacromolecule = function(macromolecule) { - var structures = []; - if (macromolecule.structure3VOs != null) { - if (macromolecule.structure3VOs.length > 0) { - for (var i = 0; i < macromolecule.structure3VOs.length; i++) { - structures.push(macromolecule.structure3VOs[i]); - } - } - } - return structures; -}; - -FitStructureToExperimentDataGrid.prototype._prepareData = function(subtractions) { - var data = []; - for (var i = 0; i < subtractions.length; i++) { - - for (var j = 0; j < subtractions[i].fitStructureToExperimentalData3VOs.length; j++) { - var fit = subtractions[i].fitStructureToExperimentalData3VOs[j]; - data.push({ - fit : fit.fitFilePath, - fitStructureToExperimentalDataId : fit.fitStructureToExperimentalDataId, - mixtureToStructure3VOs : fit.mixtureToStructure3VOs, - subtractedFile : subtractions[i].substractedFilePath - }); - } - } - return data; - -}; - -FitStructureToExperimentDataGrid.prototype.refresh = function(subtractions) { - this.store.loadData(this._prepareData(subtractions), false); -}; - -FitStructureToExperimentDataGrid.prototype.getPanel = function() { - var _this = this; - this.store = Ext.create('Ext.data.Store', { - fields : [ 'fit', 'subtractedFile', 'structureId', 'fitFilePath', 'mixtureToStructure3VOs' ], - data : [] - }); - - this.store.sort([ { - property : 'name', - direction : 'ASC' - } ]); - - this.selModel = Ext.create('Ext.selection.RowModel', { - allowDeselect : true, - mode : 'MULTI', - listeners : { - selectionchange : function(sm, selections) { - var selected = []; - for (var i = 0; i < selections.length; i++) { - selected.push(selections[i].raw); - } - _this.onSelected.notify(selected); - } - } - }); - - this.panel = Ext.create('Ext.grid.Panel', { - store : this.store, - deferredRender : false, - width : this.width, - height : this.height, - margin : 10, - selModel : this.selModel, - columns : [ { - text : 'Name', - dataIndex : 'fit', - flex : 1, - renderer : function(val, b, record) { - return BUI.getFileName(val); - } - }, { - text : 'PDB', - dataIndex : 'mixtureToStructure3VOs', - flex : 1, - renderer : function(values, b, record) { - var html = ""; - for (var i = 0; i < values.length; i++) { - var structure = BIOSAXS.proposal.getStructuresById(values[i].structureId); - html = html + ""; - if (structure != null){ - html = html + ""; - } - html = html + ""; - } - return html + "
" + structure.name + "
"; - } - }, { - text : 'Volume Fraction', - dataIndex : 'mixtureToStructure3VOs', - flex : 1, - renderer : function(values, b, record) { - var html = ""; - for (var i = 0; i < values.length; i++) { - html = html + ""; - html = html + ""; - html = html + ""; - } - - return html + "
" + values[i].volumeFraction + "
"; - } - },{ - text : 'Subtraction', - dataIndex : 'subtractedFile', - flex : 1, - renderer : function(values, b, record) { - return values.split("/")[values.split("/").length - 1]; - } - },], - - viewConfig : { - enableTextSelection : true, - preserveScrollOnRefresh : true - }, - listeners : { - cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { - }, - afterrender : function() { - } - } - }); - return this.panel; -}; - -/** Static method **/ -FitStructureToExperimentDataGrid.prototype.RUNFitScattering = function(subtractionId, structureId) { - var _this = this; - /** Add to Workflow **/ - var adapter = new BiosaxsDataAdapter(); - var workflow = { - 'workflowTitle' : 'FitExperimentalDatatoStructure', - 'comments' : 'FitExperimentalDatatoStructure run from ISPyB for subtractionId: ' + subtractionId + ' and structureId ' + structureId - } - - var inputs = [ { - name : 'subtractionId', - value : subtractionId - }, { - name : 'structureId', - value : structureId - } ]; - - adapter.onSuccess.attach(function(sender, data) { - /** Add to Fit **/ - var adapter2 = new BiosaxsDataAdapter(); - var fit = { - 'workflowId' : data.workflowId, - 'subtractionId' : subtractionId, - 'structureId' : structureId, - 'comments' : 'Sent to workflow engine' - } - adapter2.onSuccess.attach(function(sender, fit) { - _this.refresh(_this.subtractionId, _this.macromolecule); - }); - adapter2.addFitStructureData(fit); - - }); - adapter.addWorkflow(workflow, inputs); - -}; +/** + * Example form + * + * @witdh + * @height + */ +function MolarityForm(args) { + this.id = BUI.id(); + this.width = 700; + this.height = 500; + + if (args != null) { + if (args.width != null) { + this.width = args.width; + } + if (args.height != null) { + this.height = args.height; + } + } + + this.onSave = new Event(this); + this.onClose = new Event(this); +} + -/** - * It shows buffer grid with a top bar with "Add" button - * - * @height - * @searchBar - * @collapsed - * @width - */ -function FrameGrid(args) { - this.height = 500; - this.width = 500; - this.searchBar = false; - this.tbar = false; - this.collapsed = false; - - - if (args != null) { - if (args.height != null) { - this.height = args.height; - } - - if (args.searchBar != null) { - this.searchBar = args.searchBar; - } - - if (args.tbar != null) { - this.tbar = args.tbar; - } - - if (args.collapsed != null) { - this.collapsed = args.collapsed; - } - - if (args.width != null) { - this.width = args.width; - } - } -} - -FrameGrid.prototype._edit = function(bufferId) { - var _this = this; - var window = new BufferWindow(); - window.onSuccess.attach(function(sender, proposal) { - _this.store.loadData(BIOSAXS.proposal.getBuffers()); - }); - window.draw(BIOSAXS.proposal.getBufferById(bufferId)); -}; - -FrameGrid.prototype.refresh = function(buffers, experimentId) { - this.experimentId = experimentId; - this.store.loadData(this._prepareData(buffers), false); -}; - -FrameGrid.prototype._prepareData = function(buffers) { - return buffers; -}; - -FrameGrid.prototype._getTbar = function() { - var _this = this; - var actions = []; - - actions.push(Ext.create('Ext.Action', { - icon : '../images/add.png', - text : 'Add Buffer', - disabled : false, - handler : function(widget, event) { - var window = new BufferWindow(); - window.onSuccess.attach(function(sender) { - _this.store.loadData(BIOSAXS.proposal.getBuffers()); - }); - window.draw({}); - } - })); - return actions; -}; - -FrameGrid.prototype.getPanel = function(buffers) { - var _this = this; - - this.store = Ext.create('Ext.data.Store', { - fields : [ 'frameNumber', 'I0', 'Rg', 'Mass', 'Vc', 'Qr', 'quality'], - data : buffers - }); - - this.store.sort('frameNumber'); - - var type = 'Ext.grid.Panel'; - if (this.searchBar == true) { - type = 'Ext.ux.LiveSearchGridPanel'; - } - - this.grid = Ext.create(type, { - store : this.store, - height : this.height, - width : this.width, - margin : 5, - columns : [{ - text : 'Frame', - dataIndex : 'frameNumber', - flex : 1 - },{ - text : 'I0', - dataIndex : 'I0', - flex : 1, - renderer : function(val, y, sample) { - return BUI.formatValuesUnits(sample.data.I0, "", 12, 3); - } - },{ - text : 'Rg', - dataIndex : 'Rg', - flex : 1, - renderer : function(val, y, sample) { - return BUI.formatValuesUnits(sample.data.Rg, "nm", 12, 3); - } - },{ - text : 'Mass', - dataIndex : 'Mass', - flex : 1, - renderer : function(val, y, sample) { - return BUI.formatValuesUnits(sample.data.Mass, "", 12, 3); - } - },{ - text : 'Vc', - dataIndex : 'Vc', - flex : 1, - renderer : function(val, y, sample) { - return BUI.formatValuesUnits(sample.data.Vc, "", 12, 3); - } - },{ - text : 'Qr', - dataIndex : 'Qr', - flex : 1, - renderer : function(val, y, sample) { - return BUI.formatValuesUnits(sample.data.Qr, "", 12, 3); - } - },{ - text : 'Quality', - dataIndex : 'quality', - flex : 1, - renderer : function(val, y, sample) { - return (Number(sample.data.quality) * 100).toFixed(2) + "%"; - } - }, - { - text : '', - renderer : function(val, x, sample) { - if (sample != null) { - return BUI.getZipHTMLByFrameRangeId(_this.experimentId, sample.raw.frameNumber, sample.raw.frameNumber); - } - }, - width : 100 - }], - flex : 1, - viewConfig : { - stripeRows : true - } - }); - - /** Adding the tbar **/ - if (this.tbar) { - this.grid.addDocked({ - xtype : 'toolbar', - items : this._getTbar() - }); - } - return this.grid; -}; - -FrameGrid.prototype.input = function() { - return []; -}; - -FrameGrid.prototype.test = function(targetId) { - var frameGrid = new FrameGrid({ - width : 800, - height : 350, - collapsed : false, - tbar : true - }); - - var panel = frameGrid.getPanel([]); - panel.render(targetId); -}; - - -function PeakGrid(args) { - this.height = 500; - this.searchBar = false; - this.tbar = false; - this.collapsed = false; - this.width = 500; - this.showExtendedColumns = false; - - if (args != null) { - if (args.showExtendedColumns != null) { - this.showExtendedColumns = args.showExtendedColumns; - } - if (args.height != null) { - this.height = args.height; - } - if (args.searchBar != null) { - this.searchBar = args.searchBar; - } - - if (args.tbar != null) { - this.tbar = args.tbar; - } - - if (args.collapsed != null) { - this.collapsed = args.collapsed; - } - - if (args.width != null) { - this.width = args.width; - } - } -} - - -PeakGrid.prototype.refresh = function(buffers) { - this.store.loadData(this._prepareData(buffers), false); -}; - -PeakGrid.prototype._prepareData = function(buffers) { - -// for ( var i = 0; i < buffers.length; i++) { -// buffers[i].name = "Peak #" + (i+1); +MolarityForm.prototype._getNumericWithHelp = function(type, fieldLabel, fieldName, help) { + return Ext.create('Ext.container.Container', { + margin : "10 0 0 10", + items : [ { + xtype : type, + fieldLabel : fieldLabel, + name : fieldName, + id : this.id + fieldName, + decimalPrecision : 6 + }, { + xtype : 'label', + forId : 'myFieldId', + text : help, + margin : "5 0 0 105", + cls : "inline-help" + } ] + }); +}; + + +MolarityForm.prototype._getItems = function() { + this.macromoleculeCombo = BIOSAXS_COMBOMANAGER.getComboMacromoleculeByMacromolecules(this.getMacromuleculesCandidates(this.macromolecule), { + width : 250, + labelWidth : 100, + margin : 10 + }); + + return [ { + xtype : 'container', + flex : 1, + margin : '10 0 0 10', + border : 0, + layout : 'anchor', + defaultType : 'requiredtext', + items : [ this.macromoleculeCombo, + this._getNumericWithHelp("textfield", "Ratio", "ratio", "Number in assymmetric units") + ] + } ]; +}; + +MolarityForm.prototype._persist = function() { + var _this = this; + var macromoleculeId = this.macromoleculeCombo.getValue(); + var ratio = Ext.getCmp(this.id + "ratio").getValue(); + var comments = "Not used yet"; + var dataAdapter = new BiosaxsDataAdapter(); + this.panel.setLoading("Saving"); + dataAdapter.onSuccess.attach(function(sender, args) { + _this.onSave.notify(); + }); + dataAdapter.saveStoichiometry(this.macromolecule.macromoleculeId, macromoleculeId, ratio, comments); +}; + +MolarityForm.prototype._getButtons = function() { + var _this = this; + + function onClose() { + _this.onClose.notify(); + } + + return [ { + text : 'Save', + handler : function() { + _this._persist(); + } + }, { + text : 'Cancel', + handler : function() { + onClose(); + } + } ]; +}; + +MolarityForm.prototype.getPanel = function() { + this.panel = Ext.create('Ext.form.Panel', { +// width : null, + height : this.height, + margin : 2, + border : 1, + defaultType : 'requiredtext', + items : this._getItems(), + buttons : this._getButtons() + }); + return this.panel; +}; + +/** macromolecules contains all macromolecules except this one **/ +MolarityForm.prototype.getMacromuleculesCandidates = function(macromolecule) { + var macromolecules = []; + if ( BIOSAXS.proposal.macromolecules){ + for (var i = 0; i < BIOSAXS.proposal.macromolecules.length; i++) { + var m = BIOSAXS.proposal.macromolecules[i]; + if (this.macromolecule != null){ + if (m.macromoleculeId != this.macromolecule.macromoleculeId) { + macromolecules.push(m); + } + } + } + } + return macromolecules; +}; + + +/** It populates the form **/ +MolarityForm.prototype.refresh = function(macromolecule) { + this.macromolecule = macromolecule; + +}; + + +MolarityForm.prototype.input = function() { + return {}; +}; + + +/** It populates the form **/ +MolarityForm.prototype.test = function(targetId) { + var macromoleculeForm = new MolarityForm(); + var panel = macromoleculeForm.getPanel(); + panel.render(targetId); +}; + +///** +// * +// * @witdh +// * @height +// */ +//function MacromoleculeForm(args) { +// this.id = BUI.id(); +// this.width = 700; +// this.height = 500; +// +// if (args != null) { +// if (args.width != null) { +// this.width = args.width; +// } +// if (args.height != null) { +// this.height = args.height; +// } +// } +// +// this.onClose = new Event(this); +//} +// +//MacromoleculeForm.prototype.getMacromolecule = function() { +// this.macromolecule.name = Ext.getCmp(this.panel.getItemId()).getValues().name; +// this.macromolecule.acronym = Ext.getCmp(this.panel.getItemId()).getValues().acronym; +// this.macromolecule.comments = Ext.getCmp(this.panel.getItemId()).getValues().comments; +// this.macromolecule.extintionCoefficient = Ext.getCmp(this.panel.getItemId()).getValues().extintionCoefficient; +// this.macromolecule.molecularMass = Ext.getCmp(this.panel.getItemId()).getValues().molecularMass; +// return this.macromolecule; +//}; +// +//MacromoleculeForm.prototype.setMacromolecule = function(macromolecule) { +// this.pdbStore.loadData(macromolecule.structure3VOs); +// +//}; +// +//MacromoleculeForm.prototype.getForm = function(macromolecule) { +// this.panel = Ext.createWidget('form', { +// frame : false, +// border : 0, +// padding : 15, +// width : 550, +// height : 350, +// items : [ { +// xtype : 'container', +// layout : 'hbox', +// items : [ { +// xtype : 'container', +// flex : 1, +// border : false, +// layout : 'anchor', +// defaultType : 'requiredtext', +// items : [ { +// fieldLabel : 'Name', +// name : 'name', +// anchor : '95%', +// tooltip : "Name of the macromolecule", +// value : macromolecule.name +// }, { +// fieldLabel : 'Acronym', +// name : 'acronym', +// anchor : '95%', +// value : macromolecule.acronym +// } ] +// }, { +// xtype : 'container', +// flex : 1, +// layout : 'anchor', +// defaultType : 'textfield', +// items : [ { +// xtype : 'numberfield', +// fieldLabel : 'Mol. Mass (Da)', +// name : 'molecularMass', +// value : macromolecule.molecularMass, +// decimalPrecision : 6 +// }, { +// xtype : 'numberfield', +// fieldLabel : 'Extinction coef.', +// name : 'extintionCoefficient', +// value : macromolecule.extintionCoefficient, +// decimalPrecision : 6 +// } ] +// } ] +// }, { +// xtype : 'textareafield', +// name : 'comments', +// fieldLabel : 'Comments', +// value : macromolecule.comments, +// width : this.width - 10 +// }, +// +// { +// html : BUI.getWarningHTML("The macromolecule is unsaved. Save the macromolecule to get access to other tabs"), +// margin : "150 10 10 10", +// id : this.id + "unsavedWarning", +// hidden : !(!macromolecule.macromoleculeId) +// } +// +// ] +// }); +// return this.panel; +//}; +// +//MacromoleculeForm.prototype.save = function() { +// var _this = this; +// +// var adapter = new BiosaxsDataAdapter(); +// adapter.onSuccess.attach(function(sender, proposal) { +// BIOSAXS.proposal.setItems(proposal); +// _this.panel.setLoading(false); +// +// Ext.getCmp(_this.id + "assembly").enable() +// Ext.getCmp(_this.id + "advanced").enable(); +// Ext.getCmp(_this.id + "unsavedWarning").hide(); +// }); +// +// if (this.getMacromolecule().name == "") { +// BUI.showError("Name field is mandatory"); +// return; +// } +// if (this.getMacromolecule().acronym == "") { +// BUI.showError("Acroynm field is mandatory"); +// return; +// } +// +// /** Check if acronym is unique * */ +// if (this.getMacromolecule().macromoleculeId == null) { +// if (BIOSAXS.proposal.getMacromoleculeByAcronym(this.getMacromolecule().acronym) == null) { +// this.panel.setLoading("ISPyB: Saving Macromolecule") +// adapter.saveMacromolecule(this.getMacromolecule()); +// } else { +// alert("There is already an existing macromolecule with the same acronym"); +// +// } +// } else { +// this.panel.setLoading("ISPyB: Saving Macromolecule") +// adapter.saveMacromolecule(this.getMacromolecule()); +// } +// +//}; +// +//MacromoleculeForm.prototype.getPanel = function(macromolecule) { +// var _this = this; +// this.macromolecule = macromolecule; +// return Ext.createWidget('tabpanel', { +// height : this.height, +// margin : 5, +// plain : true, +// style : { +// padding : 5 +// }, +// items : [ { +// tabConfig : { +// title : "General", +// disabled : false +// }, +// items : [ this.getForm(macromolecule) ], +// bbar : [ "->", { +// text : 'Save', +// cls : 'btn-with-border', +// style : { +// +// border : 1 +// }, +// handler : function() { +// _this.save(); +// } +// }, { +// text : 'Close', +// cls : 'btn-with-border', +// handler : function() { +// _this.onClose.notify(); +// } +// } ] +// }, { +// tabConfig : { +// id : this.id + "assembly", +// title : "Assembly", +// tooltip : 'Description of subunits present in the macromolecule', +// // hidden : (!macromolecule.macromoleculeId), +// disabled : (!macromolecule.macromoleculeId) +// }, +// items : [ this.getMolarityGrid(macromolecule) ] +// }, { +// tabConfig : { +// id : this.id + "advanced", +// title : "Advanced Modeling", +// // hidden : (!macromolecule.macromoleculeId), +// tooltip : 'Definition of the description contacts and symetries', +// disabled : (!macromolecule.macromoleculeId) +// }, +// items : [ this.getRigidBodyForm(macromolecule) ] +// } ] +// }); +//}; +// +//MacromoleculeForm.prototype.getRigidBodyForm = function(macromolecule) { +// var _this = this; +// +// // [P1, P2, P3, P4 ,P5 ,P6 ,32, P42, P52, P62] + P222 +// var symmetry = Ext.create('Ext.data.Store', { +// fields : [ 's' ], +// data : [ { +// "s" : "P1" +// }, { +// "s" : "P2" +// }, { +// "s" : "P3" +// }, { +// "s" : "P4" +// }, { +// "s" : "P5" +// }, { +// "s" : "P6" +// }, { +// "s" : "P32" +// }, { +// "s" : "P42" +// }, { +// "s" : "P52" +// }, { +// "s" : "P62" +// }, { +// "s" : "P222" +// } ] +// }); +// +// if (macromolecule.symmetry == null) { +// macromolecule.symmetry = "P1"; +// } +// var comboBox = Ext.create('Ext.form.ComboBox', { +// fieldLabel : 'Symmetry', +// store : symmetry, +// id : 'comboSym', +// queryMode : 'local', +// displayField : 's', +// valueField : 's', +// value : macromolecule.symmetry, +// margin : "10 0 0 0", +// listeners : { +// change : function(combo, newValue, oldValue, eOpts) { +// macromolecule.symmetry = newValue; +// } +// } +// }); +// +// this.rigidBodyPanel = Ext.createWidget('form', { +// frame : false, +// border : 0, +// padding : 10, +// width : 550, +// height : 400, +// items : [ { +// xtype : 'container', +// layout : 'hbox', +// items : [ { +// xtype : 'container', +// border : false, +// layout : 'hbox', +// items : [ { +// xtype : 'label', +// forId : 'myFieldId', +// text : 'Contact Desc:', +// width : 105, +// margin : '0 0 0 0' +// }, { +// xtype : 'textfield', +// hideLabel : true, +// id : "contactsDescriptionFilePath", +// margin : '0 0 0 0', +// width : 300, +// value : macromolecule.contactsDescriptionFilePath +// }, { +// text : 'Upload', +// xtype : 'button', +// margin : "0 0 0 20", +// width : 100, +// handler : function() { +// _this._openUploadDialog(macromolecule.macromoleculeId, "CONTACTS", "Upload Contact Description File"); +// } +// } ] +// } ] +// }, +// +// comboBox, { +// xtype : 'checkbox', +// margin : '10 0 0 5', +// boxLabel : "I want rigid body modeling run on this stuff", +// checked : true, +// width : 300 +// }, _this.getPDBGrid(macromolecule) ] +// }); +// return this.rigidBodyPanel; +//}; +// +//MacromoleculeForm.prototype.update = function() { +// var _this = this; +// BIOSAXS.proposal.onInitialized.attach(function() { +// if (BIOSAXS.proposal != null) { +// var macromolecules = BIOSAXS.proposal.macromolecules; +// for (var i = 0; i < macromolecules.length; i++) { +// if (macromolecules[i].macromoleculeId == _this.macromolecule.macromoleculeId) { +// _this.macromolecule = macromolecules[i]; +// _this.setMacromolecule(_this.macromolecule); +// _this.molarityStore.loadData(_this.parseMolarityData(_this.macromolecule)); +// _this.pdbGrid.setLoading(false); +// Ext.getCmp("contactsDescriptionFilePath").setValue(_this.macromolecule.contactsDescriptionFilePath) +// _this.molarityGrid.setLoading(false); +// +// } +// } +// } +// }); +// this.molarityGrid.setLoading("Updating"); +// this.pdbGrid.setLoading("Updating"); +// BIOSAXS.proposal.init(); +//}; +// +///******************************************************************************* +// * MOLARITY GRID +// ******************************************************************************/ +// +//MacromoleculeForm.prototype.parseMolarityData = function(macromolecule) { +// var data = []; +// if (macromolecule.stoichiometry != null) { +// for (var i = 0; i < macromolecule.stoichiometry.length; i++) { +// data.push({ +// ratio : macromolecule.stoichiometry[i].ratio, +// acronym : macromolecule.stoichiometry[i].macromolecule3VO.acronym, +// comments : macromolecule.stoichiometry[i].macromolecule3VO.comments, +// stoichiometryId : macromolecule.stoichiometry[i].stoichiometryId, +// name : macromolecule.stoichiometry[i].macromolecule3VO.name +// }); +// } +// } +// return data; +//}; +// +//MacromoleculeForm.prototype.getMolarityGrid = function(macromolecule) { +// var _this = this; +// +// this.molarityStore = Ext.create('Ext.data.Store', { +// fields : [ 'acronym', 'ratio', 'comments', 'stoichiometryId', 'name' ], +// data : this.parseMolarityData(macromolecule), +// sorters : { +// property : 'ratio', +// direction : 'DESC' +// } +// }); +// +// this.molarityGrid = Ext.create('Ext.grid.Panel', { +// store : this.molarityStore, +// height : 350, +// padding : 5, +// columns : [ +// +// { +// text : 'Subunit', +// columns : [ { +// text : "Acronym", +// width : 100, +// hidden : false, +// dataIndex : 'acronym', +// sortable : true +// }, { +// text : "Name", +// width : 100, +// hidden : false, +// dataIndex : 'name', +// sortable : true +// }, { +// text : "Comments", +// width : 100, +// dataIndex : 'comments', +// sortable : true +// } ] +// }, { +// text : "Number
in assymmetric units", +// width : 150, +// dataIndex : 'ratio', +// tooltip : 'Number of times this subunit is present in the macromolecule', +// sortable : true +// }, { +// id : this.id + 'MOLARITY_REMOVE', +// flex : 0.1, +// sortable : false, +// renderer : function(value, metaData, record, rowIndex, colIndex, store) { +// return BUI.getRedButton('REMOVE'); +// } +// } ], +// listeners : { +// cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { +// if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'MOLARITY_REMOVE') { +// var dataAdapter = new BiosaxsDataAdapter(); +// dataAdapter.onSuccess.attach(function() { +// _this.molarityGrid.setLoading(false); +// _this.update(); +// }); +// dataAdapter.removeStoichiometry(record.data.stoichiometryId); +// _this.molarityGrid.setLoading("Removing Structure"); +// } +// } +// }, +// buttons : [ { +// text : 'Add molarity', +// handler : function() { +// +// function onClose() { +// w.destroy(); +// _this.update(); +// } +// var w = Ext.create('Ext.window.Window', { +// title : 'Molarity', +// height : 300, +// width : 500, +// modal : true, +// buttons : [ { +// text : 'Save', +// handler : function() { +// var macromoleculeId = (_this.macromoleculeCombo.getValue()); +// var ratio = Ext.getCmp(_this.id + "ratio").getValue(); +// var comments = ""; +// var dataAdapter = new BiosaxsDataAdapter(); +// dataAdapter.onSuccess.attach(function(sender, args) { +// _this.update(); +// w.destroy(); +// }); +// dataAdapter.saveStoichiometry(_this.macromolecule.macromoleculeId, macromoleculeId, ratio, comments); +// } +// }, { +// text : 'Cancel', +// handler : function() { +// onClose(); +// } +// } ], +// items : [ _this.getMolarityForm(macromolecule) ], +// listeners : { +// onEsc : function() { +// onClose(); +// }, +// close : function() { +// onClose(); +// } +// } +// }).show(); +// } +// } ] +// }); +// return this.molarityGrid; +//}; +// +///******************************************************************************* +// * PDB GRID +// ******************************************************************************/ +// +//MacromoleculeForm.prototype._openUploadDialog = function(macromoleculeId, type, title) { +// var _this = this; +// function onClose() { +// w.destroy(); +// _this.update(); +// } +// +// var w = Ext.create('Ext.window.Window', { +// title : title, +// height : 200, +// width : 400, +// modal : true, +// buttons : [ { +// text : 'Close', +// handler : function() { +// onClose(); +// } +// } ], +// layout : 'fit', +// items : { +// html : "" +// }, +// listeners : { +// onEsc : function() { +// onClose(); +// }, +// close : function() { +// onClose(); +// } +// } +// }).show(); +//}; +// +//MacromoleculeForm.prototype._getPlugins = function() { +// var _this = this; +// var plugins = []; +// // if (this.updateRowEnabled) { +// plugins.push(Ext.create('Ext.grid.plugin.RowEditing', { +// clicksToEdit : 1, +// listeners : { +// validateedit : function(grid, e) { +// /** Comments are always updatable* */ +// e.record.raw.symmetry = e.newValues.symmetry; +// e.record.raw.multiplicity = e.newValues.multiplicity; +// +// var adapter = new BiosaxsDataAdapter(); +// adapter.onSuccess.attach(function(sender, measurement) { +// // _this.grid.setLoading(false); +// }); +// adapter.onError.attach(function() { +// alert("Error"); +// // _this.grid.setLoading(false); +// }); +// +// // _this.grid.setLoading(); +// adapter.saveStructure(e.record.raw); +// } +// } +// })); +// // } +// return plugins; +//}; +// +//MacromoleculeForm.prototype.getPDBGrid = function(macromolecule) { +// var _this = this; +// +// var data = macromolecule.structure3VOs; +// +// // /** Getting PDB from subunits **/ +// // if (macromolecule != null){ +// // if (macromolecule.stoichiometry != null){ +// // for (var i =0; i < macromolecule.stoichiometry.length; i++){ +// // var stoichiometry = macromolecule.stoichiometry[i]; +// // if (stoichiometry.macromolecule3VO != null){ +// // if (stoichiometry.macromolecule3VO.structure3VOs != null){ +// // for (var j = 0; j < stoichiometry.macromolecule3VO.structure3VOs.length; +// // j++) { +// // var structure = stoichiometry.macromolecule3VO.structure3VOs[j]; +// // structure["isSubunit"] = stoichiometry.macromolecule3VO.acronym; +// // data.push(structure) +// // } +// // } +// // } +// // } +// // } +// // } +// +// this.pdbStore = Ext.create('Ext.data.Store', { +// fields : [ 'filePath', 'structureId', 'structureType', 'symmetry', 'structureId', 'name', 'multiplicity' ], +// data : macromolecule.structure3VOs, +// groupField : 'structureType', +// sorters : { +// property : 'structureId', +// direction : 'DESC' +// } +// }); +// +// var groupingFeature = Ext.create('Ext.grid.feature.Grouping', { +// groupHeaderTpl : Ext.create('Ext.XTemplate', +// "
{name:this.formatName}
", { +// formatName : function(name) { +// return name; +// } +// }), +// hideGroupedHeader : true, +// startCollapsed : false +// }); +// +// this.pdbGrid = Ext.create('Ext.grid.Panel', { +// margin : "15 0 0 5", +// height : 250, +// store : this.pdbStore, +// plugins : _this._getPlugins(), +// buttons : [ { +// // text : 'Add PDB file', +// text : 'Add Modeling Option', +// handler : function() { +// _this._openUploadDialog(macromolecule.macromoleculeId, "PDB", 'Upload PDB File'); +// } +// } +// +// ], +// columns : [ +// { +// text : "structureId", +// flex : 0.2, +// hidden : true, +// dataIndex : 'structureId', +// sortable : true +// }, +// { +// text : "File", +// flex : 0.5, +// dataIndex : 'filePath', +// sortable : true, +// hidden : true +// }, +// { +// text : "PDB", +// flex : 0.4, +// dataIndex : 'name', +// sortable : true +// }, +// { +// text : "Symmetry", +// flex : 0.4, +// dataIndex : 'symmetry', +// sortable : true, +// editor : { +// xtype : 'combobox', +// typeAhead : true, +// triggerAction : 'all', +// selectOnTab : true, +// store : [ [ "P1", "P1" ], [ "P2", "P2" ], [ "P3", "P3" ], [ "P4", "P4" ], [ "P5", "P5" ], [ "P6", "P6" ], [ "P32", "P32" ], [ "P42", "P42" ], +// [ "P52", "P52" ], [ "P62", "P62" ], [ "P222", "P222" ] ], +// } +// }, { +// text : "Multiplicity", +// flex : 0.4, +// dataIndex : 'multiplicity', +// sortable : true, +// editor : { +// xtype : 'textfield' +// } +// +// }, { +// text : "Subunit", +// flex : 0.2, +// dataIndex : 'isSubunit', +// sortable : true, +// hidden : true +// }, { +// text : "Type", +// flex : 0.2, +// dataIndex : 'structureType', +// sortable : true, +// hidden : true +// }, +// +// { +// id : this.id + 'REMOVE', +// flex : 0.2, +// hidden : true, +// sortable : false, +// renderer : function(value, metaData, record, rowIndex, colIndex, store) { +// return BUI.getRedButton('REMOVE'); +// } +// }, ], +// +// listeners : { +// itemdblclick : function(dataview, record, item, e) { +// _this._editExperiment(record.raw.experimentId); +// }, +// cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { +// +// if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'REMOVE') { +// var dataAdapter = new BiosaxsDataAdapter(); +// dataAdapter.onSuccess.attach(function() { +// _this.pdbGrid.setLoading(false); +// _this.update(); +// }); +// dataAdapter.removeStructure(record.data.structureId); +// _this.pdbGrid.setLoading("Removing PDB file"); +// } +// +// } +// }, +// viewConfig : { +// getRowClass : function(record, rowIdx, params, store) { +// if (record.raw.isSubunit != null) { +// return "blue-row"; +// } +// } +// } +// }); +// +// return this.pdbGrid; +//}; +// +//MacromoleculeForm.prototype.getMolarityForm = function(macromolecule) { +// var _this = this; +// var data = []; +// for (var i = 0; i < BIOSAXS.proposal.macromolecules.length; i++) { +// var m = BIOSAXS.proposal.macromolecules[i]; +// if (m.macromoleculeId != macromolecule.macromoleculeId) { +// data.push(m); +// } +// // } - /** Adding information of buffer **/ - if (buffers.length > 0){ - buffers.unshift({ - name : "BUFFER", - start : 0, - experimentId : buffers[0].experimentId, - end : buffers[0].start - 1 - }); - } - - - return buffers; -}; - -PeakGrid.prototype._getTbar = function() { - var _this = this; - var actions = []; +// this.macromoleculeCombo = BIOSAXS_COMBOMANAGER.getComboMacromoleculeByMacromolecules(data, { +// width : 250, +// labelWidth : 100, +// margin : 10 +// }); +// +// return Ext.createWidget('form', { +// +// frame : false, +// border : 0, +// // padding : 15, +// width : 550, +// height : 350, +// items : [ { +// xtype : 'container', +// flex : 1, +// border : false, +// layout : 'anchor', +// defaultType : 'requiredtext', +// items : [ this.macromoleculeCombo, { +// xtype : 'numberfield', +// name : 'Ratio', +// id : _this.id + "ratio", +// fieldLabel : 'Number in assymmetric units', +// value : 1, +// decimalPrecision : 6, +// margin : 10 +// }, { +// xtype : 'textareafield', +// name : 'comments', +// fieldLabel : 'Comments', +// margin : 10, +// width : 400, +// value : "" +// +// } ] +// } ] +// }); +// +//}; +// +///******************************************************************************* +// * JAVASCRIPT DOC +// ******************************************************************************/ +//MacromoleculeForm.prototype.input = function() { +// return { +// macromolecule : DATADOC.getMacromolecule_10() +// }; +//}; +// +//MacromoleculeForm.prototype.test = function(targetId) { +// var macromoleculeForm = new MacromoleculeForm(); +// var panel = macromoleculeForm.getPanel(macromoleculeForm.input().macromolecule); +// panel.render(targetId); +//}; + +function ResultSummaryForm() { + this.radiationDamageThreshold = BUI.getRadiationDamageThreshold(); + this.qualityThreshold = BUI.getQualityThreshold(); - actions.push(Ext.create('Ext.Action', { - icon : '../images/add.png', - text : 'Add Buffer', - disabled : false, - handler : function(widget, event) { - var window = new BufferWindow(); - window.onSuccess.attach(function(sender) { - _this.store.loadData(BIOSAXS.proposal.getBuffers()); - }); - window.draw({}); - } - })); - return actions; -}; + /** Data clusters with bufers **/ + this.clusterByBuffers = false; + + /** Visualization are groupeb by concenttrations. Ex: 4.5mg/ml and 4.7mg/ml will be clusterized together **/ + this.collapseConcentrations = true; + + this.summaryHeight = 350; + this.id = BUI.id(); -PeakGrid.prototype.getPanel = function(buffers) { var _this = this; - this.peakCount = 0; - this.store = Ext.create('Ext.data.Store', { - fields : ['name', {name:'start', type : 'int'}, {name:'end', type : 'int'}], - data : buffers - - }); - - this.store.sort('start'); - var type = 'Ext.grid.Panel'; - if (this.searchBar == true) { - type = 'Ext.ux.LiveSearchGridPanel'; - } - this.grid = Ext.create(type, { - store : this.store, - height : this.height, - width : this.width, - margin : 5, - columns : [ - { - text : '', - dataIndex : 'name', - width : 100, - hidden : _this.showExtendedColumns, - renderer : function(val, y, sample) { - if (sample.data.name == "BUFFER") - return "BUFFER"; - _this.peakCount++; - return "Peak #" + _this.peakCount; - } - - - }, - { - text : 'Peaks', - dataIndex : 'start', - sortable : true, - width : 100, - hidden: !_this.showExtendedColumns, - renderer : function(val, y, sample) { - return "From " + Number(sample.raw.start) + " to " + Number(sample.raw.end); - } - }, - { - text : 'Frames', - hidden : _this.showExtendedColumns, - columns : [ - { - text : 'Start', - dataIndex : 'start', - sortable : true, - hidden : _this.showExtendedColumns, - width : 100, - renderer : function(val, y, sample) { - return Number(val); - } - },{ - text : 'End', - dataIndex : 'end', - width : 100, - hidden : _this.showExtendedColumns, - renderer : function(val, y, sample) { - return Number(val); - } - },{ - text : 'Total', - dataIndex : 'end', - width : 100, - hidden : _this.showExtendedColumns, - renderer : function(val, y, sample) { - return "" + (sample.raw.end - sample.raw.start) + " frames"; - } - } - ] - } -// , -// { -// text : '', -// hidden : _this.showExtendedColumns, -// renderer : function(val, x, sample) { -// if (sample != null) { -// return BUI.getZipHTMLByFrameRangeId(sample.raw.experimentId, sample.raw.start, sample.raw.end); -// } -// }, -// width : 100 -// } - ], - flex : 1, - viewConfig : { - stripeRows : true, - getRowClass : function(record, rowIdx, params, store) { - if (record.raw.name == "BUFFER") { - return "blue-row"; - } - } - } + this.qualityControlResultsWidget = new QualityControlResultsWidget({ + qualityThreshold : this.radiationDamageThreshold, + radiationDamageThreshold : this.qualityThreshold + + }); + this.qualityControlResultsWidget.onQualityChanged.attach(function(sender, value) { + _this.qualityThreshold = value; + _this.thresholdsChanged(); }); - /** Adding the tbar **/ - if (this.tbar) { - this.grid.addDocked({ - xtype : 'toolbar', - items : this._getTbar() - }); - } - return this.grid; -}; + this.qualityControlResultsWidget.onRadiationDatamageChanged.attach(function(sender, value) { + _this.radiationDamageThreshold = value; + _this.thresholdsChanged(); -PeakGrid.prototype.input = function() { - return []; -}; + }); + this.concentrationHTMLTableWidget = new ConcentrationHTMLTableWidget({ + height : 250, + showBufferColumns : this.clusterByBuffers + }); -PeakGrid.prototype.test = function(targetId) { - var PeakGrid = new PeakGrid({ - width : 800, + this.plot = new BoxWhiskerGraph({ + targetId : _this.id + '_boxPlot', height : 350, - collapsed : false, - tbar : true + width : 450, + maxBoxWidth : 25 }); - var panel = PeakGrid.getPanel([]); - panel.render(targetId); -}; - -/** - * Macromolecule Grid showing macromolecules and adding anb updating buttons - * - * @height - * @maxHeight - * @width - * @cssFontStyle - * @searchBar makes this grid as Ext.ux.LiveSearchGridPanel - * @tbar top bar containing "Add" and "Update From SMIS" button - * @collapsed - * @collapsible - * @btnEditVisible - * @btnRemoveVisible - * @multiselect makes it multiselect using Ext.selection.CheckboxModel - * - * #onSelected - * #onMacromoleculesChanged - */ -function MacromoleculeGrid(args) { - this.height = 500; - this.width = 500; - this.id = BUI.id(); - this.maxHeight = this.height; + this.rangePlot = new RangeWhiskerGraph({ + targetId : _this.id + '_rangePlot', + height : 350, + width : 450, + maxBoxWidth : 25 + }); +} - this.searchBar = false; - this.tbar = false; +ResultSummaryForm.prototype.thresholdsChanged = function() { + var parsedData = this.prepareData(this.rawData); - this.collapsible = true; - this.collapsed = true; + var filtered = JSON.parse(JSON.stringify(parsedData)); + filtered.concentration.concentrations = []; + for ( var conc in parsedData.concentration.concentrations) { + if (parsedData.concentration.concentrations[conc].calculation.rgGnom.validNumber > 0) { + filtered.concentration.concentrations.push(parsedData.concentration.concentrations[conc]); + } + } - this.btnEditVisible = true; - this.btnRemoveVisible = false; - this.multiselect = false; + this.plotWhisker(filtered); + this.plotRange(filtered); - /** Font style applied to the acronym column **/ - this.cssFontStyle = null; + this.concentrationHTMLTableWidget.refresh(parsedData); +}; - if (args != null) { - if (args.height != null) { - this.height = args.height; - this.maxHeight = this.height; - } - if (args.maxHeight != null) { - this.maxHeight = args.maxHeight; - } - if (args.width != null) { - this.width = args.width; - } - if (args.cssFontStyle != null) { - this.cssFontStyle = args.cssFontStyle; - } +/** Given a frame object (object returned by analyzeFrames()) and depending of the threshold indicates if a datacollection has radiation damage **/ +ResultSummaryForm.prototype.hasRadiationDamage = function(frameObject) { + var bb = frameObject.bufferBeforeFramesMerged / frameObject.framesCount; + var ba = frameObject.bufferAfterFramesMerged / frameObject.framesCount; + var mol = frameObject.framesMerge / frameObject.framesCount; + return !((bb >= this.radiationDamageThreshold) && (ba >= this.radiationDamageThreshold) && (mol >= this.radiationDamageThreshold)); +}; - if (args.searchBar != null) { - this.searchBar = args.searchBar; - } - if (args.tbar != null) { - this.tbar = args.tbar; - } - if (args.collapsible != null) { - this.collapsible = args.collapsible; - } - if (args.collapsed != null) { - this.collapsed = args.collapsed; - } - if (args.btnEditVisible != null) { - this.btnEditVisible = args.btnEditVisible; - } - if (args.btnRemoveVisible != null) { - this.btnRemoveVisible = args.btnRemoveVisible; - } - if (args.multiselect != null) { - this.multiselect = args.multiselect; - } +/** Return (frameObject) an object with the information about the frames for a data collection**/ +ResultSummaryForm.prototype.analyzeFrames = function(dataCollectionRow) { + return { + bufferAfterFramesMerged : dataCollectionRow.bufferAfterFramesMerged, + bufferBeforeFramesMerged : dataCollectionRow.bufferBeforeFramesMerged, + framesCount : dataCollectionRow.framesCount, + framesMerge : dataCollectionRow.framesMerge + }; +}; + +ResultSummaryForm.prototype.detectDataCollectionWarnings = function(dataCollectionRow) { + var frameObject = this.analyzeFrames(dataCollectionRow); + var warnings = { + count : 0, + type : [] + }; + + if (this.hasRadiationDamage(frameObject)) { + warnings.count = warnings.count + 1; + warnings.type.push("RADIATION DAMAGE"); } - this.onSelected = new Event(); - - this.onMacromoleculesChanged = new Event(); -} + if (Number(dataCollectionRow.quality) < this.qualityThreshold) { + warnings.count = 1;//warnings.count + 1; + warnings.type.push("Quality <" + this.qualityThreshold); + } + return warnings; +}; +/** Return array composed by {concentration} objects **/ +ResultSummaryForm.prototype.getConcentrations = function(data) { + var concentrationsId = {}; + for ( var i = 0; i < data.length; i++) { + var warning = this.detectDataCollectionWarnings(data[i]); + var id = data[i].conc;// + "_" + data[i].bufferId; + if (this.clusterByBuffers) { + id = data[i].conc + "_" + data[i].bufferId; + } + if (concentrationsId[id] == null) { + concentrationsId[id] = { + id : id, + concentration : Number(data[i].conc).toFixed(2), + bufferId : data[i].bufferId, + bufferAcronym : data[i].bufferAcronym, + rgGuinier : [], + i0Guinier : [], + rgGnom : [], + dMax : [], + quality : [], + frames : [], + frames_warning : warning.count + }; + } else { + concentrationsId[id].frames_warning = concentrationsId[id].frames_warning + warning.count; + } + concentrationsId[id].frames.push(data[i]); -MacromoleculeGrid.prototype.edit = function(macromolecule) { - var _this = this; - var window = new MacromoleculeWindow(); - window.onSave.attach(function(sender) { - _this.store.loadData(BIOSAXS.proposal.getMacromolecules()); - _this.onMacromoleculesChanged.notify(); - }); - window.draw(macromolecule); -}; + if (warning.count == 0) { + concentrationsId[id].rgGuinier.push(data[i].rgGuinier); + concentrationsId[id].i0Guinier.push(data[i].I0); + concentrationsId[id].quality.push(data[i].quality); + concentrationsId[id].rgGnom.push(data[i].rgGnom); + concentrationsId[id].dMax.push(data[i].dmax); + } -MacromoleculeGrid.prototype.getTbar = function() { - var _this = this; - var actions = []; + } + var concentrations = []; + for ( var item in concentrationsId) { + if (concentrationsId.hasOwnProperty(item)) { + concentrations.push({ + concentration : concentrationsId[item].concentration, + id : item, + bufferId : Number(concentrationsId[item].bufferId).toFixed(2), + bufferAcronym : concentrationsId[item].bufferAcronym, + rgGuinier : concentrationsId[item].rgGuinier, + /** Frames **/ + frames : concentrationsId[item].frames, + frames_warning : concentrationsId[item].frames_warning, + /** Calculation **/ + calculation : { + rgGuinier : BUI.getStandardDeviation(concentrationsId[item].rgGuinier), + i0Guinier : BUI.getStandardDeviation(concentrationsId[item].i0Guinier), + quality : BUI.getStandardDeviation(concentrationsId[item].quality), + rgGnom : BUI.getStandardDeviation(concentrationsId[item].rgGnom), + dMax : BUI.getStandardDeviation(concentrationsId[item].dMax) - actions.push(Ext.create('Ext.Action', { - icon : '../images/add.png', - text : 'Add macromolecule', - disabled : false, - handler : function(widget, event) { - _this.edit(); - } - })); - actions.push("->"); - actions.push(Ext.create('Ext.Action', { - icon : '../images/folder_go.png', - text : 'Update From SMIS', - tooltip : "Retrieve all the macromolecules of your proposal from SMIS database", - disabled : false, - handler : function(widget, event) { - _this.grid.setLoading("Connecting to SMIS"); - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, data) { - BIOSAXS.proposal.setMacromolecules(data.macromolecules); - _this.refresh(BIOSAXS.proposal.macromolecules); - _this.grid.setLoading(false); - }); - adapter.onError.attach(function(sender, data) { - _this.grid.setLoading(false); + } }); - adapter.updateDataBaseFromSMIS(); } - })); - return actions; -}; + } -MacromoleculeGrid.prototype.deselectAll = function() { - this.grid.getSelectionModel().deselectAll(); + return { + concentrations : concentrations + }; }; -MacromoleculeGrid.prototype.selectById = function(macromoleculeId) { - this.grid.getSelectionModel().deselectAll(); - for ( var i = 0; i < this.grid.getStore().data.items.length; i++) { - var item = this.grid.getStore().data.items[i].raw; - if (item.macromoleculeId == macromoleculeId) { - this.grid.getSelectionModel().select(i); +ResultSummaryForm.prototype.prepareData = function(data) { + /** Return array composed by {acronym, bufferId} objects **/ + function getBuffers(data) { + var buffersId = {}; + for ( var i = 0; i < data.length; i++) { + buffersId[data[i].bufferId] = data[i].acronym; + } + var buffers = []; + for ( var id in buffersId) { + if (buffersId.hasOwnProperty(id)) { + buffers.push({ + acronym : buffersId[id], + bufferId : id + }); + } } + return buffers; } -}; -MacromoleculeGrid.prototype.refresh = function(macromolecules) { - this.store.loadData(macromolecules, false); + /** Get a string with all the concentrations **/ + function getConcentrationString(parseConcentrations) { + var concentrations = []; + for ( var i = 0; i < parseConcentrations.concentrations.length; i++) { + concentrations.push(parseConcentrations.concentrations[i].concentration + " mg/ml "); + } + return concentrations.toString(); + } + + var parseConcentrations = this.getConcentrations(data); + + return { + dataCollectionCount : data.length, + buffers : getBuffers(data), + concentration : parseConcentrations, + concentrationLabel : getConcentrationString(parseConcentrations) + }; }; -MacromoleculeGrid.prototype.getColumns = function() { - var _this = this; - var columns = [ - { - text : 'Acronym', - dataIndex : 'acronym', - id : this.id + "acronym", - flex : 1, - renderer : function(value, metaData, record, rowIndex, colIndex, store) { - if (_this.cssFontStyle != null) { - return "" + value + ""; +ResultSummaryForm.prototype.getDataForWhisker = function(data) { + var clusters = []; + + var concentrations = {}; + var i = 0; + var conc = 0; + if (this.collapseConcentrations) { + for (i = 0; i < data.concentration.concentrations.length; i++) { + conc = data.concentration.concentrations[i]; + var concentration = Number(conc.concentration).toFixed(0); + if (concentrations[concentration] == null) { + concentrations[concentration] = {}; + concentrations[concentration].concentration = concentration; + concentrations[concentration].calculation = {}; + concentrations[concentration].calculation.rgGuinier = {}; + concentrations[concentration].calculation.rgGuinier.values = []; + concentrations[concentration].calculation.rgGuinier.values = conc.calculation.rgGuinier.values; + concentrations[concentration].calculation.rgGnom = {}; + concentrations[concentration].calculation.rgGnom.values = []; + concentrations[concentration].calculation.rgGnom.values = conc.calculation.rgGnom.values; + } else { + concentrations[concentration].calculation.rgGuinier.values = concentrations[concentration].calculation.rgGuinier.values + .concat(conc.calculation.rgGuinier.values); + concentrations[concentration].calculation.rgGnom.values = concentrations[concentration].calculation.rgGnom.values + .concat(conc.calculation.rgGnom.values); } - return value; } - }, { - text : 'Name', - dataIndex : 'name', - id : this.id + "name", - flex : 1, - hidden : true - } ]; - if (this.btnEditVisible) { - columns.push({ - id : _this.id + 'buttonEditMacromolecule', - width : 85, - sortable : false, - renderer : function(value, metaData, record, rowIndex, colIndex, store) { - if (_this.btnEditVisible) { - return BUI.getGreenButton('EDIT'); - } - return null; + /** From object to array **/ + var array = []; + for ( var key in concentrations) { + if (concentrations.hasOwnProperty(key)) { + array.push(concentrations[key]); } + } + data.concentration.concentrations = array; + } + + for (i = 0; i < data.concentration.concentrations.length; i++) { + conc = data.concentration.concentrations[i]; + clusters.push({ + name : conc.concentration + "mg/ml", + concentration : Number(conc.concentration), + x : Number(conc.concentration), + classes : [] + }); + clusters[clusters.length - 1].classes.push({ + name : "Guinier", + color : '#9A2EFE', + values : conc.calculation.rgGuinier.values + + }); + clusters[clusters.length - 1].classes.push({ + name : "P(r)", + color : '#2E64FE', + values : conc.calculation.rgGnom.values + }); } - if (this.btnRemoveVisible) { - columns.push({ - id : _this.id + 'buttonRemoveMacromolecule', - width : 85, - sortable : false, - renderer : function(value, metaData, record, rowIndex, colIndex, store) { - if (_this.btnRemoveVisible) { - return BUI.getRedButton('REMOVE'); - } - return null; - } + return { + clusters : clusters.sort(function(a, b) { + return a.concentration - b.concentration; + }) + }; +}; + +ResultSummaryForm.prototype.getDataForWhiskerQuality = function(data) { + var clusters = []; + + for ( var i = 0; i < data.concentration.concentrations.length; i++) { + var conc = data.concentration.concentrations[i]; + clusters.push({ + name : conc.concentration + "mg/ml", + classes : [] + }); + clusters[clusters.length - 1].classes.push({ + name : "Quality", + values : conc.calculation.quality.values + }); } - return columns; + return { + clusters : clusters + }; +}; + +ResultSummaryForm.prototype.plotQualityWhisker = function(parsedData) { + this.qualityPlot.refresh(this.getDataForWhiskerQuality(parsedData)); }; +ResultSummaryForm.prototype.plotWhisker = function(parsedData) { + this.plot.refresh(this.getDataForWhisker(parsedData)); +}; -/** Returns the grid **/ -MacromoleculeGrid.prototype.getPanel = function() { +ResultSummaryForm.prototype.plotRange = function(parsedData) { + this.rangePlot.refresh(this.getDataForWhisker(parsedData)); +}; + +ResultSummaryForm.prototype.getPanel = function(data) { var _this = this; + _this.rawData = data; + var parsedData = this.prepareData(data); - this.store = Ext.create('Ext.data.Store', { - fields : [ 'macromoleculeId', 'name', 'acronym' ] - }); + this.panel = Ext + .createWidget( + 'form', + { + bodyPadding : 20, + frame : false, + border : 0, + width : this.width, + height : this.summaryHeight + 1000, + items : [ { + xtype : 'container', + layout : 'hbox', + items : [ { + xtype : 'container', + flex : 1, + border : false, + layout : 'anchor', + defaultType : 'textfield', + items : [ + _this.qualityControlResultsWidget.getPanel(), + { + xtype : 'fieldset', + width : 950, + margin : "20, 0 0 0", + height : this.summaryHeight + 500, + items : [ + { + html : "
Concentration Analysis
", + border : 0, + width : 900 - var type = 'Ext.grid.Panel'; - if (this.searchBar == true) { - type = 'Ext.ux.LiveSearchGridPanel'; - } + }, + { + html : this.concentrationHTMLTableWidget.getPanel(parsedData), + border : 0, + width : 900, + margin : "10, 0 0 10" - this.selModel = Ext.create('Ext.selection.RowModel', { -// allowDeselect : true, -// mode : 'MULTI', - listeners : { - selectionchange : function(sm, selections) { - var selected = []; - for (var i = 0; i < selections.length; i++) { - selected.push(selections[i].raw); - } - _this.onSelected.notify(selected); - } - } - }); + }, + { + xtype : 'container', + layout : 'vbox', + border : 5, + items : [ + { + html : "
Rg based on Guinier and Gnom
", + border : 8, + width : 900, + margin : "20 0 0 10" - this.grid = Ext.create(type, { - id : this.id, - title : 'Macromolecules', - collapsible : this.collapsible, - collapsed : this.collapsed, - store : this.store, - height : this.height, - maxHeight : this.maxHeight, - selModel : this.selModel, - columns : this.getColumns(), - width : this.width, - viewConfig : { - stripeRows : true, - listeners : { - 'celldblclick' : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { - _this.edit(BIOSAXS.proposal.getMacromoleculeById(record.data.macromoleculeId)); - }, - 'cellclick' : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { - if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonEditMacromolecule') { - _this.edit(BIOSAXS.proposal.getMacromoleculeById(record.data.macromoleculeId)); - } - if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonRemoveMacromolecule') { - BUI.showBetaWarning(); - } - } + }, { + xtype : 'container', + layout : 'hbox', + items : [ { + xtype : 'container', + layout : 'vbox', + items : [ { + html : "
", + border : 0, + width : this.plot.width, + padding : "10 0 0 20", + listeners : { + afterrender : function() { + _this.plotWhisker(parsedData); - } - } - }); + } + } + }, { + html : "
Concentration (mg/ml)
", + width : 450, + border : 0, + margin : "-50 0 0 0" + + } ] + }, { + xtype : 'container', + layout : 'vbox', + items : [ { + html : "
", + border : 0, + width : this.rangePlot.width, + padding : "10 0 0 10", + listeners : { + afterrender : function() { + _this.plotRange(parsedData); + } + } + }, { + html : "
Concentration (mg/ml)
", + width : 450, + border : 0, + margin : "-50 0 0 0" + + } ] + } ] + } ] + } ] + } ] + } + + ] + } ] + }); + return this.panel; - /** Adding the tbar **/ - if (this.tbar) { - this.grid.addDocked({ - xtype : 'toolbar', - items : this.getTbar() - }); - } - return this.grid; }; -MacromoleculeGrid.prototype.input = function() { +ResultSummaryForm.prototype.input = function() { return { - proposal : DATADOC.getProposal_10() + data : DATADOC.getData_3367() }; }; -MacromoleculeGrid.prototype.test = function(targetId) { - var macromoleculeGrid = new MacromoleculeGrid({ - width : 800, - height : 350, - collapsed : false, - tbar : true - }); - - BIOSAXS.proposal = new Proposal(macromoleculeGrid.input().proposal); - var panel = macromoleculeGrid.getPanel(BIOSAXS.proposal.macromolecules); +ResultSummaryForm.prototype.test = function(targetId) { + var resultSummaryForm = new ResultSummaryForm(); + var panel = resultSummaryForm.getPanel(resultSummaryForm.input().data); panel.render(targetId); + }; -/** - * Shows measurements with their attributes as specimen, exposure temperature, - * volume to load, transmission etc... - * - * @addBtnMultipleEdit: if true add a button for changing measurements' - * parameters by choosing multiple measurements. It opens - * MultipleEditMeasurementGridWindow - * @collapsed: if true it doesn't show buffer's measurements - * @editor: hashmap containing columns to be edited. It is an array of a json - * like editor of Ext - * @selModel - * @collapseBtnEnable - * @removeBtnEnabled Ext.selection.RowModel - * @addBtnEnable - * @updateRowEnabled true/false if update plugin is set to the grid - * @showTitle - * @title - * @isStatusColumnHidden - * @isPriorityColumnHidden - * @isTimeColumnHidden - * @estimateTime - * @margin - * @tbar - * @height - * @maxHeight - * @minHeight - * @width - * @maxWidth - * @resizable - * @experimentColorBased when colors for buffers and macromolecules are not - * selected by the proposal but by experiment, so the - * number of colors is smaller +/** + * Example form + * + * @witdh + * @height + */ +function RigibBodyModelingForm(args) { + this.id = BUI.id(); + this.width = 700; + this.height = 500; + + if (args != null) { + if (args.width != null) { + this.width = args.width; + } + if (args.height != null) { + this.height = args.height; + } + } + + var _this = this; + this.rigidBodyGrid = new AprioriRigidBodyGrid(); + + this.rigidBodyGrid.onUploadFile.attach(function(sender, type, title){ + _this._openUploadDialog(_this.macromolecule.macromoleculeId, type, title); + }); + + this.rigidBodyGrid.onRemove.attach(function(sender, type, title){ + _this._update(); + }); + + this.onSave = new Event(this); + +} + +RigibBodyModelingForm.prototype._getItems = function() { + var _this = this; + + + return [ { + xtype : 'label', + forId : 'myFieldId', + text : 'Information for model fit, mixture analysis and rigid body modeling', + margin : '15 0 20 10', + cls : "inline-help" + }, + this.rigidBodyGrid.getPanel(), + { + xtype : 'label', + forId : 'myFieldId', + text : 'Distance restraints may be imposed on the model using contacts conditions file (OPTIONAL)', + margin : '25 0 5 10', + cls : "inline-help" + }, + { + xtype : 'container', + layout : 'hbox', + items : [ { + xtype : 'container', + border : false, + layout : 'hbox', + items : [ { + xtype : 'label', + forId : 'myFieldId', + text : 'Contact Description File: (Optional)', + width : 150, + margin : '10 0 0 10' + }, { + id : this.id + "contactsDescriptionFilePath", + xtype : 'textfield', + hideLabel : true, + margin : '10 0 0 0', + width : 200, + disabled: true + }, { + text : 'Upload', + xtype : 'button', + margin : "10 0 0 10", + width : 80, + handler : function() { + + _this._openUploadDialog(_this.macromolecule.macromoleculeId, "CONTACTS", "Upload Contact Description File"); + } + }, { + text : 'Remove', + id : _this.id + "_remove", + xtype : 'button', + margin : "10 0 0 10", + width : 80, + handler : function() { + _this.macromolecule.contactsDescriptionFilePath = null; + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, proposal) { + /** Updating the proposal global object **/ + BIOSAXS.proposal.setItems(proposal); + _this.panel.setLoading(false); + + var saved = BIOSAXS.proposal.getMacromoleculeByAcronym(_this.macromolecule.acronym); + /** Refreshing to check that everything is ok **/ + _this.refresh(saved); + _this.onSave.notify(saved); + }); + + _this.panel.setLoading("Saving Macromolecule") + adapter.saveMacromolecule(_this.macromolecule); + } + } ] + } ] + }, { + xtype : 'panel', + html : "Go to SASREF manual for further information", + margin : "10 0 0 160", + border : 0 + + }, + { + xtype : 'checkbox', + margin : '10 0 0 5', + boxLabel : "I want rigid body modeling run on this stuff", + checked : true, + width : 300 + } ] + +}; + +/** Because update is a jsp page we don't know if the user has uploaded a file or not then we need to refresh **/ +RigibBodyModelingForm.prototype._update = function(macromoleculeId, type, title) { + var _this = this; + BIOSAXS.proposal.onInitialized.attach(function() { + if (BIOSAXS.proposal != null) { + _this.refresh(BIOSAXS.proposal.getMacromoleculeById(_this.macromolecule.macromoleculeId)); + _this.panel.setLoading(false); + } + }); + this.panel.setLoading(); + BIOSAXS.proposal.init(); +}; + +RigibBodyModelingForm.prototype._openUploadDialog = function(macromoleculeId, type, title) { + var _this = this; + function onClose() { + w.destroy(); + _this._update(); + + } + + var w = Ext.create('Ext.window.Window', { + title : title, + height : 200, + width : 400, + modal : true, + buttons : [ { + text : 'Close', + handler : function() { + onClose(); + } + } ], + layout : 'fit', + items : { + html : "" + }, + listeners : { + onEsc : function() { + onClose(); + }, + close : function() { + onClose(); + } + } + }).show(); +}; + +RigibBodyModelingForm.prototype._getButtons = function() { + return []; +}; + +RigibBodyModelingForm.prototype.getPanel = function() { + var _this = this; + this.panel = Ext.create('Ext.form.Panel', { + width : this.width, + height : this.height, + margin : 10, + border : 1, + defaultType : 'textfield', + items : this._getItems(), + buttons : this._getButtons(), + listeners : { + afterrender : function(){ + _this._populate(); + + } + } + }); + return this.panel; +}; + +/** Populates could be call when the DOM is not filled yet **/ +RigibBodyModelingForm.prototype._populate = function() { + if (this.macromolecule != null){ + if (Ext.getCmp(this.id + "contactsDescriptionFilePath") != null){ + if (this.macromolecule.contactsDescriptionFilePath != null){ + Ext.getCmp(this.id + "contactsDescriptionFilePath").setValue(this.macromolecule.contactsDescriptionFilePath); + Ext.getCmp(this.id + "_remove").enable(); + } + else{ + Ext.getCmp(this.id + "_remove").disable(); + Ext.getCmp(this.id + "contactsDescriptionFilePath").setValue(""); + + } + } + } +}; + +/** It populates the form * */ +RigibBodyModelingForm.prototype.refresh = function(macromolecule) { + this.macromolecule = macromolecule; + this.rigidBodyGrid.refresh(macromolecule); + this._populate(); +}; + +RigibBodyModelingForm.prototype.input = function() { + return {}; +}; + + +/** It populates the form **/ +RigibBodyModelingForm.prototype.test = function(targetId) { + var macromoleculeForm = new RigibBodyModelingForm(); + var panel = macromoleculeForm.getPanel(); + panel.render(targetId); +}; + + +/** + * Same form as MX part * - * #onClick #onSelected #onRemoved #onUpdateTime #onMeasurementChanged - * #onExperimentChanged + * @creationMode if true a create button appears instead of save + * @showTitle true or false */ -function MeasurementGrid(args) { +function ShipmentForm(args) { this.id = BUI.id(); - this.height = 500; - this.width = 900; - - this.maxWidth = 1200; - this.maxHeight = 600; - this.minHeight = 500; - - this.unitsFontSize = 9; - this.title = "Measurements"; - this.estimateTime = false; - this.collapsed = true; - this.tbar = true; - + this.creationMode = false; this.showTitle = true; - this.resizable = true; - this.updateRowEnabled = true; - - /** - * Hash map containing the keys of the editable columns. Ex: - * 'exposureTemperature' * - */ - this.editor = { - comments : { - xtype : 'textfield', - allowBlank : true + if (args != null) { + if (args.creationMode != null) { + this.creationMode = args.creationMode; } - }; + if (args.showTitle != null) { + this.showTitle = args.showTitle; + } + } - this.isTimeColumnHidden = false; - this.isStatusColumnHidden = false; - this.isPriorityColumnHidden = true; - this.margin = "0 0 0 0"; + this.onSaved = new Event(this); +} - this.addBtnEnable = true; - this.sorter = [ { - property : 'priority', - direction : 'ASC' - } ]; +ShipmentForm.prototype.fillStores = function() { + this.panel.setLoading("Loading Labcontacts from database"); + this.labContactForSendingStore.loadData(BIOSAXS.proposal.getLabcontacts(), false); + this.labContactForReturnStore.loadData(BIOSAXS.proposal.getLabcontacts(), false); + this.panel.setLoading(false); + if (this.shipment != null) { + this.setShipment(this.shipment); + } +}; - this.removeBtnEnabled = false; - this.collapseBtnEnable = true; - this.addBtnMultipleEdit = false; - this.sortingBtnEnable = false; +ShipmentForm.prototype.draw = function(targetId) { + this.getPanel().render(targetId); +}; +ShipmentForm.prototype.setShipment = function(shipment) { + this.shipment = shipment; var _this = this; - this.selModel = Ext.create('Ext.selection.RowModel', { - allowDeselect : true, - mode : 'MULTI', - listeners : { - selectionchange : function(sm, selections) { - var selected = []; - for (var i = 0; i < selections.length; i++) { - selected.push(selections[i].raw); - } - _this.onSelected.notify(selected); - } - } - }); - - if (args != null) { - - if (args.selModel != null) { - this.selModel = args.selModel; - } + Ext.getCmp(_this.id + "shippingName").setValue(shipment.json.shippingName); + Ext.getCmp(_this.id + "shippingStatus").setValue(shipment.json.shippingStatus); + Ext.getCmp(_this.id + "comments").setValue(shipment.json.comments); + if (shipment.json.sendingLabContactVO != null) { + this.labContactsSendingCombo.setValue(shipment.json.sendingLabContactVO.labContactId); + } + if (shipment.json.returnLabContactVO != null) { + this.labContactsReturnCombo.setValue(shipment.json.returnLabContactVO.labContactId); + } - if (args.removeBtnEnabled != null) { - this.removeBtnEnabled = args.removeBtnEnabled; - } +}; - if (args.addBtnMultipleEdit != null) { - this.addBtnMultipleEdit = args.addBtnMultipleEdit; - } +ShipmentForm.prototype._saveShipment = function() { + var _this = this; + var shippingId = null; + if (this.shipment != null) { + shippingId = this.shipment.json.shippingId; + } + var json = { + shippingId : shippingId, + name : Ext.getCmp(_this.id + "shippingName").getValue(), + status : Ext.getCmp(_this.id + "shippingStatus").getValue(), + sendingLabContactId : Ext.getCmp(_this.id + "shipmentform_sendingLabContactId").getValue(), + returnLabContactId : Ext.getCmp(_this.id + "returnLabContactId").getValue(), + returnCourier : Ext.getCmp(_this.id + "returnCourier").getValue(), + courierAccount : Ext.getCmp(_this.id + "courierAccount").getValue(), + BillingReference : Ext.getCmp(_this.id + "BillingReference").getValue(), + dewarAvgCustomsValue : Ext.getCmp(_this.id + "dewarAvgCustomsValue").getValue(), + dewarAvgTransportValue : Ext.getCmp(_this.id + "dewarAvgTransportValue").getValue(), + comments : Ext.getCmp(_this.id + "comments").getValue() + }; - // if (args.experimentColorBased != null) { - // this.experimentColorBased = args.experimentColorBased; - // } + var dataAdapter = new BiosaxsDataAdapter(); + dataAdapter.onSuccess.attach(function(sender, shipment) { + window.location = BUI.getShippingURL(shipment.shippingId); + }); - if (args.collapsed != null) { - this.collapsed = args.collapsed; - } - if (args.resizable != null) { - this.resizable = args.resizable; - } + dataAdapter.onError.attach(function(sender, error) { + _this.onError.notify(error); + }); - if (args.editor != null) { - this.editor = args.editor; - } + /** Cheking params **/ + if (json.name == "") { + BUI.showError("Name field is mandatory"); + return; + } - if (args.collapseBtnEnable != null) { - this.collapseBtnEnable = args.collapseBtnEnable; - } + if (json.sendingLabContactId == null) { + BUI.showError("Lab contact for sending field is mandatory"); + return; + } - if (args.addBtnEnable != null) { - this.addBtnEnable = args.addBtnEnable; - } - if (args.sortingBtnEnable != null) { - this.sortingBtnEnable = args.sortingBtnEnable; - } + if (json.returnLabContactId == null) { + BUI.showError("Lab contact for return field is mandatory"); + return; + } - if (args.isPriorityColumnHidden != null) { - this.isPriorityColumnHidden = args.isPriorityColumnHidden; - } + dataAdapter.createShipment(json.shippingId, json.name, json.status, json.comments, json.sendingLabContactId, json.returnLabContactId, + json.returnCourier, json.courierAccount, json.BillingReference, json.dewarAvgCustomsValue, json.dewarAvgTransportValue); - if (args.width != null) { - this.width = args.width; - } +}; - if (args.updateRowEnabled != null) { - this.updateRowEnabled = args.updateRowEnabled; - } +ShipmentForm.prototype.getPanel = function(shipment) { + var _this = this; + this.shipment = shipment; + var required = '*'; + var buttons = []; - if (args.showTitle != null) { - this.showTitle = args.showTitle; - if (this.showTitle == false) { - this.title = null; + if (_this.creationMode) { + buttons.push({ + text : 'Create', + scope : this, + handler : function() { + _this._saveShipment(); } - } - - if (args.height != null) { - this.height = args.height; - } - - if (args.maxHeight != null) { - this.maxHeight = args.maxHeight; - } - - if (args.minHeight != null) { - this.minHeight = args.minHeight; - } + }); + } else { + buttons.push({ + text : 'Save', + scope : this, + handler : function() { + _this._saveShipment(); + } + }); - if (args.maxWidth != null) { - this.maxWidth = args.maxWidth; - } + } - if (args.isStatusColumnHidden != null) { - this.isStatusColumnHidden = args.isStatusColumnHidden; - } - if (args.isTimeColumnHidden != null) { - this.isTimeColumnHidden = args.isTimeColumnHidden; - } + this.labContactForSendingStore = Ext.create('Ext.data.Store', { + fields : [ 'cardName', 'labContactId' ] + }); - if (args.title != null) { - this.title = args.title; - } - if (args.estimateTime != null) { - this.estimateTime = args.estimateTime; - } + this.labContactForReturnStore = Ext.create('Ext.data.Store', { + fields : [ 'cardName', 'labContactId' ] + }); - if (args.margin != null) { - this.margin = args.margin; - } + // Create the combo box, attached to the states data store + this.labContactsSendingCombo = Ext.create('Ext.form.ComboBox', { + id : _this.id + "shipmentform_sendingLabContactId", + fieldLabel : 'Lab contact for sending', + afterLabelTextTpl : required, + store : this.labContactForSendingStore, + queryMode : 'local', + labelWidth : 200, + displayField : 'cardName', + valueField : 'labContactId' + }); - if (args.tbar != null) { - this.tbar = args.tbar; + this.labContactsReturnCombo = Ext.create('Ext.form.ComboBox', { + id : _this.id + "returnLabContactId", + fieldLabel : 'If No, Lab-Contact for Return', + afterLabelTextTpl : required, + store : this.labContactForReturnStore, + queryMode : 'local', + labelWidth : 200, + displayField : 'cardName', + valueField : 'labContactId', + listeners : { + change : function(x, newValue) { + for ( var i = 0; i < x.getStore().data.items.length; i++) { + if (x.getStore().data.items[i].raw.labContactId == newValue) { + Ext.getCmp(_this.id + "returnCourier").setValue(x.getStore().data.items[i].raw.defaultCourrierCompany); + Ext.getCmp(_this.id + "courierAccount").setValue(x.getStore().data.items[i].raw.courierAccount); + Ext.getCmp(_this.id + "BillingReference").setValue(x.getStore().data.items[i].raw.billingReference); + Ext.getCmp(_this.id + "dewarAvgCustomsValue").setValue(x.getStore().data.items[i].raw.dewarAvgCustomsValue); + Ext.getCmp(_this.id + "dewarAvgTransportValue").setValue(x.getStore().data.items[i].raw.dewarAvgTransportValue); + } + } + } } + }); - if (args.sorter != null) { - this.sorter = args.sorter; - } + if (this.panel == null) { + this.panel = Ext.create('Ext.form.Panel', { + bodyPadding : 5, + width : 600, + border : 1, + items : [ { + xtype : 'fieldset', + title : 'Details', + collapsible : false, + defaultType : 'textfield', + layout : 'anchor', + defaults : { + anchor : '100%' + }, + items : [ { + xtype : 'requiredtext', + fieldLabel : 'Shipment Label', + allowBlank : false, + name : 'shippingName', + id : _this.id + 'shippingName', + value : '', + anchor : '50%' + }, { + xtype : 'textareafield', + name : 'comments', + id : _this.id + 'comments', + fieldLabel : 'Comments', + value : '' + }, { + fieldLabel : 'Status', + readOnly : true, + id : _this.id + 'shippingStatus', + value : 'Opened', + anchor : '50%' + } ] + }, { + xtype : 'fieldset', + title : 'Lab-Contacts', + collapsible : false, + defaultType : 'textfield', + layout : 'anchor', + defaults : { + anchor : '100%' + }, + items : [ this.labContactsSendingCombo, this.labContactsReturnCombo ] + }, { + border : 0, + html : BUI.getWarningHTML("These informations are relevant for all shipments") + }, { + xtype : 'fieldset', + title : 'Courier accounts details for return', + collapsible : false, + layout : 'anchor', + defaults : { + anchor : '100%' + }, + items : [ { + xtype : 'textfield', + labelWidth : 400, + fieldLabel : 'Courier company for return (if ESRF sends a dewar back)', + id : _this.id + 'returnCourier', + value : '' + }, { + xtype : 'textfield', + labelWidth : 400, + fieldLabel : 'Courier account', + id : _this.id + 'courierAccount', + value : '' + }, { + xtype : 'textfield', + labelWidth : 400, + fieldLabel : 'Billing reference', + id : _this.id + 'BillingReference', + value : '' + }, { + xtype : 'numberfield', + labelWidth : 400, + fieldLabel : 'Average Customs value of a dewar (Euro)', + id : _this.id + 'dewarAvgCustomsValue', + value : '' + }, { + xtype : 'numberfield', + labelWidth : 400, + fieldLabel : 'Average Transport value of a dewar (Euro)', + id : _this.id + 'dewarAvgTransportValue', + value : '' + } ] + } ], + buttons : buttons + }); } - this.onClick = new Event(this); - this.onSelected = new Event(this); - this.onRemoved = new Event(this); - this.onUpdateTime = new Event(this); - this.onMeasurementChanged = new Event(this); - this.onExperimentChanged = new Event(this); -} - -MeasurementGrid.prototype._sortBy = function(sort) { - var _this = this; - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, data) { - _this.onExperimentChanged.notify(data); - _this.grid.setLoading(false); - // wizardWidget.window.close(); - }); - adapter.onError.attach(function(sender, data) { - _this.grid.setLoading(false); - alert("Oops, there was a problem"); - }); - _this.grid.setLoading("Sorting"); - adapter.sortMeasurements(this.experiments.experiments[0].experimentId, sort); + this.fillStores(); + if (this.showTitle) { + this.panel.setTitle('Create a new Shipment'); + } + return this.panel; }; -MeasurementGrid.prototype._getMenu = function() { - var _this = this; - if (this.tbar) { +ShipmentForm.prototype.input = function() { + return { + proposal : DATADOC.getProposal_10() - var items = []; - if (_this.addBtnEnable) { - items.push({ - icon : '../images/add.png', - text : 'Add measurements', - handler : function() { - _this._openAddMeasurementWindow(); - } - }); - } + }; +}; - if (_this.addBtnMultipleEdit) { - items.push({ - icon : '../images/Edit_16x16_01.png', - text : 'Multiple Edit', - handler : function() { - var multipleEditMeasurementGridWindow = new MultipleEditMeasurementGridWindow(); - multipleEditMeasurementGridWindow.onExperimentChanged.attach(function(sender, data) { - _this.onExperimentChanged.notify(data); - }); +ShipmentForm.prototype.test = function(targetId) { + var shipmentForm = new ShipmentForm({ + creationMode : true - multipleEditMeasurementGridWindow.draw(_this.measurements, _this.experiments); + }); + BIOSAXS.proposal = new Proposal(shipmentForm.input().proposal); + shipmentForm.getPanel().render(targetId); +}; + +/** + * #onSaved + */ +function StockSolutionForm(args) { + this.id = BUI.id(); + this.actions = []; - } - }); + if (args != null) { + if (args.actions != null) { + this.actions = args.actions; } + } + this.onSaved = new Event(this); +} - items.push("->"); +StockSolutionForm.prototype.getStockSolution = function() { + if (this.stockSolution != null) { + this.stockSolution.concentration = Ext.getCmp(this.id + "stockSolution_concentration").getValue(); + this.stockSolution.storageTemperature = Ext.getCmp(this.id + "stockSolution_storageTemperature").getValue(); + this.stockSolution.volume = Ext.getCmp(this.id + "stockSolution_volume").getValue(); + this.stockSolution.comments = Ext.getCmp(this.id + "stockSolution_comments").getValue(); + this.stockSolution.name = Ext.getCmp(this.id + "stockSolution_name").getValue(); + this.stockSolution.bufferId = this.bufferCombo.getValue(); - if (_this.sortingBtnEnable) { - var split = Ext.create('Ext.button.Split', { - text : 'Sort by', - // handle a click on the button itself - handler : function() { - // alert("The button was clicked"); - }, - menu : new Ext.menu.Menu({ - items : [ - { - text : 'First Created First Measured', - handler : function() { - _this._sortBy("FIFO"); - } - }, "-", { - text : 'Default', - handler : function() { - _this._sortBy("DEFAULT"); - } - } ] - }) - }); - items.push(split); + if (this.macromoleculeCombo.getValue() != null) { + this.stockSolution.macromoleculeId = this.macromoleculeCombo.getValue(); + } else { + this.stockSolution.macromolecule3VO = null; } - if (_this.collapseBtnEnable) { - items.push({ - text : 'Collapse buffers', - enableToggle : true, - scope : this, - toggleHandler : function(item, pressed) { - this.collapsed = pressed; - this.grid.getStore().loadData(this._prepareData(this.measurements, this.experiments), false); - }, - pressed : this.collapsed - }); + } else { + return { + concentration : Ext.getCmp(this.id + "stockSolution_concentration").getValue(), + storageTemperature : Ext.getCmp(this.id + "stockSolution_storageTemperature").getValue(), + volume : Ext.getCmp(this.id + "stockSolution_volume").getValue(), + comments : Ext.getCmp(this.id + "stockSolution_comments").getValue(), + name : Ext.getCmp(this.id + "stockSolution_name").getValue(), + bufferId : this.bufferCombo.getValue(), + macromoleculeId : this.macromoleculeCombo.getValue() + }; + } + return this.stockSolution; +}; + +StockSolutionForm.prototype.setStockSolution = function(stockSolution) { + if (stockSolution != null) { + if (stockSolution.macromoleculeId != null) { + this.macromoleculeCombo.setValue(stockSolution.macromoleculeId); } - var tb = Ext.create('Ext.toolbar.Toolbar', { - items : items - }); - return tb; + this.bufferCombo.setValue(stockSolution.bufferId); + Ext.getCmp(this.id + "stockSolution_concentration").setValue(this.stockSolution.concentration); + Ext.getCmp(this.id + "stockSolution_storageTemperature").setValue(this.stockSolution.storageTemperature); + Ext.getCmp(this.id + "stockSolution_volume").setValue(this.stockSolution.volume); + Ext.getCmp(this.id + "stockSolution_name").setValue(this.stockSolution.name); + Ext.getCmp(this.id + "stockSolution_comments").setValue(this.stockSolution.comments); } - return null; }; -/** Opens WizardWidget for adding new measurements * */ -MeasurementGrid.prototype._openAddMeasurementWindow = function(measurements, experiments) { - var _this = this; - var wizardWidget = new WizardWidget(); - wizardWidget.onFinished.attach(function(sender, result) { - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, data) { - _this.onExperimentChanged.notify(data); - _this.grid.setLoading(false); - wizardWidget.window.close(); - }); - wizardWidget.current.setLoading("ISPyB: Adding measurements"); - adapter.addMeasurements(result.name, "comments", result.data, _this.experiments.experiments[0].experimentId); +StockSolutionForm.prototype.getBufferCombo = function() { + this.bufferCombo = BIOSAXS_COMBOMANAGER.getComboBuffers(BIOSAXS.proposal.getBuffers(), { + labelWidth : 120, + margin : '0 0 10 0', + width : 220 + }); + return this.bufferCombo; +}; - wizardWidget.draw(null, new MeasurementCreatorStepWizardForm(BIOSAXS.proposal.getMacromolecules(), { - noNext : true - })); +StockSolutionForm.prototype.getMacromoleculeCombo = function() { + this.macromoleculeCombo = BIOSAXS_COMBOMANAGER.getComboMacromoleculeByMacromolecules(BIOSAXS.proposal.getMacromolecules(), { + labelWidth : 120, + margin : '0 0 10 0', + width : 220 + + }); + return this.macromoleculeCombo; }; -/******************************************************************************* - * Opens WizardWidget for adding new measurements - * - * @Measurements - * @Experiments experimentList Object - ******************************************************************************/ -MeasurementGrid.prototype._prepareData = function(measurements, experiments) { - var data = []; - for (var i = 0; i < measurements.length; i++) { - var measurement = measurements[i]; - var specimen = experiments.getSampleById(measurement.specimenId); - var buffer = experiments.getBufferById(specimen.bufferId); - measurement.buffer_acronym = buffer.acronym; - measurement.bufferId = buffer.bufferId; - measurement.volume = specimen.volume; - if (specimen.macromolecule3VO != null) { - measurement.acronym = specimen.macromolecule3VO.acronym; - measurement.macromoleculeId = specimen.macromolecule3VO.macromoleculeId; - } - measurement.concentration = specimen.concentration; - if (measurement.run3VO != null) { - measurement.energy = measurement.run3VO.energy; - measurement.expExposureTemperature = measurement.run3VO.exposureTemperature; - measurement.storageTemperature = measurement.run3VO.storageTemperature; - measurement.timePerFrame = measurement.run3VO.timePerFrame; - measurement.radiationAbsolute = measurement.run3VO.radiationAbsolute; - measurement.radiationRelative = measurement.run3VO.radiationRelative; - measurement.status = "DONE"; +StockSolutionForm.prototype.refresh = function() { +}; - try { - if (measurement.run3VO.timeStart != null) { - if (measurement.run3VO.timeStart != "") { - measurement.miliseconds = moment(measurement.run3VO.timeStart).format("X"); - } - } - } catch (E) { - console.log(E); - } - } +StockSolutionForm.prototype._getTopPanel = function() { + return { + xtype : 'container', + layout : 'hbox', + border : 0, + items : [ { + xtype : 'container', + layout : 'hbox', + items : [ { + xtype : 'container', + flex : 1, + border : false, + layout : 'anchor', + defaultType : 'textfield', + items : [ - if (experiments.getDataCollectionByMeasurementId(measurement.measurementId).length > 0) { - var measurementtodatacollection3VOs = experiments.getDataCollectionByMeasurementId(measurement.measurementId)[0].measurementtodatacollection3VOs; - for (var k = 0; k < measurementtodatacollection3VOs.length; k++) { - if (measurementtodatacollection3VOs[k].dataCollectionOrder == 1) { - var specimenBuffer = experiments.getSampleById(experiments.getMeasurementById(measurementtodatacollection3VOs[k].measurementId).specimenId); - if (specimenBuffer.sampleplateposition3VO != null) { - measurement.bufferSampleplateposition3VO = specimenBuffer.sampleplateposition3VO; - measurement.bufferSampleplate = (experiments.getSamplePlateById(specimenBuffer.sampleplateposition3VO.samplePlateId)); - } - } - } - } + this.getMacromoleculeCombo(), { + xtype : 'requiredtext', + id : this.id + 'stockSolution_name', + fieldLabel : 'Acronym', + labelWidth : 120, + width : 250 + }, { + xtype : 'requiredtext', + id : this.id + 'stockSolution_concentration', + fieldLabel : 'Conc. (mg/ml)', + labelWidth : 120, + width : 250 + }, - if (this.collapsed) { - /** If collapsed only the samples * */ - if (specimen.macromolecule3VO != null) { - data.push(measurement); - } - } else { - data.push(measurement); - } + { + id : this.id + 'stockSolution_storageTemperature', + fieldLabel : 'Storage Temp.(C)', + labelWidth : 120, + width : 250 + }, { + xtype : 'requiredtext', + id : this.id + 'stockSolution_volume', + fieldLabel : 'Volume in Well (µl)', + labelWidth : 120, + width : 250 + } ] + } ] + }, { + xtype : 'container', + flex : 1, + layout : 'anchor', + defaultType : 'textfield', + margin : '0 0 0 10', + items : [ this.getBufferCombo() ] + } ] + }; - } - return data; }; -/** - * Refresh data grid with the measurements and the experiments - * - * @measurements array with measurement3VO objects - * @experiments array with experiments objects - */ -MeasurementGrid.prototype.refresh = function(measurements, experiments) { - this.experiments = experiments; - this.measurements = measurements; - this.store.loadData(this._prepareData(measurements, experiments), false); +StockSolutionForm.prototype.getPanel = function(stockSolution) { + this.stockSolution = stockSolution; + this.panel = Ext.createWidget({ + xtype : 'container', + layout : 'vbox', + border : 0, + style : { + padding : '10px' + }, + fieldDefaults : { + labelAlign : 'left', + labelWidth : 50 + }, + items : [ this._getTopPanel(stockSolution), { + id : this.id + 'stockSolution_comments', + xtype : 'textareafield', + name : 'comments', + fieldLabel : 'Comments', + labelWidth : 120, + width : '100%' + } ] + }); + + this.setStockSolution(stockSolution); + return this.panel; +}; + +StockSolutionForm.prototype.input = function() { + return { + stock : { + "stockSolutionId" : 6, + "proposalId" : 3124, + "macromoleculeId" : 5933, + "bufferId" : 811, + "instructionSet3VO" : null, + "boxId" : 305861, + "storageTemperature" : "20", + "volume" : "300", + "concentration" : "1.2", + "comments" : "Buffer EDTA with A", + "name" : "A_EDTA_1.2", + "samples" : [], + "buffer" : "EDTA", + "macromolecule" : "A" + }, + proposal : new MeasurementGrid().input().proposal + }; +}; + +StockSolutionForm.prototype.test = function(targetId) { + var stockSolutionForm = new StockSolutionForm(); + BIOSAXS.proposal = new Proposal(stockSolutionForm.input().proposal); + var panel = stockSolutionForm.getPanel(new Shipment(stockSolutionForm.input().stock)); + panel.render(targetId); }; + + +BoxWhiskerGraph.prototype.cleanArray = GenericGraph.prototype.cleanArray; +BoxWhiskerGraph.prototype.plotAxes = GenericGraph.prototype.plotAxes; +BoxWhiskerGraph.prototype.plotRuler = GenericGraph.prototype.plotRuler; +BoxWhiskerGraph.prototype.drawSVGVerticalText = GenericGraph.prototype.drawSVGVerticalText; +BoxWhiskerGraph.prototype.getClassColor = GenericGraph.prototype.getClassColor; +BoxWhiskerGraph.prototype.getDimensions = GenericGraph.prototype.getDimensions; +BoxWhiskerGraph.prototype.calculate = GenericGraph.prototype.calculate; +BoxWhiskerGraph.prototype.getMedian = GenericGraph.prototype.getMedian; +BoxWhiskerGraph.prototype.pointToPixel = GenericGraph.prototype.pointToPixel; +BoxWhiskerGraph.prototype.maxValueWhisker = GenericGraph.prototype.maxValueWhisker; +BoxWhiskerGraph.prototype.refresh = GenericGraph.prototype.refresh; /** - * Set status bar to busy (refreshing icon) + * Subclass of GenericGraph * - * @msg message to be displayed on the bar + * @maxBoxWidth */ -MeasurementGrid.prototype._showStatusBarBusy = function(msg) { - var statusBar = Ext.getCmp(this.id + 'basic-statusbar'); - statusBar.setStatus({ - text : msg, - iconCls : 'x-status-busy', - clear : false - }); +function BoxWhiskerGraph(args){ + this.maxBoxWidth = 25; + + if (args == null){ + args = new Object(); + } + args["plotHorizontalByCluster"] = true; + + GenericGraph.call(this, args); + + if (args.maxBoxWidth != null){ + this.maxBoxWidth = args.maxBoxWidth; + } }; + + /** - * Set status bar to ready (ok icon) - * - * @msg message to be displayed on the bar - */ -MeasurementGrid.prototype._showStatusBarReady = function(msg) { - var statusBar = Ext.getCmp(this.id + 'basic-statusbar'); - statusBar.setStatus({ - text : msg, - iconCls : 'x-status-valid', - clear : false - }); +There are several different methods for calculating quartiles.[1] This calculator uses a method described by Moore and McCabe to find quartile values. +The same method also used by The TI-83 to calculate quartile values. +With this method, the first quartile is the median of the numbers below the median, the third quartile is the median of the numbers above the median. + +http://www.miniwebtool.com/quartile-calculator/ +http://www.alcula.com/calculators/statistics/box-plot/ +**/ +BoxWhiskerGraph.prototype.getQ1 = function(array){ + array = array.slice(0, array.length/2); + return this.getMedian(array); }; -/** - * If updateRowEnabled returns an array with Ext.grid.plugin.RowEditing - */ -MeasurementGrid.prototype._getPlugins = function() { - var _this = this; - var plugins = []; - if (this.updateRowEnabled) { - plugins.push(Ext.create('Ext.grid.plugin.RowEditing', { - clicksToEdit : 1, - listeners : { - validateedit : function(grid, e) { - /** Setting values * */ - for ( var key in _this.editor) { - e.record.raw[key] = e.newValues[key]; - } - /** Comments are always updatable* */ - e.record.raw.comments = e.newValues.comments; +BoxWhiskerGraph.prototype.getQ3 = function(array){ + array = array.slice((array.length + 1)/2); + return this.getMedian(array); + +}; - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, measurement) { - _this.onMeasurementChanged.notify(measurement); - _this.grid.setLoading(false); - }); - adapter.onError.attach(function() { - alert("Error"); - _this.grid.setLoading(false); - }); +BoxWhiskerGraph.prototype.getQ2 = function(array){ + return this.getMedian(array); +}; - _this.grid.setLoading(); - adapter.saveMeasurement(e.record.raw, _this.experiments.experiments[0]); - } - } - })); +BoxWhiskerGraph.prototype.getBelowOutliers = function(belowLimit, array){ + var points = new Array(); + for (var i = 0; i < array.length; i++){ + if (array[i] <= belowLimit){ + points.push(array[i]); + } } - return plugins; + return points; }; -/** - * @key name of the columns mathing the this.editor[key] - */ -MeasurementGrid.prototype._getEditor = function(key) { - if (this.editor[key] != null) { - return this.editor[key]; +BoxWhiskerGraph.prototype.getAboveOutliers = function(aboveLimit, array){ + var points = new Array(); + for (var i = 0; i < array.length; i++){ + if (array[i] >= aboveLimit){ + points.push(array[i]); + } } - return null; + return points; }; -MeasurementGrid.prototype.getPanel = function(measurements, experiments) { - this.experiments = experiments; - this.measurements = measurements; - var _this = this; - this.store = Ext.create('Ext.data.Store', { - fields : [ { - name : 'miliseconds', - type : 'int' - }, 'priority', 'bufferId', { - name : 'exposureTemperature', - type : 'numeric' - }, 'volumeToLoad','code', 'transmission', 'viscosity', 'waitTime', 'flow', 'buffer_acronym', 'macromoleculeId', 'acronym', 'concentration', 'extraFlowTime', 'volume', 'energy', - 'expExposureTemperature', 'storageTemperature', 'timePerFrame', 'radiationAbsolute', 'radiationRelative', 'status', 'comments' ], - data : this._prepareData(measurements, experiments) - }); - this.store.sort(this.sorter); - var bbar = {}; - try { - bbar = Ext.create('Ext.ux.StatusBar', { - id : _this.id + 'basic-statusbar', - defaultText : 'Ready', - text : 'Ready', - iconCls : 'x-status-valid', - items : [] - }); - } catch (exp) { - console.log("bbar error"); + +BoxWhiskerGraph.prototype.minValueWhisker = function(belowLimit, q1, array){ + var points = new Array(); + + for (var i = 0; i < array.length; i++){ + if ((array[i] < q1) && (array[i]) > belowLimit){ + points.push(array[i]); + } + } + + if (points.length > 0){ + points.sort(function(a, b){return a - b;}); + return points[0]; } + return null; +}; - this.grid = Ext - .create( - 'Ext.grid.Panel', - { - id : this.id, - title : this.title, - store : this.store, - selModel : this.selModel, - plugins : this._getPlugins(), - resizable : this.resizable, - margin : this.margin, - maxHeight : this.maxHeight, - minHeight : this.minHeight, - maxWidth : this.maxWidth, - width : this.width, - tbar : this._getMenu(), - columns : [ - { - text : 'Order', - dataIndex : 'priority', - width : 50, - hidden : _this.isPriorityColumnHidden, - sortable : true +BoxWhiskerGraph.prototype.isNumber = function(value){ + if (value=="") return false; - }, - { - text : 'Run Number', - dataIndex : 'code', - width : 50, - hidden : true, - sortable : true + var d = parseInt(value); + if (!isNaN(d)) return true; else return false; - }, +}; - { - text : 'Specimen', - columns : [ +BoxWhiskerGraph.prototype.plotBoxWhisker = function(boxProperties){ + if (this.maxBoxWidth != null){ + if (boxProperties.width > this.maxBoxWidth){ + boxProperties.x = boxProperties.x + (boxProperties.width/2) - (this.maxBoxWidth/2); + boxProperties.width = this.maxBoxWidth; + } + + } - { - text : '', - dataIndex : 'macromoleculeId', - width : 30, - renderer : function(val, y, sample) { - if (val != null) { - if (_this.experiments == null) { - return BUI.getRectangleColorDIV(BIOSAXS.proposal.macromoleculeColors[val], 10, 10); - } else { - return BUI.getRectangleColorDIV(_this.experiments.macromoleculeColors[val], 10, 10); - } - } - }, - sortable : true - }, - { - text : 'Macromo.', - dataIndex : 'acronym', - width : 80, - renderer : function(val, y, sample) { - return val; - }, - sortable : true - }, - { - text : 'Conc. ', - dataIndex : 'concentration', - width : 80, - renderer : function(val, y, sample) { - if (sample.raw.macromoleculeId == null) { - return ""; - } - if (isNaN(val)) - return val; + if (this.plotPoints){ + for(var i = 0; i < boxProperties.values.length; i++){ + var value = boxProperties.values[i]; + var toPixel = this.pointToPixel(value, boxProperties); + SVG.drawCircle(boxProperties.x + (boxProperties.width/2) + 2, toPixel, this.pointRadius, this.svg, [["fill", "green"], ["fill-opacity", this.fillOpacityPoint],['stroke-opacity', this.strokeOpacityPoint], ["stroke", "black"]]); + } + } + - if (val != 0) { - return BUI.formatValuesUnits(val, '', { - fontSize : 16, - decimals : 3, - unitsFontSize : this.unitsFontSize - }); - } else { - return; - } + var boxColor = this.getClassColor(boxProperties.name); + var result = this.calculate(boxProperties.values); + /** Q1 **/ + + if (this.isNumber(result.Q1)){ + SVG.drawLine(boxProperties.x, this.pointToPixel(result.Q1, boxProperties), boxProperties.x + boxProperties.width, this.pointToPixel(result.Q1, boxProperties), this.svg, [["stroke", boxColor]]); + } + /** Q2 **/ + if (this.isNumber(result.Q2)){ + SVG.drawLine(boxProperties.x, this.pointToPixel(result.Q2, boxProperties), boxProperties.x + boxProperties.width, this.pointToPixel(result.Q2, boxProperties), this.svg, [["stroke", "blue"], ["stroke-width", "2"]]); + } + + /** Q3 **/ + if (this.isNumber(result.Q3)){ + SVG.drawLine(boxProperties.x, this.pointToPixel(result.Q3, boxProperties), boxProperties.x + boxProperties.width, this.pointToPixel(result.Q3, boxProperties), this.svg, [["stroke", boxColor]]); + } + + /** Concenting Q1 and Q3 **/ + if (this.isNumber(result.Q1)&&this.isNumber(result.Q3)){ + SVG.drawLine(boxProperties.x, this.pointToPixel(result.Q1, boxProperties), boxProperties.x, this.pointToPixel(result.Q3, boxProperties), this.svg, [["stroke", boxColor]]); + SVG.drawLine(boxProperties.x + boxProperties.width, this.pointToPixel(result.Q1, boxProperties), boxProperties.x + boxProperties.width, this.pointToPixel(result.Q3, boxProperties), this.svg, [["stroke", boxColor]]); + } + + /** min-whisker **/ + if (result["min-whisker"] != null){ + if (this.isNumber(result.Q1)){ + SVG.drawLine(boxProperties.x + (boxProperties.width/2) + 2, this.pointToPixel(result.Q1, boxProperties), boxProperties.x + (boxProperties.width/2) + 2, this.pointToPixel(result["min-whisker"], boxProperties), this.svg, [["stroke", boxColor]]); + } + SVG.drawLine(boxProperties.x,this.pointToPixel(result["min-whisker"], boxProperties), boxProperties.x + boxProperties.width, this.pointToPixel(result["min-whisker"], boxProperties) , this.svg, [["stroke", boxColor]]); + + } + + /** max-whisker **/ + if (result["max-whisker"] != null){ + if (this.isNumber(result.Q3)){ + SVG.drawLine(boxProperties.x + (boxProperties.width/2) + 2, this.pointToPixel(result.Q3, boxProperties), boxProperties.x + (boxProperties.width/2) + 2, this.pointToPixel(result["max-whisker"], boxProperties), this.svg, [["stroke", boxColor]]); + } + SVG.drawLine(boxProperties.x , this.pointToPixel(result["max-whisker"], boxProperties), boxProperties.x + boxProperties.width, this.pointToPixel(result["max-whisker"], boxProperties), this.svg, [["stroke", boxColor]]); + + } + + /** outliners **/ + if (result["above-outliers"] != null){ + for (var point in result["above-outliers"]){ + SVG.drawCircle(boxProperties.x + (boxProperties.width/2) + 2, this.pointToPixel(result["above-outliers"][point], boxProperties), this.pointRadius, this.svg, [["fill", "red"], ["fill-opacity", "1"]]); + //SVG.drawText(boxProperties.x + (boxProperties.width/2) - 2, this.pointToPixel(result["above-outliers"][point], boxProperties) + 2, "x",this.svg, [["fill", "black"], ["font-size", "x-small"]]); + } + } + if (result["below-outliers"] != null){ + for (var point in result["below-outliers"]){ + SVG.drawCircle(boxProperties.x + (boxProperties.width/2) + 2, this.pointToPixel(result["below-outliers"][point], boxProperties), this.pointRadius, this.svg, [["fill", "red"], ["fill-opacity", "1"]]); + //SVG.drawText(boxProperties.x + (boxProperties.width/2) - 2, this.pointToPixel(result["below-outliers"][point], boxProperties) + 2, "x",this.svg, [["fill", "black"], ["font-size", "x-small"]]); + } + } +}; - }, - sortable : true - }, - { - text : '', - dataIndex : 'bufferId', - width : 30, - hidden : false, - renderer : function(val, y, sample) { - if (val != null) { - var color = '#FFCCFF'; - if (_this.experiments != null) { - var dc = _this.experiments.getDataCollectionByMeasurementId(sample.raw.measurementId); - if (dc != null) { - if (dc.length > 0) { - color = _this.experiments.getSpecimenColorByBufferId(_this.experiments - .getMeasurementById(dc[0].measurementtodatacollection3VOs[0].measurementId).specimenId); - } - } - } else { - color = BIOSAXS.proposal.bufferColors[val]; - } - return BUI.getRectangleColorDIV(color, 10, 10); - } - }, - sortable : true - }, - { - text : 'Buffer', - dataIndex : 'buffer_acronym', - width : 120, - renderer : function(val, y, sample) { - if (sample.raw.bufferSampleplateposition3VO != null) { - return BIOSAXS.proposal.getBufferById(sample.raw.bufferId).acronym + " Plate: [" - + sample.raw.bufferSampleplate.slotPositionColumn + ", " - + BUI.getSamplePlateLetters()[sample.raw.bufferSampleplateposition3VO.rowNumber - 1] + "-" - + sample.raw.bufferSampleplateposition3VO.columnNumber + "]"; - } - return val; - }, - sortable : true - }, { - text : 'Position', - width : 100, - hidden : true, - renderer : function(val, y, sample) { - if (_this.experiments != null) { - return BUI.getSamplePositionHTML(_this.experiments.getSampleById(sample.raw.specimenId), _this.experiments.experiments[0]); - } - } - } ] - }, - { - text : 'Parameters', - columns : [ - { - text : 'Ex. Flow. time (s)', - dataIndex : 'extraFlowTime', - width : 100, - hidden : true, - renderer : function(val, y, sample) { - return BUI.formatValuesUnits(val, 's', { - fontSize : 12, - decimals : 0, - unitsFontSize : this.unitsFontSize - }); - } - }, - { - text : 'Exp. Temp.', - dataIndex : 'exposureTemperature', - width : 70, - renderer : function(val, y, sample) { - if (Number(val)) { - return BUI.formatValuesUnits(val, 'C', { - fontSize : 12, - decimals : 2, - unitsFontSize : this.unitsFontSize - }); - } - return null; - }, - sortable : true, - editor : this._getEditor("exposureTemperature") - }, - { - text : 'Vol. Load', - dataIndex : 'volumeToLoad', - width : 60, - hidden : false, - editor : this._getEditor("volumeToLoad"), - renderer : function(val, y, sample) { - // return val+ " µl"; - return BUI.formatValuesUnits(val, 'µl', { - fontSize : 12, - decimals : 2, - unitsFontSize : this.unitsFontSize - }); - } - }, - { - text : 'Volume in Well', - dataIndex : 'volume', - hidden : true, - editor : this._getEditor("volume"), - width : 80, - renderer : function(val, y, sample) { - // return val + "(µl)"; - return BUI.formatValuesUnits(val, 'µl', { - fontSize : 12, - decimals : 2, - unitsFontSize : this.unitsFontSize - }); - } - }, - { - text : 'Trans.', - dataIndex : 'transmission', - width : 60, - editor : this._getEditor("transmission"), - renderer : function(val, y, sample) { - return BUI.formatValuesUnits(val, '%', { - fontSize : 12, - decimals : 0, - unitsFontSize : this.unitsFontSize - }); - } - }, - { - text : 'Wait T.', - dataIndex : 'waitTime', - editor : this._getEditor("waitTime"), - width : 50, - renderer : function(val, y, sample) { - // if (val != 0) { - return BUI.formatValuesUnits(val, 's', { - fontSize : 12, - decimals : 0, - unitsFontSize : this.unitsFontSize - }); - // } - } - }, - { - text : 'Flow', - dataIndex : 'flow', - editor : this._getEditor("flow"), - width : 50, - renderer : function(val, y, sample) { - if (val == true) { - return "yes"; - } - return null; - } - }, - { - text : 'Viscosity', - dataIndex : 'viscosity', - tooltip : 'The viscosity of a fluid is a measure of its resistance to gradual deformation by shear stress or tensile stress. For liquids, it corresponds to the informal notion of "thickness"', - editor : this._getEditor("viscosity"), - width : 50, - renderer : function(val, y, sample) { - return val; - } - } ] - }, { - text : 'Status', - dataIndex : 'status', - width : 50, - hidden : _this.isStatusColumnHidden, - renderer : function(val, y, sample) { - if (val != null) { - if (val == 'DONE') { - return "" + val + " "; - } - } - } - }, { - text : 'Time', - dataIndex : 'time', - width : 80, - hidden : _this.isTimeColumnHidden, - renderer : function(val, y, sample) { - if (sample.raw.run3VO != null) { - if (sample.raw.run3VO.timeStart != null) { - if (sample.raw.run3VO.timeStart != "") { - var m = moment(sample.raw.run3VO.timeStart); - return m.format("hh:mm:ss a"); - } - } - } - } - }, { - text : 'Energy', - dataIndex : 'energy', - width : 100, - hidden : true - }, { - text : 'Real Exp. Temp.(C)', - width : 100, - dataIndex : 'expExposureTemperature', - hidden : true - }, { - text : 'Storage Temp.(C)', - width : 100, - dataIndex : 'storageTemperature', - hidden : true - }, { - text : 'Time/Frame (s)', - width : 100, - dataIndex : 'timePerFrame', - hidden : true - }, { - text : 'Radiation Relative', - dataIndex : 'radiationRelative', - width : 100, - hidden : true - }, { - text : 'Radiation Absolute', - dataIndex : 'radiationAbsolute', - width : 100, - hidden : true - }, { - text : 'Comments', - dataIndex : 'comments', - flex : 1, - hidden : false, - editor : this._getEditor("comments") - }, { - id : _this.id + 'buttonRemoveSample', - text : '', - hidden : !_this.removeBtnEnabled, - width : 100, - sortable : false, - renderer : function(value, metaData, record, rowIndex, colIndex, store) { - if (record.raw.macromoleculeId != null) { - if (_this.removeBtnEnabled) { - return BUI.getRedButton('REMOVE'); - } - } - } - } ], - bbar : bbar, - viewConfig : { - stripeRows : true, - getRowClass : function(record, index, rowParams, store) { - if (record.data.status == "DONE") { - return 'green-row'; - } - }, - listeners : { - 'itemclick' : function(grid, record, item, index, e, eOpts) { - _this.onClick.notify({ - specimen : record.raw - }); - }, - 'cellclick' : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { - if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonRemoveSample') { - grid.getStore().removeAt(rowIndex); +BoxWhiskerGraph.prototype.plotClusterTitles = function(properties){ + /** Cluster Titles **/ + var posX = this.left + this.rulerWidth; + var data = this.data; + for(var i = 0; i < data.clusters.length; i++ ){ + /** title for the clusters **/ + posX = posX + this.interClustersSpace; + + /** Drawing title of classes **/ + var cluster = data.clusters[i]; + var posXClasses = posX; + for(var j = 0; j < cluster.classes.length; j++){ + //SVG.drawRectangle(posXClasses, this.height - (this.bottom + this.clusterTitleHeight + this.rulerHeight), properties.classWidth, this.rulerHeight, this.svg, [["fill", "green"]]); +// SVG.drawText(posXClasses + 1/6*properties.classWidth, this.height - (this.bottom + this.clusterTitleHeight + (this.rulerHeight*1/4)), cluster.classes[j].name, this.svg, [["style", "font-size:xx-small"]]); + var color = cluster.classes[j].color; + this.drawSVGVerticalText(posXClasses + 1/2*(properties.classWidth), this.height - (this.bottom - this.clusterTitleHeight + (this.rulerHeight)), cluster.classes[j].name , [["style", "font-size:x-small;"], ["fill", color]]); + + // SVG.drawRectangle(posXClasses, this.height - (this.bottom + this.clusterTitleHeight), properties.classWidth, 2, this.svg, [["fill", "black"]]); + posXClasses = posXClasses + properties.classWidth + this.interClassesSpace; + } + + var clusterTitleSpace = (data.clusters[i].classes.length * properties.classWidth) + ((data.clusters[i].classes.length - 1) * this.interClassesSpace); + //SVG.drawRectangle(posX, this.height - (this.bottom + this.clusterTitleHeight), clusterTitleSpace, this.clusterTitleHeight, this.svg, [["fill", "pink"]]); + SVG.drawRectangle(posX, this.height - (this.clusterTitleHeight+this.bottom), clusterTitleSpace, 1, this.svg, []); + +// var transform = ["translate", "(" + posX + 1/3*clusterTitleSpace +", " + this.height - (this.bottom + (this.clusterTitleHeight*1/4)) +")"], ["transform", "rotate(180)"]; +// var transform = ["transform", "translate(" + (posX + 1/3*clusterTitleSpace) +", " + (this.height - (this.bottom + (this.clusterTitleHeight*1/4))) + "), rotate(-90) "]; +// this.drawSVGVerticalText(posX + 1/3*clusterTitleSpace, this.height - (this.bottom + (this.clusterTitleHeight*1/4)),data.clusters[i].name, [["style", "font-size:x-small"]]); + SVG.drawText(posX , this.height - (this.bottom + (this.clusterTitleHeight*1/4)),data.clusters[i].name ,this.svg, [["style", "font-size:x-small"]]); + posX = posX + clusterTitleSpace; + } +}; + +BoxWhiskerGraph.prototype.plotWhisters = function(data, properties){ + var colors = ["yellow", "orange", "green"]; + var posX = this.left + this.rulerWidth; + for(var i = 0; i < data.clusters.length; i++ ){ + var cluster = data.clusters[i]; + /** inter cluster space **/ + //SVG.drawRectangle(posX, this.top, this.interClustersSpace, this.height - this.top - this.bottom - this.clusterTitleHeight - this.rulerHeight, this.svg, [["fill", "black"]]); + posX = posX + this.interClustersSpace; + + for (var j = 0; j < cluster.classes.length; j ++){ + + //SVG.drawRectangle(posX, this.top, properties.classWidth, this.height - this.top - this.bottom - this.clusterTitleHeight - this.rulerHeight, this.svg, [["fill", colors[j]]]); + this.plotBoxWhisker( + { + name : cluster.classes[j].name, + values : cluster.classes[j].values, + minPoint : properties.minPoint, + maxPoint : properties.maxPoint, + x : posX, + y : this.top, + width : properties.classWidth, + height : this.height - this.top - this.bottom - this.clusterTitleHeight - this.rulerHeight + + + } + ); + posX = posX + properties.classWidth; + //SVG.drawRectangle(posX, this.top, this.interClassesSpace, this.height - this.top - this.bottom - this.clusterTitleHeight - this.rulerHeight, this.svg, [["fill", "black"]]); + if (j < cluster.classes.length - 1){ + posX = posX + this.interClassesSpace; + } + } + } +}; + +BoxWhiskerGraph.prototype.draw = function(targetId, data){ + this.targetId = targetId; + var properties = (this.getDimensions(data)); + this.svg = SVG.createSVGCanvas(document.getElementById(this.targetId), [["width", this.width], ["height", this.height]]); + this.plotAxes(properties); + this.plotClusterTitles(properties); + this.plotWhisters(data,properties); +}; + +BoxWhiskerGraph.prototype.input = function(){ + return DATADOC.getBoxWhikerData(); +}; + +BoxWhiskerGraph.prototype.test = function(targetId){ + var plot = new BoxWhiskerGraph( + { + targetId : targetId, + height : 350, + width : 450, + maxBoxWidth : 25 + } + ); + plot.refresh(this.input()); +}; + + + + + + + +function DataSet(){ + this.json = null; + +}; + + +DataSet.prototype.loadFromJSON = function(json){ + if(json != null) { + if(this.validate(json)) { + this.json = json; + } + } +}; + + +DataSet.prototype.toJSON = function(json){ + return this.json; +}; + + +/** Abstract method to be override on childs classes **/ +DataSet.prototype.validate = function(json){ + if (true){ + return true; + } + /*else{ + throw "Data validation failed"; + }*/ +}; + + + + +Edge.prototype.getName = GraphItem.prototype.getName; +Edge.prototype.setName = GraphItem.prototype.setName; +Edge.prototype.getId = GraphItem.prototype.getId; + +function Edge(id, name, nodeSource, nodeTarget, args){ + GraphItem.prototype.constructor.call(this, id, name, args); + + this.sourceNode = nodeSource; + this.targetNode = nodeTarget; + +}; + +Edge.prototype.toJSON = function(){ + return {"index": this.id,"sourceIndex":this.sourceNode.getId(),"targetIndex":this.targetNode.getId(),"args":this.args}; +}; + +Edge.prototype.getNodeSource = function(){ + return this.sourceNode; +}; + +Edge.prototype.getNodeTarget = function(){ + return this.targetNode; +}; + +Edge.prototype.remove = function(){ + //Remove edge object in the nodes + this.getNodeSource().removeEdge(this); + this.getNodeTarget().removeEdge(this); + + this.deleted.notify(this); +}; + + +EdgeGraphFormatter.prototype.setProperties = ItemGraphFormatter.prototype.setProperties; +EdgeGraphFormatter.prototype.getDefault = ItemGraphFormatter.prototype.getDefault; +EdgeGraphFormatter.prototype.getSelected = ItemGraphFormatter.prototype.getSelected; +EdgeGraphFormatter.prototype.getOver = ItemGraphFormatter.prototype.getOver; +EdgeGraphFormatter.prototype.getDragging = ItemGraphFormatter.prototype.getDragging; +EdgeGraphFormatter.prototype.getId = ItemGraphFormatter.prototype.getId; +EdgeGraphFormatter.prototype.toJSON = ItemGraphFormatter.prototype.toJSON; +EdgeGraphFormatter.prototype.loadFromJSON = ItemGraphFormatter.prototype.loadFromJSON; +EdgeGraphFormatter.prototype._setEvents = ItemGraphFormatter.prototype._setEvents; + +function EdgeGraphFormatter(id, defaultFormat, selectedFormat, overFormat, dragginFormat){ + ItemGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, dragginFormat); + + this.getDefault().args.type = "EdgeGraphFormatter"; +}; + + + + +LineEdgeGraphFormatter.prototype.setProperties = EdgeGraphFormatter.prototype.setProperties; +LineEdgeGraphFormatter.prototype.getDefault = EdgeGraphFormatter.prototype.getDefault; +LineEdgeGraphFormatter.prototype.getSelected = EdgeGraphFormatter.prototype.getSelected; +LineEdgeGraphFormatter.prototype.getOver = EdgeGraphFormatter.prototype.getOver; +LineEdgeGraphFormatter.prototype.getDragging = EdgeGraphFormatter.prototype.getDragging; +LineEdgeGraphFormatter.prototype.getId = EdgeGraphFormatter.prototype.getId; +LineEdgeGraphFormatter.prototype.toJSON = EdgeGraphFormatter.prototype.toJSON; +LineEdgeGraphFormatter.prototype.loadFromJSON = EdgeGraphFormatter.prototype.loadFromJSON; +LineEdgeGraphFormatter.prototype._setEvents = EdgeGraphFormatter.prototype._setEvents; + +function LineEdgeGraphFormatter(id, defaultFormat, selectedFormat, overFormat, dragginFormat){ + EdgeGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, dragginFormat); + this.args.type = "LineEdgeGraphFormatter"; +}; + +/** DIRECTED **/ +DirectedLineEdgeGraphFormatter.prototype.setProperties = EdgeGraphFormatter.prototype.setProperties; +DirectedLineEdgeGraphFormatter.prototype.getDefault = EdgeGraphFormatter.prototype.getDefault; +DirectedLineEdgeGraphFormatter.prototype.getSelected = EdgeGraphFormatter.prototype.getSelected; +DirectedLineEdgeGraphFormatter.prototype.getOver = EdgeGraphFormatter.prototype.getOver; +DirectedLineEdgeGraphFormatter.prototype.getDragging = EdgeGraphFormatter.prototype.getDragging; +DirectedLineEdgeGraphFormatter.prototype.getId = EdgeGraphFormatter.prototype.getId; +DirectedLineEdgeGraphFormatter.prototype.toJSON = EdgeGraphFormatter.prototype.toJSON; +DirectedLineEdgeGraphFormatter.prototype.loadFromJSON = EdgeGraphFormatter.prototype.loadFromJSON; +DirectedLineEdgeGraphFormatter.prototype._setEvents = EdgeGraphFormatter.prototype._setEvents; + +function DirectedLineEdgeGraphFormatter(id, defaultFormat, selectedFormat, overFormat, dragginFormat){ + EdgeGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, dragginFormat); + this.args.type = "DirectedLineEdgeGraphFormatter"; + this.args.arrowSize = "3"; +}; +DirectedLineEdgeGraphFormatter.prototype.getArrowSize = function(){ + return this.args.arrowSize; +}; + +OdirectedLineEdgeGraphFormatter.prototype.setProperties = EdgeGraphFormatter.prototype.setProperties; +OdirectedLineEdgeGraphFormatter.prototype.getDefault = EdgeGraphFormatter.prototype.getDefault; +OdirectedLineEdgeGraphFormatter.prototype.getSelected = EdgeGraphFormatter.prototype.getSelected; +OdirectedLineEdgeGraphFormatter.prototype.getOver = EdgeGraphFormatter.prototype.getOver; +OdirectedLineEdgeGraphFormatter.prototype.getDragging = EdgeGraphFormatter.prototype.getDragging; +OdirectedLineEdgeGraphFormatter.prototype.getId = EdgeGraphFormatter.prototype.getId; +OdirectedLineEdgeGraphFormatter.prototype.toJSON = EdgeGraphFormatter.prototype.toJSON; +OdirectedLineEdgeGraphFormatter.prototype.loadFromJSON = EdgeGraphFormatter.prototype.loadFromJSON; +OdirectedLineEdgeGraphFormatter.prototype._setEvents = EdgeGraphFormatter.prototype._setEvents; + +function OdirectedLineEdgeGraphFormatter(id, defaultFormat, selectedFormat, overFormat, dragginFormat){ + EdgeGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, dragginFormat); + this.args.type = "DirectedLineEdgeGraphFormatter"; + this.args.arrowSize = "3"; +}; + +OdirectedLineEdgeGraphFormatter.prototype.getArrowSize = function(){ + return this.args.arrowSize; +}; + + +/** CUT (inhibition) **/ +CutDirectedLineEdgeGraphFormatter.prototype.setProperties = EdgeGraphFormatter.prototype.setProperties; +CutDirectedLineEdgeGraphFormatter.prototype.getDefault = EdgeGraphFormatter.prototype.getDefault; +CutDirectedLineEdgeGraphFormatter.prototype.getSelected = EdgeGraphFormatter.prototype.getSelected; +CutDirectedLineEdgeGraphFormatter.prototype.getOver = EdgeGraphFormatter.prototype.getOver; +CutDirectedLineEdgeGraphFormatter.prototype.getDragging = EdgeGraphFormatter.prototype.getDragging; +CutDirectedLineEdgeGraphFormatter.prototype.getId = EdgeGraphFormatter.prototype.getId; +CutDirectedLineEdgeGraphFormatter.prototype.toJSON = EdgeGraphFormatter.prototype.toJSON; +CutDirectedLineEdgeGraphFormatter.prototype.loadFromJSON = EdgeGraphFormatter.prototype.loadFromJSON; +CutDirectedLineEdgeGraphFormatter.prototype._setEvents = EdgeGraphFormatter.prototype._setEvents; + +function CutDirectedLineEdgeGraphFormatter(id, defaultFormat, selectedFormat, overFormat, dragginFormat){ + EdgeGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, dragginFormat); + this.args.type = "CutDirectedLineEdgeGraphFormatter"; +}; + + + + +BezierEdgeGraphFormatter.prototype.setProperties = EdgeGraphFormatter.prototype.setProperties; +BezierEdgeGraphFormatter.prototype.setProperties = EdgeGraphFormatter.prototype.setProperties; +BezierEdgeGraphFormatter.prototype.getDefault = EdgeGraphFormatter.prototype.getDefault; +BezierEdgeGraphFormatter.prototype.getSelected = EdgeGraphFormatter.prototype.getSelected; +BezierEdgeGraphFormatter.prototype.getOver = EdgeGraphFormatter.prototype.getOver; +BezierEdgeGraphFormatter.prototype.getDragging = EdgeGraphFormatter.prototype.getDragging; +BezierEdgeGraphFormatter.prototype.getId = EdgeGraphFormatter.prototype.getId; +BezierEdgeGraphFormatter.prototype.toJSON = EdgeGraphFormatter.prototype.toJSON; +BezierEdgeGraphFormatter.prototype.loadFromJSON = EdgeGraphFormatter.prototype.loadFromJSON; +BezierEdgeGraphFormatter.prototype._setEvents = EdgeGraphFormatter.prototype._setEvents; + +function BezierEdgeGraphFormatter(id, defaultFormat, selectedFormat, overFormat, dragginFormat){ + EdgeGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, dragginFormat); + this.args.type = "BezierEdgeGraphFormatter"; +}; + + + +DotDirectedLineEdgeGraphFormatter.prototype.setProperties = EdgeGraphFormatter.prototype.setProperties; +DotDirectedLineEdgeGraphFormatter.prototype.getDefault = EdgeGraphFormatter.prototype.getDefault; +DotDirectedLineEdgeGraphFormatter.prototype.getSelected = EdgeGraphFormatter.prototype.getSelected; +DotDirectedLineEdgeGraphFormatter.prototype.getOver = EdgeGraphFormatter.prototype.getOver; +DotDirectedLineEdgeGraphFormatter.prototype.getDragging = EdgeGraphFormatter.prototype.getDragging; +DotDirectedLineEdgeGraphFormatter.prototype.getId = EdgeGraphFormatter.prototype.getId; +DotDirectedLineEdgeGraphFormatter.prototype.toJSON = EdgeGraphFormatter.prototype.toJSON; +DotDirectedLineEdgeGraphFormatter.prototype.loadFromJSON = EdgeGraphFormatter.prototype.loadFromJSON; +DotDirectedLineEdgeGraphFormatter.prototype._setEvents = EdgeGraphFormatter.prototype._setEvents; + +function DotDirectedLineEdgeGraphFormatter(id, defaultFormat, selectedFormat, overFormat, dragginFormat){ + EdgeGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, dragginFormat); + this.args.type = "DotDirectedLineEdgeGraphFormatter"; +}; + + +OdotDirectedLineEdgeGraphFormatter.prototype.setProperties = EdgeGraphFormatter.prototype.setProperties; +OdotDirectedLineEdgeGraphFormatter.prototype.getDefault = EdgeGraphFormatter.prototype.getDefault; +OdotDirectedLineEdgeGraphFormatter.prototype.getSelected = EdgeGraphFormatter.prototype.getSelected; +OdotDirectedLineEdgeGraphFormatter.prototype.getOver = EdgeGraphFormatter.prototype.getOver; +OdotDirectedLineEdgeGraphFormatter.prototype.getDragging = EdgeGraphFormatter.prototype.getDragging; +OdotDirectedLineEdgeGraphFormatter.prototype.getId = EdgeGraphFormatter.prototype.getId; +OdotDirectedLineEdgeGraphFormatter.prototype.toJSON = EdgeGraphFormatter.prototype.toJSON; +OdotDirectedLineEdgeGraphFormatter.prototype.loadFromJSON = EdgeGraphFormatter.prototype.loadFromJSON; +OdotDirectedLineEdgeGraphFormatter.prototype._setEvents = EdgeGraphFormatter.prototype._setEvents; + +function OdotDirectedLineEdgeGraphFormatter(id, defaultFormat, selectedFormat, overFormat, dragginFormat){ + EdgeGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, dragginFormat); + this.args.type = "OdotDirectedLineEdgeGraphFormatter"; +}; + + + +function GraphDataset(){ + DataSet.prototype.constructor.call(this); + this.edges = new Object(); + this.vertices = new Object(); + this.verticesIndex = new Object(); + + //Events + this.newVertex = new Event(this); + this.vertexNameChanged = new Event(this); + this.vertexDeleted = new Event(this); + + this.newEdge = new Event(this); + this.edgeNameChanged = new Event(this); + this.edgeDeleted = new Event(this); + + this.json = new Object(); + this.json.vertices = new Array(); + this.json.edges = new Array(); + this.json.relations = new Array(); +}; + +GraphDataset.prototype.loadFromJSON = DataSet.prototype.loadFromJSON; +GraphDataset.prototype.toJSON = DataSet.prototype.toJSON; +GraphDataset.prototype.validate = DataSet.prototype.validate; + +/** Devuelve el numero de edges incidentes sobre el nodo con mas edges **/ +GraphDataset.prototype.getMaxClass = function(){ + var maxClassNode = 0; + for ( var node in this.vertices) { + if (this.vertices[node].getEdgesCount() > maxClassNode){ + maxClassNode = this.vertices[node].getEdgesCount(); + } + } + return maxClassNode; +}; + +/** Devuelve el numero de edges incidentes sobre el nodo con mas edges **/ +GraphDataset.prototype.getMinClass = function(){ + var minClassNode = Math.min(); + for ( var node in this.vertices) { + if (this.vertices[node].getEdgesCount() < minClassNode){ + minClassNode = this.vertices[node].getEdgesCount(); + } + } + return minClassNode; +}; + +GraphDataset.prototype.getVertexByName = function(nodeName){ + var results = new Array(); + + for (var vertexId in this.verticesIndex[nodeName]){ + var vertexByid = this.getVertexById(this.verticesIndex[nodeName][vertexId]); + results.push(vertexByid); + //* añadido nuevo porque fallaba el anterior codigo + return vertexByid + } + + if (results <= 1){ + return this.getVertexById(this.verticesIndex[nodeName]); + } + else{ + return results; + } +}; + +GraphDataset.prototype.getVertexById = function(id){ + return this.vertices[id]; +}; + +GraphDataset.prototype.toSIF = function(){ + var sifDataAdapter = new SifFileDataAdapter(); + return sifDataAdapter.toSIF(this); +}; + +GraphDataset.prototype.toSIFID = function(){ + var sifDataAdapter = new SifFileDataAdapter(); + return sifDataAdapter.toSIFID(this); +}; + +GraphDataset.prototype.toDOT = function(){ + var dotFileDataAdapter = new DotFileDataAdapter(); + return dotFileDataAdapter.toDOT(this); +}; + +GraphDataset.prototype.toDOTID = function(){ + var dotFileDataAdapter = new DotFileDataAdapter(); + return dotFileDataAdapter.toDOTID(this); +}; + +GraphDataset.prototype._addNode = function(nodeName, args){ + return new Vertex(this._getVerticesCount()-1, nodeName, args); +}; + +GraphDataset.prototype.addNode = function(nodeName, args){ + this.json.vertices.push(nodeName); + this._addVerticesIndex(nodeName, this._getVerticesCount() - 1); + var vertex = this._addNode(nodeName, args); + this.vertices[this._getVerticesCount()-1] = vertex; + this._setNodeEvents(vertex); + this.newVertex.notify(vertex); +}; + +GraphDataset.prototype._addVerticesIndex = function(nodeName, id){ + if (this.verticesIndex[nodeName] == null){ + this.verticesIndex[nodeName] = new Array(); + } + this.verticesIndex[nodeName].push(id); +}; + +GraphDataset.prototype.addEdge = function(edgeName, nodeSourceId, nodeTargetId, args){ + this.json.edges.push(edgeName); + var nodeSource = this.getVertexById(nodeSourceId); + var nodeTarget = this.getVertexById(nodeTargetId); + var index = this.getEdgesCount() - 1; + this.edges[index] = new Edge(index, edgeName, nodeSource, nodeTarget, args); + this.json.relations.push({"index": index, "sourceIndex": nodeSourceId, "targetIndex": nodeTargetId, "args": args }); + + nodeSource.addEdge(this.edges[index]); + nodeTarget.addEdge(this.edges[index]); + this._setEdgeEvents(this.edges[index]); + this.newEdge.notify(this.edges[index]); +}; + +GraphDataset.prototype.getVertices = function(){ + return this.vertices; +}; + +GraphDataset.prototype.getEdges = function(){ + return this.edges; +}; + +GraphDataset.prototype.getEdgeById = function(edgeId){ + return this.edges[edgeId]; +}; + +GraphDataset.prototype._getVerticesCount = function(){ + return this.json.vertices.length; +}; + + +GraphDataset.prototype.getVerticesCount = function(){ + var count = 0; + for ( var vertex in this.getVertices()) { + count ++; + } + return count; +}; + + +GraphDataset.prototype.getEdgesCount = function(){ + return this.json.edges.length; +}; + +GraphDataset.prototype.init = function(){ + this.edges = new Object(); + this.vertices = new Object(); +}; + +GraphDataset.prototype._setNodeEvents = function(node){ + var _this = this; + //NODE EVENTS + node.deleted.attach(function (sender, node){ + _this._removeNode(node); + }); + + node.nameChanged.attach(function (sender, args){ + var item = args.item; + var newName = item.name; + var indexes = _this.verticesIndex[args.previousName]; + for(var i = 0; i < indexes.length; i++){ + if(indexes[i] == item.id) + indexes.splice(i,1); + } + if(indexes.length == 0){ + delete _this.verticesIndex[args.previousName]; + } + _this._addVerticesIndex(newName, item.id); + _this.json.vertices[item.id] = newName; + _this.vertexNameChanged.notify(args); + }); +}; + +GraphDataset.prototype._setEdgeEvents = function(edge){ + var _this = this; + //EDGE EVENTS + edge.nameChanged.attach(function (sender, edge){ + _this.edgeNameChanged.notify(edge); + + }); + + edge.deleted.attach(function (sender, edge){ + _this._removeEdge(edge); + }); +}; + +GraphDataset.prototype._connectVerticesByName = function(nodeNameSource, nodeNameTarget){ + var source = this.getVertexByName(nodeNameSource); + var target = this.getVertexByName(nodeNameTarget); + + if ((source != null)&&(target!=null)){ + this.addEdge(source.getName() +"_" + target.getName(), source.getId(), target.getId(), {}); + } + else{ + if (source == null){ + console.log("No encontrado: " + nodeNameSource) + } + if (target == null){ + console.log("No encontrado: " + nodeNameTarget) + } + } +}; + +GraphDataset.prototype.loadFromJSON = function(json){ + var json = json; + this.init(); + this.json = new Object(); + this.json.vertices = new Array(); + this.json.edges = new Array(); + this.json.relations = new Array(); + + for ( var i = 0; i < json.nodes.length; i++) { + if (json.nodes[i] != null){ + var name = json.nodes[i]; + this.addNode(name); + } + else{ + this.json.vertices.push(null); + } + } + + for ( var i = 0; i < json.edges.length; i++) { + if (json.edges[i] != null){ + if (json.relations[i] != null){ + var name = json.edges[i]; + this.addEdge(name, json.relations[i].sourceIndex, json.relations[i].targetIndex, json.relations[i].args); + } + } + else{ + this.json.edges.push(null); + this.json.relations.push(null); + } + } +}; + +GraphDataset.prototype.prettyPrint = function(){ + for ( var node in this.vertices) { + console.log(this.vertices[node].getName() ); + for ( var j = 0; j < this.vertices[node].getEdgesIn().length; j++) { + console.log(" --> " + this.vertices[node].getEdgesIn()[j].getNodeTarget().getName() ); + } + } +}; + +GraphDataset.prototype._removeEdge = function(edge){ + this.json.edges[edge.getId()] = null; + this.json.relations[edge.getId()] = null; + + delete this.edges[edge.getId()]; + this.edgeDeleted.notify(edge); + + +}; + +GraphDataset.prototype._removeNode = function(node){ + this.json.vertices[node.getId()] = null; + delete this.vertices[node.getId()]; + this.vertexDeleted.notify(node); +}; + +GraphDataset.prototype.toJSON = function(){ + var json = new Object(); + var nodes = new Array(); + json.nodes = this.json.vertices; //nodes; + json.edges = this.json.edges; //edges; + json.relations = this.json.relations; + return json; +}; + +GraphDataset.prototype.clone = function(){ + var dsDataset = new GraphDataset(); + dsDataset.loadFromJSON(this.toJSON()); + return dsDataset; +}; +//GraphDataset.prototype.test = function(){ +// this.loadFromJSON(this.toJSON()); +//}; + +function labels(){ + var names = new Array(); + + var dataset = interactomeViewer.graphEditorWidget.dataset; + var layout = interactomeViewer.graphEditorWidget.layout; + + for ( var vertexId in interactomeViewer.graphEditorWidget.dataset.getVertices()) { + names.push(interactomeViewer.graphEditorWidget.dataset.getVertexById(vertexId).getName()); + } + + var sorted = (names.sort()); + console.log(sorted) + var distance = 0.01; + var altura = 0.6; + for ( var i = 0; i < names.length; i++) { + var id =dataset.getVertexByName(names[i]).getId(); + + layout.getNodeById(id).setCoordenates(distance, altura); + + + distance = parseFloat(distance) + parseFloat(0.03); + + altura = parseFloat(altura) + parseFloat(0.02); + + if (parseFloat(altura) == 0.9800000000000003){ + + altura = 0.6; + distance = distance - 0.51; + } + + } + + +}; + +function GraphItem(id, name, args){ + this.id = id; + this.name = name; + this.type = "NONE"; + + this.args = new Object(); + + + if (args!=null){ + this.args = args; + if (args.type !=null){ + this.type = args.type; + } + } + + //Events + this.nameChanged = new Event(this); + this.deleted = new Event(this); +} + +GraphItem.prototype.getName = function(){ + return this.name; +}; + +GraphItem.prototype.getId = function(){ + return this.id; +}; + +GraphItem.prototype.setName = function(name){ + var oldName = this.getName(); + this.name = name; + this.nameChanged.notify({"item": this, "previousName" : oldName}); +}; + + + + + +function LayoutDataset(){ + this.dataset = null; + this.vertices = new Object(); + this.changed = new Event(this); + + + this.args = new Object(); + + //RANDOM, CIRCLE + this.args.type = "CIRCLE"; +}; + +LayoutDataset.prototype.loadFromJSON = function(dataset, json){ + var _this = this; + this.vertices = new Object(); + this.dataset = dataset; //new GraphDataset(); + for ( var vertex in json) { + this.vertices[vertex] = new NodeLayout(vertex, json[vertex].x, json[vertex].y); + this.vertices[vertex].changed.attach(function (sender, item){ + _this.changed.notify(item); + }); + } + this._attachDatasetEvents(); +}; + + +LayoutDataset.prototype.toJSON = function(){ + var serialize = new Object(); + for ( var vertex in this.vertices) { + serialize[vertex] = new Object(); + serialize[vertex].x = this.vertices[vertex].x; + serialize[vertex].y = this.vertices[vertex].y; + } + serialize.dataset = new Object(); + serialize.dataset =this.dataset.toJSON(); + return serialize; +}; + +LayoutDataset.prototype.dataBind = function(graphDataset){ + this.dataset = graphDataset; + this._attachDatasetEvents(); + this._calculateLayout(); +}; + +LayoutDataset.prototype._removeVertex = function(vertexId){ + delete this.vertices[vertexId]; +}; + +LayoutDataset.prototype._attachDatasetEvents = function(){ + var _this = this; + + this.dataset.vertexDeleted.attach(function (sender, item){ + _this._removeVertex(item.getId()); + }); + + this.dataset.newVertex.attach(function (sender, item){ + _this.vertices[item.getId()] = new NodeLayout(item.getId(), 0.5, 0.5); + _this.vertices[item.getId()].changed.attach(function (sender, item){ + _this.changed.notify(item); + }); + }); +}; + +LayoutDataset.prototype.getType = function(){ + return this.args.type; +}; + +LayoutDataset.prototype._calculateLayoutVertices = function(type, count){ + + if (type == "CIRCLE"){ + var radius = 0.4; + var centerX = 0.5; + var centerY = 0.5; + var verticesCoordinates = new Array(); + for(var i = 0; i < count; i++){ + x = centerX + radius * Math.sin(i * 2 * Math.PI/count); + y = centerY + radius * Math.cos(i * 2 * Math.PI/count); + verticesCoordinates.push({'x':x,'y':y}); + } + return verticesCoordinates; + } +}; + + +LayoutDataset.prototype._calculateLayout = function(){ + var _this = this; + if (this.getType() == "RANDOM"){ + for ( var vertex in this.dataset.getVertices()) { + if (this.vertices[vertex] == null){ + this.vertices[vertex] = new NodeLayout(vertex, 0, 0); + } + this.vertices[vertex].setCoordinates(Math.random(), Math.random()); + this.vertices[vertex].changed.attach(function (sender, item){ + _this.changed.notify(item); + }); + } + } + + if ( this.getType() == "CIRCLE"){ + + var count = this.dataset._getVerticesCount(); + var verticesCoordinates = this._calculateLayoutVertices(this.getType(), count); + + var aux = 0; + for ( var vertex in this.dataset.getVertices()) { + if (this.vertices[vertex] == null){ + this.vertices[vertex] = new NodeLayout(vertex, 0, 0); + } + this.vertices[vertex].setCoordinates(verticesCoordinates[aux].x, verticesCoordinates[aux].y);//{"x":, "y":}; + aux++; + this.vertices[vertex].changed.attach(function (sender, item){ + _this.changed.notify(item); + }); + } + } + + + if (this.getType() == "SQUARE"){ + + var count = this.dataset._getVerticesCount(); + var xMin = 0.1; + var xMax = 0.9; + var yMin = 0.1; + var yMax = 0.9; + + var rows = Math.sqrt(count); + var step = (xMax - xMin) / rows; + + var verticesCoordinates = new Array(); + for(var i = 0; i < rows; i ++){ + for ( var j = 0; j < rows; j++) { + x = i * step + xMin; + y = j * step + yMin; + verticesCoordinates.push({'x':x,'y':y}); + } + } + + var aux = 0; + for ( var vertex in this.dataset.getVertices()) { + if (this.vertices[vertex] == null){ + this.vertices[vertex] = new NodeLayout(vertex, 0, 0); + } + this.vertices[vertex].setCoordinates(verticesCoordinates[aux].x, verticesCoordinates[aux].y);//{"x":, "y":}; + aux++; + this.vertices[vertex].changed.attach(function (sender, item){ + _this.changed.notify(item); + }); + } + } + +}; + +LayoutDataset.prototype.getNodeById = function(id){ + return this.vertices[id]; +}; + +LayoutDataset.prototype.getVerticesByArea = function(x1, y1, x2, y2){ + var vertices = new Array(); + for ( var vertex in this.dataset.getVertices()) { + if ((this.vertices[vertex].x >= x1)&&(this.vertices[vertex].x <= x2)){ + if ((this.vertices[vertex].y >= y1)&&(this.vertices[vertex].y <= y2)){ + vertices.push(this.vertices[vertex]); + } + } + } + return vertices; +}; + + + + +LayoutDataset.prototype.getLayout = function(type){ + + if (type == "CIRCLE"){ + this.args.type = "CIRCLE"; + this._calculateLayout(); + return; + } + + if (type == "SQUARE"){ + this.args.type = "SQUARE"; + this._calculateLayout(); + return; + } + + if (type == "RANDOM"){ + this.args.type = "RANDOM"; + this._calculateLayout(); + return; + } + + + var dotText = this.dataset.toDOTID(); + var url = "http://bioinfo.cipf.es/utils/ws/rest/network/layout/"+type+".coords"; + var _this = this; + + $.ajax({ + async: true, + type: "POST", + url: url, + dataType: "text", + data: { + dot :dotText + }, + cache: false, + success: function(data){ + var response = JSON.parse(data); + for ( var vertexId in response) { + _this.vertices[vertexId].setCoordinates(response[vertexId].x, response[vertexId].y); + } + } + }); + +// $.ajax({ +// async: true, +// type: "POST", +// url: url, +// dataType: "script", +// data: { +// dot :dotText +// }, +// cache: false, +// success: function(data){ +// var response = JSON.parse(data); +// for ( var vertexId in response) { +// _this.vertices[vertexId].setCoordinates(response[vertexId].x, response[vertexId].y); +// } +// } +// }); + +}; + +function NodeLayout(id, x, y, args){ + this.id = id; + this.x = x; + this.y = y; + this.changed = new Event(this); +}; + +NodeLayout.prototype.getId = function(id){ + return this.id; +}; + +NodeLayout.prototype.setCoordinates = function(x, y){ + this.x = x; + this.y = y; + this.changed.notify(this); +}; + + + +function NetworkDataSetFormatter(vertexFormatProperties, defaultEdgeProperties, args){ + DataSet.prototype.constructor.call(this); + + this.args = new Object(); + + this.vertices = new Object(); + this.edges = new Object(); + this.dataset = null; + this.maxClass = 0; + this.minClass = 0; + + /** Coordenates with default Setting */ + this.args.width = 1100; + this.args.height = 500; + + /** Optional parameters */ + this.args.backgroundColor = "#FFFFFF"; + this.args.backgroundImage = null; + this.args.backgroundImageHeight = null; + this.args.backgroundImageWidth = null; + this.args.backgroundImageX = null; + this.args.backgroundImageY = null; + + + this.args.balanceNodes = false; + this.args.nodesMaxSize = 3; + this.args.nodesMinSize = 0.5; + + + /** Zoom **/ + this.args.zoomScale = 1; + this.args.zoomScaleStepFactor = 0.4; + + if (args != null){ + if (args.backgroundImage != null){ + this.args.backgroundImage = args.backgroundImage; + } + + if (args.width != null){ + this.args.width = args.width; + } + + if (args.height != null){ + this.args.height = args.height; + this.args.svgHeight = args.height; + } + + if (args.svgHeight != null){ + this.args.svgHeight = args.svgHeight; + } + + if (args.backgroundColor != null){ + this.args.backgroundColor = args.backgroundColor; + } + + if(args.balanceNodes != null){ + this.args.balanceNodes = args.balanceNodes; + } + + if(args.nodesMaxSize != null){ + this.args.nodesMaxSize = args.nodesMaxSize; + } + if(args.nodesMinSize != null){ + this.args.nodesMinSize = args.nodesMinSize; + } + } + + this.args.defaultEdgeProperties = {"fill":"#000000", "radius":"1", "stroke":"#000000", "size":1, "title":{"fontSize":10, "fontColor":"#000000"}}; + this.args.vertexFormatProperties = { "fill":"#000000", "stroke":"#000000", "stroke-opacity":"#000000"}; + + if (vertexFormatProperties!= null){ + this.args.vertexFormatProperties = vertexFormatProperties; + } + if (defaultEdgeProperties!= null){ + this.args.defaultEdgeProperties = defaultEdgeProperties; + } + + /** Events **/ + this.changed = new Event(this); + this.edgeChanged = new Event(this); + this.resized = new Event(this); + this.backgroundImageChanged= new Event(this); + this.backgroundColorChanged= new Event(this); +}; + +NetworkDataSetFormatter.prototype.loadFromJSON = function(dataset, json){ + this.args = new Object(); + this.vertices = new Object(); + this.args = json; + this._setDataset(dataset); + var _this = this; + + for ( var vertex in json.vertices) { + this.addVertex(vertex, json.vertices[vertex]); + } + + for ( var edgeId in json.edges) { + this.addEdge(edgeId, json.edges[edgeId]); + } +}; + + +NetworkDataSetFormatter.prototype.toJSON = function(){ + + +// this.args.vertices = new Object(); +// this.args.edges = new Object(); +// +// for ( var vertex in this.vertices) { +// this.args.vertices[vertex] = this.getVertexById(vertex).toJSON(); +// } +// for ( var edge in this.edges) { +// this.args.edges[edge] = this.getEdgeById(edge).toJSON(); +// } +// +// return (this.args); + + + var serialize = new Object(); + serialize = JSON.parse(JSON.stringify(this.args)); + serialize.vertices = new Object(); + serialize.edges = new Object(); + + for ( var vertex in this.vertices) { + serialize.vertices[vertex] = this.getVertexById(vertex).toJSON(); + } + for ( var edge in this.edges) { + serialize.edges[edge] = this.getEdgeById(edge).toJSON(); + } + + return (serialize); +}; + + + +NetworkDataSetFormatter.prototype._getNodeSize = function(nodeId){ + if (this.isVerticesBalanced()){ + var total = this.maxClass - this.minClass; + if (total == 0){ total = 1;} + var nodeConnection = this.dataset.getVertexById(nodeId).getEdges().length; + return ((nodeConnection*this.args.nodesMaxSize)/total) + this.args.nodesMinSize; + } + return this.getVertexById(nodeId).getDefault().getSize(); +}; + +NetworkDataSetFormatter.prototype._recalculateSize = function(){ + if (this.isVerticesBalanced()){ + this.maxClass = this.dataset.getMaxClass(); + this.minClass = this.dataset.getMinClass(); + for ( var vertexIdAll in this.vertices) { + var size = this._getNodeSize(vertexIdAll); + this.vertices[vertexIdAll].getDefault().setSize(size); + this.vertices[vertexIdAll].getSelected().setSize(size); + this.vertices[vertexIdAll].getOver().setSize(size); + } + } +}; + + +NetworkDataSetFormatter.prototype.addVertex = function(vertexId, json){ + + + if (json == null){ + this.vertices[vertexId] = new CircleVertexGraphFormatter(vertexId, this.args.vertexFormatProperties.defaultFormat, this.args.vertexFormatProperties.selected, this.args.vertexFormatProperties.over, this.args.vertexFormatProperties.dragging); + } + else{ + + /** Cargo los attributos desde el json **/ + if (json.type == null){ + this.vertices[vertexId] = new CircleVertexGraphFormatter(vertexId, this.args.vertexFormatProperties.defaultFormat, this.args.vertexFormatProperties.selected, this.args.vertexFormatProperties.over, this.args.vertexFormatProperties.dragging); + } + + if ((json.type == "SquareVertexGraphFormatter")||(json.type == "SquareVertexNetworkFormatter")){ + this.vertices[vertexId] = new SquareVertexGraphFormatter(vertexId); + this.vertices[vertexId].loadFromJSON(json); + } + + if ((json.type == "CircleVertexGraphFormatter")||(json.type == "CircleVertexNetworkFormatter")){ + this.vertices[vertexId] = new CircleVertexGraphFormatter(vertexId); + this.vertices[vertexId].loadFromJSON(json); + } + + if ((json.type == "EllipseVertexGraphFormatter")||(json.type == "EllipseVertexNetworkFormatter")){ + this.vertices[vertexId] = new EllipseVertexGraphFormatter(vertexId); + this.vertices[vertexId].loadFromJSON(json); + } + + if ((json.type == "RectangleVertexGraphFormatter")||(json.type == "RectangleVertexNetworkFormatter")){ + this.vertices[vertexId] = new RectangleVertexGraphFormatter(vertexId); + this.vertices[vertexId].loadFromJSON(json); + } + + if ((json.type == "RoundedVertexGraphFormatter")||(json.type == "RoundedVertexNetworkFormatter")){ + this.vertices[vertexId] = new RoundedVertexGraphFormatter(vertexId); + this.vertices[vertexId].loadFromJSON(json); + } + + } + + + var _this = this; + this.vertices[vertexId].stateChanged.attach(function (sender, item){ + _this.changed.notify(item); + }); + + var size = this._getNodeSize(vertexId); + this.vertices[vertexId].defaultFormat.args.size = size; + this.vertices[vertexId].selected.args.size = size; + this.vertices[vertexId].over.args.size = size; + +}; + +NetworkDataSetFormatter.prototype.addEdge = function(edgeId, json){ + + /** Es un edge nuevo que le doy los atributos por defecto **/ + if (json == null){ + if (this.dataset.getEdgeById(edgeId).getNodeSource().getId() == this.dataset.getEdgeById(edgeId).getNodeTarget().getId()){ + this.edges[edgeId] = new BezierEdgeGraphFormatter(edgeId, this.args.defaultEdgeProperties.defaultFormat, this.args.defaultEdgeProperties.selected, this.args.defaultEdgeProperties.over, this.args.defaultEdgeProperties.dragging); + }else{ + this.edges[edgeId] = new DirectedLineEdgeGraphFormatter(edgeId, this.args.defaultEdgeProperties.defaultFormat, this.args.defaultEdgeProperties.selected, this.args.defaultEdgeProperties.over, this.args.defaultEdgeProperties.dragging); + } + } + else{ + /** Cargo los attributos desde el json **/ + if (json.type == null){ + this.edges[edgeId] = new LineEdgeGraphFormatter(edgeId, this.args.defaultEdgeProperties.defaultFormat, this.args.defaultEdgeProperties.selected, this.args.defaultEdgeProperties.over, this.args.defaultEdgeProperties.dragging); + } + + if ((json.type == "LineEdgeGraphFormatter")||(json.type == "LineEdgeNetworkFormatter")){ + this.edges[edgeId] = new LineEdgeGraphFormatter(edgeId); + this.edges[edgeId].loadFromJSON(json); + } + if ((json.type == "DirectedLineEdgeGraphFormatter")||(json.type == "DirectedLineEdgeNetworkFormatter")){ + this.edges[edgeId] = new DirectedLineEdgeGraphFormatter(edgeId); + this.edges[edgeId].loadFromJSON(json); + } + + if ((json.type == "BezierEdgeGraphFormatter")||(json.type == "BezierEdgeNetworkFormatter")){ + this.edges[edgeId] = new BezierEdgeGraphFormatter(edgeId); + this.edges[edgeId].loadFromJSON(json); + } + + + if ((json.type == "CutDirectedLineEdgeGraphFormatter")||(json.type == "CutDirectedLineEdgeNetworkFormatter")){ + this.edges[edgeId] = new CutDirectedLineEdgeGraphFormatter(edgeId); + this.edges[edgeId].loadFromJSON(json); + } + + if ((json.type == "DotDirectedLineEdgeGraphFormatter")||(json.type == "DotDirectedLineEdgeNetworkFormatter")){ + this.edges[edgeId] = new DotDirectedLineEdgeGraphFormatter(edgeId); + this.edges[edgeId].loadFromJSON(json); + } + if ((json.type == "OdotDirectedLineEdgeGraphFormatter")||(json.type == "OdotDirectedLineEdgeNetworkFormatter")){ + this.edges[edgeId] = new OdotDirectedLineEdgeGraphFormatter(edgeId); + this.edges[edgeId].loadFromJSON(json); + } + + if ((json.type == "OdirectedLineEdgeGraphFormatter")||(json.type == "OdirectedLineEdgeNetworkFormatter")){ + this.edges[edgeId] = new OdirectedLineEdgeGraphFormatter(edgeId); + this.edges[edgeId].loadFromJSON(json); + } + } + + var _this = this; + this.edges[edgeId].stateChanged.attach(function (sender, item){ + _this.edgeChanged.notify(item); + }); + + this._recalculateSize(); +}; + +NetworkDataSetFormatter.prototype._setDataset = function(dataset){ + this.dataset = dataset; + this.maxClass = dataset.getMaxClass(); + this.minClass = dataset.getMinClass(); + this._attachDatasetEvents(); +}; + +NetworkDataSetFormatter.prototype.changeEdgeType = function(edgeId, type){ + if ((type == "LineEdgeGraphFormatter")||(type == "LineEdgeNetworkFormatter")){ + this.edges[edgeId] = new LineEdgeGraphFormatter(edgeId, this.args.defaultEdgeProperties.defaultFormat, this.args.defaultEdgeProperties.selected, this.args.defaultEdgeProperties.over, this.args.defaultEdgeProperties.dragging); + + } + if ((type == "DirectedLineEdgeGraphFormatter")||(type == "DirectedLineEdgeNetworkFormatter")){ + this.edges[edgeId] = new DirectedLineEdgeGraphFormatter(edgeId, this.args.defaultEdgeProperties.defaultFormat, this.args.defaultEdgeProperties.selected, this.args.defaultEdgeProperties.over, this.args.defaultEdgeProperties.dragging); + } + + if ((type == "CutDirectedLineEdgeGraphFormatter")||(type == "CutDirectedLineEdgeNetworkFormatter")){ + this.edges[edgeId] = new CutDirectedLineEdgeGraphFormatter(edgeId, this.args.defaultEdgeProperties.defaultFormat, this.args.defaultEdgeProperties.selected, this.args.defaultEdgeProperties.over, this.args.defaultEdgeProperties.dragging); + } + + if ((type == "DotDirectedLineEdgeGraphFormatter")||(type == "DotDirectedLineEdgeNetworkFormatter")){ + this.edges[edgeId] = new DotDirectedLineEdgeGraphFormatter(edgeId, this.args.defaultEdgeProperties.defaultFormat, this.args.defaultEdgeProperties.selected, this.args.defaultEdgeProperties.over, this.args.defaultEdgeProperties.dragging); + } + + if ((type == "OdotDirectedLineEdgeGraphFormatter")||(type == "OdotDirectedLineEdgeNetworkFormatter")){ + this.edges[edgeId] = new OdotDirectedLineEdgeGraphFormatter(edgeId, this.args.defaultEdgeProperties.defaultFormat, this.args.defaultEdgeProperties.selected, this.args.defaultEdgeProperties.over, this.args.defaultEdgeProperties.dragging); + } + + if ((type == "OdirectedLineEdgeGraphFormatter")||(type == "OdirectedLineEdgeNetworkFormatter")){ + this.edges[edgeId] = new OdirectedLineEdgeGraphFormatter(edgeId, this.args.defaultEdgeProperties.defaultFormat, this.args.defaultEdgeProperties.selected, this.args.defaultEdgeProperties.over, this.args.defaultEdgeProperties.dragging); + } + + + + var _this = this; + this.edges[edgeId].stateChanged.attach(function (sender, item){ + _this.edgeChanged.notify(item); + }); + _this.edgeChanged.notify(this.edges[edgeId]); +}; +/* +classe = "SquareNetworkNodeFormatter"; +} +if (value == "circle"){ + classe = "CircleNetworkNodeFormatter"; + **/ +NetworkDataSetFormatter.prototype.changeNodeType = function(vertexId, type){ + var defaultFormat = JSON.parse(JSON.stringify(this.vertices[vertexId].getDefault())); + var selectedFormat = JSON.parse(JSON.stringify(this.vertices[vertexId].getSelected())); + var defaultFormat = JSON.parse(JSON.stringify(this.vertices[vertexId].getDefault())); + var defaultFormat = JSON.parse(JSON.stringify(this.vertices[vertexId].getDefault())); + + if ((type == "SquareVertexGraphFormatter")||(type == "SquareVertexNetworkFormatter")){ + this.vertices[vertexId] = new SquareVertexGraphFormatter(vertexId,defaultFormat, this.args.vertexFormatProperties.selected, this.args.vertexFormatProperties.over, this.args.vertexFormatProperties.dragging); + } + + if ((type == "CircleVertexGraphFormatter")||(type == "CircleVertexNetworkFormatter")){ + this.vertices[vertexId] = new CircleVertexGraphFormatter(vertexId, defaultFormat, this.args.vertexFormatProperties.selected, this.args.vertexFormatProperties.over, this.args.vertexFormatProperties.dragging); + } + + if ((type == "EllipseVertexGraphFormatter")||(type == "EllipseVertexNetworkFormatter")){ + this.vertices[vertexId] = new EllipseVertexGraphFormatter(vertexId, defaultFormat, this.args.vertexFormatProperties.selected, this.args.vertexFormatProperties.over, this.args.vertexFormatProperties.dragging); + } + + if ((type == "RectangleVertexGraphFormatter")||(type == "RectangleVertexNetworkFormatter")){ + this.vertices[vertexId] = new RectangleVertexGraphFormatter(vertexId, defaultFormat, this.args.vertexFormatProperties.selected, this.args.vertexFormatProperties.over, this.args.vertexFormatProperties.dragging); + } + + if ((type == "RoundedVertexGraphFormatter")||(type == "RoundedVertexNetworkFormatter")){ + this.vertices[vertexId] = new RoundedVertexGraphFormatter(vertexId, defaultFormat, this.args.vertexFormatProperties.selected, this.args.vertexFormatProperties.over, this.args.vertexFormatProperties.dragging); + } + + if ((type == "OctagonVertexGraphFormatter")||(type == "OctagonVertexNetworkhFormatter")){ + this.vertices[vertexId] = new OctagonVertexGraphFormatter(vertexId, defaultFormat, this.args.vertexFormatProperties.selected, this.args.vertexFormatProperties.over, this.args.vertexFormatProperties.dragging); + } + + + var _this = this; + this.vertices[vertexId].stateChanged.attach(function (sender, item){ + _this.changed.notify(item); + }); + _this.changed.notify(this.vertices[vertexId]); +}; + + + +NetworkDataSetFormatter.prototype.dataBind = function(networkDataSet){ + var _this = this; + this._setDataset(networkDataSet); + + for ( var vertex in this.dataset.getVertices()) { + this.addVertex(vertex); + } + + for ( var edge in this.dataset.getEdges()) { + this.addEdge(edge); + } +}; + +NetworkDataSetFormatter.prototype._removeEdge = function(edgeId){ + delete this.edges[edgeId]; +}; + +NetworkDataSetFormatter.prototype._removeVertex = function(vertexId){ + delete this.vertices[vertexId]; +}; + +NetworkDataSetFormatter.prototype._attachDatasetEvents = function(id){ + var _this = this; + this.dataset.vertexDeleted.attach(function (sender, item){ + _this._removeVertex(item.getId()); + }); + + this.dataset.edgeDeleted.attach(function (sender, item){ + _this._removeEdge(item.getId()); + }); + + this.dataset.newVertex.attach(function (sender, item){ + _this.addVertex(item.getId()); + }); + + this.dataset.newEdge.attach(function (sender, item){ + _this.addEdge(item.getId()); + }); +}; + + +NetworkDataSetFormatter.prototype.getVertexById = function(id){ + return this.vertices[id]; +}; + +NetworkDataSetFormatter.prototype.getEdgeById = function(id){ + return this.edges[id]; +}; + +NetworkDataSetFormatter.prototype.makeLabelsBigger = function(){ +for ( var vertexId in this.vertices) { + var fontSize = this.vertices[vertexId].getDefault().getFontSize() + 2; + this.vertices[vertexId].getDefault().setFontSize(fontSize); + } +}; + +NetworkDataSetFormatter.prototype.makeLabelsSmaller = function(){ + for ( var vertexId in this.vertices) { + var fontSize = this.vertices[vertexId].getDefault().getFontSize() - 2; + this.vertices[vertexId].getDefault().setFontSize(fontSize); + } +}; + + +NetworkDataSetFormatter.prototype.resize = function(width, height){ + this.args.width = width; + this.args.height = height; + this.resized.notify(); +}; + +/** ZOOM GETTERS **/ +NetworkDataSetFormatter.prototype.getZoomScaleStepFactor = function(){return this.args.zoomScaleStepFactor;}; +NetworkDataSetFormatter.prototype.setZoomScaleStepFactor = function(scaleFactor){this.args.zoomScaleStepFactor = scaleFactor;}; +NetworkDataSetFormatter.prototype.getZoomScale = function(){return this.args.zoomScale;}; +NetworkDataSetFormatter.prototype.setZoomScale = function(scale){this.args.zoomScale = scale;}; + +NetworkDataSetFormatter.prototype.getNodesMaxSize = function(){return this.args.nodesMaxSize;}; +NetworkDataSetFormatter.prototype.getNodesMinSize = function(){return this.args.nodesMinSize;}; + +/** SIZE SETTERS AND GETTERS **/ +NetworkDataSetFormatter.prototype.isVerticesBalanced = function(){return this.args.balanceNodes;}; +NetworkDataSetFormatter.prototype.getWidth = function(){return this.args.width;}; +NetworkDataSetFormatter.prototype.getHeight = function(){return this.args.height;}; + +/** OPTIONAL PARAMETERS GETTERS AND SETTERS **/ +NetworkDataSetFormatter.prototype.getBackgroundImage = function(){return this.args.backgroundImage;}; +NetworkDataSetFormatter.prototype.setBackgroundImage = function(value){this.args.backgroundImage = value; this.backgroundImageChanged.notify(this);}; + + +NetworkDataSetFormatter.prototype.getBackgroundImageWidth = function(){return this.args.backgroundImageWidth;}; +NetworkDataSetFormatter.prototype.setBackgroundImageWidth = function(value){this.args.backgroundImageWidth = value; this.backgroundImageChanged.notify(this);}; + +NetworkDataSetFormatter.prototype.getBackgroundImageHeight = function(){return this.args.backgroundImageHeight;}; +NetworkDataSetFormatter.prototype.setBackgroundImageHeight = function(value){this.args.backgroundImageHeight = value; this.backgroundImageChanged.notify(this);}; + +NetworkDataSetFormatter.prototype.getBackgroundImageX = function(){return this.args.backgroundImageX;}; +NetworkDataSetFormatter.prototype.setBackgroundImageX = function(value){this.args.backgroundImageX = value; this.backgroundImageChanged.notify(this);}; + +NetworkDataSetFormatter.prototype.getBackgroundImageY = function(){return this.args.backgroundImageX;}; +NetworkDataSetFormatter.prototype.setBackgroundImageY = function(value){this.args.backgroundImageY = value; this.backgroundImageChanged.notify(this);}; + + + +NetworkDataSetFormatter.prototype.getBackgroundColor = function(){return this.args.backgroundColor;}; +NetworkDataSetFormatter.prototype.setBackgroundColor = function(value){this.args.backgroundColor = value; this.backgroundColorChanged.notify(this);}; + +NetworkDataSetFormatter.prototype.getWidth = function(){return this.args.width;}; +NetworkDataSetFormatter.prototype.setWidth = function(value){this.args.width = value;}; + +NetworkDataSetFormatter.prototype.getHeight = function(){return this.args.height;}; +NetworkDataSetFormatter.prototype.setHeight = function(value){this.args.height = value;}; + + + + +Vertex.prototype.getName = GraphItem.prototype.getName; +Vertex.prototype.setName = GraphItem.prototype.setName; +Vertex.prototype.getId = GraphItem.prototype.getId; + +function Vertex(id, name, args){ + GraphItem.prototype.constructor.call(this, id, name, args); + this.edgesIn= new Array(); + this.edgesOut= new Array(); +}; + +Vertex.prototype.getEdges = function(){ + return this.edgesIn.concat(this.edgesOut); +}; + +Vertex.prototype.getEdgesCount = function(){ + return this.getEdges().length; +}; + +Vertex.prototype.getEdgesIn = function(){ + return this.edgesIn; +}; + +Vertex.prototype.getEdgesOut = function(){ + return this.edgesOut; +}; + +Vertex.prototype.addEdge = function(edge){ + if (edge.getNodeSource().getId() == this.id){ + this.edgesIn.push(edge); + } + else{ + this.edgesOut.push(edge); + } +}; + +Vertex.prototype.removeEdge = function(edge){ + for ( var i = 0; i < this.getEdgesIn().length; i++) { + var edgeIn = this.edgesIn[i]; + if (edgeIn.getId() == edge.getId()){ + this.edgesIn.splice(i, 1); + } + } + for ( var i = 0; i < this.getEdgesOut().length; i++) { + var edgeIn = this.edgesOut[i]; + if (edgeIn.getId() == edge.getId()){ + this.edgesOut.splice(i, 1); + } + } +}; + +Vertex.prototype.remove = function(){ + var edges = this.getEdges(); + for ( var i = 0; i < edges.length; i++) { + var edge = edges[i]; + edge.remove(); + } + this.deleted.notify(this); +}; + + + + + +VertexGraphFormatter.prototype.getDefault = ItemGraphFormatter.prototype.getDefault; +VertexGraphFormatter.prototype.getSelected = ItemGraphFormatter.prototype.getSelected; +VertexGraphFormatter.prototype.getOver = ItemGraphFormatter.prototype.getOver; +VertexGraphFormatter.prototype.getDragging = ItemGraphFormatter.prototype.getDragging; +VertexGraphFormatter.prototype.getId = ItemGraphFormatter.prototype.getId; +VertexGraphFormatter.prototype.toJSON = ItemGraphFormatter.prototype.toJSON; +VertexGraphFormatter.prototype.loadFromJSON = ItemGraphFormatter.prototype.loadFromJSON; +VertexGraphFormatter.prototype._setEvents = ItemGraphFormatter.prototype._setEvents; + + +function VertexGraphFormatter(defaultFormat, selectedFormat, overFormat, draggingFormat){ + ItemGraphFormatter.prototype.constructor.call(this, defaultFormat, selectedFormat, overFormat, draggingFormat); +}; + + +CircleVertexGraphFormatter.prototype.getDefault = VertexGraphFormatter.prototype.getDefault; +CircleVertexGraphFormatter.prototype.getSelected = VertexGraphFormatter.prototype.getSelected; +CircleVertexGraphFormatter.prototype.getOver = VertexGraphFormatter.prototype.getOver; +CircleVertexGraphFormatter.prototype.getDragging = VertexGraphFormatter.prototype.getDragging; +CircleVertexGraphFormatter.prototype.getId = VertexGraphFormatter.prototype.getId; +CircleVertexGraphFormatter.prototype.toJSON = VertexGraphFormatter.prototype.toJSON; +CircleVertexGraphFormatter.prototype.loadFromJSON = VertexGraphFormatter.prototype.loadFromJSON; +CircleVertexGraphFormatter.prototype._setEvents = VertexGraphFormatter.prototype._setEvents; + +function CircleVertexGraphFormatter(id, defaultFormat, selectedFormat, overFormat, draggingFormat){ + VertexGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, draggingFormat); + this.args.type = "CircleVertexGraphFormatter"; + if (defaultFormat != null){ + if (defaultFormat.radius != null){ + this.defaultFormat.args.radius = defaultFormat.radius; + } + } + + if (selectedFormat != null){ + if (selectedFormat.radius != null){ + this.selected.args.radius = selectedFormat.radius; + } + } + + if (overFormat != null){ + if (overFormat.radius != null){ + this.over.args.radius = overFormat.radius; + } + } + + if (draggingFormat != null){ + if (draggingFormat.radius != null){ + this.dragging.args.draggingFormat = draggingFormat.radius; + } + } +}; + + + +SquareVertexGraphFormatter.prototype.getDefault = VertexGraphFormatter.prototype.getDefault; +SquareVertexGraphFormatter.prototype.getSelected = VertexGraphFormatter.prototype.getSelected; +SquareVertexGraphFormatter.prototype.getOver = VertexGraphFormatter.prototype.getOver; +SquareVertexGraphFormatter.prototype.getDragging = VertexGraphFormatter.prototype.getDragging; +SquareVertexGraphFormatter.prototype.getId = VertexGraphFormatter.prototype.getId; +SquareVertexGraphFormatter.prototype.toJSON = VertexGraphFormatter.prototype.toJSON; +SquareVertexGraphFormatter.prototype.loadFromJSON = VertexGraphFormatter.prototype.loadFromJSON; +SquareVertexGraphFormatter.prototype._setEvents = VertexGraphFormatter.prototype._setEvents; + +function SquareVertexGraphFormatter(id, defaultFormat, selectedFormat, overFormat, draggingFormat){ + VertexGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, draggingFormat); + this.args.type = "SquareVertexGraphFormatter"; + if (defaultFormat != null){ + if (defaultFormat.radius != null){ + this.defaultFormat.args.radius = defaultFormat.radius; + } + } + + if (selectedFormat != null){ + if (selectedFormat.radius != null){ + this.selected.args.radius = selectedFormat.radius; + } + } + + if (overFormat != null){ + if (overFormat.radius != null){ + this.over.args.radius = overFormat.radius; + } + } + + if (draggingFormat != null){ + if (draggingFormat.radius != null){ + this.dragging.args.draggingFormat = draggingFormat.radius; + } + } +}; + + +EllipseVertexGraphFormatter.prototype.getDefault = VertexGraphFormatter.prototype.getDefault; +EllipseVertexGraphFormatter.prototype.getSelected = VertexGraphFormatter.prototype.getSelected; +EllipseVertexGraphFormatter.prototype.getOver = VertexGraphFormatter.prototype.getOver; +EllipseVertexGraphFormatter.prototype.getDragging = VertexGraphFormatter.prototype.getDragging; +EllipseVertexGraphFormatter.prototype.getId = VertexGraphFormatter.prototype.getId; +EllipseVertexGraphFormatter.prototype.toJSON = VertexGraphFormatter.prototype.toJSON; +EllipseVertexGraphFormatter.prototype.loadFromJSON = VertexGraphFormatter.prototype.loadFromJSON; +EllipseVertexGraphFormatter.prototype._setEvents = VertexGraphFormatter.prototype._setEvents; + +function EllipseVertexGraphFormatter(id, defaultFormat, selectedFormat, overFormat, draggingFormat){ + VertexGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, draggingFormat); + this.args.type = "EllipseVertexGraphFormatter"; + if (defaultFormat != null){ + if (defaultFormat.radius != null){ + this.defaultFormat.args.radius = defaultFormat.radius; + } + } + + if (selectedFormat != null){ + if (selectedFormat.radius != null){ + this.selected.args.radius = selectedFormat.radius; + } + } + + if (overFormat != null){ + if (overFormat.radius != null){ + this.over.args.radius = overFormat.radius; + } + } + + if (draggingFormat != null){ + if (draggingFormat.radius != null){ + this.dragging.args.draggingFormat = draggingFormat.radius; + } + } +}; + + + +RectangleVertexGraphFormatter.prototype.getDefault = ItemGraphFormatter.prototype.getDefault; +RectangleVertexGraphFormatter.prototype.getSelected = ItemGraphFormatter.prototype.getSelected; +RectangleVertexGraphFormatter.prototype.getOver = ItemGraphFormatter.prototype.getOver; +RectangleVertexGraphFormatter.prototype.getDragging = ItemGraphFormatter.prototype.getDragging; +RectangleVertexGraphFormatter.prototype.getId = ItemGraphFormatter.prototype.getId; +RectangleVertexGraphFormatter.prototype.toJSON = ItemGraphFormatter.prototype.toJSON; +RectangleVertexGraphFormatter.prototype.loadFromJSON = ItemGraphFormatter.prototype.loadFromJSON; +RectangleVertexGraphFormatter.prototype._setEvents = ItemGraphFormatter.prototype._setEvents; + +function RectangleVertexGraphFormatter(id, defaultFormat, selectedFormat, overFormat, draggingFormat){ + VertexGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, draggingFormat); + this.args.type = "RectangleVertexGraphFormatter"; + if (defaultFormat != null){ + if (defaultFormat.radius != null){ + this.defaultFormat.args.radius = defaultFormat.radius; + } + } + + if (selectedFormat != null){ + if (selectedFormat.radius != null){ + this.selected.args.radius = selectedFormat.radius; + } + } + + if (overFormat != null){ + if (overFormat.radius != null){ + this.over.args.radius = overFormat.radius; + } + } + + if (draggingFormat != null){ + if (draggingFormat.radius != null){ + this.dragging.args.draggingFormat = draggingFormat.radius; + } + } +}; + + + +RoundedVertexGraphFormatter.prototype.getDefault = ItemGraphFormatter.prototype.getDefault; +RoundedVertexGraphFormatter.prototype.getSelected = ItemGraphFormatter.prototype.getSelected; +RoundedVertexGraphFormatter.prototype.getOver = ItemGraphFormatter.prototype.getOver; +RoundedVertexGraphFormatter.prototype.getDragging = ItemGraphFormatter.prototype.getDragging; +RoundedVertexGraphFormatter.prototype.getId = ItemGraphFormatter.prototype.getId; +RoundedVertexGraphFormatter.prototype.toJSON = ItemGraphFormatter.prototype.toJSON; +RoundedVertexGraphFormatter.prototype.loadFromJSON = ItemGraphFormatter.prototype.loadFromJSON; +RoundedVertexGraphFormatter.prototype._setEvents = ItemGraphFormatter.prototype._setEvents; + +function RoundedVertexGraphFormatter(id, defaultFormat, selectedFormat, overFormat, draggingFormat){ + VertexGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, draggingFormat); + this.args.type = "RoundedVertexGraphFormatter"; + if (defaultFormat != null){ + if (defaultFormat.radius != null){ + this.defaultFormat.args.radius = defaultFormat.radius; + } + } + + if (selectedFormat != null){ + if (selectedFormat.radius != null){ + this.selected.args.radius = selectedFormat.radius; + } + } + + if (overFormat != null){ + if (overFormat.radius != null){ + this.over.args.radius = overFormat.radius; + } + } + + if (draggingFormat != null){ + if (draggingFormat.radius != null){ + this.dragging.args.draggingFormat = draggingFormat.radius; + } + } +}; + + + +OctagonVertexGraphFormatter.prototype.getDefault = ItemGraphFormatter.prototype.getDefault; +OctagonVertexGraphFormatter.prototype.getSelected = ItemGraphFormatter.prototype.getSelected; +OctagonVertexGraphFormatter.prototype.getOver = ItemGraphFormatter.prototype.getOver; +OctagonVertexGraphFormatter.prototype.getDragging = ItemGraphFormatter.prototype.getDragging; +OctagonVertexGraphFormatter.prototype.getId = ItemGraphFormatter.prototype.getId; +OctagonVertexGraphFormatter.prototype.toJSON = ItemGraphFormatter.prototype.toJSON; +OctagonVertexGraphFormatter.prototype.loadFromJSON = ItemGraphFormatter.prototype.loadFromJSON; +OctagonVertexGraphFormatter.prototype._setEvents = ItemGraphFormatter.prototype._setEvents; + +function OctagonVertexGraphFormatter(id, defaultFormat, selectedFormat, overFormat, draggingFormat){ + VertexGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, draggingFormat); + this.args.type = "OctagonVertexGraphFormatter"; + if (defaultFormat != null){ + if (defaultFormat.radius != null){ + this.defaultFormat.args.radius = defaultFormat.radius; + } + } + + if (selectedFormat != null){ + if (selectedFormat.radius != null){ + this.selected.args.radius = selectedFormat.radius; + } + } + + if (overFormat != null){ + if (overFormat.radius != null){ + this.over.args.radius = overFormat.radius; + } + } + + if (draggingFormat != null){ + if (draggingFormat.radius != null){ + this.dragging.args.draggingFormat = draggingFormat.radius; + } + } +}; + + + +var Colors = new function() +{ + this.hashColor = []; + this.getColorByScoreArrayValue = function (arrayScore) + { + var array = new Array(); + + for (var i = 0; i< arrayScore.length; i++) + { + + var color = this.getColorByScoreValue(arrayScore[i]) + array.push( color); + + } + return array; + }; + + this.getHexStringByScoreArrayValue = function (arrayScore) + { + var arrayColor = this.getColorByScoreArrayValue(arrayScore); + var arrayHex = new Array(); + for (var i = 0; i< arrayColor.length; i++) + { + arrayHex.push( arrayColor[i].HexString()); + } + return arrayHex; + }; + + this.getColorByScoreValue = function (score) + { + + var truncate = score.toString().substr(0,4); + if (this.hashColor[truncate]!=null) + { + return this.hashColor[truncate]; + } + + + if(isNaN(score)) { + return Colors.ColorFromRGB(0,0,0); + } + var value; + + var from, to; + if(score < 0.5) { + from = Colors.ColorFromRGB(0,0,255); + to = Colors.ColorFromRGB(255,255,255); + value = (score * 2); + } else { + from = Colors.ColorFromRGB(255,255,255); + to = Colors.ColorFromRGB(255,0,0); + value = (score * 2) - 1; + } + + var x = value; + var y = 1.0 - value; + var color = Colors.ColorFromRGB(y * from.Red() + x * to.Red(), y * from.Green() + x * to.Green(), y * from.Blue() + x * to.Blue()); + + this.hashColor[truncate] = color; + + return color; + }; + + this.ColorFromHSV = function(hue, sat, val) + { + var color = new Color(); + color.SetHSV(hue,sat,val); + return color; + }; + + this.ColorFromRGB = function(r, g, b) + { + var color = new Color(); + color.SetRGB(r,g,b); + return color; + }; + + this.ColorFromHex = function(hexStr) + { + var color = new Color(); + color.SetHexString(hexStr); + return color; + }; + + function Color() { + //Stored as values between 0 and 1 + var red = 0; + var green = 0; + var blue = 0; + + //Stored as values between 0 and 360 + var hue = 0; + + //Strored as values between 0 and 1 + var saturation = 0; + var value = 0; + + this.SetRGB = function(r, g, b) + { + red = r/255.0; + green = g/255.0; + blue = b/255.0; + calculateHSV(); + }; + + this.Red = function() { return Math.round(red*255); }; + + this.Green = function() { return Math.round(green*255); }; + + this.Blue = function() { return Math.round(blue*255); }; + + this.SetHSV = function(h, s, v) + { + hue = h; + saturation = s; + value = v; + calculateRGB(); + }; + + this.Hue = function() + { return hue; }; + + this.Saturation = function() + { return saturation; }; + + this.Value = function() + { return value; }; + + this.SetHexString = function(hexString) + { + if(hexString == null || typeof(hexString) != "string") + { + this.SetRGB(0,0,0); + return; + } + + if (hexString.substr(0, 1) == '#') + hexString = hexString.substr(1); + + if(hexString.length != 6) + { + this.SetRGB(0,0,0); + return; + } + + var r = parseInt(hexString.substr(0, 2), 16); + var g = parseInt(hexString.substr(2, 2), 16); + var b = parseInt(hexString.substr(4, 2), 16); + if (isNaN(r) || isNaN(g) || isNaN(b)) + { + this.SetRGB(0,0,0); + return; + } + + this.SetRGB(r,g,b); + }; + + this.HexString = function() + { + + var rStr = this.Red().toString(16); + if (rStr.length == 1) + rStr = '0' + rStr; + var gStr = this.Green().toString(16); + if (gStr.length == 1) + gStr = '0' + gStr; + var bStr = this.Blue().toString(16); + if (bStr.length == 1) + bStr = '0' + bStr; + return ('#' + rStr + gStr + bStr).toUpperCase(); + }; + + this.Complement = function() + { + var newHue = (hue >= 180) ? hue - 180 : hue + 180; + var newVal = (value * (saturation - 1) + 1); + var newSat = (value*saturation) / newVal; + var newColor = new Color(); + newColor.SetHSV(newHue, newSat, newVal); + return newColor; + } ; + + function calculateHSV() + { + var max = Math.max(Math.max(red, green), blue); + var min = Math.min(Math.min(red, green), blue); + + value = max; + + saturation = 0; + if(max != 0) + saturation = 1 - min/max; + + hue = 0; + if(min == max) + return; + + var delta = (max - min); + if (red == max) + hue = (green - blue) / delta; + else if (green == max) + hue = 2 + ((blue - red) / delta); + else + hue = 4 + ((red - green) / delta); + hue = hue * 60; + if(hue < 0) + hue += 360; + } + + function calculateRGB() + { + red = value; + green = value; + blue = value; + + if(value == 0 || saturation == 0) + return; + + var tHue = (hue / 60); + var i = Math.floor(tHue); + var f = tHue - i; + var p = value * (1 - saturation); + var q = value * (1 - saturation * f); + var t = value * (1 - saturation * (1 - f)); + switch(i) + { + case 0: + red = value; green = t; blue = p; + break; + case 1: + red = q; green = value; blue = p; + break; + case 2: + red = p; green = value; blue = t; + break; + case 3: + red = p; green = q; blue = value; + break; + case 4: + red = t; green = p; blue = value; + break; + default: + red = value; green = p; blue = q; + break; + } + } + } +} +(); +/* + * Clase gestiona la insercción, eliminación de los elementos en el DOM + * + * Vital hacerla SIEMPRE compatible hacia atrás + * + * Last update: 28-10-2010 + * + */ + + +var DOM = {}; + +DOM.createNewElement = function(type, nodeParent, attributes) +{ + + var node = document.createElement(type); + for (var i=0; i0) + { + parent.removeChild(parent.childNodes[0]); + + } +}; + +DOM.select = function(targetID) +{ + return document.getElementById(targetID); +// return $("#"+targetID); +}; +var Geometry = +{ + + /** From tow points obtains the angles formed with the cartesian side **/ + getAngleBetweenTwoPoints : function(x1, y1, x2, y2){ + //var m = (y1 - y2)/ (x1 - x2); + return Math.atan2(y2-y1, x2-x1); + }, + + getAdjacentSideOfRectangleRight : function(angle, hypotenuse){ + return Math.abs(Math.cos(angle)*hypotenuse); + }, + + getOppositeSideOfRectangleRight : function(angle, hypotenuse){ + return Math.abs(Math.sin(angle)*hypotenuse); + }, + + toDegree: function(radian){ + return radian*180/Math.PI; + + } + + +}; + +var SVG = +{ + svgns : 'http://www.w3.org/2000/svg', + xlinkns : "http://www.w3.org/1999/xlink", + + createSVGCanvas: function(parentNode, attributes) + { + + attributes.push( ['xmlns', SVG.svgns], ['xmlns:xlink', 'http://www.w3.org/1999/xlink']); + var svg = document.createElementNS("http://www.w3.org/2000/svg", "svg"); + this._setProperties(svg, attributes); + parentNode.appendChild( svg); + return svg; + + }, + + createRectangle : function (x, y, width, height, attributes){ + var rect = document.createElementNS(this.svgns, "rect"); + rect.setAttribute("x",x); + rect.setAttribute("y",y); + rect.setAttribute("width",width); + rect.setAttribute("height",height); + SVG._setProperties(rect, attributes); + return rect; + }, + + drawImage64 : function (x, y, width, height, base64, svgNode, attributes) { + var node = SVG.createImage64(x, y, width, height, base64, attributes); + svgNode.appendChild(node); + return node; + }, + + createImage64 : function (x, y, width, height, base64, attributes) { + var img = document.createElementNS(this.svgns, "image"); + img.setAttribute("x",x); + img.setAttribute("y",y); + img.setAttribute("width",width); + img.setAttribute("height",height); + img.setAttribute("xlink:href",base64); + SVG._setProperties(img, attributes); + return img; + }, + + createLine: function (x1, y1, x2, y2, attributes){ + var line = document.createElementNS(this.svgns,"line"); + line.setAttribute("x1",x1); + line.setAttribute("y1",y1); + line.setAttribute("x2", x2); + line.setAttribute("y2", y2); + SVG._setProperties(line, attributes); + return line; + }, + + createClip: function (id, nodeToClip, attributes){ + var clip = document.createElementNS(this.svgns,"clipPath"); + clip.setAttribute("id",id); + clip.appendChild(nodeToClip); + return clip; + }, + + drawClip : function (id, nodeToClip, svgNode) { + var node = SVG.createClip(id, nodeToClip); + svgNode.appendChild(node); + return node; + }, + + drawRectangle : function (cx, cy, width, height, svgNode, attributes) { + try{ + var node = SVG.createRectangle(cx, cy, width, height, attributes); + svgNode.appendChild(node); + } + catch(e){ + + console.log("-------------------- "); + console.log("Error on drawRectangle " + e); + console.log(attributes); + console.log("-------------------- "); + } + return node; + }, + + createEllipse : function (x, y, rx, ry, attributes){ + var rect = document.createElementNS(this.svgns, "ellipse"); + rect.setAttribute("cx",x); + rect.setAttribute("cy",y); + rect.setAttribute("rx",rx); + rect.setAttribute("ry",ry); + SVG._setProperties(rect, attributes); + return rect; + }, + + drawEllipse : function (cx, cy, rx, ry, svgNode, attributes) { + var node = SVG.createEllipse(cx, cy, rx, ry, attributes); + svgNode.appendChild(node); + return node; + }, + + drawImage : function (x, y, canvasSVG, attributes) { + var image = document.createElementNS(this.svgns, "image"); + image.setAttribute("x",x); + image.setAttribute("y",y); + canvasSVG.appendChild(image); + SVG._setProperties(image, attributes); + }, + + drawLine : function (x1, y1, x2, y2, nodeSVG, attributes) { + try{ + var line = SVG.createLine(x1, y1, x2, y2, attributes); + nodeSVG.appendChild(line); + }catch(e){ + } + return line; + }, + + + drawPath: function (d, nodeSVG, attributes) { + var path = SVG.createPath(d, attributes); + nodeSVG.appendChild(path); + return path; + }, + + createPoligon : function (points, attributes){ + var poligon = document.createElementNS(this.svgns, "polygon"); + poligon.setAttribute("points",points); + SVG._setProperties(poligon, attributes); + return poligon; + }, + + drawPoligon : function (points, canvasSVG, attributes){ + var poligon = SVG.createPoligon(points, attributes); + canvasSVG.appendChild(poligon); + return poligon; + }, + // + + createPath : function (d, attributes){ + var path = document.createElementNS(this.svgns, "path"); + path.setAttribute("d",d); + SVG._setProperties(path, attributes); + return path; + }, + + drawCircle : function (x, y, radio, canvasSVG, attributes) { + + var newText = document.createElementNS(this.svgns,"circle"); + newText.setAttribute("cx",x); + newText.setAttribute("cy",y); + newText.setAttribute("r",radio); + + canvasSVG.appendChild(newText); + attributes["cursor"] = "pointer"; + this._setProperties(newText, attributes); + return newText; + }, + + + _setProperties: function(node, attributes) + { + if (attributes instanceof Array){ + for (var i=0; i< attributes.length; i++) + { + node.setAttribute(attributes[i][0], attributes[i][1]); + } + } + else{ + for ( var key in attributes){ + node.setAttribute(key, attributes[key]); + } + } + }, + +/* drawPath: function(pointsArray, canvasSVG, attributes){ + var path = document.createElementNS(this.svgns,"polyline"); + path.setAttribute ('id', id); + + var d= pointsArray[0].x+ " "+ pointsArray[0].y; + for (var i=1; i< pointsArray.length; i++) + { + d=d+" "+pointsArray[i].x+" "+pointsArray[i].y; + } + path.setAttribute ('points', d); + canvasSVG.appendChild(path); + },*/ + + createText : function (x, y, text, attributes) { + var node = document.createElementNS(this.svgns,"text"); + node.setAttributeNS(null , "x",x); + node.setAttributeNS(null, "y",y); + + var textNode = document.createTextNode(text); + node.appendChild(textNode); + + this._setProperties(node, attributes); + return node; + }, + + drawText : function (x, y, text, canvasSVG, attributes) { + var text = SVG.createText(x, y, text, attributes); + canvasSVG.appendChild(text); + return text; + }, + + + + drawGroup: function(svgNode, attributes) + { + var group = SVG.createGroup(attributes); + svgNode.appendChild(group); + return group; + }, + + createGroup: function(attributes){ + var group = document.createElementNS(this.svgns,"g"); + this._setProperties(group, attributes); + return group; + } + +}; + + + +var CanvasToSVG = { + + convert: function(sourceCanvas, targetSVG, x, y, id, attributes) { + + var img = this._convert(sourceCanvas, targetSVG, x, y, id); + + for (var i=0; i< attributes.length; i++) + { + img.setAttribute(attributes[i][0], attributes[i][1]); + } + }, + + _convert: function(sourceCanvas, targetSVG, x, y, id) { + var svgNS = "http://www.w3.org/2000/svg"; + var xlinkNS = "http://www.w3.org/1999/xlink"; + // get base64 encoded png from Canvas + var image = sourceCanvas.toDataURL(); + + // must be careful with the namespaces + var svgimg = document.createElementNS(svgNS, "image"); + + svgimg.setAttribute('id', id); + + //svgimg.setAttribute('class', class); + //svgimg.setAttribute('xlink:href', image); + svgimg.setAttributeNS(xlinkNS, 'xlink:href', image); + + + + + svgimg.setAttribute('x', x ? x : 0); + svgimg.setAttribute('y', y ? y : 0); + svgimg.setAttribute('width', sourceCanvas.width); + svgimg.setAttribute('height', sourceCanvas.height); + //svgimg.setAttribute('cursor', 'pointer'); + svgimg.imageData = image; + + targetSVG.appendChild(svgimg); + return svgimg; + }, + + importSVG: function(sourceSVG, targetCanvas) { + svg_xml = sourceSVG;//(new XMLSerializer()).serializeToString(sourceSVG); + var ctx = targetCanvas.getContext('2d'); + + var img = new Image(); + img.src = "data:image/svg+xml;base64," + btoa(svg_xml); +// img.onload = function() { + ctx.drawImage(img, 0, 0); +// }; + } + +}; +/* +Graph.prototype.importSVG = function(sourceSVG, targetCanvas) { + sourceSVG = this._svg; + targetCanvas = document.createElementNS('canvas'); + // https://developer.mozilla.org/en/XMLSerializer + svg_xml = (new XMLSerializer()).serializeToString(sourceSVG); + var ctx = targetCanvas.getContext('2d'); + // this is just a JavaScript (HTML) image + var img = new Image(); + // http://en.wikipedia.org/wiki/SVG#Native_support + // https://developer.mozilla.org/en/DOM/window.btoa + img.src = "data:image/svg+xml;base64," + btoa(svg_xml); + img.onload = function() { + // after this, Canvas’ origin-clean is DIRTY + ctx.drawImage(img, 0, 0); + } +}; +*/ + +/* + +Normalizacion de datos para dibujar colores +Issues: + No sé como debería llamarse esta libreria + No sé si ya existe una funciçon en javascript que lo haga + + +*/ + + +var Normalizer = new function() +{ + this.normalizeArray = function (arrayData) + { + + return this.standardizeArray(this.normal(arrayData)); + +// var result = this._getMaxAndMin(arrayData); +// var min =result[0]; +// var max = result[1]; +// +// +// //los hacemos todos positivos +// for (var i = 0; i< arrayData.length; i++) +// { +// arrayData[i]= Math.abs(min) + parseFloat(arrayData[i]); +// } +// +// var result = this._getMaxAndMin(arrayData); +// var min =result[0]; +// var max = result[1]; +// +// +// var resultArray = new Array(); +// for (var i = 0; i< arrayData.length; i++) +// { +// resultArray.push(arrayData[i]*1/max); +// } +// return resultArray; + }; + + this.normal = function(arrayData){ + var mean = this._getMean(arrayData); + var deviation = this._getStdDeviation(arrayData); + var result = this._getMaxAndMin(arrayData); + var min = result[0]; + var max = result[1]; + + var resultArray = new Array(); + for (var i = 0; i< arrayData.length; i++) { + if (deviation!=0){ + resultArray.push((arrayData[i]-mean)/deviation); + }else{ + resultArray.push(arrayData[i]); + } + } + return resultArray; + }; + + this.standardizeArray = function(arrayData) + { + var result = this._getMaxAndMin(arrayData); + var min = result[0]; + var max = result[1]; + + var offset = ( min <= 0 ) ? Math.abs(min) : (-1 * min); + var resultArray = new Array(); + for (var i = 0; i< arrayData.length; i++) { + if(max + offset!=0){ + resultArray.push((arrayData[i] + offset) / (max + offset)); + }else{ + resultArray.push(arrayData[i]+offset); + } + } + return resultArray; + }; + + + this._getMean = function(arrayData) { + var sum = 0; + for (var i = 0; i< arrayData.length; i++) { + sum = sum + parseFloat(arrayData[i]); + } + return sum/arrayData.length; + }; + + this._getStdDeviation = function(arrayData) { + var mean = this._getMean(arrayData); + var acum = 0.0; + for(var i=0; i max) max = parseFloat(arrayData[i]); + } + + return [min, max]; + }; +}; +function GraphCanvas(componentID, targetNode, args) { + this.args = {}; + /** target */ + this.targetID = targetNode.id; + + /** id manage */ + this.id = componentID; + this.args.idGraph = this.id + "main"; + this.args.idBackgroundNode = this.id + "background"; + + this.args.idEdgesGraph = this.id + "edges"; + this.args.idNodesGraph = this.id + "vertices"; + this.args.idLabelGraph = this.id + "label"; + this.args.idBackground = this.id + "background"; + + /** Objects Graph **/ + this.dataset = null; + this.formatter = null; + this.layout = null; + + /** Drawing **/ + this.circleDefaultRadius = 2; + this.squareDefaultSide = this.circleDefaultRadius * 1.5; + + /** Directed Arrow **/ + this.arrowDefaultSize = this.circleDefaultRadius; + + /** Groups **/ + this.GraphGroup = null; + this.GraphNodeGroup = null; + this.GraphLabelGroup = null; + this.GraphBackground = null; + + /** SETTINGS FLAGS **/ + this.args.draggingCanvasEnabled = false; //Flag to set if the canvas can be dragged + this.args.multipleSelectionEnabled = false; + this.args.interactive = false; + this.args.labeled = false; + this.args.linkEnabled = false; + + /** If numberEdge > maxNumberEdgesMoving then only it will move edges when mouse up **/ + this.args.maxNumberEdgesMoving = 3; + this.args.maxNumberEdgesFiringEvents = 50; + + /** Linking edges **/ + this.args.linking = false; + this.linkStartX = 0; + this.linkStartY = 0; + this.linkSVGNode = null; + this.linkNodeSource = null; + this.linkNodeTarget = null; + + /** Dragging Control **/ + this.draggingElement = null; + this.dragging = false; + this.nMouseOffsetX = 0; + this.nMouseOffsetY = 0; + this.dragStartX = 0; + this.dragStartY = 0; + this.desplazamientoX = 0; + this.desplazamientoY = 0; + + /** Selection Control **/ + this.selecting = false; + this.selectorX = null; + this.selectorY = null; + this.selectorSVGNode = null; + + /** Node status **/ + this.args.isVertexSelected = {}; + this.args.selectedVertices = []; + + /** Edges status **/ + this.args.isEdgeSelected = {}; + //this.args.selectedEdges = []; + + if (args != null) { + if (args.multipleSelectionEnabled != null) { + this.args.multipleSelectionEnabled = args.multipleSelectionEnabled; + this.args.draggingCanvasEnabled = !(this.args.multipleSelectionEnabled); + } + if (args.draggingCanvasEnabled != null) { + this.args.draggingCanvasEnabled = args.draggingCanvasEnabled; + this.args.multipleSelectionEnabled = !(this.args.draggingCanvasEnabled); + } + if (args.interactive != null) { + this.args.interactive = args.interactive; + } + if (args.labeled != null) { + this.args.labeled = args.labeled; + } + + } + + /** Hashmap with the svg node labels **/ + this.svgLabels = {}; + + /** EVENTS **/ + this.onVertexOut = new Event(this); + this.onVertexOver = new Event(this); + this.onVertexSelect = new Event(this); + this.onEdgeSelect = new Event(this); + this.onCanvasClicked = new Event(this); + this.onVertexUp = new Event(this); +} + +GraphCanvas.prototype.showLabels = function(value) { + this.args.labeled = value; + this.removeLabels(); + if (value) { + this.renderLabels(); + } +}; + +GraphCanvas.prototype.getSelectedVertices = function() { + return this.args.selectedVertices; +}; + +GraphCanvas.prototype.getSelectedEdges = function() { + var selected = []; + for ( var selectedEdge in this.args.isEdgeSelected) { + selected.push(selectedEdge); + } + return selected; +}; + +GraphCanvas.prototype.createSVGDom = function(targetID, id, width, height, backgroundColor) { + var container = document.getElementById(targetID); + this._svg = SVG.createSVGCanvas(container, [ + [ "style", "background-color:" + backgroundColor + ";" ], [ "id", id ], [ "dragx", 0 ], [ "dragy", 0 ], + [ "height", this.getFormatter().getHeight() ], [ "width", this.getFormatter().getWidth() ] ]); + return this._svg; +}; + +/** MULTIPLE SELECTION **/ +GraphCanvas.prototype.isMultipleSelectionEnabled = function() { + return this.args.multipleSelectionEnabled; +}; + +GraphCanvas.prototype.setMultipleSelection = function(value) { + this.args.multipleSelectionEnabled = value; + this.args.draggingCanvasEnabled = (!value); +}; + +GraphCanvas.prototype.setSelecting = function(value) { + this.selecting = value; +}; + +/** linking **/ +GraphCanvas.prototype.setLinking = function(value) { + this.args.linkEnabled = value; + this.selecting = !value; + this.dragging = !value; +}; + +/** CANVAS MOVING **/ +GraphCanvas.prototype.setDraggingCanvas = function(value) { + this.args.draggingCanvasEnabled = value; + this.args.multipleSelectionEnabled = !value; +}; + +GraphCanvas.prototype.isDraggingCanvasEnabled = function() { + return this.args.draggingCanvasEnabled; +}; +/** ZOOM **/ +GraphCanvas.prototype.getScale = function() { + return this.getFormatter().getZoomScale(); +}; + +GraphCanvas.prototype.setScale = function(scale) { + var graphNode = document.getElementById(this.args.idGraph); + graphNode.setAttribute("transform", graphNode.getAttribute("transform").replace("scale(" + this.getScale() + ")", "scale(" + scale + ")")); + this.getFormatter().setZoomScale(scale); +}; + +GraphCanvas.prototype.zoomIn = function() { + this.setScale(this.getScale() + this.getFormatter().getZoomScaleStepFactor()); +}; + +GraphCanvas.prototype.zoomOut = function() { + this.setScale(this.getScale() - this.getFormatter().getZoomScaleStepFactor()); + +}; + +/** SVG COORDENATES **/ +GraphCanvas.prototype.getSVGCoordenates = function(evt) { + var p = this._svg.createSVGPoint(); + p.x = evt.clientX; + p.y = evt.clientY; + + var m = this._svg.getScreenCTM(document.documentElement); + p = p.matrixTransform(m.inverse()); + return p; +}; + +/** SVG EVENTS **/ +GraphCanvas.prototype.mouseClick = function(event) { + if (event.button == 0) { + if (!this.args.interactive) { + return; + } + + if (this.isVertex(event.target)) { + this.clickNode(this.getVertexIdFromSVGId(event.target.id)); + } + /** Como el evento mouseClick viene despues del mouse up es aqui donde manejo el tema de deseccionar los elementos que estoy dragging **/ + if (this.dragging) { + this.dragging = false; + } + } +}; + +GraphCanvas.prototype.mouseMove = function(evt) { + if (this.selecting) { + this.clearLabels(); + + var width = (this.getSVGCoordenates(evt).x - this.selectorX); + var height = (this.getSVGCoordenates(evt).y - this.selectorY); + if ((width > 0) && (height > 0)) { + this.displaySelection(this.selectorX, this.selectorY, width, height); + } + if ((width > 0) && (height < 0)) { + this.displaySelection(this.selectorX, this.getSVGCoordenates(evt).y, width, Math.abs(height)); + } + if ((width < 0) && (height < 0)) { + this.displaySelection(this.getSVGCoordenates(evt).x, this.getSVGCoordenates(evt).y, Math.abs(width), Math.abs(height)); + } + if ((width < 0) && (height > 0)) { + this.displaySelection(this.selectorX + width, this.selectorY, Math.abs(width), Math.abs(height)); + } + + var x1 = (parseFloat(this.selectorSVGNode.getAttribute("x")) - DOM.select(this.id).getAttribute("dragx")) / this.getFormatter().getWidth(); + var y1 = (parseFloat(this.selectorSVGNode.getAttribute("y")) - DOM.select(this.id).getAttribute("dragy")) / this.getFormatter().getHeight(); + var x2 = (x1 + parseFloat(this.selectorSVGNode.getAttribute("width") / this.getFormatter().getWidth())); + var y2 = (y1 + parseFloat(this.selectorSVGNode.getAttribute("height") / this.getFormatter().getHeight())); + + this.deselectNodes(this.getLayout()); + var verticesSelected = this.getLayout().getVerticesByArea(x1 / this.getFormatter().getZoomScale(), y1 / this.getFormatter().getZoomScale(), + x2 / this.getFormatter().getZoomScale(), y2 / this.getFormatter().getZoomScale()); + for ( var i = 0; i < verticesSelected.length; i++) { + this.selectNode(verticesSelected[i].getId()); + this.renderLabel(verticesSelected[i].getId()); + } + + } + var p = null; + if (this.args.linking) { + p = this.getSVGCoordenates(evt); + if (this.linkSVGNode != null) { + this.linkSVGNode.setAttribute("x2", p.x - 2); + this.linkSVGNode.setAttribute("y2", p.y - 2); + } + } + + if (this.dragging) { + p = this.getSVGCoordenates(evt); + p.x -= this.nMouseOffsetX; + p.y -= this.nMouseOffsetY; + this.desplazamientoX = (this.getSVGCoordenates(evt).x - this.dragStartX);// + parseFloat(DOM.select(this.id).getAttribute("dragx")); + this.desplazamientoY = (this.getSVGCoordenates(evt).y - this.dragStartY);// + parseFloat(DOM.select(this.id).getAttribute("dragy")); + + if (this.draggingElement != null) { + /** Click sobre el recct del banground que provoca que mueva todo el canvas **/ + if (this.isNodeCanvas(this.draggingElement)) { + + p = this.getSVGCoordenates(evt); + p.x = this.desplazamientoX; + p.y = this.desplazamientoY; + + this.draggingElement.setAttribute("dragx", p.x); + this.draggingElement.setAttribute("dragy", p.y); + this.draggingElement = document.getElementById(this.args.idGraph); + this.draggingElement.setAttribute("transform", "translate(" + p.x + "," + p.y + "), scale(" + this.getScale() + ")"); + + DOM.select(this.id).setAttribute("dragx", p.x); + DOM.select(this.id).setAttribute("dragy", p.y); + + if (this.NodeSVGbackgroundImage != null) { + this.NodeSVGbackgroundImage.setAttribute("dragx", p.x); + this.NodeSVGbackgroundImage.setAttribute("dragy", p.y); + } + } else { + if (this.isVertex(this.draggingElement)) { + this.selectNode(this.getVertexIdFromSVGId(this.draggingElement.id)); + this.desplazamientoX = this.desplazamientoX / this.getFormatter().getZoomScale(); + this.desplazamientoY = this.desplazamientoY / this.getFormatter().getZoomScale(); + this.moveSelectedNodes(this.desplazamientoX, this.desplazamientoY); + + this.dragStartX = this.getSVGCoordenates(evt).x; + this.dragStartY = this.getSVGCoordenates(evt).y; + } else { + if (this.isNodeBackground(this.draggingElement)) { + + this.draggingElement.setAttribute("dragx", p.x); + this.draggingElement.setAttribute("dragy", p.y); + this.draggingElement = document.getElementById(this.args.idGraph); + this.draggingElement.setAttribute("transform", "translate(" + p.x + "," + p.y + "), scale(" + this.getScale() + ")"); + } else { + this.draggingElement.setAttribute("transform", "translate(" + p.x + "," + p.y + ")"); + } + } + } + } + } +}; + +GraphCanvas.prototype.moveSelectedNodes = function(offsetX, offsetY) { + for ( var i = 0; i < this.getSelectedVertices().length; i++) { + + var nodeId = this.getSelectedVertices()[i]; + var svgNodeId = this.getSVGNodeId(nodeId); + + var x = parseFloat(DOM.select(svgNodeId).getAttribute("dragx")) + parseFloat(offsetX);// - parseFloat(DOM.select(this.id).getAttribute("dragx")); + var y = parseFloat(DOM.select(svgNodeId).getAttribute("dragy")) + parseFloat(offsetY);// + parseFloat(DOM.select(this.id).getAttribute("dragy")); + + this._movingNode(DOM.select(svgNodeId), x, y); + } +}; + +GraphCanvas.prototype.mouseDown = function(evt) { + if (event.button == 0) { + + /** if !no interactive mouse events do anything **/ + if (!this.args.interactive) { + return; + } + + var p = this.getSVGCoordenates(evt); + + /** When click on canvas or background deselect all **/ + if (this.isNodeCanvas(evt.target) || this.isNodeBackground(evt.target)) { + this.deselectNodes(); + this.deselectEdges(); + this.onCanvasClicked.notify(); + } + + /** if I am linking vertices **/ + if (this.args.linkEnabled) { + + if (!this.args.linking) { + this.args.linking = true; + if (this.isVertex(evt.target)) { + this.linkStartX = p.x; + this.linkStartY = p.y; + this.linkSVGNode = SVG.drawLine(p.x, p.y, p.x, p.y, this._svg, { + "stroke" : "#FF0000" + }); + this.linkNodeSource = this.getVertexIdFromSVGId(evt.target.id); + } + } else { + this.linkNodeTarget = this.getVertexIdFromSVGId(evt.target.id); + this.args.linking = false; + this.args.linkEnabled = false; + if (this.isVertex(evt.target)) { + this.getDataset().addEdge(this.linkNodeSource + "_" + this.linkNodeTarget, this.linkNodeSource, this.linkNodeTarget, {}); + } + this.linkSVGNode.parentNode.removeChild(this.linkSVGNode); + } + return; + } + + /** Id is a vertex or the canvas **/ + if (this.isVertex(evt.target) || this.isNodeCanvas(evt.target) || this.isNodeBackground(evt.target)) { + this._startDragging(evt); + } + /** if i is edge **/ + if (this.isEdge(evt.target)) { + this.selectEdge(this.getEdgeIdFromSVGId(evt.target.getAttribute("id"))); + } + + if (this.args.multipleSelectionEnabled) { + if (!this.dragging) { + this.setSelecting(true); + this.selectorX = p.x; + this.selectorY = p.y; + this.displaySelection(p.x, p.y, 1, 1); + } + } + + } + if (event.button == 1) { + this.setLinking(false); + this.setMultipleSelection(false); + this.selecting = false; + + /** Id is a vertex or the canvas **/ + if (this.isVertex(evt.target) || this.isNodeCanvas(evt.target) || this.isNodeBackground(evt.target)) { + this._startDragging(evt); + } + } +}; + +GraphCanvas.prototype.mouseUp = function(event) { + if (!this.args.interactive) { + return; + } + + if (this.dragging) { + this._stopDragging(event); + if (this.isVertex(event.target)) { + var vertexId = this.getVertexIdFromSVGId(event.target.id); + if (this.getDataset().getVertexById(vertexId).getEdges().length >= this.args.maxNumberEdgesMoving) { + this.moveEdge(vertexId); + } + } + } + + if (this.selecting) { + this.setSelecting(false); + + var x1 = (parseFloat(this.selectorSVGNode.getAttribute("x")) - DOM.select(this.id).getAttribute("dragx")) / this.getFormatter().getWidth(); + var y1 = (parseFloat(this.selectorSVGNode.getAttribute("y")) - DOM.select(this.id).getAttribute("dragy")) / this.getFormatter().getHeight(); + var x2 = (x1 + parseFloat(this.selectorSVGNode.getAttribute("width") / this.formatter.getWidth())); + var y2 = (y1 + parseFloat(this.selectorSVGNode.getAttribute("height") / this.formatter.getHeight())); + + var verticesSelected = this.getLayout().getVerticesByArea(x1 / this.getFormatter().getZoomScale, y1 / this.getFormatter().getZoomScale, + x2 / this.getFormatter().getZoomScale, y2 / this.getFormatter().getZoomScale); + + for ( var i = 0; i < verticesSelected.length; i++) { + this.selectNode(verticesSelected[i].getId()); + } + + if (this.selectorSVGNode != null) { + this._svg.removeChild(this.selectorSVGNode); + } + + if (this.args.labeled) { + this.clearLabels(); + this.renderLabels(); + } + + this.selectorSVGNode = null; + // this.renderLabels(); + } +}; + +/** SELECTION **/ +GraphCanvas.prototype.displaySelection = function(x, y, width, height) { + if (this.selectorSVGNode != null) { + this.selectorSVGNode.setAttribute("x", x); + this.selectorSVGNode.setAttribute("y", y); + this.selectorSVGNode.setAttribute("width", width); + this.selectorSVGNode.setAttribute("height", height); + } else { + this.selectorSVGNode = SVG.drawRectangle(x, y, width, height, this._svg, { + "fill" : "red", + "stroke" : "black", + "opacity" : "0.2", + "stroke-opacity" : "1" + }); + } +}; + +/** DRAGGING **/ +GraphCanvas.prototype._startDragging = function(evt) { + if (!this.isDraggingCanvasEnabled()) { + if (this.isNodeCanvas(evt.target)) { + this.draggingElement = null; + } + } + + if (this.isVertex(evt.target) || (this.isNodeBackground(evt.target) && (this.isDraggingCanvasEnabled()))|| (this.isNodeCanvas(evt.target) && (this.isDraggingCanvasEnabled()))) { + this.clearLabels(); + this.draggingElement = evt.target; + this.dragging = true; + var p = this.getSVGCoordenates(evt); + + this.nMouseOffsetX = p.x - parseInt(evt.target.getAttribute("dragx")); + this.nMouseOffsetY = p.y - parseInt(evt.target.getAttribute("dragy")); + + if (this.isVertex(evt.target)) { + this.dragStartX = parseInt(this.draggingElement.getAttribute("dragx")) * this.getFormatter().getZoomScale() + + parseFloat(DOM.select(this.id).getAttribute("dragx")); + this.dragStartY = parseInt(this.draggingElement.getAttribute("dragy")) * this.getFormatter().getZoomScale() + + parseFloat(DOM.select(this.id).getAttribute("dragy")); + } else { + this.dragStartX = p.x - parseInt(this.draggingElement.getAttribute("dragx"));// + parseFloat(DOM.select(this.id).getAttribute("dragx")); + this.dragStartY = p.y - parseInt(this.draggingElement.getAttribute("dragy"));// + parseFloat(DOM.select(this.id).getAttribute("dragy")); + } + } +}; + +GraphCanvas.prototype._stopDragging = function(event) { + this.nMouseOffsetX = 0; + this.nMouseOffsetX = 0; + /** despues del evento up viene el evento click entonces acabo el dragging en el mouseclick **/ + this.dragging = false; + this.draggingElement = null; + this.renderLabels(); + + this.setLinking(false); + this.setMultipleSelection(true); + this.selecting = false; + +}; + +/** Move the edges of the vertex with the vertexId indicado **/ +GraphCanvas.prototype.moveEdge = function(vertexId) { + var x = this.getLayout().getNodeById(vertexId).x * this.getFormatter().getWidth(); + var y = this.getLayout().getNodeById(vertexId).y * this.getFormatter().getHeight(); + + /** Moving edges out **/ + for ( var i = 0; i < this.getDataset().getVertexById(vertexId).getEdgesOut().length; i++) { + var edgeId = this.getDataset().getVertexById(vertexId).getEdgesOut()[i].getId(); + var svgEdgeId = this.getSVGEdgeId(edgeId); + var edgeFormatter = this.getFormatter().getEdgeById(edgeId); + if (edgeFormatter instanceof LineEdgeGraphFormatter) { + DOM.select(svgEdgeId + "_shadow").setAttribute("x2", x); + DOM.select(svgEdgeId + "_shadow").setAttribute("y2", y); + DOM.select(svgEdgeId).setAttribute("x2", x); + DOM.select(svgEdgeId).setAttribute("y2", y); + } + + if ((edgeFormatter instanceof DirectedLineEdgeGraphFormatter) || (edgeFormatter instanceof OdirectedLineEdgeGraphFormatter) + || (edgeFormatter instanceof OdotDirectedLineEdgeGraphFormatter) || (edgeFormatter instanceof DotDirectedLineEdgeGraphFormatter) + || (edgeFormatter instanceof CutDirectedLineEdgeGraphFormatter)) { + this.removeEdge(edgeId); + this.renderEdge(edgeId); + } + } + + /** Moving edges in **/ + for ( var i = 0; i < this.getDataset().getVertexById(vertexId).getEdgesIn().length; i++) { + var edgeId = this.getDataset().getVertexById(vertexId).getEdgesIn()[i].getId(); + var svgEdgeId = this.getSVGEdgeId(edgeId); + var edgeFormatter = this.getFormatter().getEdgeById(edgeId); + if (edgeFormatter instanceof LineEdgeGraphFormatter) { + DOM.select(svgEdgeId).setAttribute("x1", x); + DOM.select(svgEdgeId).setAttribute("y1", y); + DOM.select(svgEdgeId + "_shadow").setAttribute("x1", x); + DOM.select(svgEdgeId + "_shadow").setAttribute("y1", y); + } + + if ((edgeFormatter instanceof DirectedLineEdgeGraphFormatter) || (edgeFormatter instanceof OdirectedLineEdgeGraphFormatter) + || (edgeFormatter instanceof OdotDirectedLineEdgeGraphFormatter) || (edgeFormatter instanceof DotDirectedLineEdgeGraphFormatter) + || (edgeFormatter instanceof CutDirectedLineEdgeGraphFormatter)) { + this.removeEdge(edgeId); + this.renderEdge(edgeId); + } + + if (edgeFormatter instanceof BezierEdgeGraphFormatter) { + var radius = this.getFormatter().getVertexById(vertexId).getDefault().getSize() * this.getFormatter().getNodesMaxSize(); + var d = this.calculateCoordenatesBezier(radius, x, y); + DOM.select(svgEdgeId).setAttribute("d", d); + } + } +}; + +GraphCanvas.prototype.moveNode = function(vertexId) { + var x = this.getLayout().getNodeById(vertexId).x * this.getFormatter().getWidth(); + var y = this.getLayout().getNodeById(vertexId).y * this.getFormatter().getHeight(); + var svgNodeElement = DOM.select(this.getSVGNodeId(vertexId)); + + svgNodeElement.setAttribute("dragx", x); + svgNodeElement.setAttribute("dragy", y); + svgNodeElement.setAttribute("transform", "translate(" + x + "," + y + ")"); + + if (this.getDataset().getVertexById(vertexId).getEdges().length < this.args.maxNumberEdgesMoving) { + this.moveEdge(vertexId); + } +}; + +GraphCanvas.prototype._movingNode = function(svgNodeElement, x, y) { + var vertexId = this.getVertexIdFromSVGId(svgNodeElement.getAttribute("id")); + this.getLayout().getNodeById(vertexId).setCoordinates(x / this.getFormatter().getWidth(), y / this.getFormatter().getHeight()); + this.desplazamientoX = 0; + this.desplazamientoY = 0; + this.removeLabel(vertexId); + this.renderLabel(vertexId); +}; + +/** INIT **/ +GraphCanvas.prototype.init = function() { + + this._svg = this.createSVGDom(this.targetID, this.id, this.getFormatter().getWidth(), this.getFormatter().getHeight(), this.getFormatter() + .getBackgroundColor()); + this.GraphGroup = SVG.drawGroup(this._svg, [ [ "id", this.args.idGraph ], [ "transform", "translate(0,0), scale(1)" ] ]); + this.GraphBackground = SVG.drawGroup(this.GraphGroup, [ [ "id", this.args.idBackground ] ]); + this.GraphEdgeGroup = SVG.drawGroup(this.GraphGroup, [ [ "id", this.args.idEdgesGraph ] ]); + this.GraphNodeGroup = SVG.drawGroup(this.GraphGroup, [ [ "id", this.args.idNodesGraph ] ]); + this.GraphLabelGroup = SVG.drawGroup(this.GraphGroup, [ [ "id", this.args.idLabelGraph ] ]); + + if ((this.getFormatter().getBackgroundImage() != null) && (this.getFormatter().getBackgroundImage() != "")) { + this.setBackgroundImage(this.getFormatter().getBackgroundImage()); + } + /** SVG Events listener */ + var _this = this; + this._svg.addEventListener("click", function(event) { + _this.mouseClick(event); + }, false); + this._svg.addEventListener("mousemove", function(event) { + _this.mouseMove(event, _this); + }, false); + this._svg.addEventListener("mousedown", function(event) { + _this.mouseDown(event, _this); + }, false); + this._svg.addEventListener("mouseup", function(event) { + _this.mouseUp(event, _this); + }, false); +}; + +/* + GraphCanvas.prototype.backgroungToSVG = function(){ + var _this = this; + var canvas = document.createElement('canvas'); + canvas.setAttribute("id", "canvas"); + canvas.width = this.formatter.getWidth(); + canvas.height = this.formatter.getHeight(); + + this._svg.parentNode.parentNode.appendChild(canvas); + var ctx = document.getElementById('canvas').getContext('2d'); + var img = new Image(); + + img.src = this.formatter.getBackgroundImage(); + ctx.drawImage(img,0,0 ,_this.formatter.getWidth(), _this.formatter.getHeight()); + + + img.onload = function() { + canvas.parentNode.removeChild(canvas); + } + + this.NodeSVGbackgroundImage.setAttribute("xlink:href", document.getElementById("canvas").toDataURL()); + this.NodeSVGbackgroundImage.removeAttribute("href"); + + // + + };*/ + +GraphCanvas.prototype.setBackgroundImage = function() { + if (this.NodeSVGbackgroundImage != null) { + this.NodeSVGbackgroundImage.parentNode.removeChild(this.NodeSVGbackgroundImage); + } + $('#' + this.targetID).svg(); + $('#' + this.targetID).svg("get"); + + $('#' + this.targetID).svg("get")._svg = document.getElementById(this.id); + + var svg = $('#' + this.targetID).svg("get"); + this.NodeSVGbackgroundImage = svg.image(0, 0, this.getFormatter().getWidth(), this.getFormatter().getHeight(), this.getFormatter() + .getBackgroundImage()); + this.NodeSVGbackgroundImage.setAttribute("id", this.args.idBackgroundNode); + + this.NodeSVGbackgroundImage.setAttribute("x", 0); + this.NodeSVGbackgroundImage.setAttribute("y", 0); + + this.NodeSVGbackgroundImage.setAttribute("dragx", 0); + this.NodeSVGbackgroundImage.setAttribute("dragy", 0); + + if (this.getFormatter().args.backgroundImageHeight != null) { + this.NodeSVGbackgroundImage.setAttribute("height", this.getFormatter().args.backgroundImageHeight); + } + if (this.getFormatter().args.backgroundImageWidth != null) { + this.NodeSVGbackgroundImage.setAttribute("width", this.getFormatter().args.backgroundImageWidth); + } + + if (this.getFormatter().args.backgroundImageX != null) { + this.NodeSVGbackgroundImage.setAttribute("x", this.getFormatter().args.backgroundImageX); + } + if (this.getFormatter().args.backgroundImageY != null) { + this.NodeSVGbackgroundImage.setAttribute("y", this.getFormatter().args.backgroundImageY); + } + + this.GraphBackground.appendChild(this.NodeSVGbackgroundImage); + this.NodeSVGbackgroundImage.removeAttribute("href"); + this.NodeSVGbackgroundImage.setAttribute("xlink:href", this.getFormatter().getBackgroundImage()); +}; + +GraphCanvas.prototype.removeBackgroundImage = function() { + if (this.NodeSVGbackgroundImage != null) { + this.NodeSVGbackgroundImage.parentNode.removeChild(this.NodeSVGbackgroundImage); + } +}; + +GraphCanvas.prototype._setBackgroundColor = function(color) { + var attributes = [ [ "fill", color ] ]; + SVG.drawRectangle(0, 0, this.getFormatter().getWidth(), this.getFormatter().getHeight(), this.GraphBackground, attributes); +}; + +/** Serialize **/ +GraphCanvas.prototype.toJSON = function() { + var json = {}; + json.dataset = {}; + json.formatter = {}; + json.layout = {}; + json.dataset = this.getDataset().toJSON(); + json.formatter = this.getFormatter().toJSON(); + json.layout = this.getLayout().toJSON(); + return json; +}; + +GraphCanvas.prototype.toHTML = function() { + //this.backgroungToSVG(); + var html = this._svg.parentElement.innerHTML; + + var start = html.indexOf("") + 6; + + return html.substr(start, end); +}; + +/** DRAW **/ +GraphCanvas.prototype.draw = function(graphdataset, graphformatter, graphlayout) { + this.setDataset(graphdataset); + this.setFormatter(graphformatter); + this.setLayout(graphlayout); + + var _this = this; + this.getFormatter().changed.attach(function(sender, item) { + _this.removeNode(item.getId()); + _this.renderNode(item.getId()); + if (_this.args.labeled) { + _this.removeLabel(item.getId()); + _this.renderLabel(item.getId()); + } + + }); + //TODO + this.getFormatter().edgeChanged.attach(function(sender, item) { + _this.removeEdge(item.getId()); + _this.renderEdge(item.getId()); + }); + + this.getFormatter().resized.attach(function(sender, item) { + _this.resize(_this.getFormatter().getWidth(), _this.getFormatter().getHeight()); + }); + + this.getFormatter().backgroundImageChanged.attach(function(sender, item) { + _this.setBackgroundImage(_this.getFormatter().getBackgroundImage()); + }); + + this.getFormatter().backgroundColorChanged.attach(function(sender, item) { + _this._setBackgroundColor(_this.getFormatter().getBackgroundColor()); + }); + + this.getLayout().changed.attach(function(sender, item) { + _this.moveNode(item.getId()); + _this.moveEdge(item.getId()); + if (_this.args.labeled) { + _this.removeLabel(item.getId()); + _this.renderLabel(item.getId()); + } + }); + + this.getDataset().newVertex.attach(function(sender, item) { + + _this.renderNode(item.getId()); + if (_this.args.labeled) { + _this.renderLabel(item.getId()); + } + }); + + this.getDataset().newEdge.attach(function(sender, item) { + _this.renderEdge(item.getId()); + }); + + this.getDataset().vertexDeleted.attach(function(sender, item) { + _this.removeNode(item.getId()); + if (_this.args.labeled) { + _this.removeLabel(item.getId()); + } + }); + + this.getDataset().edgeDeleted.attach(function(sender, item) { + _this.removeEdge(item.getId()); + }); + + this.getDataset().vertexNameChanged.attach(function(sender, args) { + if (_this.args.labeled) { + _this.removeLabel(args.item.getId()); + _this.removeLabel(args.item.getId()); + _this.renderLabel(args.item.getId()); + } + }); + this.init(); + this.render(); +}; + +GraphCanvas.prototype.render = function() { + for ( var id in this.getDataset().getVertices()) { + this.renderNode(id); + } + this.renderLabels(); + this.renderEdges(); +}; + +GraphCanvas.prototype.renderLabels = function() { + if (this.args.labeled) { + for ( var id in this.getDataset().getVertices()) { + this.renderLabel(id); + } + } +}; + +GraphCanvas.prototype.removeLabels = function() { + for ( var id in this.getDataset().getVertices()) { + this.removeLabel(id); + } +}; + +/** Utilities method for nodes **/ +GraphCanvas.prototype.isNodeCanvas = function(node) { + return ((node.id == this.args.idGraph) || (node.id == this.id)); +}; + +GraphCanvas.prototype.isNodeBackground = function(node) { + return ((node.id == this.args.idBackgroundNode)); +}; + +GraphCanvas.prototype.isVertex = function(node) { + if (node.getAttribute("id") != null) { + if (node.getAttribute("id").indexOf("_v_") != -1) { + return true; + } + } + return false; +}; + +GraphCanvas.prototype.isLabel = function(node) { + if (node.getAttribute("id") != null) { + if (node.getAttribute("id").indexOf("_l_") != -1) { + return true; + } + } + return false; +}; + +GraphCanvas.prototype.isEdge = function(node) { + if (node.getAttribute("id") != null) { + if (node.getAttribute("id").indexOf("_e_") != -1) { + return true; + } + } + return false; +}; + +/** Resize **/ +GraphCanvas.prototype.resize = function(width, height) { + // this._svg.setAttribute("width", width); + // this._svg.setAttribute("height", height); + if (this.NodeSVGbackgroundImage != null) { + this.NodeSVGbackgroundImage.setAttribute("width", width); + this.NodeSVGbackgroundImage.setAttribute("height", height); + } + + this._svg.setAttribute("width", width); + this._svg.setAttribute("height", height); + + this.clearCanvas(); + this.render(); +}; + +GraphCanvas.prototype.clearCanvas = function() { + DOM.removeChilds(this.GraphEdgeGroup.getAttribute("id")); + DOM.removeChilds(this.GraphNodeGroup.getAttribute("id")); + this.clearLabels(); +}; + +GraphCanvas.prototype.clearLabels = function() { + DOM.removeChilds(this.GraphLabelGroup.getAttribute("id")); +}; + +/** ID'S converter **/ +GraphCanvas.prototype.getSVGNodeId = function(nodeId) { + return this.id + "_v_" + nodeId; +}; + +GraphCanvas.prototype.getSVGEdgeId = function(edgeId) { + return this.id + "_e_" + edgeId; +}; + +GraphCanvas.prototype.getSVGArrowEdgeId = function(edgeId) { + return this.id + "_arrow_" + edgeId; +}; + +GraphCanvas.prototype.getSVGLabelId = function(edgeId) { + return this.id + "_l_" + edgeId; +}; + +GraphCanvas.prototype.blinkVertexById = function(vertexId) { + $("#" + this.getSVGNodeId(vertexId)).fadeIn().fadeOut().fadeIn().fadeOut().fadeIn().fadeOut(); +}; + +GraphCanvas.prototype.getVertexIdFromSVGId = function(svgVertexId) { + return svgVertexId.replace(this.id, "").replace("_v_", ""); +}; + +GraphCanvas.prototype.getEdgeIdFromSVGId = function(svgEdgeId) { + return svgEdgeId.replace(this.id, "").replace("_e_", ""); +}; + +/** VERTEX **/ +GraphCanvas.prototype.getVertexById = function(id) { + return document.getElementById(this.getSVGNodeId(id)); +}; + +GraphCanvas.prototype.renderNodes = function() { + for ( var id in this.getDataset().getVertices()) { + this.renderNode(id); + } +}; + +GraphCanvas.prototype.overNode = function(nodeId) { + if (!this.args.interactive) { + return; + } + /** If selected we don't change the format **/ + if (this.args.isVertexSelected[nodeId] == null) { + var args = this.getFormatter().getVertexById(nodeId).getOver(); + args.args["cursor"] = 'pointer'; + this.changeVertexFormat(nodeId, args); + } +}; + +GraphCanvas.prototype.outNode = function(nodeId) { + if (!this.args.interactive) { + return; + } + + /** If selected we don't change the format **/ + if (this.args.isVertexSelected[nodeId] == null) { + this.changeVertexFormat(nodeId, this.getFormatter().getVertexById(nodeId).getDefault()); + } +}; + +GraphCanvas.prototype.overLabel = function(nodeId) { + this.overNode(nodeId); + // this.svgLabels[nodeId].setAttribute("cursor", "pointer"); +}; + +GraphCanvas.prototype.outLabel = function(nodeId) { + this.outNode(nodeId); + // this.svgLabels[nodeId].setAttribute("cursor", ""); +}; + +GraphCanvas.prototype.clickNode = function(nodeId) { + if (!this.args.interactive) { + return; + } + + /** si el evento se dispara oprque estaba dragging entonces no activo nada **/ + if (this.args.isVertexSelected[nodeId] == null) { + this.selectNode(nodeId); + } else { + this.deselectNode(nodeId); + } +}; + +GraphCanvas.prototype.selectNode = function(nodeId) { + for ( var i = 0; i < this.args.selectedVertices.length; i++) { + var format = this.getFormatter().getVertexById(nodeId).getSelected(); + format.opacity = 0.2; + this.changeVertexFormat(nodeId, this.getFormatter().getVertexById(nodeId).getSelected()); + } + + if (this.args.isVertexSelected[nodeId] == null) { + var format = this.getFormatter().getVertexById(nodeId).getSelected(); + format.opacity = 1; + this.changeVertexFormat(nodeId, this.getFormatter().getVertexById(nodeId).getSelected()); + this.args.selectedVertices.push(nodeId); + this.args.isVertexSelected[nodeId] = this.args.selectedVertices.length - 1; + this.onVertexSelect.notify(nodeId); + } +}; + +GraphCanvas.prototype.selectAllEdges = function() { + this.deselectNodes(); + this.deselectEdges(); + + for ( var edgesId in this.getDataset().edges) { + this.selectEdge(edgesId); + } +}; + +GraphCanvas.prototype.selectAllNodes = function() { + this.deselectNodes(); + this.deselectEdges(); + + for ( var vertexId in this.getDataset().vertices) { + this.selectNode(vertexId); + } +}; + +GraphCanvas.prototype.selectAll = function() { + this.deselectNodes(); + this.deselectEdges(); + + for ( var vertexId in this.getDataset().vertices) { + this.selectNode(vertexId); + } + + for ( var edgesId in this.getDataset().edges) { + this.selectEdge(edgesId); + } +}; + +GraphCanvas.prototype.selectEdge = function(edgeId) { + if (this.args.isEdgeSelected[edgeId] == null) { + this.changeEdgeFormat(edgeId, this.getFormatter().getEdgeById(edgeId).getSelected()); + //this.args.selectedEdges.push(edgeId); + this.args.isEdgeSelected[edgeId] = true; //this.args.selectedEdges.length - 1; + this.onEdgeSelect.notify(edgeId); + } +}; + +GraphCanvas.prototype.selectEdges = function(edges) { + + for ( var i = 0; i < edges.length; i++) { + this.selectEdge(edges[i]); + } +}; + +GraphCanvas.prototype.deselectNode = function(nodeId) { + if (this.args.isVertexSelected[nodeId] != null) { + this.changeVertexFormat(nodeId, this.getFormatter().getVertexById(nodeId).getDefault()); + this.args.selectedVertices.splice(this.args.isVertexSelected[nodeId], 1); + var index = this.args.isVertexSelected[nodeId]; + delete this.args.isVertexSelected[nodeId]; + + for ( var vertex in this.args.isVertexSelected) { + if (this.args.isVertexSelected[vertex] > index) { + this.args.isVertexSelected[vertex] = this.args.isVertexSelected[vertex] - 1; + } + } + } +}; + +GraphCanvas.prototype.deselectNodes = function() { + var selected = JSON.parse(JSON.stringify(this.getSelectedVertices())); + for ( var i = 0; i < selected.length; i++) { + this.deselectNode(selected[i]); + } +}; +GraphCanvas.prototype.selectNodes = function(idNodes) { + + for ( var i = 0; i < idNodes.length; i++) { + this.selectNode(idNodes[i]); + } + + // for ( var vertex in this.args.isVertexSelected) { + // if (this.args.isVertexSelected[vertex] > index){ + // this.args.isVertexSelected[vertex] = this.args.isVertexSelected[vertex] - 1; + // } + // } + +}; + +GraphCanvas.prototype.changeVertexFormat = function(nodeId, format) { + var svgNode = DOM.select(this.getSVGNodeId(nodeId)); + if (svgNode != null) { + var properties = format.toJSON(); + for ( var item in properties) { + svgNode.setAttribute(item, properties[item]); + } + + if (this.getFormatter().getVertexById(nodeId) instanceof CircleVertexGraphFormatter) { + var transform = "translate(" + svgNode.getAttribute("dragx") + "," + svgNode.getAttribute("dragy") + "), scale(" + format.getSize() + ")"; + svgNode.setAttribute("transform", transform); + } + } +}; + +GraphCanvas.prototype.renderLabel = function(nodeId) { + var x = Math.ceil(this.getLayout().getNodeById(nodeId).x * this.getFormatter().getWidth()); + var y = Math.ceil(this.getLayout().getNodeById(nodeId).y * this.getFormatter().getHeight()); + + var svgAttributesNode = JSON.parse(JSON.stringify(this.getFormatter().getVertexById(nodeId).getDefault().toJSON().title)); + svgAttributesNode.id = this.getSVGLabelId(this.getDataset().getVertexById(nodeId).getId()); + svgAttributesNode.dx = (-1) * (this.getDataset().getVertexById(nodeId).getName().length * svgAttributesNode["font-size"]) / 4 - 4; + + svgAttributesNode.dy = parseFloat((this.getFormatter().getVertexById(nodeId).getDefault().getSize())) + + parseFloat(svgAttributesNode["font-size"]) + parseFloat(this.getFormatter().getVertexById(nodeId).getDefault().getStrokeWidth()) - 4; + svgAttributesNode.dragx = Math.ceil(this.getLayout().getNodeById(nodeId).x * this.getFormatter().getWidth()); + + var gragy = parseFloat(this.getFormatter().getVertexById(nodeId).getDefault().getSize()) + + Math.ceil(this.getLayout().getNodeById(nodeId).y * this.getFormatter().getHeight()); + svgAttributesNode.dragy = gragy; + svgAttributesNode.transform = "translate(" + svgAttributesNode.dragx + "," + svgAttributesNode.dragy + ")";//, scale("+this.formatter.getVertexById(nodeId).getDefault().getSize()+")"; + + var nodeSVG = SVG.drawText(0, 0, this.getDataset().getVertexById(nodeId).getName(), this.GraphLabelGroup, svgAttributesNode); + + this.svgLabels[nodeId] = nodeSVG; + + /** Events for the SVG node **/ + var _this = this; + if (nodeSVG != null) { + nodeSVG.addEventListener("mouseover", function() { + _this.overLabel(nodeId); + }, false); + nodeSVG.addEventListener("mouseout", function() { + _this.outLabel(nodeId); + }, false); + } + +}; + +GraphCanvas.prototype.removeLabel = function(labelId) { + if (DOM.select(this.getSVGLabelId(labelId)) != null) { + DOM.select(this.getSVGLabelId(labelId)).parentNode.removeChild(DOM.select(this.getSVGLabelId(labelId))); + } +}; + +GraphCanvas.prototype.renderNode = function(nodeId) { + var svgAttributesNode = JSON.parse(JSON.stringify(this.getFormatter().getVertexById(nodeId).getDefault().toJSON())); + svgAttributesNode.dragx = Math.ceil(this.getLayout().getNodeById(nodeId).x * this.getFormatter().getWidth()); + svgAttributesNode.dragy = Math.ceil(this.getLayout().getNodeById(nodeId).y * this.getFormatter().getHeight()); + svgAttributesNode.transform = "translate(" + svgAttributesNode.dragx + "," + svgAttributesNode.dragy + ")"; + svgAttributesNode.id = this.getSVGNodeId(nodeId); + /*svgAttributesNode["stroke-width"] = 3 ; + svgAttributesNode["stroke-opacity"] = 1 ; + svgAttributesNode["fill-opacity"] = svgAttributesNode["opacity"] ; + svgAttributesNode["opacity"] = 1 ;*/ + this.circleDefaultRadius = this.getFormatter().getVertexById(nodeId).getDefault().getSize(); + var nodeSVG; + + if (this.getFormatter().getVertexById(nodeId) instanceof CircleVertexGraphFormatter) { + nodeSVG = SVG.drawCircle(0, 0, this.circleDefaultRadius, this.GraphNodeGroup, svgAttributesNode); + } + + if (this.getFormatter().getVertexById(nodeId) instanceof SquareVertexGraphFormatter) { + //nodeSVG = SVG.drawRectangle(0 - (this.circleDefaultRadius) ,0 - (this.formatter.getVertexById(nodeId).getDefault().getSize()) , (this.getFormatter().getVertexById(nodeId).getDefault().getSize()*2), (this.getFormatter().getVertexById(nodeId).getDefault().getSize()*2), this.GraphNodeGroup, svgAttributesNode); + nodeSVG = SVG.drawRectangle(0 - (this.circleDefaultRadius), 0 - (this.circleDefaultRadius), (this.circleDefaultRadius * 2), + (this.circleDefaultRadius * 2), this.GraphNodeGroup, svgAttributesNode); + } + + if (this.getFormatter().getVertexById(nodeId) instanceof EllipseVertexGraphFormatter) { + nodeSVG = SVG.drawEllipse(0, 0, this.circleDefaultRadius * 1.5, this.circleDefaultRadius, this.GraphNodeGroup, svgAttributesNode); + } + + if (this.getFormatter().getVertexById(nodeId) instanceof RectangleVertexGraphFormatter) { + //nodeSVG = SVG.drawRectangle(0 - (this.circleDefaultRadius) ,0 - ((this.circleDefaultRadius*2)/2) , (this.circleDefaultRadius*2), (this.circleDefaultRadius), this.GraphNodeGroup, svgAttributesNode); + nodeSVG = SVG.drawRectangle(0 - (this.circleDefaultRadius * 1.5), 0 - (this.circleDefaultRadius), (this.circleDefaultRadius * 2 * 1.5), + (this.circleDefaultRadius * 2), this.GraphNodeGroup, svgAttributesNode); + + } + + if (this.getFormatter().getVertexById(nodeId) instanceof RoundedVertexGraphFormatter) { + svgAttributesNode.ry = 2;// this.formatter.getVertexById(nodeId).getDefault().getSize()/4; + svgAttributesNode.rx = 2;// this.formatter.getVertexById(nodeId).getDefault().getSize()/4; + nodeSVG = SVG.drawRectangle(0 - (this.circleDefaultRadius * 1.5), 0 - (this.circleDefaultRadius), (this.circleDefaultRadius * 2 * 1.5), + (this.circleDefaultRadius * 2), this.GraphNodeGroup, svgAttributesNode); + } + + // + + if (this.getFormatter().getVertexById(nodeId) instanceof OctagonVertexGraphFormatter) { + svgAttributesNode.ry = 2; + svgAttributesNode.rx = 2; + nodeSVG = SVG.drawRectangle(0 - (this.circleDefaultRadius * 1.5), 0 - (this.circleDefaultRadius), (this.circleDefaultRadius * 2 * 1.5), + (this.circleDefaultRadius * 2), this.GraphNodeGroup, svgAttributesNode); + } + + nodeSVG.internalId = nodeId; + // + var _this = this; + + /** Events for the SVG node **/ + if (nodeSVG != null) { + nodeSVG.addEventListener("mouseover", function() { + _this.onVertexOver.notify(nodeId); + _this.overNode(nodeId); + }, false); + nodeSVG.addEventListener("mouseout", function() { + _this.onVertexOut.notify(nodeId); + _this.outNode(nodeId); + }, false); + //nodeSVG.addEventListener("click", function(){_this.clickNode(nodeId);}, false); + // + nodeSVG.addEventListener("mouseup", function() { + _this.onVertexUp.notify(nodeId); + }, false); + } +}; + +GraphCanvas.prototype.removeNode = function(nodeId) { + DOM.select(this.getSVGNodeId(nodeId)).parentNode.removeChild(DOM.select(this.getSVGNodeId(nodeId))); + if (this.args.labeled) { + this.removeLabel(nodeId); + } +}; + +/** REMOVING **/ +GraphCanvas.prototype.removeSelected = function() { + /** El orden importa **/ + this.removeSelectedEdges(); + this.removeSelectedNode(); + +}; + +GraphCanvas.prototype.removeSelectedNode = function() { + var selected = JSON.parse(JSON.stringify(this.getSelectedVertices())); + this.deselectNodes(); + var sorted = selected.sort(function(a, b) { + return a - b + }); + for ( var i = 0; i < sorted.length; i++) { + if (this.getDataset().getVertexById(sorted[i]) != null) { + this.getDataset().getVertexById(sorted[i]).remove(); + } + } +}; + +/** EDGES **/ +GraphCanvas.prototype.removeEdge = function(edgeId) { + if (DOM.select(this.getSVGEdgeId(edgeId)) != null) { + DOM.select(this.getSVGEdgeId(edgeId)).parentNode.removeChild(DOM.select(this.getSVGEdgeId(edgeId))); + } + + if (DOM.select(this.getSVGEdgeId(edgeId) + "_shadow") != null) { + DOM.select(this.getSVGEdgeId(edgeId) + "_shadow").parentNode.removeChild(DOM.select(this.getSVGEdgeId(edgeId) + "_shadow")); + } + + if (DOM.select(this.getSVGArrowEdgeId(edgeId)) != null) { + DOM.select(this.getSVGArrowEdgeId(edgeId)).parentNode.removeChild(DOM.select(this.getSVGArrowEdgeId(edgeId))); + } +}; + +GraphCanvas.prototype.overEdge = function(edgeId) { + if ((!this.args.interactive) || this.dragging || this.selecting) { + return; + } + + /** If selected we don't change the format **/ + if (this.args.isEdgeSelected[edgeId] == null) { + var format = this.getFormatter().getEdgeById(edgeId).getOver(); + format.args["cursor"] = "pointer"; + this.changeEdgeFormat(edgeId, format); + } +}; + +GraphCanvas.prototype.outEdge = function(edgeId) { + if (!this.args.interactive) { + return; + } + + /** If selected we don't change the format **/ + if (this.args.isEdgeSelected[edgeId] == null) { + this.changeEdgeFormat(edgeId, this.getFormatter().getEdgeById(edgeId).getDefault()); + } +}; + +GraphCanvas.prototype.changeEdgeFormat = function(edgeId, format) { + var svgEdge = DOM.select(this.getSVGEdgeId(edgeId) + "_shadow"); + if (svgEdge != null) { + var properties = format.toJSON(); + for ( var item in properties) { + svgEdge.setAttribute(item, properties[item]); + } + } +}; + +GraphCanvas.prototype.deselectEdge = function(edgeID) { + if (this.args.isEdgeSelected[edgeID] != null) { + this.changeEdgeFormat(edgeID, this.getFormatter().getEdgeById(edgeID).getDefault()); + var index = this.args.isEdgeSelected[edgeID]; + delete this.args.isEdgeSelected[edgeID]; + } +}; + +GraphCanvas.prototype.deselectEdges = function() { + var selected = JSON.parse(JSON.stringify(this.getSelectedEdges())); + for ( var i = 0; i < selected.length; i++) { + this.deselectEdge(selected[i]); + } +}; + +GraphCanvas.prototype.removeSelectedEdges = function() { + var selected = JSON.parse(JSON.stringify(this.getSelectedEdges())); + this.deselectEdges(); + for ( var i = 0; i < selected.length; i++) { + if (this.getDataset().getEdgeById(selected[i]) != null) { + this.getDataset().getEdgeById(selected[i]).remove(); + } + } +}; + +GraphCanvas.prototype.renderEdge = function(edgeId) { + var svgAttributesEdge = this.getFormatter().getEdgeById(edgeId).getDefault().toJSON(); + var edge = this.getDataset().getEdgeById(edgeId); + + var svgNodeTarget = this.getVertexById(edge.getNodeTarget().getId()); + var svgNodeSource = this.getVertexById(edge.getNodeSource().getId()); + svgAttributesEdge.id = this.getSVGEdgeId(edge.getId()) + "_shadow"; + + var svgEdge = null; + + if (this.getFormatter().getEdgeById(edgeId) instanceof LineEdgeGraphFormatter) { + var coordenateSourceX = svgNodeSource.getAttribute("dragx"); + var coordenateSourceY = svgNodeSource.getAttribute("dragy"); + var coordenateTargetX = svgNodeTarget.getAttribute("dragx"); + var coordenateTargetY = svgNodeTarget.getAttribute("dragy"); + + SVG.drawLine(coordenateSourceX, coordenateSourceY, coordenateTargetX, coordenateTargetY, this.GraphEdgeGroup, svgAttributesEdge); + var attributesShadow = {}; + attributesShadow.id = this.getSVGEdgeId(edge.getId()); + attributesShadow["stroke-opacity"] = 0; + attributesShadow["stroke-width"] = 4; + attributesShadow["stroke"] = "black"; + svgEdge = SVG.drawLine(svgNodeSource.getAttribute("dragx"), svgNodeSource.getAttribute("dragy"), svgNodeTarget.getAttribute("dragx"), + svgNodeTarget.getAttribute("dragy"), this.GraphEdgeGroup, attributesShadow); + } + + if (this.getFormatter().getEdgeById(edgeId) instanceof BezierEdgeGraphFormatter) { + var nodeId = edge.getNodeTarget().getId(); + var nodeSize = this.formatter.getVertexById(nodeId).getDefault().getSize() * this.getFormatter().getNodesMaxSize(); + svgAttributesEdge.fill = "none"; + svgAttributesEdge.id = this.getSVGEdgeId(edgeId); + var d = this.calculateCoordenatesBezier(nodeSize, svgNodeSource.getAttribute("dragx"), svgNodeSource.getAttribute("dragy")); + svgEdge = SVG.drawPath(d, this.GraphEdgeGroup, svgAttributesEdge); + } + ; + + if ((this.getFormatter().getEdgeById(edgeId) instanceof DirectedLineEdgeGraphFormatter) + || (this.getFormatter().getEdgeById(edgeId) instanceof CutDirectedLineEdgeGraphFormatter) + || (this.getFormatter().getEdgeById(edgeId) instanceof DotDirectedLineEdgeGraphFormatter) + || (this.getFormatter().getEdgeById(edgeId) instanceof OdotDirectedLineEdgeGraphFormatter) + || (this.getFormatter().getEdgeById(edgeId) instanceof OdirectedLineEdgeGraphFormatter)) { + var coordenateSourceX = svgNodeSource.getAttribute("dragx"); + var coordenateSourceY = svgNodeSource.getAttribute("dragy"); + var coordenateTargetX = svgNodeTarget.getAttribute("dragx"); + var coordenateTargetY = svgNodeTarget.getAttribute("dragy"); + + var offset = parseFloat(this.getFormatter().getVertexById(this.getDataset().getEdgeById(edgeId).getNodeTarget().getId()).getDefault() + .getSize() + * this.circleDefaultRadius); + var point = this._calculateEdgePointerPosition(coordenateSourceX, coordenateSourceY, coordenateTargetX, coordenateTargetY, offset); + coordenateTargetX = point.x; + coordenateTargetY = point.y; + SVG.drawLine(coordenateSourceX, coordenateSourceY, coordenateTargetX, coordenateTargetY, this.GraphEdgeGroup, svgAttributesEdge); + + var attributesShadow = {}; + attributesShadow.id = this.getSVGEdgeId(edge.getId()); + attributesShadow["stroke-opacity"] = 0; + attributesShadow["stroke-width"] = 4; + attributesShadow["stroke"] = "black"; + svgEdge = SVG.drawLine(coordenateSourceX, coordenateSourceY, coordenateTargetX, coordenateTargetY, this.GraphEdgeGroup, attributesShadow); + } + + if (this.getFormatter().getEdgeById(edgeId) instanceof DirectedLineEdgeGraphFormatter + || (this.getFormatter().getEdgeById(edgeId) instanceof OdirectedLineEdgeGraphFormatter)) { + var coordenateSourceX = svgNodeSource.getAttribute("dragx"); + var coordenateSourceY = svgNodeSource.getAttribute("dragy"); + var coordenateTargetX = svgNodeTarget.getAttribute("dragx"); + var coordenateTargetY = svgNodeTarget.getAttribute("dragy"); + + var point = this._calculateEdgePointerPosition(coordenateSourceX, coordenateSourceY, coordenateTargetX, coordenateTargetY, offset); + coordenateTargetX = point.x; + coordenateTargetY = point.y; + + var angle = Geometry.toDegree(point.angle) + 90; + this.arrowDefaultSize = this.getFormatter().getEdgeById(edgeId).getArrowSize(); //getDefault().getArrowSize(); + var d = "-" + this.arrowDefaultSize + ",0 0,-" + parseFloat(this.arrowDefaultSize) * 2 + " " + this.arrowDefaultSize + ",0"; + + var attributes; + + if (this.getFormatter().getEdgeById(edgeId) instanceof DirectedLineEdgeGraphFormatter) { + attributes = [ + [ "fill", this.getFormatter().getEdgeById(edgeId).getDefault().getStroke() ], + [ "stroke", this.getFormatter().getEdgeById(edgeId).getDefault().getStroke() ], [ "id", this.getSVGArrowEdgeId(edgeId) ] ]; + } else { + attributes = [ + [ "fill", "#FFFFFF" ], [ "stroke", this.getFormatter().getEdgeById(edgeId).getDefault().getStroke() ], + [ "id", this.getSVGArrowEdgeId(edgeId) ] ]; + } + + var flechaSVGNode = SVG.drawPoligon(d, this.GraphEdgeGroup, attributes);//, ["transform", "rotate("+angle+"), translate(0,0)"]]); + flechaSVGNode.setAttribute("transform", " translate(" + coordenateTargetX + ", " + coordenateTargetY + "), rotate(" + angle + ")"); + } + ; + + if (this.getFormatter().getEdgeById(edgeId) instanceof CutDirectedLineEdgeGraphFormatter) { + var coordenateSourceX = svgNodeSource.getAttribute("dragx"); + var coordenateSourceY = svgNodeSource.getAttribute("dragy"); + var coordenateTargetX = svgNodeTarget.getAttribute("dragx"); + var coordenateTargetY = svgNodeTarget.getAttribute("dragy"); + + var point = this._calculateEdgePointerPosition(coordenateSourceX, coordenateSourceY, coordenateTargetX, coordenateTargetY, offset); + coordenateTargetX = point.x; + coordenateTargetY = point.y; + + var angle = Geometry.toDegree(point.angle) + 90; + + //this.arrowDefaultSize = 2; //getDefault().getArrowSize(); + var d = "-4,0 4,0 4,-2 -4,-2"; + + var flechaSVGNode = SVG.drawPoligon(d, this.GraphEdgeGroup, [ + [ "fill", this.getFormatter().getEdgeById(edgeId).getDefault().getStroke() ], + [ "stroke", this.getFormatter().getEdgeById(edgeId).getDefault().getStroke() ], [ "id", this.getSVGArrowEdgeId(edgeId) ] ]);//, ["transform", "rotate("+angle+"), translate(0,0)"]]); + flechaSVGNode.setAttribute("transform", " translate(" + coordenateTargetX + ", " + coordenateTargetY + "), rotate(" + angle + ")"); + } + ; + + if ((this.getFormatter().getEdgeById(edgeId) instanceof DotDirectedLineEdgeGraphFormatter) + || (this.getFormatter().getEdgeById(edgeId) instanceof OdotDirectedLineEdgeGraphFormatter)) { + var coordenateSourceX = svgNodeSource.getAttribute("dragx"); + var coordenateSourceY = svgNodeSource.getAttribute("dragy"); + var coordenateTargetX = svgNodeTarget.getAttribute("dragx"); + var coordenateTargetY = svgNodeTarget.getAttribute("dragy"); + var point = this._calculateEdgePointerPosition(coordenateSourceX, coordenateSourceY, coordenateTargetX, coordenateTargetY, offset); + coordenateTargetX = point.x; + coordenateTargetY = point.y; + var angle = Geometry.toDegree(point.angle) + 90; + // this.arrowDefaultSize = this.formatter.getEdgeById(edgeId).getArrowSize(); //getDefault().getArrowSize(); + var attributes = []; + if (this.getFormatter().getEdgeById(edgeId) instanceof OdotDirectedLineEdgeGraphFormatter) { + attributes = [ + [ "fill", "#FFFFFF" ], [ "stroke", this.getFormatter().getEdgeById(edgeId).getDefault().getStroke() ], + [ "id", this.getSVGArrowEdgeId(edgeId) ] ]; + } else { + attributes = [ + [ "fill", this.getFormatter().getEdgeById(edgeId).getDefault().getStroke() ], + [ "stroke", this.getFormatter().getEdgeById(edgeId).getDefault().getStroke() ], [ "id", this.getSVGArrowEdgeId(edgeId) ] ]; + } + var flechaSVGNode = SVG.drawCircle(0, 0, 4, this.GraphEdgeGroup, attributes); + flechaSVGNode.setAttribute("transform", " translate(" + coordenateTargetX + ", " + coordenateTargetY + "), rotate(" + angle + ")"); + } + ; + + var _this = this; + /** Events for the SVG edge **/ + if (svgEdge != null) { + if (this.getDataset().getEdgesCount() < this.args.maxNumberEdgesFiringEvents) { + svgEdge.addEventListener("mouseover", function() { + _this.overEdge(edgeId); + }, false); + svgEdge.addEventListener("mouseout", function() { + _this.outEdge(edgeId); + }, false); + } + } +}; + +GraphCanvas.prototype._calculateEdgePointerPosition = function(sourceX, sourceY, targetX, targetY, radius) { + var angle = Geometry.getAngleBetweenTwoPoints(sourceX, sourceY, targetX, targetY); + + /** Suponiendo el node source que este a la derecha **/ + if ((targetX - sourceX) < 0) { + var b = Geometry.getAdjacentSideOfRectangleRight(angle, radius); + targetX = parseFloat(targetX) + parseFloat(b); + arrowX = parseFloat(targetX) + parseFloat(b) + this.arrowDefaultSize / 2; + } else { + var b = Geometry.getAdjacentSideOfRectangleRight(angle, radius); + targetX = parseFloat(targetX) - parseFloat(b); + arrowX = parseFloat(targetX) - parseFloat(b) - this.arrowDefaultSize / 2; + } + + /** Suponiendo el node source que este a la arriba **/ + if ((targetY - sourceY) > 0) { + var a = Geometry.getOppositeSideOfRectangleRight(angle, radius); + targetY = parseFloat(targetY) - parseFloat(a); + arrowY = parseFloat(targetY) - parseFloat(a) - this.arrowDefaultSize / 2; + } else { + var a = Geometry.getOppositeSideOfRectangleRight(angle, radius); + targetY = parseFloat(targetY) + parseFloat(a); + arrowY = parseFloat(targetY) + parseFloat(a) - this.arrowDefaultSize / 2; + + } + + return { + "x" : arrowX, + "y" : arrowY, + "angle" : angle + }; +}; + +GraphCanvas.prototype.calculateCoordenatesBezier = function(nodeSize, x1, y1) { + var x11 = x1 - (nodeSize / 2); + var y11 = y1 - (nodeSize / 2); + + var x12 = parseFloat(x1) + parseFloat(nodeSize / 2); + var y12 = y1 - (nodeSize / 2); + + var curvePointX = (x12 - x11) / 2 + x11; + var curvePointY = y1 - (nodeSize * 2); + var d = "M" + x11 + "," + y11 + " T" + curvePointX + "," + curvePointY + " " + x12 + "," + y12; + return d; + +}; + +GraphCanvas.prototype.renderEdges = function() { + for ( var edge in this.getDataset().getEdges()) { + this.renderEdge(this.getDataset().getEdgeById(edge).getId()); + + } +}; + +GraphCanvas.prototype.getLastSelectedNode = function() { + var node = null; + if (this.getSelectedVertices().length > 0) { + var nodeId = this.getSelectedVertices()[this.getSelectedVertices().length - 1]; + node = this.getDataset().getVertexById(nodeId); + } + return node; +}; +/* + GraphCanvas.prototype.getNodeByNameAndIndex = function(node, index){ + var nodeId = this.getDataset().verticesIndex[node][index]; + var nodeItem = this.getDataset().getVertexById(nodeId); + return nodeItem; + }; + */ + +GraphCanvas.prototype.setDataset = function(dataset) { + this.dataset = dataset; +}; + +GraphCanvas.prototype.setFormatter = function(formatter) { + this.formatter = formatter; +}; + +GraphCanvas.prototype.setLayout = function(layout) { + this.layout = layout; +}; + +/** API **/ +GraphCanvas.prototype.getDataset = function() { + return this.dataset; +}; + +GraphCanvas.prototype.getFormatter = function() { + return this.formatter; +}; + +GraphCanvas.prototype.getLayout = function() { + return this.layout; +}; + +/** API DATASET **/ +GraphCanvas.prototype.addVertex = function(name, args) { + this.getDataset().addNode(name, args); +}; + +GraphCanvas.prototype.removeVertex = function(vertexId) { + this.getDataset().getVertexById(vertexId).remove(); +}; + +GraphCanvas.prototype.addEdge = function(edgeName, nodeSourceId, nodeTargetId, args) { + this.getDataset().addEdge(edgeName, nodeSourceId, nodeTargetId, args); +}; +/* + GraphCanvas.prototype.removeEdge = function(edgeId){ + this.getDataset().getEdgeById(edgeId).remove(); + }; + */ + +/** API FORMATTER **/ +GraphCanvas.prototype.getWidth = function() { + return this.getFormatter().getWidth(); +}; + +GraphCanvas.prototype.getHeight = function() { + return this.getFormatter().getHeight(); +}; + +GraphCanvas.prototype.getBackgroundImage = function() { + return this.getFormatter().getBackgroundImage(); +}; + +//GraphCanvas.prototype.setBackgroundImage = function(value){ +// this.getFormatter().setBackgroundImage(value); +//}; + +GraphCanvas.prototype.getBackgroundColor = function() { + return this.getFormatter().getBackgroundColor(); +}; + +GraphCanvas.prototype.setBackgroundColor = function() { + this.getFormatter().setBackgroundColor(value); +}; + +//GraphCanvas.prototype.setEdgeFill = function(edgeId, value){ +// this.getFormatter().getEdgeById(edgeId).getDefault().setFill(value); +//}; +// +//GraphCanvas.prototype.getEdgeFill = function(edgeId){ +// return this.getFormatter().getEdgeById(edgeId).getDefault().getFill(); +//}; + +/** VERTICES FORMATTER **/ +GraphCanvas.prototype.setVertexSize = function(vertexId, value) { + this.getFormatter().getVertexById(vertexId).getDefault().setSize(value); +}; + +GraphCanvas.prototype.getVertexSize = function(vertexId) { + return this.getFormatter().getVertexById(vertexId).getDefault().getSize(); +}; + +GraphCanvas.prototype.setVertexStroke = function(vertexId, value) { + this.getFormatter().getVertexById(vertexId).getDefault().setStroke(value); +}; + +GraphCanvas.prototype.getVertexStroke = function(vertexId) { + return this.getFormatter().getVertexById(vertexId).getDefault().getStroke(); +}; + +GraphCanvas.prototype.setVertexStrokeOpacity = function(vertexId, value) { + this.getFormatter().getVertexById(vertexId).getDefault().setStrokeOpacity(value); +}; + +GraphCanvas.prototype.getVertexStrokeOpacity = function(vertexId) { + return this.getFormatter().getVertexById(vertexId).getDefault().getStrokeOpacity(); +}; + +GraphCanvas.prototype.setVertexOpacity = function(vertexId, value) { + this.getFormatter().getVertexById(vertexId).getDefault().setOpacity(value); +}; + +GraphCanvas.prototype.getVertexOpacity = function(vertexId) { + return this.getFormatter().getVertexById(vertexId).getDefault().getOpacity(); +}; + +GraphCanvas.prototype.setVertexFill = function(vertexId, color) { + this.getFormatter().getVertexById(vertexId).getDefault().setFill(color); +}; + +GraphCanvas.prototype.getVertexFill = function(vertexId) { + return this.getFormatter().getVertexById(vertexId).getDefault().getFill(); +}; + +/** EDGES FORMATTER **/ +GraphCanvas.prototype.setEdgeSize = function(edgeId, value) { + this.getFormatter().getEdgeById(edgeId).getDefault().setSize(value); +}; + +GraphCanvas.prototype.getEdgeSize = function(edgeId) { + return this.getFormatter().getEdgeById(edgeId).getDefault().getSize(); +}; + +GraphCanvas.prototype.setEdgeStroke = function(edgeId, value) { + this.getFormatter().getEdgeById(edgeId).getDefault().setStroke(value); +}; + +GraphCanvas.prototype.getEdgeStroke = function(edgeId) { + return this.getFormatter().getEdgeById(edgeId).getDefault().getStroke(); +}; + +GraphCanvas.prototype.setEdgeStrokeOpacity = function(edgeId, value) { + this.getFormatter().getEdgeById(edgeId).getDefault().setStrokeOpacity(value); +}; + +GraphCanvas.prototype.getEdgeStrokeOpacity = function(edgeId) { + return this.getFormatter().getEdgeById(edgeId).getDefault().getStrokeOpacity(); +}; + +GraphCanvas.prototype.setEdgeFill = function(edgeId, color) { + this.getFormatter().getEdgeById(edgeId).getDefault().setFill(color); +}; + +GraphCanvas.prototype.getEdgeFill = function(edgeId) { + return this.getFormatter().getEdgeById(edgeId).getDefault().getFill(); +}; + +/** API LAYOUT **/ +GraphCanvas.prototype.setCoordinates = function(vertexId, x, y) { + return this.getLayout().getEdgeById(vertexId).setCoordinates(x, y); +}; + + + +function HPLCGraph(args) { + this.width = 600; + this.height = 600; + this.title = ''; + this.bbar = false; + this.plotInnerPanelPadding = 10; + this.plotPanelPadding = 5; + this.id = BUI.id(); - if (record.raw.measurementId != null) { - /** For testing * */ - grid.setLoading("ISPyB: Removing measurement"); - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, data) { - grid.setLoading(false); - /** - * We get and refresh experiment - * because specimens has changed * - */ - var adapter2 = new BiosaxsDataAdapter(); - adapter2.onSuccess.attach(function(sender, experiment) { - _this.onRemoved.notify(experiment); - _this._showStatusBarReady('Ready'); - }); - if (_this.experiments.experiments[0].experimentId != null) { - adapter2.getExperimentById(_this.experiments.experiments[0].experimentId, "MEDIUM"); - _this._showStatusBarBusy("ISPyB: Removing Unused Specimens"); - } - }); + this.hidePlots = null; + this.xlabel = ""; + this.scaled = false; + this.xParam = null; + this.showRangeSelector = true; + this.interactionModel = null; + + /** for each stat the max and minimum value when it is scaled in order to show correctly in the legend **/ + this.ranges = {}; + if (args != null) { + if (args.interactionModel != null) { + this.interactionModel = args.interactionModel; + } + if (args.width != null) { + this.width = args.width; + } + if (args.height != null) { + this.height = args.height; + } + if (args.bbar != null) { + this.bbar = args.bbar; + } + if (args.title != null) { + this.title = args.title; + } + if (args.plots != null) { + this.plots = args.plots; + } + + if (args.scaled != null) { + this.scaled = args.scaled; + } + if (args.xlabel != null) { + this.xlabel = args.xlabel; + } + if (args.xParam != null) { + this.xParam = args.xParam; + } + if (args.showRangeSelector != null) { + this.showRangeSelector = args.showRangeSelector; + } + } + + this.onZoomX = new Event(this); + this.onResetZoom = new Event(this); + this.dblclick = new Event(this); +} + +HPLCGraph.prototype.getMenu = function () { + var _this = this; + /** Actions buttons **/ + var actions = []; + + function toggle(item, pressed) { + if (pressed) { + _this.plots[item.param] = true; + } else { + delete _this.plots[item.param]; + } + _this.reloadData(this.hplcData); + } + + for (var i = 0; i < this.hplcData.length; i++) { + if (this.hplcData[i].showOnMenu != false) { + var param = this.hplcData[i].param; + var style = "style='padding:0 0px 0 5px;'"; + actions.push({ + text : "
" + BUI.getRectangleColorDIV(this.hplcData[i].color, 10, 10) + " " + this.hplcData[i].label + "
", + id : _this.id + param, + param : param, + enableToggle : true, + scope : this, + toggleHandler : toggle, + pressed : (_this.plots[param] != null) + }); + } + } + actions.push("-"); + + actions.push({ + text : "Scale", + enableToggle : true, + scope : this, + pressed : this.scaled, + icon : '../images/icon_graph.png', + toggleHandler : function (item, pressed) { + _this.scaled = pressed; + _this.reloadData(this.hplcData); + } + }); + + actions.push("->"); + actions.push({ + text : "Save", + scope : this, + icon : '../images/save.gif', + handler : function (item, pressed) { + var largeImage = document.createElement("img"); + largeImage.style.display = 'block'; + largeImage.style.width = 200 + "px"; + largeImage.style.height = 200 + "px"; + largeImage.setAttribute('src', Dygraph.Export.asCanvas(this.dygraphObject.dygraph).toDataURL()); + window.open(Dygraph.Export.asCanvas(this.dygraphObject.dygraph).toDataURL(), 'Image', ''); + } + }); + + return actions; +}; - adapter.onError.attach(function(sender, data) { - alert("Error: " + data); - grid.setLoading(false); - }); +/** Looks for the maximum value and then divide everything but that value **/ +HPLCGraph.prototype.scaledData = function (data) { + for (var i = 0; i < data.length; i++) { + var values = this.getMaxAndMinValue(data[i]); + data[i] = this.divideValuesByMax(data[i], values.max); + this.ranges[data[i].label] = values; + } + return data; +}; - adapter.removeMeasurement(record.raw); - } - } - } +/** Given a stat float[] and a max number it will divide each value by max **/ +HPLCGraph.prototype.divideValuesByMax = function (stat, max) { + for (var j = 0; j < stat.data.length; j++) { + if (max != 0) { + stat.data[j] = Number(stat.data[j]) / max; + stat.std[j] = Number(stat.std[j]) / max; + } + } + return stat; +}; - } - } - }); +/** returns max value of a stat **/ +HPLCGraph.prototype.getMaxAndMinValue = function (stat) { + var max = 0; + var min = stat.data[0]; + for (var j = 0; j < stat.data.length; j++) { + if (Number(stat.data[j]) > max) { + max = Number(stat.data[j]); + } + if (Number(stat.std[j]) > max) { + max = Number(stat.std[j]); + } + if (Number(stat.data[j]) < min) { + min = Number(stat.data[j]); + } + } + return { + max : Number(max), + min : Number(min) + }; +}; - this.grid.on("afterrender", function() { +HPLCGraph.prototype.getPoint = function (data, i) { + var point = [ 10, 10, 10 ]; + var y = parseFloat(data.data[i]); + var error = parseFloat(data.std[i]); + if (data.fdata == null) { + return [ y - error, y, y + error ]; + } else { + if (data.fstd != null) { + return [ data.fstd(y - error), data.fdata(y), data.fstd(y + error) ]; + } + return [ data.fdata(y) - error, data.fdata(y), data.fdata(y) + error ]; + } + return point; +}; - function updateTime() { - try { - _this.estimatedTime = _this.estimatedTime - 1; +HPLCGraph.prototype.reloadData = function(hplcData) { + this.panel.setLoading(false); + this.hplcData = hplcData; - _this.onUpdateTime.notify({ - hours : (Number(_this.estimatedTime / 3600).toFixed()), - minutes : (Number((_this.estimatedTime / 60) % 60).toFixed()), - seconds : (Number(_this.estimatedTime % 60).toFixed()) + var data = hplcData; + - }); + /** In case of having peaks **/ + if (this.peaks != null) { + for (var peak in this.peaks) { + var values = []; + var std = []; + for (var i = 0; i < this.peaks[peak].length; i++) { + values.push(this.peaks[peak][i][1]); + std.push(this.peaks[peak][i][2]); + } + data.push({ + param : peak, + data : values, + showOnMenu : false, + fdata : function (a) { + var value = (Math.log(parseFloat(a))); + if (isNumber(value)) { + return value; + } + }, + fstd : function (a) { + var value = Math.log(Math.abs(parseFloat(a))); + if (isNumber(value)) + return value; + }, + std : std, + color : this.colorPeak[peak], + label : peak + }); + + } + } + + + if (this.scaled) { + data = this.scaledData(JSON.parse(JSON.stringify(hplcData))); + } - if (Number(_this.estimatedTime) < 0) { - _this.timer = null; - grid.setTitle(_this.title); + var paramIndex = {}; + var parsed = []; + var j = 0; + for (var i = 0; i < data[0].data.length - 1; i++) { + var aux = []; + for (j = 0; j < data.length; j++) { + if (this.plots != null) { + if (this.plots[data[j].param] != null) { + aux.push(this.getPoint(data[j], i)); + paramIndex[data[j].param] = aux.length - 1; } + } else { + aux.push([ data[j].data[i] - data[j].std[i], data[j].data[i], data[j].data[i] + data[j].std[i] ]); + } + } + parsed.push([]); - } catch (e) { - console.log(e); - _this.timer = null; + var index = i; + if (this.xParam != null) { + index = parseFloat(data[this.xParam].data[i]); + } + + parsed[parsed.length - 1].push(index); + + for (j = 0; j < data.length; j++) { + if (this.plots != null) { + if (this.plots[data[j].param] != null) { + parsed[parsed.length - 1].push(aux[paramIndex[data[j].param]]); + } + } else { + parsed[parsed.length - 1].push(aux[j]); } } + } - if (_this.estimateTime) { - var experimentList = _this.experiments; - var collected = experimentList.getMeasurementsCollected(); - if (collected.length > 0) { - if (collected[0].run3VO != null) { - try { - var end = collected[0].run3VO.timeEnd; - var start = collected[0].run3VO.timeStart; - var dstart = moment(start); - var dend = moment(end); - var seconds = Number(dend.diff(dstart) / 1000).toFixed(); + var colors = []; + var labels = [ "" ]; + for (j = 0; j < data.length; j++) { + if (this.plots != null) { + if (this.plots[data[j].param] != null) { + colors.push(data[j].color); + labels.push(data[j].label); + } + } else { + parsed[parsed.length - 1].push(aux[j]); + } + } - _this.estimatedTime = (seconds * experimentList.getMeasurementsNotCollected().length); + this._renderDygraph(parsed, colors, labels); +}; - if (_this.estimatedTime > 0) { - updateTime(); - _this.timer = setInterval(function() { - updateTime(); - }, 1000); +HPLCGraph.prototype._renderDygraph = function (parsed, colors, labels) { + this.dygraphObject = new StdDevDyGraph(this.id, { + width : this.width, + height : this.height - 10, + xlabel : this.xlabel, + showRangeSelector : this.showRangeSelector, + interactionModel : this.interactionModel, + scaled : this.scaled, + ranges : this.ranges + }); + this.dygraphObject.draw(parsed, colors, labels); + + var _this = this; + this.dygraphObject.onZoomX.attach(function (sender, args) { + try { + _this.onZoomX.notify(args); + } catch (e) { + } + }); + + this.dygraphObject.onResetZoom.attach(function (sender, args) { + try { + _this.onResetZoom.notify(args); + } catch (e) { + } + }); + + this.dygraphObject.dblclick.attach(function (sender, args) { + try { + _this.dblclick.notify(args); + } catch (e) { + } + }); + +}; + +HPLCGraph.prototype.loadData = function (data) { + var _this = this; + this.reloadData(data); + this.panel.addDocked({ + xtype : 'toolbar', + items : this.getMenu() + }); + + + if (this.bbar == true){ + this.panel.addDocked({ + xtype : 'toolbar', + dock: 'bottom', + items : [ + { + xtype: 'numberfield', + id: 'main_field_start', + fieldLabel: 'Range from', + width: 170, + labelWidth : 70, + value: 0, + minValue: 0 + }, + { + xtype: 'numberfield', + id: 'main_field_end', + fieldLabel: 'to', + width: 130, + labelWidth : 30, + value: 0, + minValue: 0 + }, + { + xtype: 'button', + text: 'Go', + handler: function () { + var start = parseFloat(Ext.getCmp("main_field_start").getValue()); + var end = parseFloat(Ext.getCmp("main_field_end").getValue()); + + if (start < 0) { + start = 0; + } + if (end < 0) { + end = 0; + } + if (start > end) { + var aux = end; + end = start; + start = aux; + } + + _this.dygraphObject.dygraph.updateOptions({ isZoomedIgnoreProgrammaticZoom: true, dateWindow: [start, end] }); + } } + ] + }); + } +}; - } catch (e) { - } - } - } - } +HPLCGraph.prototype.getPanel = function () { + this.panel = Ext.create('Ext.panel.Panel', { + padding : this.plotPanelPadding, + width : this.width + 4 * this.plotInnerPanelPadding, + height : this.height + 4 * this.plotInnerPanelPadding, + items : [ { + html : "", + id : this.id, + width : this.width, + height : this.height + } ] }); - return this.grid; + return this.panel; }; -/** Method for testing * */ -MeasurementGrid.prototype.input = function() { - var experiment = DATADOC.getExperiment_10(); - var measurements = DATADOC.getMeasurements_10(); - var proposal = DATADOC.getProposal_10(); - return { - experiment : experiment, - measurements : measurements, - proposal : proposal - }; +HPLCGraph.prototype.input = function () { + return DATADOC.getHPLCData(); }; -MeasurementGrid.prototype.test = function(targetId) { - var measurementGrid = new MeasurementGrid({ - tbar : true - - }); - BIOSAXS.proposal = new Proposal(measurementGrid.input().proposal); - var panel = measurementGrid.getPanel(measurementGrid.input().measurements, new ExperimentList([ new Experiment(measurementGrid.input().experiment) ])); - panel.render(targetId); +HPLCGraph.prototype.getDataByFrameNumber = function (frameNumber) { + var data = {}; + data.frameNumber = frameNumber; + for (var key in this.hplcData){ + data[this.hplcData[key].label] = this.hplcData[key].data[frameNumber]; + } + console.log(data); + return data; }; - -/** - * A shipment may contains one or more cases where stock solutions and sample - * plates are stored - * - * @height - * @btnEditVisible - * @btnRemoveVisible - * - * #onEditButtonClicked - */ -function MolarityGrid(args) { - this.height = 100; - this.width = 100; - if (args != null) { - if (args.height != null) { - this.height = args.height; - } - if (args.width != null) { - this.width = args.width; + +HPLCGraph.prototype.test = function (targetId) { + var mainPlotPanel = new HPLCGraph({ + title : 'I0', + width : 800, + height : 400, + plots : { + "I0" : true, + "Rg" : true, + "Mass" : true + }, + xlabel : "HPLC Frames", + scaled : this.scaled, + interactionModel : { + 'dblclick' : function (event, g, context) { + } } - } + }); + mainPlotPanel.getPanel().render(targetId); + mainPlotPanel.loadData(mainPlotPanel.input()); - var _this = this; - - this.molarityForm = new MolarityForm({height : 180, width : 455}); +}; - this.molarityForm.onSave.attach(function(sender){ - _this.molarityWindow.destroy(); - _this.updateProposal(); - - }); - - this.molarityForm.onClose.attach(function(sender){ - _this.molarityWindow.destroy(); - - }); + +function MergesHPLCGraph(args) { + HPLCGraph.prototype.constructor.call(this, args); - /** Events * */ - this.onEditButtonClicked = new Event(this); +// this.peakColors = ["#00FB42", "#00BA31", "#007C21", "#003E10"]; + this.peakColors = ["#DEBD00", "#6D9100", "#872900", "#0092CC"]; } -MolarityGrid.prototype._getColumns = function() { - return [ { - text : 'Subunit', - columns : [ { - text : "Acronym", - width : 100, - hidden : false, - dataIndex : 'acronym', - sortable : true - }, { - text : "Name", - width : 125, - hidden : false, - dataIndex : 'name', - sortable : true - }, { - text : "MM Est.", - width : 100, - dataIndex : 'molecularMass', - sortable : true, - renderer : function(grid, cls, record){ - return BUI.formatValuesUnits(record.data.molecularMass , "Da", 10, 2); - - } - } ] - }, { -// text : "Number
in assymmetric units", - text : "Ratio", - width : 100, - dataIndex : 'ratio', - tooltip : 'Number of times the subunit is present in the macromolecule', - sortable : true - }, { - id : this.id + 'MOLARITY_REMOVE', - flex : 0.1, - sortable : false, - renderer : function(value, metaData, record, rowIndex, colIndex, store) { - return BUI.getRedButton('REMOVE'); - } - } ]; -}; -MolarityGrid.prototype._openMolarityWindow = function() { - this.molarityWindow = Ext.create('Ext.window.Window', { - title : 'Molarity', - height : 220, - width : 500, - modal : true, - items : [this.molarityForm.getPanel() ] - }).show(); -}; +MergesHPLCGraph.prototype.scaledData = HPLCGraph.prototype.scaledData; +MergesHPLCGraph.prototype.divideValuesByMax = HPLCGraph.prototype.divideValuesByMax; +MergesHPLCGraph.prototype.getMaxAndMinValue = HPLCGraph.prototype.getMaxAndMinValue; +MergesHPLCGraph.prototype.getPoint = HPLCGraph.prototype.getPoint; +MergesHPLCGraph.prototype.reloadData = HPLCGraph.prototype.reloadData; +MergesHPLCGraph.prototype._renderDygraph = HPLCGraph.prototype._renderDygraph; +MergesHPLCGraph.prototype.loadData = HPLCGraph.prototype.loadData; +MergesHPLCGraph.prototype.getPanel = HPLCGraph.prototype.getPanel; +MergesHPLCGraph.prototype.getDataByFrameNumber = HPLCGraph.prototype.getDataByFrameNumber; -MolarityGrid.prototype._getButtons = function() { - var _this = this; - return [ { - text : 'Add subunit', - icon : '../images/add.png', - handler : function() { - _this._openMolarityWindow(); + +MergesHPLCGraph.prototype.setPeaks = function (data) { + this.peaks = data; + /** get size of peaks **/ + this.peakKeys = []; + this.colorPeak = {}; + var colorCount = 1; + for (var key in this.peaks) { + if (this.peaks.hasOwnProperty(key)) { + var color = this.peakColors[colorCount % this.peakColors.length]; + colorCount = colorCount + 1; + this.peakKeys.push(key); + this.colorPeak[key] = color; } - }]; + } + this.peakKeys.sort(); }; -MolarityGrid.prototype.updateProposal = function() { +MergesHPLCGraph.prototype.getMenu = function () { var _this = this; - this.panel.setLoading(); - BIOSAXS.proposal.onInitialized.attach(function() { - if (BIOSAXS.proposal != null) { - var macromolecules = BIOSAXS.proposal.macromolecules; - for (var i = 0; i < macromolecules.length; i++) { - - if (macromolecules[i].macromoleculeId == _this.macromolecule.macromoleculeId) { - _this.refresh(macromolecules[i]); - _this.panel.setLoading(false); + /** Actions buttons **/ + var actions = []; + + function toggle(item, pressed) { + if (pressed) { + _this.plots[item.param] = true; + } else { + delete _this.plots[item.param]; + } + _this.reloadData(_this.hplcData); + } + + + /** Toolbar for peaks Average **/ + if (this.peaks != null) { + var items = []; + for (var i = 0; i < this.peakKeys.length; i++) { + var color = this.colorPeak[this.peakKeys[i]]; + items.push({ + text: "Peak #" + i + " " + this.peakKeys[i].replace("- ", " to #").replace(".0", "").replace(".0", "") + "", + peakid : this.peakKeys[i], + checked: false, + checkHandler: function (sender, pressed) { + var item = new Object(); + item.param = sender.peakid; + toggle(item, pressed); } - } + }); } - }); - BIOSAXS.proposal.init(); -}; - + + var menu = Ext.create('Ext.menu.Menu', { + id: 'mainMenu', + style: { + overflow: 'visible' + }, + items: items + }); + var tb = Ext.create('Ext.toolbar.Toolbar'); + tb.add({ + text: 'Peaks Avg.', + menu: menu + }); + actions.push(tb); + } -MolarityGrid.prototype.getPanel = function() { - var _this = this; + - this.molarityStore = Ext.create('Ext.data.Store', { - fields : [ 'acronym', 'ratio', 'comments', 'stoichiometryId', 'name', 'molecularMass' ], - sorters : { - property : 'ratio', - direction : 'DESC' + for (var i = 0; i < this.hplcData.length; i++) { + if (this.hplcData[i].showOnMenu != false) { + var param = this.hplcData[i].param; + var style = "style='padding:0 0px 0 5px;'"; + actions.push({ + text : "
" + BUI.getRectangleColorDIV(this.hplcData[i].color, 10, 10) + " " + this.hplcData[i].label + "
", + id : _this.id + param, + param : param, + enableToggle : true, + scope : this, + margin : 5, + toggleHandler : toggle, + pressed : (_this.plots[param] != null) + }); } - }); - - this.panel = Ext.create('Ext.grid.Panel', { - store : this.molarityStore, - height : this.height, - padding : 5, - columns : this._getColumns(), - listeners : { - cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { - - /** Remove entry * */ - if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'MOLARITY_REMOVE') { - var dataAdapter = new BiosaxsDataAdapter(); - dataAdapter.onSuccess.attach(function(sender) { - _this.updateProposal(); - - }); - dataAdapter.removeStoichiometry(record.data.stoichiometryId); - _this.panel.setLoading("Removing Structure"); - } - } - }, - tbar : this._getButtons() - }); - return this.panel; -}; + } -MolarityGrid.prototype._prepareData = function(macromolecule) { - /** Return an array of [{ratio,acronym, stoichiometryId, name}] **/ - var data = []; - if (macromolecule.stoichiometry != null) { - for (var i = 0; i < macromolecule.stoichiometry.length; i++) { - var hostMacromolecule = BIOSAXS.proposal.getMacromoleculeById(macromolecule.stoichiometry[i].macromoleculeId); - data.push({ - ratio : macromolecule.stoichiometry[i].ratio, - acronym : hostMacromolecule.acronym, - comments : hostMacromolecule.comments, - molecularMass : hostMacromolecule.molecularMass, - stoichiometryId : macromolecule.stoichiometry[i].stoichiometryId, - name : hostMacromolecule.name - }); - } + actions.push("->"); + actions.push({ + text : "Save", + scope : this, + icon : '../images/save.gif', + handler : function (item, pressed) { + var largeImage = document.createElement("img"); + largeImage.style.display = 'block'; + largeImage.style.width = 200 + "px"; + largeImage.style.height = 200 + "px"; + largeImage.setAttribute('src', Dygraph.Export.asCanvas(this.dygraphObject.dygraph).toDataURL()); + window.open(Dygraph.Export.asCanvas(this.dygraphObject.dygraph).toDataURL(), 'Image', ''); } - return data; -}; + }); -MolarityGrid.prototype.refresh = function(macromolecule) { - if (macromolecule != null){ - this.molarityStore.loadData(this._prepareData(macromolecule)); - this.molarityForm.refresh(macromolecule); - this.macromolecule = macromolecule; - } + return actions; }; -MolarityGrid.prototype.input = function() { - return { - proposal : DATADOC.getProposal_10(), - dewars : DATADOC.getDewars_10() - }; +MergesHPLCGraph.prototype.input = function () { + return DATADOC.getScatteringHPLCFrameData(); }; -MolarityGrid.prototype.test = function(targetId) { - var MolarityGrid = new MolarityGrid({ - height : 150 +MergesHPLCGraph.prototype.test = function (targetId) { + var mainPlotPanel = new MergesHPLCGraph({ + title : 'Scattering', + width : this.plotWidth, + height : 500, + showRangeSelector : false, + xParam : 0, + xlabel : "scattering_I", + plots : { + "scattering_I" : true, + "subtracted_I" : true, + "buffer_I" : true + } }); - BIOSAXS.proposal = new Proposal(MolarityGrid.input().proposal); - var panel = MolarityGrid.getPanel(MolarityGrid.input().dewars); - panel.render(targetId); - + mainPlotPanel.getPanel().render(targetId); + mainPlotPanel.loadData(mainPlotPanel.input()); }; + + +function NetworkWidget(args) { + this.id = "NetworkViewer_" + Math.random().toString().replace(".", "_"); + + this.label = true; + if (args != null) { + if (args.targetId != null) { + this.targetId = args.targetId; + } + if (args.label != null) { + this.label = args.label; + } + } + + this.onVertexOver = new Event(this); + this.onVertexOut = new Event(this); +} + +NetworkWidget.prototype.draw = function(dataset, formatter, layout) { + + this.graphCanvas = new GraphCanvas(this.id, document.getElementById(this.targetId), { + "labeled" : this.label, + "multipleSelectionEnabled" : false, + "draggingCanvasEnabled" : false + }); + this.graphCanvas.draw(dataset, formatter, layout); + + var _this = this; + this.graphCanvas.onVertexOver.attach(function(sender, nodeId) { + _this.onVertexOver.notify(nodeId); + }); + + this.graphCanvas.onVertexOut.attach(function(sender, nodeId) { + _this.onVertexOut.notify(nodeId); + }); +}; + +/** SELECT VERTICES BY NAME * */ +NetworkWidget.prototype.selectVertexByName = function(vertexName) { + var vertices = this.getDataset().getVertexByName(vertexName); + if (vertices != null) { + for ( var nodeId in vertices) { + if (vertices.hasOwnProperty(nodeId)) { + var vertexId = vertices[nodeId].getId(); + this.selectVertexById(vertexId); + } + } + } +}; + +NetworkWidget.prototype.selectVerticesByName = function(verticesName) { + for ( var i = 0; i < verticesName.length; i++) { + this.selectVertexByName(verticesName[i]); + } +}; + +/** SELECT VERTICES BY ID * */ +NetworkWidget.prototype.selectVertexById = function(vertexId) { + this.graphCanvas.selectNode(vertexId); + this.blinkVertexById(vertexId); +}; + +NetworkWidget.prototype.selectVerticesById = function(verticesId) { + for ( var i = 0; i < verticesId.length; i++) { + this.selectVertexById(verticesId[i]); + } +}; + +/** VECINDARIO * */ +NetworkWidget.prototype.selectNeighbourhood = function() { + this.selectEdgesFromVertices(); + this.selectAdjacent(); +}; +/** DESELECT * */ +NetworkWidget.prototype.deselectNodes = function() { + this.graphCanvas.deselectNodes(); +}; + +/** SELECT ALL NODES * */ +NetworkWidget.prototype.selectAllNodes = function() { + this.getGraphCanvas().selectAllNodes(); +}; + +/** SELECT EVERYTHING * */ +NetworkWidget.prototype.selectAll = function() { + this.getGraphCanvas().selectAll(); +}; + +/** SELECT ALL EDGES * */ +NetworkWidget.prototype.selectAllEdges = function() { + this.getGraphCanvas().selectAllEdges(); +}; + +/** ZOOM * */ +NetworkWidget.prototype.setScale = function(value) { + this.graphCanvas.setScale(value); +}; + +NetworkWidget.prototype.getScale = function() { + return this.graphCanvas.getScale(value); +}; + +/** SELECT ADJACENT VERTICES FROM SELECTED VERTICES * */ +NetworkWidget.prototype.selectAdjacent = function() { + var selectedVertices = this.getGraphCanvas().getSelectedVertices(); + var edges = []; + for ( var i = 0; i < selectedVertices.length; i++) { + edges = edges.concat(this.getGraphCanvas().getDataset().getVertexById(selectedVertices[i]).getEdges()); + } + var vertices = []; + for ( i = 0; i < edges.length; i++) { + vertices.push(edges[i].getNodeSource().getId()); + vertices.push(edges[i].getNodeTarget().getId()); + } + + this.selectVerticesById(vertices); +}; + +/** SELECT EDGES FROM SELECTED VERTICES * */ +NetworkWidget.prototype.selectEdgesFromVertices = function() { + var selectedVertices = this.getGraphCanvas().getSelectedVertices(); + var edges = []; + for ( var i = 0; i < selectedVertices.length; i++) { + edges = edges.concat(this.getGraphCanvas().getDataset().getVertexById(selectedVertices[i]).getEdges()); + } + var edgesId = []; + for ( i = 0; i < edges.length; i++) { + edgesId.push(edges[i].getId()); + } + this.getGraphCanvas().selectEdges(edgesId); +}; + +/** BLINKING * */ +NetworkWidget.prototype.blinkVertexById = function(vertexId) { + this.graphCanvas.blinkVertexById(vertexId); +}; + +/** COMPONENTE CONEXA DE LOS NODOS SELECCIONADOS * */ +NetworkWidget.prototype.selectConnectedComponent = function() { + var elements = this.getConnectedComponent(); + this.selectVerticesById(elements.nodes); + this.graphCanvas.selectEdges(elements.edges); +}; + +NetworkWidget.prototype.getConnectedComponent = function() { + var nodosVisitados = {}; + var aristasVisitadas = {}; + var arrNodos = []; + var arrAristas = []; + var selectedGraphNodesId = this.getGraphCanvas().getSelectedVertices(); + for ( var i = 0; i < selectedGraphNodesId.length; i++) { + this.visitNode(selectedGraphNodesId[i], nodosVisitados, aristasVisitadas, arrNodos, arrAristas); + } + return { + nodes : arrNodos, + edges : arrAristas + }; +}; + +NetworkWidget.prototype.visitNode = function(nodeId, nodosVisitados, aristasVisitadas, arrNodos, arrAristas) { + nodosVisitados[nodeId] = true; + arrNodos.push(nodeId); + var nodeEdges = this.getDataset().getVertexById(nodeId).getEdges(); + for ( var j = 0; j < nodeEdges.length; j++) { + var edge = nodeEdges[j]; + var edgeId = edge.getId(); + if (aristasVisitadas[edgeId] == null) { + aristasVisitadas[edgeId] = true; + arrAristas.push(edgeId); + var nodeTargetId = edge.getNodeTarget().getId(); + if (nodosVisitados[nodeTargetId] == null) { + this.visitNode(nodeTargetId, nodosVisitados, aristasVisitadas, arrNodos, arrAristas); + } + var nodeSourceId = edge.getNodeSource().getId(); + if (nodosVisitados[nodeSourceId] == null) { + this.visitNode(nodeSourceId, nodosVisitados, aristasVisitadas, arrNodos, arrAristas); + } + } + } +}; + +/** COLLAPSE SELECTED VERTICES * */ +NetworkWidget.prototype.collapse = function() { + var selectedVertices = this.getGraphCanvas().getSelectedVertices(); + var xMin = -Infinity; + var xMax = Infinity; + var yMin = -Infinity; + var yMax = Infinity; + + for ( var i = 0; i < selectedVertices.length; i++) { + var vertex = this.getGraphCanvas().getLayout().getNodeById(selectedVertices[i]); + if (xMin < vertex.x) { + xMin = vertex.x; + } + if (xMax > vertex.x) { + xMax = vertex.x; + } + if (yMin < vertex.y) { + yMin = vertex.y; + } + if (yMax > vertex.y) { + yMax = vertex.y; + } + } + + var centerX = xMin - xMax; + var centerY = yMin - yMax; + var radius = (xMax - xMin) / 4; + + var count = selectedVertices.length; + var verticesCoordinates = []; + + for ( i = 0; i < selectedVertices.length; i++) { + x = centerX + radius * Math.sin(i * 2 * Math.PI / count); + y = centerY + radius * Math.cos(i * 2 * Math.PI / count); + verticesCoordinates.push({ + 'x' : x, + 'y' : y + }); + } + + for ( i = 0; i < selectedVertices.length; i++) { + this.getGraphCanvas().getLayout().getNodeById(selectedVertices[i]).setCoordinates(verticesCoordinates[i].x, verticesCoordinates[i].y); + } +}; +/** SETTER FONT SIZE * */ +NetworkWidget.prototype.setVerticesFontSize = function(value) { + for ( var nodeId in this.getDataset().getVertices()) { + if (this.getDataset().getVertices().hasOwnProperty(nodeId)) { + this.getFormatter().getVertexById(nodeId).getDefault().setFontSize(value); + } + } +}; + +/** GETTERS * */ +NetworkWidget.prototype.getFormatter = function() { + return this.getGraphCanvas().getFormatter(); +}; +NetworkWidget.prototype.getLayout = function() { + return this.getGraphCanvas().getLayout(); +}; + +NetworkWidget.prototype.getDataset = function() { + return this.getGraphCanvas().getDataset(); +}; + +NetworkWidget.prototype.getGraphCanvas = function() { + return this.graphCanvas; +}; + +RangeWhiskerGraph.prototype.cleanArray = GenericGraph.prototype.cleanArray; +RangeWhiskerGraph.prototype.plotAxes = GenericGraph.prototype.plotAxes; +RangeWhiskerGraph.prototype.plotRuler = GenericGraph.prototype.plotRuler; +RangeWhiskerGraph.prototype.drawSVGVerticalText = GenericGraph.prototype.drawSVGVerticalText; +RangeWhiskerGraph.prototype.getClassColor = GenericGraph.prototype.getClassColor; +RangeWhiskerGraph.prototype.getDimensions = GenericGraph.prototype.getDimensions; +RangeWhiskerGraph.prototype.calculate = GenericGraph.prototype.calculate; +RangeWhiskerGraph.prototype.getMedian = GenericGraph.prototype.getMedian; +RangeWhiskerGraph.prototype.pointToPixel = GenericGraph.prototype.pointToPixel; +RangeWhiskerGraph.prototype.maxValueWhisker = GenericGraph.prototype.maxValueWhisker; +RangeWhiskerGraph.prototype.refresh = GenericGraph.prototype.refresh; + /** - * Given data analysis parsed with ResultsAssemblyWidget makes a selector grid with buffer/protein - **/ -function SpecimenSelectorResultGrid() { - this.id = BUI.id(); -} + * Subclass of GenericGraph + * + * @plotHorizontalByCluster + */ +function RangeWhiskerGraph(args) { + this.maxBoxWidth = 25; -SpecimenSelectorResultGrid.prototype._prepareData = function(data) { - var parsed = []; - for ( var i = 0; i < data.length; i++) { - var row = data[i]; - for ( var j = 0; j < row.conditions.length; j++) { - parsed.push({ - bufferId : row.conditions[j].bufferId, - macromoleculeId : row.macromoleculeId, - macromoleculeAcronym : BIOSAXS.proposal.getMacromoleculeById(row.macromoleculeId).acronym, - bufferAcronym : BIOSAXS.proposal.getBufferById(row.conditions[j].bufferId).acronym - }); - } + if (args == null) { + args = {}; } - return parsed; -}; - -SpecimenSelectorResultGrid.prototype.refresh = function(data) { - this.store.loadData(this._prepareData(data)); -}; - -SpecimenSelectorResultGrid.prototype.getPanel = function(data) { - var _this = this; - this.store = Ext.create('Ext.data.Store', { - fields : [ 'macromoleculeId', 'bufferId', 'macromoleculeAcronym', 'bufferAcronym', 'concentration' ], - data : this._prepareData(data), - groupField : 'macromoleculeAcronym' - }); + args.plotHorizontalByCluster = false; - this.store.sort('concentration'); - this.grid = Ext.create('Ext.grid.Panel', { - id : this.id, - store : this.store, - width : this.width, - height : this.height, - maxHeight : this.maxHeight, - border : 1, - features : [ { - ftype : 'grouping', - groupHeaderTpl : '{name}', - hideGroupedHeader : true, - startCollapsed : false - } ], - selModel : Ext.create('Ext.selection.CheckboxModel', { - allowDeselect : true, - mode : 'MULTI', - listeners : { - selectionchange : function(sm, selections) { - _this.selected = []; - for ( var i = 0; i < selections.length; i++) { - _this.selected.push(selections[i].raw); - } - } - } - }), - margin : 10, - sortableColumns : true, - columns : [ { - text : 'Macromolecule', - dataIndex : 'macromoleculeAcronym', - flex : 1 - }, { - text : '', - dataIndex : 'bufferId', - width : 20, - hidden : false, - renderer : function(val, y, sample) { - return BUI.getRectangleColorDIV(BIOSAXS.proposal.bufferColors[val], 10, 10); - } - }, { - text : 'Buffer', - dataIndex : 'bufferAcronym', - flex : 1 - }, { - text : 'Concentration', - dataIndex : 'concentration', - flex : 1, - renderer : function(val, y, sample) { - return val + " mg/ml"; - } - } ] - }); + GenericGraph.call(this, args); - /** Adding the tbar **/ - if (this.tbar) { - this.grid.addDocked({ - xtype : 'toolbar', - items : this.getTbar() - }); + if (args.maxBoxWidth != null) { + this.maxBoxWidth = args.maxBoxWidth; } - return this.grid; -}; +} -SpecimenSelectorResultGrid.prototype.input = function() { - return { - data : new ResultsAssemblyGrid()._prepareData(new ResultsAssemblyGrid().input().data), - proposal : new ResultsAssemblyGrid().input().proposal - }; +RangeWhiskerGraph.prototype.refresh = function(data) { + document.getElementById(this.targetId).innerHTML = ""; + this.draw(this.targetId, data); }; -SpecimenSelectorResultGrid.prototype.test = function(targetId) { - var specimenSelectorResultGrid = new SpecimenSelectorResultGrid(); - BIOSAXS.proposal = new Proposal(specimenSelectorResultGrid.input().proposal); - var panel = specimenSelectorResultGrid.getPanel(specimenSelectorResultGrid.input().data); - panel.render(targetId); -}; +RangeWhiskerGraph.prototype.isNumber = function(value) { + if (value == "") + return false; -/** - * Show all buffer conditions for each macromolecules pointing out the measurements quality - * - * @height - * @maxHeight - * @width - * @searchBar - * @tbar - * @btnResultVisible - * - * #onClick - */ -function ResultsAssemblyGrid(args) { - this.height = 500; - this.id = BUI.id(); - this.maxHeight = this.height; + var d = parseInt(value); + if (!isNaN(d)) + return true; + else + return false; - this.width = 900; - this.searchBar = false; - this.tbar = false; - this.btnResultVisible = false; +}; - /** For processing **/ - this.processed = {}; - this.renderedPlotIndex = 0; +/** + There are several different methods for calculating quartiles.[1] This calculator uses a method described by Moore and McCabe to find quartile values. + The same method also used by The TI-83 to calculate quartile values. + With this method, the first quartile is the median of the numbers below the median, the third quartile is the median of the numbers above the median. - this.plotWidth = 210; - this.plotHeight = 80; + http://www.miniwebtool.com/quartile-calculator/ + http://www.alcula.com/calculators/statistics/box-plot/ - /** Colors **/ - this.validColor = BUI.getValidColor(); - this.warningColor = BUI.getWarningColor(); - this.notValidColor = BUI.getErrorColor(); + **/ +RangeWhiskerGraph.prototype.getQ1 = function(array) { + array = array.slice(0, array.length / 2); + return this.getMedian(array); +}; - /** Show warning if guinier quality less than **/ - this.guinierQuality = BUI.getQualityThreshold(); - this.framePercentageThreshold = BUI.getRadiationDamageThreshold(); +RangeWhiskerGraph.prototype.getQ3 = function(array) { + array = array.slice((array.length + 1) / 2); + return this.getMedian(array); - if (args != null) { - if (args.height != null) { - this.height = args.height; - this.maxHeight = this.height; - } - if (args.maxHeight != null) { - this.maxHeight = args.maxHeight; - } - if (args.width != null) { - this.width = args.width; - } - if (args.searchBar != null) { - this.searchBar = args.searchBar; +}; + +RangeWhiskerGraph.prototype.getQ2 = function(array) { + return this.getMedian(array); +}; + +RangeWhiskerGraph.prototype.getBelowOutliers = function(belowLimit, array) { + var points = []; + for ( var i = 0; i < array.length; i++) { + if (array[i] <= belowLimit) { + points.push(array[i]); } + } + return points; +}; - if (args.tbar != null) { - this.tbar = args.tbar; +RangeWhiskerGraph.prototype.getAboveOutliers = function(aboveLimit, array) { + var points = []; + for ( var i = 0; i < array.length; i++) { + if (array[i] >= aboveLimit) { + points.push(array[i]); } - if (args.btnResultVisible != null) { - this.btnResultVisible = args.btnResultVisible; + } + return points; +}; + +RangeWhiskerGraph.prototype.minValueWhisker = function(belowLimit, q1, array) { + var points = []; + + for ( var i = 0; i < array.length; i++) { + if ((array[i] < q1) && (array[i]) > belowLimit) { + points.push(array[i]); } + } + if (points.length > 0) { + points.sort(function(a, b) { + return a - b; + }); + return points[0]; } + return null; +}; - this.onClick = new Event(); -} +//RangeWhiskerGraph.prototype.maxValueWhisker = function(aboveLimit, q3, array){ +// var points = []; +// for (var i = 0; i < array.length; i++){ +// if ((array[i] > q3) && (array[i]) <= aboveLimit){ +// points.push(array[i]); +// } +// } +// if (points.length > 0){ +// points.sort(function(a, b){return a - b;}); +// return points[points.length - 1]; +// } +// return null; +//}; -ResultsAssemblyGrid.prototype.edit = function(macromoleculeId) { - var _this = this; - var window = new MacromoleculeWindow(); - window.onSuccess.attach(function(sender, proposal) { - _this.store.loadData(_this._prepareData(BIOSAXS.proposal.getMacromolecules())); - }); - window.draw(BIOSAXS.proposal.getMacromoleculeById(macromoleculeId)); +RangeWhiskerGraph.prototype.drawPoints = function(boxProperties) { + if (this.plotPoints) { + for ( var i = 0; i < boxProperties.values.length; i++) { + var value = boxProperties.values[i]; + var toPixel = this.pointToPixel(value, boxProperties); + SVG.drawCircle(boxProperties.x, toPixel, this.pointRadius, this.svg, + [ + [ "fill", "green" ], [ "fill-opacity", this.fillOpacityPoint ], [ 'stroke-opacity', this.strokeOpacityPoint ], + [ "stroke", "black" ] ]); + } + } }; -ResultsAssemblyGrid.prototype.getTbar = function() { - var _this = this; - var actions = []; +RangeWhiskerGraph.prototype.plotRangeQ1Q3 = function(data, properties) { + var posX = this.left + this.rulerWidth; - actions.push(Ext.create('Ext.Action', { - icon : '../images/add.png', - text : 'Add Macromolecule', - disabled : false, - handler : function(widget, event) { - var window = new MacromoleculeWindow(); - window.onSuccess.attach(function(sender) { - _this.refresh(); - }); - window.draw({}); - } - })); + var boxBordersPointsQ1 = []; + var boxBordersPointsQ3 = []; + var Q2 = []; - actions.push(Ext.create('Ext.Action', { - icon : '../images/add.png', - text : 'Define an Assembly', - disabled : false, - handler : function(widget, event) { - var createAssemblywindow = new CreateAssemblyWindow(); - createAssemblywindow.onSaved.attach(function(evt, args) { - if (args.macromoleculeIds.length > 0) { - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, proposal) { - _this.refresh(); - }); - adapter.saveAssembly(args.assemblyId, args.macromoleculeIds); + for ( var i = 0; i < data.clusters.length; i++) { + var cluster = data.clusters[i]; + /** inter cluster space **/ + posX = posX + this.interClustersSpace; - } + for ( var j = 0; j < cluster.classes.length; j++) { + var ratio = properties.width / (properties.xValues.range); + var coorX = (cluster.x - properties.xValues.min) * ratio + this.rulerWidth; + var boxProperties = { + name : cluster.classes[j].name, + values : cluster.classes[j].values, + minPoint : properties.minPoint, + maxPoint : properties.maxPoint, + x : coorX + this.left, + y : this.top, + width : properties.classWidth, + height : this.height - this.top - this.bottom - this.clusterTitleHeight - this.rulerHeight + }; - }); - createAssemblywindow.draw(_this.experiment); - } - })); + var result = this.calculate(boxProperties.values); + var boxColor = this.getClassColor(boxProperties.name); - return actions; -}; + if (this.isNumber(result.Q1) && this.isNumber(result.Q3)) { + var x = boxProperties.x; + var y = this.pointToPixel(result.Q1, boxProperties); + if (boxBordersPointsQ1[cluster.classes[j].name] == null) { + boxBordersPointsQ1[cluster.classes[j].name] = []; + } + boxBordersPointsQ1[cluster.classes[j].name].push({ + x : x, + y : y + }); -ResultsAssemblyGrid.prototype.refresh = function() { - this.store.loadData(this._prepareData()); -}; + x = boxProperties.x; + y = this.pointToPixel(result.Q3, boxProperties); + if (boxBordersPointsQ3[cluster.classes[j].name] == null) { + boxBordersPointsQ3[cluster.classes[j].name] = []; + } + boxBordersPointsQ3[cluster.classes[j].name].push({ + x : x, + y : y + }); + } else { -ResultsAssemblyGrid.prototype.addCondition = function(record, data_parsed, i) { - function getCondition(record) { - return { - concentration : record.conc, - quality : record.quality, - bufferBeforeFramesMerged : record.bufferBeforeFramesMerged, - bufferAfterFramesMerged : record.bufferAfterFramesMerged, - framesCount : record.framesCount, - framesMerge : record.framesMerge - }; - } + if (this.isNumber(result.Q2)) { - if (data_parsed[i].conditions != null) { - var bufferFound = false; - for ( var index in data_parsed[i].conditions) { - condition = data_parsed[i].conditions[index]; + if (boxBordersPointsQ1[cluster.classes[j].name] == null) { + boxBordersPointsQ1[cluster.classes[j].name] = []; + } - if ((condition.macromoleculeId == record.macromoleculeId) && (condition.bufferId == record.bufferId)) { - data_parsed[i].conditions[index].concentrations.push(getCondition(record)); - bufferFound = true; + if (boxBordersPointsQ3[cluster.classes[j].name] == null) { + boxBordersPointsQ3[cluster.classes[j].name] = []; + } + + boxBordersPointsQ1[cluster.classes[j].name].push({ + x : boxProperties.x, + y : this.pointToPixel(result.Q2, boxProperties) + }); + boxBordersPointsQ3[cluster.classes[j].name].push({ + x : boxProperties.x, + y : this.pointToPixel(result.Q2, boxProperties) + }); + } } } - if (!bufferFound) { - data_parsed[i].conditions.push({ - macromoleculeId : record.macromoleculeId, - bufferId : record.bufferId, - concentrations : [ getCondition(record) ] - }); + } + for ( var classe in boxBordersPointsQ1) { + var points = boxBordersPointsQ1[classe]; + var pointsSVG = ""; + for (var k = 0; k < points.length; k++) { + pointsSVG = Number(points[k].x).toFixed(1) + "," + Number(points[k].y).toFixed(1) + " " + pointsSVG; + } + + points = boxBordersPointsQ3[classe]; + for (var z = points.length - 1; z >= 0; z--) { + pointsSVG = Number(points[z].x).toFixed(1) + "," + Number(points[z].y).toFixed(1) + " " + pointsSVG; } + + SVG.drawPoligon(pointsSVG, this.svg, [ + [ "fill", this.getClassColor(classe) ], [ "opacity", "0.3" ], [ "stroke", "black" ], [ "stroke-width", 1 ] ]); } }; -ResultsAssemblyGrid.prototype.process = function(record, data_parsed) { - if (this.processed[record.macromoleculeId] == null) { - this.processed[record.macromoleculeId] = true; - record.measurementCount = 1; - record.subtractionCount = 1; - record.averageCount = 1; - record.conditions = []; - data_parsed.push(record); - } +RangeWhiskerGraph.prototype.plotWhisters = function(data, properties) { + var colors = [ "yellow", "orange", "green" ]; - for ( var i = 0; i < data_parsed.length; i++) { - if (data_parsed[i].macromoleculeId == record.macromoleculeId) { - data_parsed[i].measurementCount = data_parsed[i].measurementCount + 1; + this.plotRangeQ1Q3(data, properties); + var Q2 = {}; - if (record.subtractionId != null) { - data_parsed[i].subtractionCount = data_parsed[i].subtractionCount + 1; - this.addCondition(record, data_parsed, i); - } + for ( var i = 0; i < data.clusters.length; i++) { + var cluster = data.clusters[i]; + for ( var j = 0; j < cluster.classes.length; j++) { + var ratio = properties.width / (properties.xValues.range); + var coorX = (cluster.x - properties.xValues.min) * ratio + this.left + this.rulerWidth - this.rulerStroke; + var boxProperties = { + name : cluster.classes[j].name, + values : cluster.classes[j].values, + minPoint : properties.minPoint, + maxPoint : properties.maxPoint, + x : coorX, + y : this.top, + width : properties.classWidth, + height : this.height - this.top - this.bottom - this.clusterTitleHeight - this.rulerHeight + }; - if (record.framesMerge != null) { - data_parsed[i].averageCount = data_parsed[i].averageCount + 1; - } + this.drawPoints(boxProperties); - if (record.timeStart != null) { - if (data_parsed[i].timeStart != null) { - if (moment(data_parsed[i].timeStart).format("X") > moment(record.timeStart).format("X")) { - data_parsed[i].timeStart = record.timeStart; - } + /** PLOTTING Q2 **/ + var result = this.calculate(boxProperties.values); + var boxColor = this.getClassColor(boxProperties.name); + if (this.isNumber(result.Q2)) { + if (Q2[boxProperties.name] != null) { + SVG.drawLine(boxProperties.x, this.pointToPixel(result.Q2, boxProperties), Q2[boxProperties.name].x, Q2[boxProperties.name].y, + this.svg, [ [ "stroke", boxColor ], [ "stroke-width", "2" ] ]); + } + Q2[boxProperties.name] = { + x : boxProperties.x, + y : this.pointToPixel(result.Q2, boxProperties) } } } } - return data_parsed; - }; -ResultsAssemblyGrid.prototype._prepareData = function(data) { - var data_parsed = []; - for ( var i = 0; i < data.length; i++) { - data_parsed = this.process(data[i], data_parsed); - } - this.data = data_parsed; - return data_parsed; +RangeWhiskerGraph.prototype.draw = function(targetId, data) { + //this.calculate(data); + this.targetId = targetId; + var properties = (this.getDimensions(data)); + this.svg = SVG.createSVGCanvas(document.getElementById(this.targetId), [ [ "width", this.width ], [ "height", this.height ] ]); + this.plotAxes(properties); + this.plotWhisters(data, properties); }; -/** Given an array of conditions it returns distinct(concentrations) order by concentration and hash map with number of ocurrences**/ -ResultsAssemblyGrid.prototype.parseConcentrations = function(val, conditions, differentConcentration, quality) { - var conditions = []; - var differentConcentration = {}; - var quality = []; - for ( var i = 0; i < val.length; i++) { - var conc = Number(val[i].concentration).toFixed(1); - var quality = Number(val[i].quality).toFixed(2); - if (differentConcentration[conc] == null) { - differentConcentration[conc] = 0; - conditions.push({ - concentration : conc, - quality : [ quality ], - frames : [ { - bufferAfterFramesMerged : val[i].bufferAfterFramesMerged, - bufferBeforeFramesMerged : val[i].bufferBeforeFramesMerged, - framesMerge : val[i].framesMerge, - framesCount : val[i].framesCount - } ] - }); - } else { - /** Add quality **/ - for ( var j = 0; j < conditions.length; j++) { - if (conditions[j].concentration == conc) { - conditions[j].quality.push(quality); - conditions[j].frames.push({ - bufferAfterFramesMerged : val[i].bufferAfterFramesMerged, - bufferBeforeFramesMerged : val[i].bufferBeforeFramesMerged, - framesMerge : val[i].framesMerge, - framesCount : val[i].framesCount - }); - } - } - } +RangeWhiskerGraph.prototype.input = function() { + return DATADOC.getBoxWhikerData(); +}; - differentConcentration[conc] = differentConcentration[conc] + 1; - } - /** sorting concentrations **/ - conditions.sort(function(a, b) { - return a.concentration - b.concentration; +RangeWhiskerGraph.prototype.test = function(targetId) { + var plot = new RangeWhiskerGraph({ + targetId : targetId, + height : 350, + width : 450, + maxBoxWidth : 25 }); - return { - concentrations : conditions, - differentConcentration : differentConcentration - }; + plot.refresh(this.input()); }; + +StdDevDyGraph.prototype.dblclick = DygraphWidget.prototype.dblclick; +StdDevDyGraph.prototype._createHTLMWrapper = DygraphWidget.prototype._createHTLMWrapper; +StdDevDyGraph.prototype.draw = DygraphWidget.prototype.draw; -ResultsAssemblyGrid.prototype.getConditionWarnings = function(condition) { - - var withWarnings = 0; - - for ( var i = 0; i < condition.frames.length; i++) { - if (condition.quality[i] == null) { - withWarnings = withWarnings + 1; - continue; - } else { - if (Number(condition.quality[i]) < this.guinierQuality) { - withWarnings = withWarnings + 1; - continue; - } - } - - if (condition.frames[i].bufferBeforeFramesMerged / condition.frames[i].framesCount < this.framePercentageThreshold|| condition.frames[i].framesMerged / condition.frames[i].framesCount < this.framePercentageThreshold|| condition.frames[i].bufferAfterFramesMerged / condition.frames[i].framesCount < this.framePercentageThreshold) { - withWarnings = withWarnings + 1; - continue; - } +function StdDevDyGraph(targetId, args) { + this.scaled = false; + if (args == null) { + args = {}; } + args.customBars = true; + DygraphWidget.prototype.constructor.call(this, targetId, args); +} +StdDevDyGraph.prototype.input = function () { return { - withWarnings : withWarnings, - withoutWarnings : condition.frames.length - withWarnings + data : [ [ 1, [ 2, 3, 3.5 ], [ 4, 4.2, 5 ] ], [ 2, [ 5, 5.5, 5.7 ], [ 4, 4.2, 5 ] ] ], + colors : [ "blue", "red" ], + labels : [ "", 'data1', 'data2' ] }; }; -ResultsAssemblyGrid.prototype.getFrameHTMLTable = function(warnings) { - var html = ""; - if (warnings.withWarnings > 0) { - html = html + "
" + warnings.withWarnings + - "x
"; - } - - if (warnings.withoutWarnings > 0) { - html = html + "
" + warnings.withoutWarnings + - "x
"; - } - return html; -}; +StdDevDyGraph.prototype.test = function (targetId) { + var dygraphObject = new StdDevDyGraph(targetId, { + width : 500, + height : 400, + xlabel : "xLabel", + showRangeSelector : false + }); -ResultsAssemblyGrid.prototype.createConcentrationRow = function(numberOcu, condition, warnings){ - var html = ""; - if (numberOcu > 1){ - html = html + "" +numberOcu + "x " + BUI.formatConcentration(condition.concentration); - } - else{ - html = html + BUI.formatConcentration(condition.concentration); - } - html = html + ""; - html = html + this.getFrameHTMLTable(warnings); + ""; - html = html + ""; - return html; + dygraphObject.draw(dygraphObject.input().data, dygraphObject.input().colors, dygraphObject.input().labels); }; + + + +function AbinitioGrid(args) { + this.height = null; + this.width = null; + this.id = BUI.id(); + + if (args != null) { + if (args.height != null) { + this.height = args.height; + + if (args.width != null) { + this.width = args.width; + } + } + } + + this.onSelected = new Event(this); +}; + + +AbinitioGrid.prototype.refresh = function(subtractions){ + this.store.loadData(this._prepareData(subtractions)); +}; + +AbinitioGrid.prototype._prepareData = function(subtractions){ + /** Parsing data * */ + var models = []; + for (var l = 0; l < subtractions.length; l++) { + var subtraction = subtractions[l]; + for (var k = 0; k < subtraction.substractionToAbInitioModel3VOs.length; k++) { + var data = subtraction.substractionToAbInitioModel3VOs[k].abinitiomodel3VO; + if (data.averagedModel != null) { + models.push(data.averagedModel); + models[models.length - 1].type = "Reference"; + } + + if (data.shapeDeterminationModel != null) { + models.push(data.shapeDeterminationModel); + models[models.length - 1].type = "Refined"; + } + + if (data.modelList3VO != null) { + if (data.modelList3VO.modeltolist3VOs != null) { + for (var i = 0; i < data.modelList3VO.modeltolist3VOs.length; i++) { + models.push(data.modelList3VO.modeltolist3VOs[i].model3VO); + models[models.length - 1].type = "Model"; + } + } + } + } + } + return models; +}; + +AbinitioGrid.prototype.getPanel = function(){ + var _this = this; + + + var modelFields = [ "modelId", "type", "chiSqrt", "dmax", "firFile", "logFile", "fitFile", "pdbFile", "rfactor", "rg", "volume" ]; + Ext.define('AbinitioModel', { + extend : 'Ext.data.Model', + fields : modelFields + + }); + + /** + * Store in Memory + */ + this.store = Ext.create('Ext.data.Store', { + model : 'AbinitioModel', + autoload : true, + groupField : 'type' + }); + + + var groupingFeature = Ext.create('Ext.grid.feature.Grouping',{ + groupHeaderTpl: '{name} ({rows.length} model{[values.rows.length > 1 ? "s" : ""]})', + startCollapsed: true, + collapsible : true + }); + + this.grid = Ext.create('Ext.grid.Panel', { + collapsible : false, + resizable : true, + features: [groupingFeature], + autoscroll : true, + multiSelect : true, + store : this.store, + height : this.height, + width : this.width, + margin : 10, + columns : [ { + text : "Type", + dataindex : "type", + hidden : true, + renderer : function(a, b, record) { + return record.data.type; + }, + flex : 1 + }, + { + text : "ModelId", + dataindex : "modelId", + hidden : true, + renderer : function(a, b, record) { + return record.data.modelId; + + }, + flex : 1 + }, + + { + text : "chiSqrt", + dataindex : "chiSqrt", + renderer : function(a, b, record) { + if (record.data.dmax != null) { + return BUI.formatValuesUnits(record.data.chiSqrt, "", 12, this.decimals); + } + + }, + flex : 1 + }, + { + text : "Dmax", + dataindex : "dmax", + renderer : function(a, b, record) { + if (record.data.dmax != null) { + return BUI.formatValuesUnits(record.data.dmax, "nm", 12, this.decimals); + } + + }, + flex : 1 + }, { + text : "rFactor", + dataindex : "rfactor", + hidden : true, + renderer : function(a, b, record) { + if (record.data.rfactor != null) { + return record.data.rfactor; + } + }, + flex : 1 + }, { + text : "Rg", + dataindex : "rg", + renderer : function(a, b, record) { + if (record.data.rg != null) { + return BUI.formatValuesUnits(record.data.rg, "nm", 12, this.decimals); + } + + }, + flex : 1 + }, + { + text : "Volume", + dataindex : "volume", + renderer : function(a, b, record) { + if (record.raw.volume != null){ + return BUI.formatValuesUnits(record.raw.volume, '') + " nm3"; + } + }, + flex : 1 + }, + { + text : "PDB", + dataindex : "pdbFile", + renderer : function(a, b, record) { + if (record.data.pdbFile != null){ + return record.data.pdbFile.split("/")[record.data.pdbFile.split("/").length - 1]; + } + }, + flex : 1 + }, { + text : "Fir", + dataindex : "firFile", + renderer : function(a, b, record) { + if (record.data.firFile != null){ + return record.data.firFile.split("/")[record.data.firFile.split("/").length - 1]; + } + }, + flex : 1 + }, { + text : "LOG", + dataindex : "logFile", + hidden : true, + renderer : function(a, b, record) { + if (record.data.logFile != null){ + return record.data.logFile.split("/")[record.data.logFile.split("/").length - 1]; + } + }, + flex : 1 + } + ], + viewConfig : { + enableTextSelection : true, + preserveScrollOnRefresh : true, + stripeRows : true, + listeners : { + 'celldblclick' : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + }, + 'cellclick' : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + var models = []; + for (var i = 0; i < grid.getSelectionModel().selected.items.length; i++) { + models.push(grid.getSelectionModel().selected.items[i].raw); + } + _this.onSelected.notify(models); + } + } + } + }); + return this.grid; + +}; + +function AdditiveGrid(args) { + this.onRemoveButtonClicked = new Event(this); +} -ResultsAssemblyGrid.prototype.getConditionHTMLTable = function(val, style, record) { - var maxNumberColumns = 2; - var html = "
"; - var nColumns = 0; - for ( var r = 0; r < val.length; r++) { - if (nColumns == maxNumberColumns) { - nColumns = 0; - html = html + ""; - } - html = html + ""; - nColumns = nColumns + 1; - } - html = html + "
"; - var value = val[r]; - - var bufferAcronym = (BIOSAXS.proposal.getBufferById(value.bufferId).acronym); - - var parsed = (this.parseConcentrations(value.concentrations)); - var conditions = parsed.concentrations; - var differentConcentration = parsed.differentConcentration; - - /** Checking warnings **/ - var warnings = []; - var concentrationValidPerconcentration = 0; - var measurements = 0; - for ( var i = 0; i < conditions.length; i++) { - measurements = measurements + conditions[i].frames.length; - var warning = this.getConditionWarnings(conditions[i]); - warnings.push(warning); - if (warning.withoutWarnings > 0) { - concentrationValidPerconcentration = concentrationValidPerconcentration + 1; - } - } - - this.validColor = '#E0F8E0'; - this.warningColor = '#F5DA81'; - this.notValidColor = '#F6CED8'; - - var color = this.warningColor; - if (concentrationValidPerconcentration > 2) { - color = this.validColor; - } - /** More measurement need to be done **/ - if (measurements < 3) { - color = this.notValidColor; - } - - html = html + ""; - html = html + ""; - for ( var i = 0; i < conditions.length; i++) { - html = html + this.createConcentrationRow(differentConcentration[conditions[i].concentration], conditions[i], warnings[i]); - } - - html = html + ""; - html = html + "
" + bufferAcronym.toUpperCase() + - "
"; - - html = html + "
"; - return html; +AdditiveGrid.prototype.getBuffer = function() { + return this.buffer; }; -ResultsAssemblyGrid.prototype._getTbar = function() { - function goTo(url) { - window.location = url; - } - +AdditiveGrid.prototype._getActions = function() { var _this = this; - return [ { - icon : '../images/application_view_list.png', - text : 'Multiple Select', - handler : function() { - var specimenSelectorResultGrid = new SpecimenSelectorResultGrid(); - var window = Ext.create('Ext.window.Window', { - title : 'Multiple select', - height : 600, - width : 600, - layout : 'fit', - items : [ specimenSelectorResultGrid.getPanel(_this.data) ], - buttons : [ { - text : 'Go', - handler : function() { - var array = []; - for ( var i = 0; i < specimenSelectorResultGrid.selected.length; i++) { - var row = specimenSelectorResultGrid.selected[i]; - array.push({ - macromoleculeId : row.macromoleculeId, - bufferId : row.bufferId - }); - } - goTo(BUI.getMacromoleculeResultsURLByMultipleSearch(array)); + /** Actions buttons **/ + var actions = []; - } - }, { - text : 'Cancel', - handler : function() { - window.close(); - } - } ] + actions.push(Ext.create('Ext.Action', { + icon : '../images/add.png', + text : 'Add', + disabled : true, + alwaysEnabled : true, + handler : function(widget, event) { + _this.buffer.bufferhasadditive3VOs.push(BIOSAXS_BEANS.getBufferhasAdditive3VO()); + _this.refresh(_this.buffer, _this.experiment); + } + })); - }).show(); + return actions; +}; +AdditiveGrid.prototype.refresh = function(buffer, experiment) { + this.buffer = buffer; + this.experiment = experiment; + if (buffer) { + if (buffer.bufferhasadditive3VOs) { + this.features = buffer.bufferhasadditive3VOs; } - } ]; + } + this.experiment = experiment; + this.store.loadData(this._prepareData(), false); }; -ResultsAssemblyGrid.prototype.getLegendPanel = function() { - return { - html : '
' + BUI.getRectangleColorDIV(this.validColor, 10, 10) + - 'Good quality measurements' + - BUI.getRectangleColorDIV(this.warningColor, 10, 10) + - 'Probably valid with manual processing' + - BUI.getRectangleColorDIV(this.notValidColor, 10, 10) + - 'More measurements need to be done
' - }; -}; -/** Returns the grid **/ -ResultsAssemblyGrid.prototype.getPanel = function(macromolecules) { - var _this = this; +AdditiveGrid.prototype.getAdditives = function() { + var additives = []; + for ( var i = 0; i < this.store.getCount(); i++) { + var bufferHasAdditive = BIOSAXS_BEANS.getBufferhasAdditive3VO(); + var data = this.store.getAt(i).getData(); - this.store = Ext.create('Ext.data.Store', { - fields : [ - 'macromoleculeId', 'macromoleculeAcronym', 'measurementCount', 'subtractionCount', 'averageCount', 'timeStart', 'concentrationArray', - 'bufferConditions', 'conditions' ], - data : this._prepareData(macromolecules) - }); + bufferHasAdditive.additive3VO.name = data.name; + bufferHasAdditive.additive3VO.comments = data.comments; + bufferHasAdditive.additive3VO.additiveType = data.additiveType; - this.store.sort('macromoleculeAcronym'); + bufferHasAdditive.bufferId = this.buffer.bufferId; + bufferHasAdditive.bufferHasAdditiveId = data.bufferHasAdditiveId; + bufferHasAdditive.quantity = data.quantity; + additives.push(bufferHasAdditive); + } - this.grid = Ext.create('Ext.grid.Panel', { - id : this.id, - title : 'Macromolecules', - store : this.store, - tbar : this._getTbar(), - bbar : [ this.getLegendPanel() ], - width : this.width, - height : this.height, - maxHeight : this.maxHeight, - sortableColumns : true, - columns : [ - { - text : '', - dataIndex : 'macromoleculeId', - width : 20, - hidden : false, - renderer : function(val, y, sample) { - return BUI.getRectangleColorDIV(BIOSAXS.proposal.macromoleculeColors[val], 10, 10); - } - }, - { - text : 'Macromolecule', - dataIndex : 'macromoleculeAcronym', - width : 200 + return additives; +}; + +AdditiveGrid.prototype._getFields = function(buffers) { + var columns = [ + + { + header : 'Name', + dataIndex : 'name', + type : 'string', + editor : { + allowBlank : true }, - { - text : 'Buffer Conditions', - dataIndex : 'conditions', - flex : 1, - renderer : function(val, style, record) { - return _this.getConditionHTMLTable(val, style, record); - } + flex : 1 + }, + { + header : 'Type', + name : 'additiveType', + dataIndex : 'additiveType', + editor : { + xtype : 'combobox', + typeAhead : true, + triggerAction : 'all', + selectOnTab : true, + store : BIOSAXS.proposal.getAdditiveTypes(), + lazyRender : true, + listClass : 'x-combo-list-small' }, - { - header : 'Average', - dataIndex : 'percentageCollected', - name : 'percentageCollected', - type : 'string', - hidden : true, - renderer : function(val, y, sample) { - return "
" + - BUI.getProgessBar((sample.raw.averageCount / sample.raw.measurementCount) * 100, sample.raw.averageCount + "/" + - sample.raw.measurementCount) + "
"; - }, - width : 100, - sorter : false + flex : 0.6 + }, + { + header : 'Quantity', + dataIndex : 'quantity', + name : 'quantity', + editor : { + allowBlank : true }, - { - header : 'Subtractions', - dataIndex : 'percentageCollected', - name : 'percentageCollected', - type : 'string', - hidden : true, - renderer : function(val, y, sample) { - return "
"+ BUI.getProgessBar((sample.raw.subtractionCount / sample.raw.measurementCount) * 100, sample.raw.subtractionCount + "/" + - sample.raw.measurementCount) + "
"; - }, - width : 100 - }, { - header : 'Date', - dataIndex : 'timeStart', - name : 'timeStart', - type : 'string', - renderer : function(value, metaData, record, rowIndex, colIndex, store) { - if (record.raw.timeStart != null) { - return moment(record.raw.timeStart).format("MMM Do YY"); + type : 'string', + flex : 1 + }, + { + xtype : 'actioncolumn', + items : [ { + icon : '../images/cancel.png', + tooltip : 'Delete additive', + scope : this, + handler : function(grid, rowIndex, colIndex) { + if ((grid.getStore().getAt(rowIndex).data.bufferHasAdditiveId == null)|| (grid.getStore().getAt(rowIndex).data.bufferHasAdditiveId == "")) { + grid.getStore().removeAt(rowIndex); + } else { + this.onRemoveButtonClicked.notify({ + 'bufferId' : this.buffer.bufferId, + 'bufferHasAdditiveId' : this.store.getAt(rowIndex).data.bufferHasAdditiveId + }); } } - }, { - header : 'Download', - dataIndex : 'percentageCollected', - name : 'percentageCollected', - type : 'string', - renderer : function(val, y, sample) { - return BUI.getZipHTMLByMacromoleculeId(sample.raw.macromoleculeId); - }, - width : 100 - }, + } ] + } ]; + + return columns; +}; + +AdditiveGrid.prototype._prepareData = function() { + var data = []; + if (this.features == null) { + this.features = []; + } + for (var i = 0; i < this.features.length; i++) { + var object = this.features[i]; + object.name = this.features[i].additive3VO.name; + object.additiveType = this.features[i].additive3VO.additiveType; + object.comments = this.features[i].additive3VO.comments; + object.additiveId = this.features[i].additive3VO.additiveId; + data.push(object); + } + return data; +}; - { - id : 'btnResultVisible', - width : 85, - sortable : false, - renderer : function(value, metaData, record, rowIndex, colIndex, store) { - return BUI.getGreenButton('GO'); - } - } ], +AdditiveGrid.prototype.getPanel = function(buffer, experiment) { + this.buffer = buffer; + this.features = buffer.bufferhasadditive3VOs; + this.experiment = experiment; + return this._renderGrid(); +}; + +AdditiveGrid.prototype.getStore = function() { + var _this = this; + var store = Ext.create('Ext.data.Store', { + fields : [ "name", "additiveType", "comments", "additiveId", "bufferHasAdditiveId", "quantity" ], + autoload : false, + data : this._prepareData(), + listeners : { + update : function(store, record) { + record.index = _this.grid.getSelectionModel().getCurrentPosition().row; + _this.buffer.bufferhasadditive3VOs[record.index].additive3VO.name = record.data.name; + _this.buffer.bufferhasadditive3VOs[record.index].additive3VO.additiveType = record.data.additiveType; + _this.buffer.bufferhasadditive3VOs[record.index].additive3VO.comments = record.data.comments; + _this.buffer.bufferhasadditive3VOs[record.index].quantity = record.data.quantity; + } + } + }); + + // store.sort('bufferHasAdditiveId', 'ASC'); + store.loadData(this._prepareData(), false); + return store; +}; + +AdditiveGrid.prototype._renderGrid = function() { + var _this = this; + this.store = this.getStore(); + + var cellEditing = Ext.create('Ext.grid.plugin.CellEditing', { + clicksToEdit : 1 + }); + + this.grid = Ext.create('Ext.grid.Panel', { + dockedItems : [ { + xtype : 'toolbar', + items : this._getActions() + } ], + store : this.store, + height : 230, + title : "Additives", + width : "100%", + columns : _this._getFields(), + plugins : [ cellEditing ], viewConfig : { stripeRows : true, listeners : { - afterrender : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { - }, - celldblclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { - _this.edit(record.data.macromoleculeId); - }, - cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { - if (grid.getGridColumns()[cellIndex].getId() == 'buttonEditMacromolecule') { - _this.edit(record.data.macromoleculeId); - } - if (grid.getGridColumns()[cellIndex].getId() == 'buttonRemoveMacromolecule') { - BUI.showBetaWarning(); - } - if (grid.getGridColumns()[cellIndex].getId() == 'btnResultVisible') { - window.location = BUI.getMacromoleculeResultsURL(record.data.macromoleculeId); - } + itemcontextmenu : function(view, rec, node, index, e) { + e.stopEvent(); + contextMenu.showAt(e.getXY()); + return false; } } + }, + selModel : { + mode : 'SINGLE' } }); - /** Adding the tbar **/ - if (this.tbar) { - this.grid.addDocked({ - xtype : 'toolbar', - items : this.getTbar() - }); - } return this.grid; }; -ResultsAssemblyGrid.prototype.input = function(targetId) { - return { - data : DATADOC.getData_3367(), - proposal : DATADOC.getProposal_3367() - }; +AdditiveGrid.prototype.input = function() { }; -ResultsAssemblyGrid.prototype.test = function(targetId) { - var grid = new ResultsAssemblyGrid({ - height : 600, - searchBar : false, - tbar : false, - btnResultVisible : true, - width : 1000 - }); - BIOSAXS.proposal = new Proposal(grid.input().proposal); - var panel = grid.getPanel(grid.input().data); +AdditiveGrid.prototype.test = function(targetId) { + var grid = new AdditiveGrid(); + var panel = grid.getPanel([]); panel.render(targetId); }; +/** AnalysisGrid **/ +function AnalysisGrid(args) { + var _this = this; -function RigidModelGrid(args) { - this.height = null; - this.width = null; this.id = BUI.id(); + if (Ext.get("mainPanel")){ + this.width = Ext.get("mainPanel").getWidth(); + } + else{ + this.width = this.maxWidth; + } + this.maxWidth = 1500; + + /** Visibles **/ + this.isGuinierTabVisible = true; + this.isGnomTabVisible = true; + this.isPorodTabVisible = true; + this.isScatteringPlotVisible = true; + this.isAbinitioTabVisible = true; + this.showButtonsVisible = true; + this.isI0Visible = true; + + this.margin = null; + this.grouped = true; + + this.hideNulls = false; + this.decimals = 4; + this.height = null; + this.sorters = [ { + property : 'conc', + direction : 'ASC' + } ]; if (args != null) { + if (args.grouped != null) { + this.grouped = args.grouped; + } + + if (args.showButtonsVisible != null) { + this.showButtonsVisible = args.showButtonsVisible; + } + if (args.isI0Visible != null) { + this.isI0Visible = args.isI0Visible; + } + if (args.sorters != null) { + this.sorters = args.sorters; + } + if (args.isScatteringPlotVisible != null) { + this.isScatteringPlotVisible = args.isScatteringPlotVisible; + } + if (args.isGuinierTabVisible != null) { + this.isGuinierTabVisible = args.isGuinierTabVisible; + } + if (args.isGnomTabVisible != null) { + this.isGnomTabVisible = args.isGnomTabVisible; + } + if (args.isPorodTabVisible != null) { + this.isPorodTabVisible = args.isPorodTabVisible; + } + if (args.hideNulls != null) { + this.hideNulls = args.hideNulls; + } if (args.height != null) { this.height = args.height; + } + if (args.width != null) { + this.width = args.width; + } + if (args.maxWidth != null) { + this.maxWidth = args.maxWidth; + } + } +} - if (args.width != null) { - this.width = args.width; +AnalysisGrid.prototype.refresh = function(data, args) { + this.store.loadData(this._prepareData(data), false); + if (args != null){ + if (args.experiment != null){ + this.experiment = args.experiment; + } + } +}; + +AnalysisGrid.prototype._getPorod = function() { + return { + text : 'Porod', + name : 'Porod', + columns : [ + { + text : 'Volume', + dataIndex : 'volumeEdna', + width : 75, + sortable : true, + hidden : !this.isPorodTabVisible, + renderer : function(val, y, sample) { + if (sample.raw.volume != null) + return BUI.formatValuesUnits(sample.raw.volume, '') + " nm3"; + } + }, + { + text : 'MM Vol. est.', + dataIndex : 'volumeEdna', + tooltip : '[Volume/2 - Volume/1.5] (Guinier)', + sortable : true, + width : 95, + hidden : !this.isPorodTabVisible, + renderer : function(val, y, sample) { + if (sample.raw.volume != null) + return Number(sample.raw.volume / 2).toFixed(1) + " - " + Number(sample.raw.volume / 1.5).toFixed(1)+ "kD"; + } + } ] + }; +}; + + +AnalysisGrid.prototype._getFramesColumn = function() { + var _this = this; + return { + text : 'Frames (Averaged/Total)', + dataIndex : 'datacollection', + name : 'datacollection', + sortable : true, + width : 150, + renderer : function(dataCollections, y, data) { + /** Bug of Webservices: frames count were swapped with frames averages **/ + if (data.raw.bufferAfterFramesCount){ + if (data.raw.bufferAfterFramesMerged){ + if (parseInt(data.raw.bufferAfterFramesMerged) > parseInt(data.raw.bufferAfterFramesCount)){ + var aux = parseInt(data.raw.bufferAfterFramesCount); + data.raw.bufferAfterFramesCount= data.raw.bufferAfterFramesMerged; + data.raw.bufferAfterFramesMerged = aux; + } + } + } + + if (data.raw.bufferBeforeFramesCount){ + if (data.raw.bufferBeforeFramesMerged){ + if (parseInt(data.raw.bufferBeforeFramesMerged) > parseInt(data.raw.bufferBeforeFramesCount)){ + var aux = parseInt(data.raw.bufferBeforeFramesCount); + data.raw.bufferBeforeFramesCount= data.raw.bufferBeforeFramesMerged; + data.raw.bufferBeforeFramesMerged = aux; + } + } + + } + + var bufferAcronym = data.raw.bufferAcronym; + var macromoleculeAcronym = data.raw.macromoleculeAcronym; + var bbmerges = data.raw.bufferBeforeFramesMerged; + var molmerges = data.raw.framesMerge; + var bamerges = data.raw.bufferAfterFramesMerged; + var totalframes = data.raw.framesCount; + var bufferId = data.raw.bufferId; + var macromoleculeId = data.raw.macromoleculeId; + var macromoleculeColor = null; + if (_this.experiment != null){ + macromoleculeColor = _this.experiment.macromoleculeColors[data.raw.macromoleculeId]; } - } - } - - this.onSelected = new Event(this); -} - - -RigidModelGrid.prototype.refresh = function(subtractions) { - var data = []; - if (subtractions != null){ - if (subtractions.length > 0){ - for (j in subtractions){ - var subtraction = subtractions[j]; - if (subtraction.rigidBodyModeling3VOs != null){ - for (i in subtraction.rigidBodyModeling3VOs){ - data.push(subtraction.rigidBodyModeling3VOs[i]); + + /** BUG in the database to be fixed **/ + try{ + if (totalframes != null){ + if(molmerges != null){ + if (parseFloat(totalframes) < parseFloat(molmerges)){ + var aux = totalframes; + totalframes = molmerges; + molmerges = aux; + } } } } + catch(e){ + + } + + return BUI.getHTMLTableForFrameAveraged(bufferAcronym, + macromoleculeAcronym, + bbmerges, + molmerges, + bamerges, + totalframes, + bufferId, + macromoleculeId, + macromoleculeColor); } - } - this.store.loadData(data); + }; }; -RigidModelGrid.prototype.getPanel = function() { +AnalysisGrid.prototype._getColumns = function() { var _this = this; - this.store = Ext.create('Ext.data.Store', { - fields : [ 'symmetry', 'subtractionId', 'subUnitConfigFilePath', 'rigidBodyModelingId', 'rigidBodyModelFilePath', 'logFilePath', 'fitFilePath', 'curveConfigFilePath', - 'crossCorrConfigFilePath', 'contactDescriptionFilePath' ], - data : [] - }); - - this.store.sort([ { - property : 'name', - direction : 'ASC' - } ]); + return [ + { + name : 'groupeField', + dataIndex : 'groupeField', + hidden : true - this.panel = Ext.create('Ext.grid.Panel', { - store : this.store, - width : this.width, - height : this.height, - margin : 10, - deferredRender : false, - columns : [ { - text : 'RBM', - dataIndex : 'rigidBodyModelFilePath', - hidden : false, - flex : 1, - renderer : function(val){ - return BUI.getFileName(val); - } - }, { - text : 'Sub Unit Conf.', - dataIndex : 'subUnitConfigFilePath', + }, + { + "header" : "subtractionId", hidden : true, - flex : 1, - renderer : function(val){ - return BUI.getFileName(val); + "dataIndex" : "subtractionId", + "name" : "subtractionId" + }, + { + header : "Macromolecule", + dataIndex : "macromoleculeAcronym", + name : "macromoleculeAcronym", + renderer : function(val, y, sample) { + return '
' + val + '
' + + BUI.formatValuesUnits(sample.raw.conc, "mg/ml", 10, this.decimals) + '
' + + BUI.formatValuesUnits(sample.raw.exposureTemperature, "C", 10, this.decimals) + '
' } - }, { - text : 'Log', - dataIndex : 'logFilePath', + }, + { + header : "Order", + dataIndex : "priorityLevelId", + name : "priorityLevelId", + hidden : true + }, + { + header : "Code", + dataIndex : "code", + name : "code", + hidden : true + }, + { + header : "Conc.", + dataIndex : "conc", + width : 80, + name : "conc", + type : "number", hidden : true, - flex : 1, - renderer : function(val){ - return BUI.getFileName(val); - } - }, { - text : 'Fit', - dataIndex : 'fitFilePath', - hidden : false, - flex : 1, - renderer : function(val){ - return BUI.getFileName(val); - } - }, { - text : 'Curve Conf.', - dataIndex : 'curveConfigFilePath', - hidden : false, - flex : 1, - renderer : function(val){ - return BUI.getFileName(val); + renderer : function(val, y, sample) { + return BUI.formatValuesUnits(val, "mg/ml", 10, this.decimals); } - }, { - text : 'Cross Corr.', - dataIndex : 'crossCorrConfigFilePath', - hidden : false, - flex : 1, - renderer : function(val){ - return BUI.getFileName(val); + }, + { + text : 'Scattering', + width : 100, + dataIndex : 'subtractionId', + name : 'subtractionId', + hidden : !this.isScatteringPlotVisible, + renderer : function(val, y, sample) { + var url = BUI.getURL() + '&type=scattering&subtractionId=' + sample.raw.subtractionId; + var event = "OnClick= window.open('" + url + "')"; + return ''; } - }, { - text : 'Contact Desc.', - dataIndex : 'contactDescriptionFilePath', + }, + { + text : 'Kratky', + width : 100, + dataIndex : 'subtractionId', hidden : true, - flex : 1, - renderer : function(val){ - return BUI.getFileName(val); + name : 'subtractionId', + renderer : function(val, y, sample) { + var url = BUI.getURL() + '&type=kratky&subtractionId=' + sample.raw.subtractionId; + var event = "OnClick= window.open('" + url + "')"; + return ''; } - } ], - - viewConfig : { - enableTextSelection : true, - preserveScrollOnRefresh : true }, - listeners : { - cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { - - }, - afterrender : function() { + this._getFramesColumn(), + { + text : 'File Name', + dataIndex : 'averageFilePath', + name : 'averageFilePath', + sortable : true, + width : 150, + hidden : true, + renderer : function(dataCollections, y, data) { + return BUI.getHTMLTableForPrefixes(data.raw.bufferBeforeAverageFilePath, data.raw.averageFilePath, + data.raw.bufferAfterAverageFilePath); } - } - }); - return this.panel; -}; + }, - -/** - * shows shipments - * - * @height - * @width - * @minHeight - * @btnEditVisible - */ -function ShipmentGrid(args) { - this.id = BUI.id(); - this.height = 100; - this.width = null; - this.minHeight = null; - this.btnEditVisible = true; + { + text : 'Guinier', + name : 'Guinier', + columns : [ + { + text : 'Rg', + dataIndex : 'rgGuinier', + name : 'rgGuinier', + hidden : !this.isGuinierTabVisible, + width : 75, + tooltip : 'In polymer physics, the radius of gyration is used to describe the dimensions of a polymer chain.', + sortable : true, + renderer : function(val, y, sample) { + if (sample.raw.rgGuinier != null) { + /** Show warning if rgGuinier and rgGnom differ more than 10% **/ + if (Math.abs(sample.raw.rgGuinier - sample.raw.rgGnom) > (sample.raw.rgGuinier * 0.1)) { + return "" + BUI.formatValuesUnits(sample.raw.rgGuinier, "") + ""; - if (args != null) { - if (args.height != null) { - this.height = args.height; - } - if (args.width != null) { - this.width = args.width; - } - if (args.minHeight != null) { - this.minHeight = args.minHeight; - } - if (args.btnEditVisible != null) { - this.btnEditVisible = args.btnEditVisible; - } - } -} + } + return BUI.formatValuesUnits(sample.raw.rgGuinier, "nm", 12, this.decimals); + } + } + }, + { + text : 'Points', + dataIndex : 'points', + sortable : true, + width : 100, + type : 'string', + hidden : !this.isGuinierTabVisible, + renderer : function(val, y, sample) { + if ((sample.raw.firstPointUsed == "") || (sample.raw.firstPointUsed == null)) + return; + return "" + sample.raw.firstPointUsed + " - " + sample.raw.lastPointUsed + " (" + + (sample.raw.lastPointUsed - sample.raw.firstPointUsed) + " )"; + } + }, + { + text : 'Quality', + dataIndex : 'quality', + hidden : !this.isGuinierTabVisible, + tooltip : 'Estimated data quality. 1.0 - means ideal quality, 0.0 - unusable data. In table format it is given in percent (100% - ideal quality, 0% - unusable data). Please note that this estimation is based only on the Guinier interval (very low angles).', + width : 60, + sortable : true, + renderer : function(val, y, sample) { + if (sample.raw.quality != null) { + val = sample.raw.quality; + if ((val != null) && (val != "")) { + return "" + (Number(val) * 100).toFixed(2) + " %"; + } + } + } + }, { + text : 'I(0)', + dataIndex : 'I0', + sortable : true, + hidden : !this.isI0Visible, + tooltip : 'Extrapolated scattering intensity at zero angle I(0) (forward scattering)', + width : 75, + type : 'string', + renderer : function(val, y, sample) { + if (sample.raw.I0 != null) { + return BUI.formatValuesErrorUnitsScientificFormat(sample.raw.I0, sample.raw.i0stdev, ""); + } + } + }, { + text : 'Aggregated', + tooltip : "If aggregation was detected from the slope of the data curve at low angles the value is '1', otherwise '0'.", + dataIndex : 'isagregated', + hidden : true, + width : 75, + renderer : function(val, y, sample) { + if ((sample.raw.isagregated != null)) { + if (val == true) { + return "Yes"; + } else { + return "No"; + } + } + } + }, { + text : 'Guinier', + sortable : true, + dataIndex : 'subtractionId', + type : 'string', + width : 100, + hidden : true, + renderer : function(val, y, sample) { -ShipmentGrid.prototype._getColumns = function() { - var _this = this; - var columns = [ - { - text : 'Name', - dataIndex : 'shippingName', - flex : 1 + if (sample.raw.subtractionId != null) { + var url = BUI.getURL() + '&type=guinier&subtractionId=' + sample.raw.subtractionId; + var event = "OnClick= window.open('" + url + "')"; + return ''; + } + } + } ] }, { - header : 'Type', - dataIndex : 'shippingType', - flex : 1, - hidden : true, - renderer : function(val, comp, record) { - if (val != null) { - return val.toUpperCase(); - } + text : 'Gnom', + name : 'Gnom', - } - }, - { - header : 'Status', - type : 'string', - flex : 1, - hidden : false, - renderer : function(comp, val, record) { - if (record.raw.shippingStatus != null) { - if (new String(record.raw.shippingStatus).toUpperCase() == 'OPENED') { - return "" + new String(record.raw.shippingStatus).toUpperCase() + ""; + columns : [ { + text : 'Rg', + dataIndex : 'rgGnom', + type : 'string', + width : 65, + hidden : !this.isGnomTabVisible, + sortable : true, + renderer : function(val, y, sample) { + /** Show warning if rgGuinier and rgGnom differ more than 10% **/ + if (sample.raw.rgGnom != null) { + if (Math.abs(sample.raw.rgGuinier - sample.raw.rgGnom) > (sample.raw.rgGuinier * 0.1)) { + return "" + BUI.formatValuesUnits(sample.raw.rgGnom, "") + ""; + + } + return BUI.formatValuesUnits(sample.raw.rgGnom, "nm"); } - return "" + new String(record.raw.shippingStatus).toUpperCase() + ""; } - } + }, { + text : 'Total', + dataIndex : 'total', + width : 65, + hidden : !this.isGnomTabVisible, + sortable : true, + renderer : function(val, y, sample) { + if (sample.raw.total != null) + return BUI.formatValuesUnits(sample.raw.total, ''); + } + }, { + text : 'Dmax', + dataIndex : 'dmax', + sortable : true, + width : 75, + hidden : !this.isGnomTabVisible, + renderer : function(val, y, sample) { + if (sample.raw.dmax != null) + return BUI.formatValuesUnits(sample.raw.dmax, "") + " nm"; + } + }, { + text : 'P(r)', + sortable : true, + hidden : true, + width : 100, + dataIndex : 'subtractionId', + type : 'string', + renderer : function(val, y, sample) { + var url = BUI.getURL() + '&type=gnom&subtractionId=' + sample.raw.subtractionId; + var event = "OnClick= window.open('" + url + "')"; + return ''; + } + } ] }, + this._getPorod(), { - text : 'Cases', - flex : 1, - hidden : false, - renderer : function(comp, val, record) { - var shipment = BIOSAXS.proposal.getShipmentById(record.data.shippingId); - var container = ""; - if (shipment.dewarVOs.length > 0) { - container = container + ""; - } else { - return "Empty"; - } - return container + "
" + shipment.dewarVOs.length + "x
"; - } - }, { - header : 'Comments', - dataIndex : 'comments', - flex : 1, - hidden : false - }, { - header : 'Creation Date', - dataIndex : 'creationDate', - hidden : true - } ]; - - if (this.btnEditVisible) { - columns.push({ - id : _this.id + 'buttonEdit', - text : '', - hidden : !_this.btnEditVisible, - width : 100, - sortable : false, - renderer : function(value, metaData, record, rowIndex, colIndex, store) { - return BUI.getGreenButton('EDIT'); - } - }); - } - - columns.push({ - id : _this.id + 'buttonRemove', - text : '', - width : 100, - sortable : false, - renderer : function(value, metaData, record, rowIndex, colIndex, store) { - var shipment = BIOSAXS.proposal.getShipmentById(record.data.shippingId); - if (shipment.dewarVOs.length == 0) { - return BUI.getRedButton('REMOVE'); - } - - } - }); - - return columns; -}; - -ShipmentGrid.prototype._getTopButtons = function() { - var _this = this; - /** Actions buttons **/ - var actions = []; - actions.push(Ext.create('Ext.Action', { - icon : '../images/add.png', - text : 'Add Shipment', - handler : function(widget, event) { - //window.location = BUI.getCreateShipmentURL(); - var _this = this; - - var shipmentForm = new ShipmentForm({ - creationMode : true, - showTitle : false - }); - shipmentForm.onSaved.attach(function(sender, shipment) { - _this.showShipmentTabs(shipment, targetId); - }); - - var window = Ext.create('Ext.window.Window', { - title : 'New Shipment', - height : 600, - width : 800, - layout : 'fit', - items : [ shipmentForm.getPanel() ] - }).show(); - - } - })); - return actions; -}; - -ShipmentGrid.prototype.refresh = function(shippings) { - this.features = shippings; - this.store.loadData(this._prepareData(), false); -}; - -ShipmentGrid.prototype._prepareData = function() { - var data = []; - for ( var i = 0; i < this.features.length; i++) { - data.push(this.features[i]); - } - return data; -}; - -ShipmentGrid.prototype.getPanel = function(shipments) { - this.features = shipments; - return this._renderGrid(); -}; - -ShipmentGrid.prototype.edit = function(shippingId) { - window.location = BUI.getShippingURL(shippingId); -}; - -ShipmentGrid.prototype._getStoreFields = function(data) { - var _this = this; - return [ { - name : 'shippingId' - }, { - name : 'shippingName' - }, { - name : 'shippingStatus' - }, { - name : 'shippingType' - }, { - name : 'creationDate' - }, { - name : 'comments' - } ]; -}; - -ShipmentGrid.prototype._renderGrid = function() { - var _this = this; - - /** Store **/ - var data = this._prepareData(); - this.store = Ext.create('Ext.data.Store', { - fields : this._getStoreFields(data), - autoload : true, - data : data - }); - - this.store.loadData(data, false); + text : 'AbInitio Modeling', + name : 'AbInitio_modeling', - this.grid = Ext.create('Ext.grid.Panel', { - title : "Shipping", - icon : '/ispyb/images/plane.gif', - width : this.width, - minWidth : this.minWidth, - height : this.height, - store : this.store, - columns : this._getColumns(), - viewConfig : { - stripeRows : true, - listeners : { - itemdblclick : function(dataview, record, item, e) { - _this.edit(record.raw.shippingId); + columns : [ + { + text : 'NSD', + dataIndex : 'volumeEdna', + width : 100, + sortable : true, + hidden : true, + renderer : function(val, y, sample) { + var url = BUI.getNSDImageURL(sample.raw.modelListId); + var event = "OnClick= window.open('" + url + "')"; + return ''; + } }, - cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { - if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonEdit') { - _this.edit(record.data.shippingId); + { + text : 'Chi2', + dataIndex : 'volumeEdna', + sortable : true, + hidden : true, + width : 100, + renderer : function(val, y, sample) { + var url = BUI.getCHI2ImageURL(sample.raw.modelListId); + var event = "OnClick= window.open('" + url + "')"; + return ''; } + }, + { + text : 'Pdb', + dataIndex : 'volumeEdna', + tooltip : 'Damaver: The program suite DAMAVER is a set of programs to align ab initio models, select the most typical one and build an averaged model. Dammif : Rapid ab initio shape determination by simulated annealing using a single phase dummy atom model. Dammin :Ab initio shape determination by simulated annealing using a single phase dummy atom model', + sortable : true, + width : 125, + hidden : !this.isAbinitioTabVisible, + renderer : function(val, y, sample) { + var html = new String(); + var split = null; + var url = null; + var file = sample.raw.averagedModel; + if (file != null) { + split = file.split("/"); + if (split.length > 0) { + url = BUI.getPdbURL() + '&type=AVERAGED&abInitioModelId=' + sample.raw.abInitioModelId; + html = html + ''+ split[split.length - 1] + '

'; - if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonRemove') { - var shipment = BIOSAXS.proposal.getShipmentById(record.data.shippingId); - if (shipment.dewarVOs.length == 0) { + } + } - var adapter = new BiosaxsDataAdapter(); - _this.grid.setLoading("ISPyB: Removing shipment"); - adapter.onSuccess.attach(function(sender) { - BIOSAXS.proposal.onInitialized.attach(function(sender) { - _this.refresh(BIOSAXS.proposal.getShipments()); - _this.grid.setLoading(false); - }); - BIOSAXS.proposal.init(); - }); + file = sample.raw.rapidShapeDeterminationModel; + if (file != null) { + split = file.split("/"); + if (split.length > 0) { + url = BUI.getPdbURL() + '&type=RAPIDSHAPEDETERMINATIONMODEL&abInitioModelId=' + sample.raw.abInitioModelId; + html = html + '' + split[split.length - 1] + '

'; + } + } - adapter.onError.attach(function(sender) { - alert("Error"); - _this.grid.setLoading(false); - }); - adapter.removeShipment(record.data.shippingId); + file = sample.raw.shapeDeterminationModel; + if (file != null) { + split = file.split("/"); + if (split.length > 0) { + url = BUI.getPdbURL() + '&type=SHAPEDETERMINATIONMODEL&abInitioModelId=' + sample.raw.abInitioModelId; + html = html + ''+ split[split.length - 1] + ''; + } } + return html; } - } - } - }, - selModel : { - mode : 'SINGLE' - } - }); - - var actions = _this._getTopButtons(); - this.grid.addDocked({ - xtype : 'toolbar', - items : actions - }); - - this.grid.getSelectionModel().on({ - selectionchange : function(sm, selections) { - if (selections.length) { - for ( var i = 0; i < actions.length; i++) { - if (actions[i].enable) { - actions[i].enable(); + }, + { + text : 'Damaver', + dataIndex : 'volumeEdna', + hidden : true, + tooltip : 'Damaver: The program suite DAMAVER is a set of programs to align ab initio models, select the most typical one and build an averaged model. ', + sortable : true, + width : 125, + renderer : function(val, y, sample) { + var file = sample.raw.averagedModel; + if (file != null) { + var split = file.split("/"); + if (split.length > 0) { + var url = BUI.getPdbURL() + '&type=AVERAGED&abInitioModelId=' + sample.raw.abInitioModelId; + return '' + split[split.length - 1]+ ''; + } + } + return sample.raw.averagedModel; } - } - } else { - for ( var i = 0; i < actions.length; i++) { - if (actions[i].alwaysEnabled == false) { - if (actions[i].disable) { - actions[i].disable(); + }, + { + text : 'Dammif', + dataIndex : 'volumeEdna', + hidden : true, + tooltip : 'Dammif : Rapid ab initio shape determination by simulated annealing using a single phase dummy atom model', + sortable : true, + width : 125, + renderer : function(val, y, sample) { + var file = sample.raw.rapidShapeDeterminationModel; + if (file != null) { + var split = file.split("/"); + if (split.length > 0) { + var url = BUI.getPdbURL() + '&type=RAPIDSHAPEDETERMINATIONMODEL&abInitioModelId=' + sample.raw.abInitioModelId; + return '' + split[split.length - 1] + ''; + } } + return sample.raw.averagedModel; } - } - } - } - }); - return this.grid; -}; - -ShipmentGrid.prototype.input = function() { - return { - proposal : new MeasurementGrid().input().proposal, - shippings : DATADOC.getShippings_10() - }; -}; - -ShipmentGrid.prototype.test = function(targetId) { - var shipmentGrid = new ShipmentGrid({ - minHeight : 300, - height : 440 - }); - BIOSAXS.proposal = new Proposal(shipmentGrid.input().proposal); - var panel = shipmentGrid.getPanel(targetId); - panel.render(targetId); - shipmentGrid.refresh(shipmentGrid.input().shippings); -}; - -function SpecimenGrid(args) { - this.id = BUI.id(); - this.height = 500; - this.unitsFontSize = 9; - this.editEnabled = false; - this.isPositionColumnHidden = false; - this.removeBtnEnabled = false; - - this.selectionMode = "MULTI"; - this.updateRowEnabled = false; - this.grouped = true; - this.width = 900; - this.title = 'Specimens'; - - this.margin = "0 0 0 0"; -// this.experimentColorBased = false; - - if (args != null) { - if (args.height != null) { - this.height = args.height; - } - - if (args.showTitle == false) { - this.title = null; - } - - if (args.margin == false) { - this.margin = args.margin; - } - - if (args.grouped == false) { - this.grouped = null; - } - - if (args.width != null) { - this.width = args.width; - } - - - if (args.editEnabled != null) { - this.editEnabled = args.editEnabled; - } - if (args.removeBtnEnabled != null) { - this.removeBtnEnabled = args.removeBtnEnabled; - } - if (args.isPositionColumnHidden != null) { - this.isPositionColumnHidden = args.isPositionColumnHidden; - } - if (args.selectionMode != null) { - this.selectionMode = args.selectionMode; - } - if (args.updateRowEnabled != null) { - this.updateRowEnabled = args.updateRowEnabled; - } - - } - this.onClick = new Event(this); - this.onSelected = new Event(this); - this.onRemoved = new Event(this); - this.onSpecimenChanged = new Event(); -} - -SpecimenGrid.prototype._prepareData = function(experiment) { - var data = []; - - var samples = experiment.getSamples(); - for ( var i = 0; i < samples.length; i++) { - var sample = samples[i]; - if (sample.macromolecule3VO != null) { - sample.macromolecule = sample.macromolecule3VO.acronym; - sample.exposureTemperature = []; - sample.macromoleculeId = sample.macromolecule3VO.macromoleculeId; - } + }, + { + text : 'Dammin', + dataIndex : 'volumeEdna', + hidden : true, + tooltip : 'Dammin :Ab initio shape determination by simulated annealing using a single phase dummy atom model', + sortable : true, + width : 125, + renderer : function(val, y, sample) { + var file = sample.raw.shapeDeterminationModel; + if (file != null) { + var split = file.split("/"); + if (split.length > 0) { + var url = BUI.getPdbURL() + '&type=SHAPEDETERMINATIONMODEL&abInitioModelId=' + sample.raw.abInitioModelId; + return '' + split[split.length - 1]+ ''; + } + } + return sample.raw.averagedModel; + } + } ] + }, { + text : 'Time', + dataIndex : 'substractionCreationTime', + name : 'substractionCreationTime', + hidden : true, + width : 80, + sortable : true, + renderer : function(value, metaData, record, rowIndex, colIndex, store) { + try { + if (record.raw.substractionCreationTime != null) { + return moment(record.raw.substractionCreationTime).format('h:mm:ss a'); + } + } catch (e) { + return "NA"; + } + } + }, { + text : 'Date', + dataIndex : 'substractionCreationTime', + name : 'substractionCreationTime', + hidden : true, + width : 80, + sortable : true, + renderer : function(value, metaData, record, rowIndex, colIndex, store) { + try { + if (record.raw.substractionCreationTime != null) { + return moment(record.raw.substractionCreationTime).format("LL"); + } + } catch (e) { + return "NA"; + } + } + }, + this._getButtons()]; +}; - if (sample.sampleplateposition3VO != null) { - if (sample.sampleplateposition3VO.samplePlateId != null) { - sample.samplePlateId = sample.sampleplateposition3VO.samplePlateId; - sample.rowNumber = sample.sampleplateposition3VO.rowNumber; - sample.columnNumber = sample.sampleplateposition3VO.columnNumber; - if (experiment.getSamplePlateById(sample.sampleplateposition3VO.samplePlateId).plategroup3VO != null) { - sample.plateGroupName = experiment.getSamplePlateById(sample.sampleplateposition3VO.samplePlateId).plategroup3VO.name; - sample.samplePlateName = experiment.getSamplePlateById(sample.sampleplateposition3VO.samplePlateId).name + " [" + sample.plateGroupName + "]"; - sample.slotPositionColumn = experiment.getSamplePlateById(sample.sampleplateposition3VO.samplePlateId).slotPositionColumn; +AnalysisGrid.prototype._getButtons = function() { + return { + id : this.id + 'buttonPlot', + name : 'buttonPlot', + hidden : !this.showButtonsVisible, + width : 110, + sortable : false, + renderer : function(value, metaData, sample, rowIndex, colIndex, store) { + var html = ""; + + if (sample.raw.subtractionId) { + html = html + ""; + if (sample.raw.rapidShapeDeterminationModelId != null) { + html = html + ""; } } - } else { - sample.samplePlateName = "Unallocated Specimens"; + return html + "
" + BUI.getGreenButton('Calibration', { + height : 20, + width : 100 + }) + "
" + BUI.getGreenButton('Primary Data Proc.', { + height : 20, + width : 100 + }) + "
" + BUI.getGreenButton('AbInitio Modeling', { + height : 20, + width : 100 + }) + "
"; } - - /** For grouping, because sencha has not option for multiple grouping I add a field to your store with a convert function that concatenates these two fields and then group by that field.**/ - sample.groupIndex = sample.bufferId + sample.macromoleculeId; - var macromolecule = BIOSAXS.proposal.getMacromoleculeById(sample.macromoleculeId); - - sample.acronym = "Buffers"; - if (macromolecule != null) { - sample.acronym = BIOSAXS.proposal.getMacromoleculeById(sample.macromoleculeId).acronym; + }; +}; +AnalysisGrid.prototype._prepareData = function(data) { + if (this.hideNulls) { + var result = []; + for ( var i = 0; i < data.length; i++) { + if (data[i].subtractionId != null) { + data[i].groupeField = data[i].macromoleculeAcronym + " " + data[i].bufferAcronym; + result.push(data[i]); + } } - - sample.buffer = experiment.getBufferById(sample.bufferId); - - sample.volumeToLoad = experiment.getVolumeToLoadBySampleId(sample.sampleId); - data.push(sample); + return result; + } else { + return data; } - return data; }; -SpecimenGrid.prototype.deselectAll = function() { - this.grid.getSelectionModel().deselectAll(); -}; +AnalysisGrid.prototype.getPanel = function(data) { + var _this = this; + var columns = this._getColumns(); -SpecimenGrid.prototype.selectById = function(specimenId) { - this.grid.getSelectionModel().deselectAll(); - for ( var i = 0; i < this.grid.getStore().data.items.length; i++) { - var item = this.grid.getStore().data.items[i].raw; - if (item.specimenId == specimenId) { - this.grid.getSelectionModel().select(i); - } - } -}; + var fields = JSON.parse(JSON.stringify(columns)); + fields.push({ + name : 'experimentId', + dataIndex : 'experimentId' -SpecimenGrid.prototype.getStore = function() { - return this.store; -}; + }); + this.store = Ext.create('Ext.data.Store', { + fields : fields, + autoload : true, + data : this._prepareData(data), + groupField : 'groupeField' + }); -SpecimenGrid.prototype.getPlugins = function() { - var _this = this; + this.store.sort(this.sorters); - var plugins = []; + var features = []; - if (this.updateRowEnabled) { - plugins.push(Ext.create('Ext.grid.plugin.RowEditing', { - clicksToEdit : 1, + if (this.grouped) { + features.push({ + ftype : 'grouping', + groupHeaderTpl : '{name}', + hideGroupedHeader : false, + startCollapsed : false + }); + } + + this.grid = Ext.create('Ext.grid.Panel', { + style : { + padding : 5 + }, + maxWidth : this.maxWidth, + width : this.width, + height : this.height, + store : this.store, + columns : columns, + resizable : true, + features : features, + viewConfig : { + preserveScrollOnRefresh : true, + stripeRows : true, listeners : { - validateedit : function(grid, e) { - var measurements = []; + cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { - if (e.newValues.bufferId != e.record.raw.bufferId) { - /** If buffer has changed we have to change all the specimens sharing same datacollection **/ - var dataCollections = []; - if (e.record.raw.macromoleculeId == null) { - dataCollections = dataCollections.concat(_this.experiment.getDataCollectionsBySpecimenId(e.record.raw.specimenId)); - } else { - var sampleDataCollections = _this.experiment.getDataCollectionsBySpecimenId(e.record.raw.specimenId); - for ( var i = 0; i < sampleDataCollections.length; i++) { - var sampleDataCollection = sampleDataCollections[i]; - if (sampleDataCollection != null) { - for ( var j = 0; j < sampleDataCollection.measurementtodatacollection3VOs.length; j++) { - var measurementTODc = sampleDataCollection.measurementtodatacollection3VOs[j]; - if (measurementTODc.dataCollectionOrder == 1) { - dataCollections = dataCollections.concat(_this.experiment.getDataCollectionsBySpecimenId(_this.experiment - .getMeasurementById(measurementTODc.measurementId).specimenId)); - } - } - } - } - } - var i = null; - for ( i = 0; i < dataCollections.length; i++) { - var dataCollection = dataCollections[i]; - var specimens = _this.experiment.getSpecimenByDataCollectionId(dataCollection.dataCollectionId); - measurements = measurements.concat(specimens); + if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonPlot') { + if (e.target.defaultValue == 'AbInitio Modeling') { + var url = BUI.getPDBVisualizerURL(record.raw.averagedModelId, record.raw.subtractionId, record.raw.experimentId); + window.open(url, "_blank"); } - for ( i = 0; i < measurements.length; i++) { - var measurement = measurements[i]; - var specimen = _this.experiment.getSpecimenById(measurement.specimenId); - specimen.bufferId = e.newValues.bufferId; - new BiosaxsDataAdapter().saveSpecimen(specimen, _this.experiment); + if (e.target.defaultValue == 'Primary Data Proc.') { + _this._edit(record.raw); } - } - - /** Setting values **/ - e.record.raw.concentration = e.newValues.concentration; - e.record.raw.volume = e.newValues.volume; - /** Position **/ - if (e.record.raw.sampleplateposition3VO != null) { - var samplePlate = _this.experiment.getSamplePlateBySlotPositionColumn(e.newValues.slotPositionColumn); - if (samplePlate != null) { - e.record.raw.sampleplateposition3VO = { - columnNumber : e.newValues.columnNumber, - rowNumber : e.newValues.rowNumber, - samplePlateId : samplePlate.samplePlateId - }; - } - } else { - if (e.newValues.slotPositionColumn != null) { - var samplePlate = _this.experiment.getSamplePlateBySlotPositionColumn(e.newValues.slotPositionColumn); - if (samplePlate != null) { - e.record.raw.sampleplateposition3VO = { - columnNumber : e.newValues.columnNumber, - rowNumber : e.newValues.rowNumber, - samplePlateId : samplePlate.samplePlateId - }; - } + if (e.target.defaultValue == 'Calibration') { + _this._showCalibration(record.raw); } } - - var macromoleculeId = e.record.data.macromoleculeId; - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, specimen) { - /** Because macromolecule3VO is fecthed LAZY **/ - if (macromoleculeId != null) { - specimen.macromolecule3VO = BIOSAXS.proposal.getMacromoleculeById(macromoleculeId); - } - _this.onSpecimenChanged.notify(specimen); - _this.grid.setLoading(false); - }); - adapter.onError.attach(function() { - alert("Error"); - _this.grid.setLoading(false); - }); - _this.grid.setLoading(); - adapter.saveSpecimen(e.record.raw, _this.experiment); } } - })); - } - return plugins; + }, + selModel : { + mode : 'SINGLE' + } + }); + + return this.grid; + +}; + +AnalysisGrid.prototype._openVisualizarBySubstractionId = function(record) { + //experimentId, subtractionId) { + var experimentId = record.experimentId; + var subtractionId = record.subtractionId; + + var _this = this; + var adapter = new BiosaxsDataAdapter(); + this.subtractionId = subtractionId; + + adapter.onSuccess.attach(function(sender, data) { + var experiment = new Experiment(data); + var experimentList = new ExperimentList([ experiment ]); + var subtraction = experiment.getSubtractionById(_this.subtractionId); + _this.grid.setLoading(false); + _this._openVisualizer(experimentList, subtraction, record); + }); + this.grid.setLoading("ISPyB: Fetching experiment"); + adapter.getExperimentById(experimentId, "MEDIUM"); }; -SpecimenGrid.prototype._getRowCombo = function() { - var data = []; - for ( var i = 1; i <= 8; i++) { - data.push({ - rowNumber : i, - name : BUI.getSamplePlateLetters()[i - 1] +AnalysisGrid.prototype._edit = function(record) { + var experimentId = record.experimentId; + var _this = this; + + if (BIOSAXS.proposal.macromolecules == null) { + this.grid.setLoading("ISPyB: Fetching proposal"); + BIOSAXS.proposal.onInitialized.attach(function(sender, data) { + _this._openVisualizarBySubstractionId(record); }); + BIOSAXS.proposal.init(); + } else { + this._openVisualizarBySubstractionId(record); } +}; - var positionsStore = Ext.create('Ext.data.Store', { - fields : [ 'rowNumber', 'name' ], - data : data - }); - return Ext.create('Ext.form.ComboBox', { - store : positionsStore, - queryMode : 'local', - displayField : 'name', - valueField : 'rowNumber' +AnalysisGrid.prototype._showCalibration = function(row) { + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, data) { + Ext.create('Ext.window.Window', { + title : 'Calibration', + width : 900, + resizable : true, + height : 400, + modal : true, + frame : false, + draggable : true, + closable : true, + autoscroll : true, + paddin : 5, + layout : { + type : 'vbox', + align : 'stretch' + }, + items : [ new AnalysisGrid({ + isGuinierTabVisible : false, + isGnomTabVisible : false, + isPorodTabVisible : false, + isAbinitioTabVisible : false, + isI0Visible : true, + showButtonsVisible : false, + height : 350, + sorters : [ { + property : 'experimentId', + direction : 'DESC' + } ] + }).getPanel(data) ] + }).show(); }); + adapter.getAnalysisCalibrationByProposalId(); }; -SpecimenGrid.prototype._getColumnCombo = function() { - var data = []; - for ( var i = 1; i <= 12; i++) { - data.push({ - columnNumber : i - }); +AnalysisGrid.prototype._openVisualizer = function(experimentList, subtraction, record) { + var merges = experimentList.getMergesByDataCollectionId(subtraction.dataCollectionId); + var mergeIdList = []; + for ( var i = 0; i < merges.length; i++) { + mergeIdList.push(merges[i].mergeId); } - var positionsStore = Ext.create('Ext.data.Store', { - fields : [ 'columnNumber' ], - data : data + var dataAdapter = new BiosaxsDataAdapter(); + dataAdapter.onSuccess.attach(function(sender, merges) { + var dataCollectionCurveVisualizer = new DataCollectionCurveVisualizer(); + dataCollectionCurveVisualizer.draw(); + dataCollectionCurveVisualizer.refresh(merges, experimentList, subtraction.dataCollectionId, record); }); - return Ext.create('Ext.form.ComboBox', { - store : positionsStore, - queryMode : 'local', - displayField : 'columnNumber', - valueField : 'columnNumber' + dataAdapter.onError.attach(function(sender, error) { }); + + dataAdapter.getMergesByIdsList(mergeIdList); }; -SpecimenGrid.prototype._getSlotColumBombo = function() { - if (this.experiment){ - var length = this.experiment.getSamplePlates().length; - - var data = []; - for ( var i = 1; i <= length; i++) { - data.push({ - slotPositionColumn : i - }); - } +AnalysisGrid.prototype.input = function() { + return { + data : DATADOC.getData_3367().slice(20, 30),//[{"total":"0.447505552082","bufferBeforeMeasurementId":22150,"sampleMergeId":12373,"averagedModelId":43636,"I0":"32.50776","proposalCode":"OPD","averagedModel":"/data/pyarch/bm29/opd29/1137/1d/damaver.pdb","bufferAfterMergeId":12374,"framesCount":"10","bufferBeforeMergeId":12372,"averageFilePath":" /data/pyarch/bm29/opd29/1137/1d/MG386_016_ave.dat","bufferAfterMeasurementId":22152,"rgGuinier":"2.96883","subtractedFilePath":" /data/pyarch/bm29/opd29/1137/1d/MG386_016_sub.dat","firstPointUsed":"30","chi2RgFilePath":"/data/pyarch/bm29/opd29/1137/1d/chi2_R.png","abInitioModelId":272,"macromoleculeId":112,"code":"_20.0_1.25","transmission":"100.0","timeStart":"2013-07-17 17:10:25.743734","bufferAcronym":"MES","quality":"0.87091","shapeDeterminationModelId":43638,"expermientComments":"[BsxCube] Generated from BsxCube","bufferId":707,"isagregated":"False","dmax":"10.390905","bufferBeforeAverageFilePath":" /data/pyarch/bm29/opd29/1137/1d/MG386_015_ave.dat","exposureTemperature":"20.0","subtractionId":5165,"conc":"1.25","rapidShapeDeterminationModel":"/data/pyarch/bm29/opd29/1137/1d/damfilt.pdb","experimentCreationDate":"Jul 17, 2013 5:08:37 PM","lastPointUsed":"92","modelListId":272,"framesMerge":"10","rapidShapeDeterminationModelId":43637,"bufferAfterAverageFilePath":" /data/pyarch/bm29/opd29/1137/1d/MG386_017_ave.dat","nsdFilePath":"/data/pyarch/bm29/opd29/1137/1d/nsd.png","bufferAfterFramesMerged":"10","experimentId":1137,"macromoleculeAcronym":"MG386","i0stdev":"0.05726128","sampleMeasurementId":22151,"measurementComments":"[1] MES","priorityLevelId":2,"bufferBeforeFramesMerged":"10","substractionCreationTime":"Jul 17, 2013 5:12:32 PM","proposalNumber":"29","rgGnom":"3.05242714339","volume":"44.1406","shapeDeterminationModel":"/data/pyarch/bm29/opd29/1137/1d/dammin.pdb","comments":null},{"total":"0.582641941147","bufferBeforeMeasurementId":22152,"sampleMergeId":12375,"averagedModelId":43809,"I0":"31.4366153846","proposalCode":"OPD","averagedModel":"/data/pyarch/bm29/opd29/1137/1d/damaver.pdb","bufferAfterMergeId":12376,"framesCount":"10","bufferBeforeMergeId":12374,"averageFilePath":" /data/pyarch/bm29/opd29/1137/1d/MG386_018_ave.dat","bufferAfterMeasurementId":22155,"rgGuinier":"2.95541","subtractedFilePath":" /data/pyarch/bm29/opd29/1137/1d/MG386_018_sub.dat","firstPointUsed":"21","chi2RgFilePath":"/data/pyarch/bm29/opd29/1137/1d/chi2_R.png","abInitioModelId":273,"macromoleculeId":112,"code":"_20.0_0.65000000000000002","transmission":"100.0","timeStart":"2013-07-17 17:12:55.837462","bufferAcronym":"MES","quality":"0.870164","shapeDeterminationModelId":43811,"expermientComments":"[BsxCube] Generated from BsxCube","bufferId":707,"isagregated":"False","dmax":"10.343935","bufferBeforeAverageFilePath":" /data/pyarch/bm29/opd29/1137/1d/MG386_017_ave.dat","exposureTemperature":"20.0","subtractionId":5166,"conc":"0.65000000000000002","rapidShapeDeterminationModel":"/data/pyarch/bm29/opd29/1137/1d/damfilt.pdb","experimentCreationDate":"Jul 17, 2013 5:08:37 PM","lastPointUsed":"80","modelListId":273,"framesMerge":"10","rapidShapeDeterminationModelId":43810,"bufferAfterAverageFilePath":" /data/pyarch/bm29/opd29/1137/1d/MG386_019_ave.dat","nsdFilePath":"/data/pyarch/bm29/opd29/1137/1d/nsd.png","bufferAfterFramesMerged":"10","experimentId":1137,"macromoleculeAcronym":"MG386","i0stdev":"0.100250461538","sampleMeasurementId":22154,"measurementComments":"[2] MES","priorityLevelId":4,"bufferBeforeFramesMerged":"10","substractionCreationTime":"Jul 17, 2013 5:15:02 PM","proposalNumber":"29","rgGnom":"2.9963404542","volume":"42.2255","shapeDeterminationModel":"/data/pyarch/bm29/opd29/1137/1d/dammin.pdb","comments":null}], + proposal : new ResultsAssemblyGrid().input().proposal + }; +}; + +AnalysisGrid.prototype.test = function(targetId) { + var analysisGrid = new AnalysisGrid(); + BIOSAXS.proposal = new Proposal(analysisGrid.input().proposal); + var panel = analysisGrid.getPanel(analysisGrid.input().data); + panel.render(targetId); +}; + + +function HPLCAnalysisGrid(args) { + AnalysisGrid.prototype.constructor.call(this, args); +} - var positionsStore = Ext.create('Ext.data.Store', { - fields : [ 'slotPositionColumn' ], - data : data - }); +HPLCAnalysisGrid.prototype._edit = AnalysisGrid.prototype._edit; +HPLCAnalysisGrid.prototype._getColumns = AnalysisGrid.prototype._getColumns; +HPLCAnalysisGrid.prototype._openVisualizarBySubstractionId = AnalysisGrid.prototype._openVisualizarBySubstractionId; +HPLCAnalysisGrid.prototype._openVisualizer = AnalysisGrid.prototype._openVisualizer; +HPLCAnalysisGrid.prototype._prepareData = AnalysisGrid.prototype._prepareData; +HPLCAnalysisGrid.prototype._showCalibration = AnalysisGrid.prototype._showCalibration; +HPLCAnalysisGrid.prototype.getPanel = AnalysisGrid.prototype.getPanel; +HPLCAnalysisGrid.prototype.input = AnalysisGrid.prototype.input; +HPLCAnalysisGrid.prototype.test = AnalysisGrid.prototype.test; +HPLCAnalysisGrid.prototype.refresh = AnalysisGrid.prototype.refresh; +HPLCAnalysisGrid.prototype._getPorod = AnalysisGrid.prototype._getPorod; + +HPLCAnalysisGrid.prototype._getButtons = function() { + return { + id : this.id + 'buttonPlot', + name : 'buttonPlot', + hidden : !this.showButtonsVisible, + width : 110, + sortable : false, + renderer : function(value, metaData, sample, rowIndex, colIndex, store) { + var html = ""; - return Ext.create('Ext.form.ComboBox', { - store : positionsStore, - queryMode : 'local', - displayField : 'slotPositionColumn', - valueField : 'slotPositionColumn' - }); + if (sample.raw.subtractionId) { + if (sample.raw.rapidShapeDeterminationModelId != null) { + html = html + ""; + } + } + return html + "
" + BUI.getGreenButton('AbInitio Modeling', { + height : 20, + width : 100 + }) + "
"; + } + }; +}; + +HPLCAnalysisGrid.prototype._getFramesColumn = function() { + return { + text : 'Frames', + dataIndex : 'datacollection', + name : 'datacollection', + sortable : true, + width : 150, + renderer : function(dataCollections, y, data) { + molmerges = data.raw.framesMerge; + totalframes = data.raw.framesCount; + return molmerges +" - "+ totalframes; + } + }; +}; + + + + +/** + * Rigid body grid to show PDB, symmetry and multiplicity + * + * + * #onUploadFile click on upload file + */ +function AprioriRigidBodyGrid(args) { + + this.height = 250; + this.btnEditVisible = true; + this.btnRemoveVisible = true; + + if (args != null) { + if (args.height != null) { + this.height = args.height; + } + if (args.btnEditVisible != null) { + this.btnEditVisible = args.btnEditVisible; + } + if (args.btnRemoveVisible != null) { + this.btnRemoveVisible = args.btnRemoveVisible; + } + } + + /** Events **/ + this.onUploadFile = new Event(this); + this.onRemove = new Event(this); +} + +AprioriRigidBodyGrid.prototype._getColumns = function() { +}; + +AprioriRigidBodyGrid.prototype._getTopButtons = function() { + var _this = this; + /** Actions buttons **/ + var actions = []; + + /** ADD BUTTON **/ + actions.push(Ext.create('Ext.Action', { + icon : '../images/add.png', + text : 'Add', + disabled : false, + handler : function(widget, event) { + _this.onAddButtonClicked.notify(); + } + })); + + return actions; +}; + +AprioriRigidBodyGrid.prototype.refresh = function(macromolecule) { + this.macromolecule = macromolecule; + if (macromolecule != null){ + this.pdbStore.loadData(macromolecule.structure3VOs); + } +}; + +AprioriRigidBodyGrid.prototype._prepareData = function() { + var data = []; + for ( var i = 0; i < this.features.length; i++) { + data.push(this.features[i]); + } + return data; +}; + +AprioriRigidBodyGrid.prototype._getPlugins = function() { + var _this = this; + var plugins = []; + plugins.push(Ext.create('Ext.grid.plugin.RowEditing', { + clicksToEdit : 1, + listeners : { + validateedit : function(grid, e) { + /** Comments are always updatable* */ + e.record.raw.symmetry = e.newValues.symmetry; + e.record.raw.multiplicity = e.newValues.multiplicity; + + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, proposal) { + BIOSAXS.proposal.setItems(proposal); + _this.refresh(BIOSAXS.proposal.getMacromoleculeById(_this.macromolecule.macromoleculeId)); + _this.panel.setLoading(false); + }); + adapter.onError.attach(function() { + alert("Error"); + }); + + _this.panel.setLoading(); + adapter.saveStructure(e.record.raw); + } + } + })); + return plugins; +}; + +AprioriRigidBodyGrid.prototype.getPanel = function() { + var _this = this; + + this.pdbStore = Ext.create('Ext.data.Store', { + fields : [ 'filePath', 'structureId', 'structureType', 'symmetry', 'structureId', 'name', 'multiplicity' ], + groupField : 'structureType', + sorters : { + property : 'structureId', + direction : 'DESC' + } + }); + + var groupingFeature = Ext.create('Ext.grid.feature.Grouping', { + groupHeaderTpl : Ext.create('Ext.XTemplate', + "
{name:this.formatName}
", { + formatName : function(name) { + return name; + } + }), + hideGroupedHeader : true, + startCollapsed : false + }); + + this.panel = Ext.create('Ext.grid.Panel', { + margin : "15 10 0 10", + height : this.height, + store : this.pdbStore, + plugins : _this._getPlugins(), + tbar : [ { + text : 'Add Modeling Option (PDB)', + icon : '../images/add.png', + handler : function() { + _this.onUploadFile.notify('PDB', 'Upload PDB File'); + } + } + + ], + columns : [ + { + text : "structureId", + flex : 0.2, + hidden : true, + dataIndex : 'structureId', + sortable : true + }, + { + text : "File", + flex : 0.5, + dataIndex : 'filePath', + sortable : true, + hidden : true + }, + { + text : "PDB", + flex : 0.4, + dataIndex : 'name', + sortable : true + }, + { + text : "Symmetry", + flex : 0.2, + dataIndex : 'symmetry', + sortable : true, + editor : { + xtype : 'combobox', + typeAhead : true, + triggerAction : 'all', + selectOnTab : true, + store : [ [ "P1", "P1" ], [ "P2", "P2" ], [ "P3", "P3" ], [ "P4", "P4" ], [ "P5", "P5" ], [ "P6", "P6" ], [ "P32", "P32" ], [ "P42", "P42" ], + [ "P52", "P52" ], [ "P62", "P62" ], [ "P222", "P222" ] ], + } + }, { + text : "Multiplicity", + flex : 0.2, + dataIndex : 'multiplicity', + sortable : true, + editor : { + xtype : 'textfield' + } + + }, { + text : "Subunit", + flex : 0.2, + dataIndex : 'isSubunit', + sortable : true, + hidden : true + }, { + text : "Type", + flex : 0.2, + dataIndex : 'structureType', + sortable : true, + hidden : true + }, + + { + id : this.id + 'REMOVE', + flex : 0.2, + sortable : false, + renderer : function(value, metaData, record, rowIndex, colIndex, store) { + return BUI.getRedButton('REMOVE'); + } + }, ], + + listeners : { + itemdblclick : function(dataview, record, item, e) { + _this._editExperiment(record.raw.experimentId); + }, + cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + + if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'REMOVE') { + var dataAdapter = new BiosaxsDataAdapter(); + dataAdapter.onSuccess.attach(function() { + _this.panel.setLoading(false); + _this.onRemove.notify(); + }); + _this.panel.setLoading("Removing PDB file"); + dataAdapter.removeStructure(record.data.structureId); + } + + } + }, + viewConfig : { + getRowClass : function(record, rowIdx, params, store) { + if (record.raw.isSubunit != null) { + return "blue-row"; + } + } + } + }); + + return this.panel; +}; + + + + +AprioriRigidBodyGrid.prototype.input = function() { + return { + proposal : DATADOC.getProposal_10(), + dewars : DATADOC.getDewars_10() + + }; +}; + +AprioriRigidBodyGrid.prototype.test = function(targetId) { + var AprioriRigidBodyGrid = new AprioriRigidBodyGrid({ + height : 150 + }); + BIOSAXS.proposal = new Proposal(AprioriRigidBodyGrid.input().proposal); + var panel = AprioriRigidBodyGrid.getPanel(AprioriRigidBodyGrid.input().dewars); + panel.render(targetId); + +}; + +/** + * It shows buffer grid with a top bar with "Add" button + * + * @height + * @searchBar + * @collapsed + * @width + */ +function BufferGrid(args) { + this.height = 500; + this.searchBar = false; + this.tbar = false; + this.collapsed = false; + this.id = BUI.id(); + + if (args != null) { + if (args.height != null) { + this.height = args.height; + } + if (args.searchBar != null) { + this.searchBar = args.searchBar; + } + + if (args.tbar != null) { + this.tbar = args.tbar; + } + + if (args.collapsed != null) { + this.collapsed = args.collapsed; + } + + if (args.width != null) { + this.width = args.width; + } } +} + +BufferGrid.prototype._edit = function(bufferId) { + var _this = this; + var window = new BufferWindow(); + window.onSuccess.attach(function(sender, proposal) { + _this.store.loadData(BIOSAXS.proposal.getBuffers()); + }); + window.draw(BIOSAXS.proposal.getBufferById(bufferId)); }; -SpecimenGrid.prototype.getPanelByExperiment = function(experiment) { - this.experiment = experiment; - var data = this._prepareData(experiment); - return this.getPanel(data); +BufferGrid.prototype.refresh = function(buffers) { + this.store.loadData(this._prepareData(buffers), false); }; -SpecimenGrid.prototype.refresh = function(experiment) { - this.experiment = experiment; - var data = this._prepareData(experiment); - this.store.loadData(data); +BufferGrid.prototype._prepareData = function(buffers) { + return buffers; }; -SpecimenGrid.prototype.getPanel = function() { +BufferGrid.prototype._getTbar = function() { var _this = this; - this.store = Ext.create('Ext.data.Store', { - fields : [ - 'buffer', 'bufferId', 'code', 'macromolecule', 'acronym', 'macromoleculeId', 'concentration', 'volume', 'samplePlateId', - 'slotPositionColumn', 'rowNumber', 'columnNumber', 'groupIndex' ], - data : [], - groupField : 'acronym' - }); - this.store.sort([ { - property : 'concentration', - direction : 'ASC' - }, { - property : 'buffer', - direction : 'ASC' - } ]); + var actions = []; - var selModel = Ext.create('Ext.selection.RowModel', { - allowDeselect : true, - mode : this.selectionMode, - listeners : { - selectionchange : function(sm, selections) { - var selected = []; - for ( var i = 0; i < selections.length; i++) { - selected.push(selections[i].raw); - } - _this.onSelected.notify(selected); - } + actions.push(Ext.create('Ext.Action', { + icon : '../images/add.png', + text : 'Add buffer', + disabled : false, + handler : function(widget, event) { + var window = new BufferWindow(); + window.onSuccess.attach(function(sender) { + _this.store.loadData(BIOSAXS.proposal.getBuffers()); + }); + window.draw({}); } - }); - - var features = []; - - if (this.grouped) { - features.push({ - ftype : 'grouping', - groupHeaderTpl : '{name}', - hideGroupedHeader : false, - startCollapsed : false, - id : 'myGroupedStore' - }); - } + })); + return actions; +}; - this.grid = Ext.create( - 'Ext.grid.Panel', - { - title : this.title, - height : this.height, - width : this.width, - selModel : selModel, - store : this.store, - features : features, - margin : this.margin, - plugins : this.getPlugins(), - columns : [ - { - text : '', - dataIndex : 'macromolecule', - width : 20, - renderer : function(val, y, sample) { - var macromoleculeId = null; - if (sample.raw.macromolecule3VO != null) { - macromoleculeId = sample.raw.macromolecule3VO.macromoleculeId; - } - else{ - macromoleculeId = sample.raw.macromoleculeId; - } - - if (macromoleculeId == null) return; - return BUI.getRectangleColorDIV(_this.experiment.macromoleculeColors[macromoleculeId], 10, 10); - } - }, - { - text : 'Macromolecule', - dataIndex : 'macromolecule', - width : 100 - }, - { - text : '', - dataIndex : 'buffer', - width : 20, - renderer : function(val, y, sample) { - var color = "black"; - if (sample.raw.bufferId != null) { - if (_this.experiment.getDataCollectionsBySpecimenId(sample.raw.specimenId)[0] != null){ - color = _this.experiment.getSpecimenColorByBufferId(_this.experiment.getMeasurementById(_this.experiment.getDataCollectionsBySpecimenId(sample.raw.specimenId)[0].measurementtodatacollection3VOs[0].measurementId).specimenId); - } - return BUI.getRectangleColorDIV(color, 10, 10); - } - } - }, { - text : 'Buffer', - dataIndex : 'bufferId', - width : 140, - renderer : function(val, y, sample) { - if (sample.raw.bufferId != null) { - return BIOSAXS.proposal.getBufferById(val).acronym; - } - }, - editor : BIOSAXS_COMBOMANAGER.getComboBuffers(BIOSAXS.proposal.getBuffers(), { - noLabel : true, - width : 300 - }) - }, { - text : 'Conc.', - dataIndex : 'concentration', - width : 100, - editor : { - allowBlank : false - }, - renderer : function(val, meta, sample) { - if (isNaN(val)) { - meta.tdCls = 'yellow-cell'; - return val; - } else { - if (val != 0) { - return BUI.formatValuesUnits(val, 'mg/ml', { - fontSize : 16, - decimals : 3, - unitsFontSize : this.unitsFontSize - }); - } else { - return; - } - } - } - }, { - text : 'Vol. Well', - dataIndex : 'volume', - width : 70, - editor : { - allowBlank : true - }, - renderer : function(val, y, sample) { - return BUI.formatValuesUnits(sample.data.volume, 'µl', { - fontSize : 12, - decimals : 2, - unitsFontSize : this.unitsFontSize - }); - } - }, { - text : 'Position', - hidden : true, - flex : 1, - renderer : function(val, y, sample) { - return BUI.getSamplePositionHTML(sample.raw, _this.experiment); - } - }, { - text : 'samplePlateId', - dataIndex : 'samplePlateId', - hidden : true - }, { - text : 'Plate', - hidden : this.isPositionColumnHidden, - dataIndex : 'slotPositionColumn', - editor : _this._getSlotColumBombo(), - flex : 1, - renderer : function(val, meta, sample) { - if ((val != null) & (val != "")) { - return val; - } else { - meta.tdCls = 'yellow-cell'; - } - } - }, { - text : 'Row', - hidden : this.isPositionColumnHidden, - dataIndex : 'rowNumber', - editor : this._getRowCombo(), - flex : 1, - renderer : function(val, meta, sample) { - if ((val != null) && (val != "")) { - return BUI.getSamplePlateLetters()[val - 1]; - } else { - meta.tdCls = 'yellow-cell'; - } - } - }, { - text : 'Well', - hidden : this.isPositionColumnHidden, - dataIndex : 'columnNumber', - editor : this._getColumnCombo(), - flex : 1, - renderer : function(val, meta, sample) { - if ((val != null) && (val != "")) { - return val; - } else { - meta.tdCls = 'yellow-cell'; - } - } - }, { - id : _this.id + 'buttonEditSample', - text : 'Edit', - width : 80, - sortable : false, - hidden : !_this.editEnabled, - renderer : function(value, metaData, record, rowIndex, colIndex, store) { - if (_this.editEnabled) { - return BUI.getGreenButton('EDIT'); - } - } - }, { - id : _this.id + 'buttonRemoveSample', - text : '', - hidden : !_this.removeBtnEnabled, - width : 100, - sortable : false, - renderer : function(value, metaData, record, rowIndex, colIndex, store) { - if (_this.removeBtnEnabled) { - return BUI.getRedButton('REMOVE'); - } - } - } +BufferGrid.prototype.getPanel = function(buffers) { + var _this = this; - ], - viewConfig : { - preserveScrollOnRefresh : true, - stripeRows : true, - getRowClass : function(record) { - var specimens = _this.experiment.getSampleByPosition(record.data.samplePlateId, record.data.rowNumber, - record.data.columnNumber); - if (specimens.length > 1) { - return 'red-row'; + this.store = Ext.create('Ext.data.Store', { + fields : [ 'bufferId', 'acronym', 'name', 'composition' ], + data : buffers + }); - } - }, - listeners : { - selectionchange : function(grid, selected) { - _this.onClick.notify(record.raw); - }, - cellclick : function(grid, td, cellIndex, record, tr) { - if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonEditSample') { - } - if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonRemoveSample') { - grid.getStore().removeAt(rowIndex); - _this.onRemoved.notify(); - } + this.store.sort('acronym'); - } + var type = 'Ext.grid.Panel'; + if (this.searchBar == true) { + type = 'Ext.ux.LiveSearchGridPanel'; + } - } - } - }); + this.grid = Ext.create(type, { + title : 'Buffers', + collapsible : true, + collapsed : this.collapsed, + store : this.store, + height : this.height, + width : this.width, + columns : [ + /*{ + text : '', + dataIndex : 'bufferId', + width : 20, + renderer : function(val, y, sample) { + return BUI.getRectangleColorDIV(BIOSAXS.proposal.bufferColors[val], 10, 10); + } + },*/ + { + text : 'Acronym', + dataIndex : 'acronym', + flex : 1 + }, { + text : 'Name', + dataIndex : 'name', + flex : 1, + hidden : true + }, { + text : 'Composition', + dataIndex : 'composition', + flex : 1, + hidden : true + }, { + id : _this.id + 'buttonEditBuffer', + width : 80, + sortable : false, + renderer : function(value, metaData, record, rowIndex, colIndex, store) { + return BUI.getGreenButton('EDIT'); + } + }, { + id : _this.id + 'buttonRemoveBuffer', + width : 85, + hidden : true, + sortable : false, + renderer : function(value, metaData, record, rowIndex, colIndex, store) { + return BUI.getRedButton('REMOVE'); + } + } ], + flex : 1, + viewConfig : { + stripeRows : true, + listeners : { + 'celldblclick' : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + _this._edit(record.data.bufferId); + }, + 'cellclick' : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonEditBuffer') { + _this._edit(record.data.bufferId); + } + + if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonRemoveBuffer') { + BUI.showBetaWarning(); + } + } + + } + } + }); + + /** Adding the tbar **/ + if (this.tbar) { + this.grid.addDocked({ + xtype : 'toolbar', + items : this._getTbar() + }); + } return this.grid; }; -SpecimenGrid.prototype.input = function() { - return { - experiment : DATADOC.getExperiment_10(), - proposal : DATADOC.getProposal_10() - }; +BufferGrid.prototype.input = function() { + return new MacromoleculeGrid().input(); }; -SpecimenGrid.prototype.test = function(targetId) { - var specimenGrid = new SpecimenGrid({ - height : 400, - maxHeight : 400, - width : 1000 +BufferGrid.prototype.test = function(targetId) { + var bufferGrid = new BufferGrid({ + width : 800, + height : 350, + collapsed : false, + tbar : true }); - BIOSAXS.proposal = new Proposal(specimenGrid.input().proposal); - var experiment = new Experiment(specimenGrid.input().experiment); - var panel = specimenGrid.getPanelByExperiment(experiment); + BIOSAXS.proposal = new Proposal(bufferGrid.input().proposal); + var panel = bufferGrid.getPanel(BIOSAXS.proposal.macromolecules); panel.render(targetId); - }; /** - * Shows a list of stock solutions with macromolecule, buffer, storage temperature, concentration, shipment and comments + * A shipment may contains one or more cases where stock solutions and sample plates are stored * - * @multiselect allows multiple selection - * @height - * @minHeight - * @width - * @tbar - * @showTitle - * @isPackedVisible shows is stock solution is in a box - * @btnEditVisible shows edit button - * @btnAddVisible - * @btnAddExisting - * @btnUnpackVisible allows to unpack a stock solution - * @btnRemoveVisible allow to remove a stock solution + * @height + * @btnEditVisible + * @btnRemoveVisible + * + * #onEditButtonClicked + * #onAddButtonClicked + * #onRemoveButtonClicked + * #onDuplicateButtonClicked */ +function CaseGrid(args) { -function StockSolutionGrid(args) { - this.id = BUI.id(); this.height = 100; - this.width = null; - this.minHeight = null; - this.tbar = true; - - this.title = "Stock Solutions"; - - /** Visible buttons and actions **/ this.btnEditVisible = true; this.btnRemoveVisible = true; - this.btnAddVisible = true; - this.btnAddExisting = false; - this.isPackedVisible = true; - this.btnUnpackVisible = false; - - /** Selectors **/ - this.multiselect = false; - this.selectedStockSolutions = []; if (args != null) { - if (args.btnUnpackVisible != null) { - this.btnUnpackVisible = args.btnUnpackVisible; - } - if (args.multiselect != null) { - this.multiselect = args.multiselect; - } if (args.height != null) { this.height = args.height; } if (args.btnEditVisible != null) { this.btnEditVisible = args.btnEditVisible; } - if (args.btnAddVisible != null) { - this.btnAddVisible = args.btnAddVisible; - } - if (args.btnAddExisting != null) { - this.btnAddExisting = args.btnAddExisting; - } - if (args.width != null) { - this.width = args.width; - } - if (args.minHeight != null) { - this.minHeight = args.minHeight; - } - if (args.tbar != null) { - this.tbar = args.tbar; - } if (args.btnRemoveVisible != null) { this.btnRemoveVisible = args.btnRemoveVisible; } - if (args.isPackedVisible != null) { - this.isPackedVisible = args.isPackedVisible; - } - if (args.showTitle != null) { - this.showTitle = args.showTitle; - if (this.showTitle == false) { - this.title = null; + } + + /** Events **/ + this.onEditButtonClicked = new Event(this); + this.onAddButtonClicked = new Event(this); + this.onRemoveButtonClicked = new Event(this); + this.onDuplicateButtonClicked = new Event(this); +} + +CaseGrid.prototype._getColumns = function() { + var _this = this; + + function actionItemRenderer(value, meta, record, rowIx, ColIx, store) { + /*if (record.raw.specimen3VOs.length > 0){ + return 'x-hide-display'; + }*/ + } + + var columns = [ + { + header : 'Code', + dataIndex : 'code', + name : 'code', + type : 'string', + flex : 1 + }, + { + header : 'Bar Code', + dataIndex : 'barCode', + name : 'barCode', + type : 'string', + flex : 1, + hidden : true + }, + { + header : 'BeamLine', + dataIndex : 'barCode', + type : 'string', + flex : 1, + renderer : function(comp, val, record) { + if (record.raw.sessionVO != null) { + return "" + record.raw.sessionVO.beamlineName + ""; + } + } + }, + { + header : 'Session', + dataIndex : 'barCode', + type : 'string', + flex : 1, + renderer : function(comp, val, record) { + if (record.raw.sessionVO != null) { + return "" + moment(record.raw.sessionVO.startDate).format("MMM Do YY") + ""; + } } - } - - } - - /** Events **/ - this.onProposalChanged = new Event(this); - this.onStockSolutionSelected = new Event(this); -} - -StockSolutionGrid.prototype._getColumns = function() { - var _this = this; - var columns = [ - - { - header : 'Macromolecule', - dataIndex : 'macromolecule', - id : _this.id + 'macromolecule', - type : 'string', - renderer : function(val, y, specimen) { - return '' + val + ''; }, - hidden : false, - flex : 1 - }, { - header : 'Buffer', - dataIndex : 'buffer', - name : 'buffer', - hidden : false, - renderer : function(val, y, specimen) { - return '' + val + ''; + { + header : 'Status', + dataIndex : 'dewarStatus', + name : 'dewarStatus', + type : 'string', + flex : 1, + renderer : function(comp, val, record) { + if (new String(record.raw.dewarStatus).toUpperCase() == 'OPENED') { + return "" + new String(record.raw.dewarStatus).toUpperCase() + ""; + } + return "" + new String(record.raw.dewarStatus).toUpperCase() + ""; + } }, - type : 'string', - flex : 1 - }, { - header : 'Acronym', - dataIndex : 'name', - name : 'name', - type : 'string', - flex : 1, - hidden : true - }, { - header : 'Storage Temp.', - dataIndex : 'storageTemperature', - name : 'storageTemperature', - type : 'string', - flex : 1, - hidden : false, - renderer : function(val) { - return BUI.formatValuesUnits(val, 'C', { - fontSize : 12, - decimals : 2, - unitsFontSize : 10 - }); - } - }, { - header : 'Volume', - dataIndex : 'volume', - type : 'string', - flex : 1, - hidden : false, - renderer : function(val) { - return BUI.formatValuesUnits(val, 'µl', { - fontSize : 12, - decimals : 2, - unitsFontSize : 10 - }); - } - }, { - header : 'Concentration', - dataIndex : 'concentration', - name : 'concentration', - type : 'string', - flex : 1, - renderer : function(val) { - return BUI.formatValuesUnits(val, 'mg/ml', { - fontSize : 12, - decimals : 2, - unitsFontSize : 10 - }); - } - }, { - header : 'Packed', - dataIndex : 'comments', - id : _this.id + "box", - type : 'string', - width : 50, - hidden : !this.isPackedVisible, - renderer : function(val, cmp, a) { - if (a.raw.boxId != null) { - return "
"; + { + header : 'Type', + dataIndex : 'type', + name : 'type', + type : 'string', + flex : 1, + hidden : true + }, + { + header : 'Sample Plates', + dataIndex : 'plates', + name : 'plates', + type : 'string', + flex : 1, + hidden : true + }, + { + header : 'Stock Solutions', + dataIndex : 'plates', + name : 'plates', + type : 'string', + width : 100, + renderer : function(comp, val, record) { + var html = "
"; + var stockSolutions = BIOSAXS.proposal.getStockSolutionsByDewarId(record.raw.dewarId); + html = html + "" + stockSolutions.length + " x"; + return html + "
"; } - + }, { + header : 'isStorageDewar', + dataIndex : 'isStorageDewar', + name : 'isStorageDewar', + type : 'string', + flex : 1, + hidden : true + }, { + header : 'Tracking Number From Synchrotron', + dataIndex : 'trackingNumberFromSynchrotron', + name : 'trackingNumberFromSynchrotron', + type : 'string', + flex : 1, + hidden : true + }, { + header : 'Tracking Number To Synchrotron', + dataIndex : 'trackingNumberToSynchrotron', + name : 'trackingNumberToSynchrotron', + type : 'string', + flex : 1, + hidden : true + }, { + header : 'Storage Location', + dataIndex : 'storageLocation', + name : 'storageLocation', + type : 'string', + flex : 1, + hidden : false + }, { + header : 'Comments', + dataIndex : 'comments', + name : 'comments', + type : 'string', + flex : 1, + hidden : false } - }, { - header : 'Comments', - dataIndex : 'comments', - type : 'string', - flex : 1 - } ]; + ]; if (this.btnEditVisible) { columns.push({ id : _this.id + 'buttonEdit', + text : '', + hidden : !_this.btnEditVisible, width : 85, sortable : false, renderer : function(value, metaData, record, rowIndex, colIndex, store) { - if (_this.btnEditVisible) { - return BUI.getGreenButton('EDIT'); - } + return BUI.getGreenButton('EDIT'); } }); } @@ -22014,4646 +16857,9803 @@ StockSolutionGrid.prototype._getColumns = function() { }); } - if (this.btnUnpackVisible) { - columns.push({ - id : _this.id + 'buttonUnpack', - width : 85, - sortable : false, - renderer : function(value, metaData, record, rowIndex, colIndex, store) { - if (_this.btnUnpackVisible) { - return BUI.getBlueButton('UNPACK'); - } - } - }); - } + columns.push({ + dataIndex : 'comments', + type : 'string', + width : 85, + hidden : false, + renderer : function(comp, val, record) { + return BUI.getBlueButton("LABELS", { + href : BUI.getPrintcomponentURL(record.raw.dewarId) + }); + } + }); + return columns; }; -StockSolutionGrid.prototype._getTopButtons = function() { - var _this = this; - /** Actions buttons **/ - var actions = []; - - /** ADD BUTTON **/ - if (this.btnAddVisible) { - actions.push(Ext.create('Ext.Action', { - icon : '../images/add.png', - text : 'Add Stock Solution', - tooltip : 'Will create a new stock solution', - disabled : false, - alwaysEnabled : true, - handler : function(widget, event) { - _this.edit(); - } - })); - } - - if (this.btnAddExisting) { - actions.push(Ext.create('Ext.Action', { - icon : '../images/add.png', - text : 'Add Existing', - tooltip : 'Allows to select upacked stock solutions', - disabled : false, - alwaysEnabled : true, - handler : function(widget, event) { - var stockSolutionGrid = new StockSolutionGrid({ - btnAddVisible : false, - btnEditVisible : false, - btnRemoveVisible : false, - btnAddExisting : false, - isPackedVisible : true, - multiselect : true - }); - - var window = Ext.create('Ext.window.Window', { - title : 'Select', - height : 400, - width : 800, - layout : 'fit', - items : [ stockSolutionGrid.getPanel() ], - buttons : [ { - text : 'Pack', - handler : function() { - _this.onStockSolutionSelected.notify(stockSolutionGrid.selectedStockSolutions); - window.close(); - } - }, { - text : 'Cancel', - handler : function() { - window.close(); - } - } ] - - }).show(); +CaseGrid.prototype._getTopButtons = function() { + var _this = this; + /** Actions buttons **/ + var actions = []; - stockSolutionGrid.refresh(BIOSAXS.proposal.getUnpackedStockSolutions()); - } - })); - } + /** ADD BUTTON **/ + actions.push(Ext.create('Ext.Action', { + icon : '../images/add.png', + text : 'Add', + disabled : false, + handler : function(widget, event) { + _this.onAddButtonClicked.notify(); + } + })); return actions; }; -StockSolutionGrid.prototype.refresh = function(stockSolutions) { - this.features = stockSolutions; - this.store.loadData(this._prepareData(), false); +CaseGrid.prototype.refresh = function(dewars) { + this.features = dewars; + this.store.loadData(this._prepareData(dewars), false); }; -StockSolutionGrid.prototype._prepareData = function() { +CaseGrid.prototype._sort = function(store) { + store.sort('dewarId', 'DESC'); +}; + +CaseGrid.prototype._prepareData = function() { var data = []; for ( var i = 0; i < this.features.length; i++) { - var stockSolution = this.features[i]; - stockSolution.buffer = BIOSAXS.proposal.getBufferById(stockSolution.bufferId).acronym; - if (stockSolution.macromoleculeId != null) { - stockSolution.macromolecule = BIOSAXS.proposal.getMacromoleculeById(stockSolution.macromoleculeId).acronym; - } - data.push(stockSolution); + data.push(this.features[i]); } return data; }; -StockSolutionGrid.prototype.getPanel = function() { +CaseGrid.prototype.getPanel = function(dewars, plates) { + this.features = dewars; + this.plates = plates; return this._renderGrid(); }; -StockSolutionGrid.prototype.edit = function(stockSolutionId) { +CaseGrid.prototype._edit = function(dewar) { var _this = this; - var stockSolutionWindow = new StockSolutionWindow(); - /** On stock solution SAVED **/ - stockSolutionWindow.onSaved.attach(function(sender, stockSolution) { - _this.onProposalChanged.notify(stockSolution); + var caseWindow = new CaseWindow(); + /**SAVED **/ + caseWindow.onSaved.attach(function(sender, dewar) { + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, shipment) { + _this.refresh(shipment.dewarVOs); + }); + adapter.saveCase(BIOSAXS.proposal.getShipmentByDewarId(dewar.dewarId).shippingId, dewar); }); - stockSolutionWindow.draw(BIOSAXS.proposal.getStockSolutionById(stockSolutionId)); + caseWindow.draw(dewar); }; -StockSolutionGrid.prototype._getStoreFields = function() { +CaseGrid.prototype._getStoreFields = function(data) { return [ { - name : 'name', + name : 'dewarId', type : 'string' }, { - name : 'stockSolutionId', + name : 'barCode', type : 'string' }, { - name : 'macromolecule', + name : 'code', type : 'string' }, { - name : 'buffer', + name : 'comments', type : 'string' }, { - name : 'storageTemperature', - type : 'numeric' + name : 'dewarStatus', + type : 'string' }, { - name : 'volume', + name : 'isStorageDewar', type : 'string' }, { - name : 'concentration', + name : 'plates', type : 'string' }, { - name : 'buffer', + name : 'transportValue', type : 'string' }, { - name : 'comments', + name : 'trackingNumberFromSynchrotron', type : 'string' - } ]; -}; + }, { + name : 'trackingNumberToSynchrotron', + type : 'string' + }, { + name : 'timeStamp', + type : 'string' + }, { + name : 'storageLocation', + type : 'string' + }, { + name : 'type', + type : 'string' + } -//StockSolutionGrid.prototype.refresh = function() { -// this.proposal.onInitialized.attach(function(sender){ -// -// }); -// this.proposal.init(Ext.urlDecode(window.location.href).sessionId); -//}; + ]; +}; -StockSolutionGrid.prototype._renderGrid = function() { +CaseGrid.prototype._renderGrid = function() { var _this = this; /** Store **/ + var data = this._prepareData(); this.store = Ext.create('Ext.data.Store', { - fields : this._getStoreFields(), //columns, - autoload : true + fields : this._getStoreFields(data), + autoload : true, + data : data }); + this._sort(this.store); - var filters = { - ftype : 'filters', - local : true, - filters : this.filters - }; - - var selModel = null; - - if (this.multiselect) { - selModel = Ext.create('Ext.selection.CheckboxModel', { - //multiSelect : false,//this.multiselect, - mode : 'SINGLE', - listeners : { - selectionchange : function(sm, selections) { - _this.selectedStockSolutions = []; - for ( var i = 0; i < selections.length; i++) { - _this.selectedStockSolutions.push(selections[i].raw); - } - } - } - }); - } else { - selModel = { - mode : 'SINGLE' - }; - } - - this.store.sort("stockSolutionId", "desc"); + this.store.loadData(data, false); this.grid = Ext.create('Ext.grid.Panel', { style : { padding : 5 }, - icon : '/ispyb/images/SampleHolder_24x24_01.png', - title : this.title, height : this.height, - width : this.width, - minWidth : this.minWidth, - selModel : selModel, store : this.store, columns : this._getColumns(), viewConfig : { stripeRows : true, listeners : { itemdblclick : function(dataview, record, item, e) { - _this.edit(record.raw.stockSolutionId); - }, - cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { - var adapter = null; - if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonUnpack') { - _this.grid.setLoading("ISPyB: Unpacking stock solution"); - adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender) { - _this.onProposalChanged.notify(); - _this.grid.setLoading(false); - }); - adapter.onError.attach(function(sender) { - _this.onProposalChanged.notify(); - _this.grid.setLoading(false); - }); - record.raw.boxId = null; - adapter.saveStockSolution(record.raw); - } - - if (grid.getGridColumns()[cellIndex].getId() == _this.id + "box") { - window.location = BUI.getShippingURL(BIOSAXS.proposal.getShipmentByDewarId(record.raw.boxId).shippingId); - } - if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonEdit') { - _this.edit(record.data.stockSolutionId); - } - - if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonRemove') { - _this.grid.setLoading("ISPyB: Removing stock solution"); - adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender) { - _this.onProposalChanged.notify(); - _this.grid.setLoading(false); - }); - adapter.onError.attach(function(sender) { - _this.onProposalChanged.notify(); - _this.grid.setLoading(false); - }); - adapter.removeStockSolution(record.data.stockSolutionId); - } - } - } - } - - }); - - var actions = _this._getTopButtons(); - this.grid.addDocked({ - xtype : 'toolbar', - items : actions - }); - - var i = null; - this.grid.getSelectionModel().on({ - selectionchange : function(sm, selections) { - if (selections.length) { - for ( i = 0; i < actions.length; i++) { - if (actions[i].enable) { - actions[i].enable(); - } - } - } else { - for ( i = 0; i < actions.length; i++) { - if (actions[i].alwaysEnabled == false) { - if (actions[i].disable) { - actions[i].disable(); - } - } - } - } - } - }); - return this.grid; -}; - -StockSolutionGrid.prototype.input = function() { - return { - proposal : DATADOC.getProposal_10() - }; -}; - -StockSolutionGrid.prototype.test = function(targetId) { - var stockSolutionGrid = new StockSolutionGrid({ - height : 300, - width : 900 - }); - BIOSAXS.proposal = new Proposal(stockSolutionGrid.input().proposal); - var panel = stockSolutionGrid.getPanel(); - stockSolutionGrid.refresh(BIOSAXS.proposal.getStockSolutions()); - panel.render(targetId); -}; - - -function SuperpositionGrid(args) { - this.height = null; - this.width = null; - this.id = BUI.id(); - - if (args != null) { - if (args.height != null) { - this.height = args.height; - - if (args.width != null) { - this.width = args.width; - } - } - } - - this.onSelected = new Event(this); -} - -SuperpositionGrid.prototype._prepareData = function(data) { - return data; -}; + _this._edit(record.raw); + }, + cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonEdit') { + _this._edit(record.raw); + } -SuperpositionGrid.prototype.refresh = function(subtractions) { - var data = []; - if (subtractions != null){ - if (subtractions.length > 0){ - for (j in subtractions){ - var subtraction = subtractions[j]; - if (subtraction.superposition3VOs != null){ - for (i in subtraction.superposition3VOs){ - data.push(subtraction.superposition3VOs[i]); + if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonRemove') { + _this.onRemoveButtonClicked.notify(_this.store.getAt(rowIndex).raw.dewarId); } } } + }, + selModel : { + mode : 'SINGLE' } - } - this.store.loadData(data); -}; + }); -SuperpositionGrid.prototype.getPanel = function() { - var _this = this; - this.store = Ext.create('Ext.data.Store', { - fields : [ 'abinitioModelPdbFilePath', 'aprioriPdbFilePath', 'alignedPdbFilePath' ], - data : [] + var actions = _this._getTopButtons(); + this.grid.addDocked({ + xtype : 'toolbar', + items : actions }); - this.selModel = Ext.create('Ext.selection.RowModel', { - allowDeselect : true, - mode : 'MULTI', - listeners : { - selectionchange : function(sm, selections) { - var selected = []; - for (var i = 0; i < selections.length; i++) { - selected.push(selections[i].raw); + this.grid.getSelectionModel().on({ + selectionchange : function(sm, selections) { + if (selections.length) { + for ( var i = 0; i < actions.length; i++) { + if (actions[i].enable) { + actions[i].enable(); + } + } + } else { + for ( var i = 0; i < actions.length; i++) { + if (actions[i].alwaysEnabled == false) { + if (actions[i].disable) { + actions[i].disable(); + } + } } - _this.onSelected.notify(selected); } } }); - - this.store.sort([ { - property : 'name', - direction : 'ASC' - } ]); + return this.grid; +}; - this.panel = Ext.create('Ext.grid.Panel', { - store : this.store, - selModel : this.selModel, - width : this.width, - height : this.height, - margin : 10, - deferredRender : false, - columns : [ { - text : 'Abinitio', - dataIndex : 'abinitioModelPdbFilePath', - hidden : false, - flex : 1, - renderer : function(val){ - return BUI.getFileName(val); - } - }, { - text : 'Apriori', - dataIndex : 'aprioriPdbFilePath', - hidden : false, - flex : 1, - renderer : function(val){ - return BUI.getFileName(val); - } - }, { - text : 'Aligned', - dataIndex : 'alignedPdbFilePath', - hidden : false, - flex : 1, - renderer : function(val){ - return BUI.getFileName(val); - } - } ], +CaseGrid.prototype.input = function() { + return { + proposal : DATADOC.getProposal_10(), + dewars : DATADOC.getDewars_10() - viewConfig : { - enableTextSelection : true, - preserveScrollOnRefresh : true - }, - listeners : { - cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + }; +}; - }, - afterrender : function() { - } - } +CaseGrid.prototype.test = function(targetId) { + var CaseGrid = new CaseGrid({ + height : 150 }); - return this.panel; + BIOSAXS.proposal = new Proposal(CaseGrid.input().proposal); + var panel = CaseGrid.getPanel(CaseGrid.input().dewars); + panel.render(targetId); + }; +/** + * A shipment may contains one or more cases where stock solutions and sample plates are stored + * + * @height + * @btnEditVisible + * @btnRemoveVisible + * + * #onEditButtonClicked + * #onAddButtonClicked + * #onRemoveButtonClicked + * #onDuplicateButtonClicked + */ +function ExampleGrid(args) { + + this.height = 100; + this.btnEditVisible = true; + this.btnRemoveVisible = true; + + if (args != null) { + if (args.height != null) { + this.height = args.height; + } + if (args.btnEditVisible != null) { + this.btnEditVisible = args.btnEditVisible; + } + if (args.btnRemoveVisible != null) { + this.btnRemoveVisible = args.btnRemoveVisible; + } + } + + /** Events **/ + this.onEditButtonClicked = new Event(this); + this.onAddButtonClicked = new Event(this); + this.onRemoveButtonClicked = new Event(this); + this.onDuplicateButtonClicked = new Event(this); +} + +ExampleGrid.prototype._getColumns = function() { + var _this = this; + + function actionItemRenderer(value, meta, record, rowIx, ColIx, store) { + /*if (record.raw.specimen3VOs.length > 0){ + return 'x-hide-display'; + }*/ + } + + var columns = [ + { + header : 'Code', + dataIndex : 'code', + name : 'code', + type : 'string', + flex : 1 + }, + { + header : 'Bar Code', + dataIndex : 'barCode', + name : 'barCode', + type : 'string', + flex : 1, + hidden : true + }, + { + header : 'BeamLine', + dataIndex : 'barCode', + type : 'string', + flex : 1, + renderer : function(comp, val, record) { + if (record.raw.sessionVO != null) { + return "" + record.raw.sessionVO.beamlineName + ""; + } + } + }, + { + header : 'Session', + dataIndex : 'barCode', + type : 'string', + flex : 1, + renderer : function(comp, val, record) { + if (record.raw.sessionVO != null) { + return "" + moment(record.raw.sessionVO.startDate).format("MMM Do YY") + ""; + } + } + }, + { + header : 'Status', + dataIndex : 'dewarStatus', + name : 'dewarStatus', + type : 'string', + flex : 1, + renderer : function(comp, val, record) { + if (new String(record.raw.dewarStatus).toUpperCase() == 'OPENED') { + return "" + new String(record.raw.dewarStatus).toUpperCase() + ""; + } + return "" + new String(record.raw.dewarStatus).toUpperCase() + ""; + } + }, + { + header : 'Type', + dataIndex : 'type', + name : 'type', + type : 'string', + flex : 1, + hidden : true + }, + { + header : 'Sample Plates', + dataIndex : 'plates', + name : 'plates', + type : 'string', + flex : 1, + hidden : true + }, + { + header : 'Stock Solutions', + dataIndex : 'plates', + name : 'plates', + type : 'string', + width : 100, + renderer : function(comp, val, record) { + var html = "
"; + var stockSolutions = BIOSAXS.proposal.getStockSolutionsByDewarId(record.raw.dewarId); + html = html + "" + stockSolutions.length + " x"; + return html + "
"; + } + }, { + header : 'isStorageDewar', + dataIndex : 'isStorageDewar', + name : 'isStorageDewar', + type : 'string', + flex : 1, + hidden : true + }, { + header : 'Tracking Number From Synchrotron', + dataIndex : 'trackingNumberFromSynchrotron', + name : 'trackingNumberFromSynchrotron', + type : 'string', + flex : 1, + hidden : true + }, { + header : 'Tracking Number To Synchrotron', + dataIndex : 'trackingNumberToSynchrotron', + name : 'trackingNumberToSynchrotron', + type : 'string', + flex : 1, + hidden : true + }, { + header : 'Storage Location', + dataIndex : 'storageLocation', + name : 'storageLocation', + type : 'string', + flex : 1, + hidden : false + }, { + header : 'Comments', + dataIndex : 'comments', + name : 'comments', + type : 'string', + flex : 1, + hidden : false + } + ]; + + if (this.btnEditVisible) { + columns.push({ + id : _this.id + 'buttonEdit', + text : '', + hidden : !_this.btnEditVisible, + width : 85, + sortable : false, + renderer : function(value, metaData, record, rowIndex, colIndex, store) { + return BUI.getGreenButton('EDIT'); + } + }); + } + + if (this.btnRemoveVisible) { + columns.push({ + id : _this.id + 'buttonRemove', + width : 85, + sortable : false, + renderer : function(value, metaData, record, rowIndex, colIndex, store) { + if (_this.btnRemoveVisible) { + return BUI.getRedButton('REMOVE'); + } + } + }); + } + + columns.push({ + dataIndex : 'comments', + type : 'string', + width : 85, + hidden : false, + renderer : function(comp, val, record) { + return BUI.getBlueButton("LABELS", { + href : BUI.getPrintcomponentURL(record.raw.dewarId) + }); + } + }); + + return columns; +}; + +ExampleGrid.prototype._getTopButtons = function() { + var _this = this; + /** Actions buttons **/ + var actions = []; + + /** ADD BUTTON **/ + actions.push(Ext.create('Ext.Action', { + icon : '../images/add.png', + text : 'Add', + disabled : false, + handler : function(widget, event) { + _this.onAddButtonClicked.notify(); + } + })); + + return actions; +}; + +ExampleGrid.prototype.refresh = function(dewars) { + this.features = dewars; + this.store.loadData(this._prepareData(dewars), false); +}; + +ExampleGrid.prototype._sort = function(store) { + store.sort('dewarId', 'DESC'); +}; + +ExampleGrid.prototype._prepareData = function() { + var data = []; + for ( var i = 0; i < this.features.length; i++) { + data.push(this.features[i]); + } + return data; +}; + +ExampleGrid.prototype.getPanel = function() { + this.grid = Ext.create('Ext.grid.Panel', { + style : { + padding : 5 + }, + height : this.height, + store : this.store, + columns : this._getColumns(), + viewConfig : { + stripeRows : true, + listeners : { + itemdblclick : function(dataview, record, item, e) { + _this._edit(record.raw); + }, + cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonEdit') { + _this._edit(record.raw); + } + + if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonRemove') { + _this.onRemoveButtonClicked.notify(_this.store.getAt(rowIndex).raw.dewarId); + } + } + } + }, + selModel : { + mode : 'SINGLE' + } + }); + + var actions = _this._getTopButtons(); + this.grid.addDocked({ + xtype : 'toolbar', + items : actions + }); + + this.grid.getSelectionModel().on({ + selectionchange : function(sm, selections) { + if (selections.length) { + for ( var i = 0; i < actions.length; i++) { + if (actions[i].enable) { + actions[i].enable(); + } + } + } else { + for ( var i = 0; i < actions.length; i++) { + if (actions[i].alwaysEnabled == false) { + if (actions[i].disable) { + actions[i].disable(); + } + } + } + } + } + }); + return this.grid; +}; + + +ExampleGrid.prototype._getStoreFields = function(data) { + return [ { + name : 'dewarId', + type : 'string' + }, { + name : 'barCode', + type : 'string' + }, { + name : 'code', + type : 'string' + }, { + name : 'comments', + type : 'string' + }, { + name : 'dewarStatus', + type : 'string' + }, { + name : 'isStorageDewar', + type : 'string' + }, { + name : 'plates', + type : 'string' + }, { + name : 'transportValue', + type : 'string' + }, { + name : 'trackingNumberFromSynchrotron', + type : 'string' + }, { + name : 'trackingNumberToSynchrotron', + type : 'string' + }, { + name : 'timeStamp', + type : 'string' + }, { + name : 'storageLocation', + type : 'string' + }, { + name : 'type', + type : 'string' + } + + ]; +}; + +ExampleGrid.prototype._renderGrid = function() { + var _this = this; + + /** Store **/ + var data = this._prepareData(); + this.store = Ext.create('Ext.data.Store', { + fields : this._getStoreFields(data), + autoload : true, + data : data + }); + this._sort(this.store); + + this.store.loadData(data, false); + + this.grid = Ext.create('Ext.grid.Panel', { + style : { + padding : 5 + }, + height : this.height, + store : this.store, + columns : this._getColumns(), + viewConfig : { + stripeRows : true, + listeners : { + itemdblclick : function(dataview, record, item, e) { + _this._edit(record.raw); + }, + cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonEdit') { + _this._edit(record.raw); + } + + if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonRemove') { + _this.onRemoveButtonClicked.notify(_this.store.getAt(rowIndex).raw.dewarId); + } + } + } + }, + selModel : { + mode : 'SINGLE' + } + }); + + var actions = _this._getTopButtons(); + this.grid.addDocked({ + xtype : 'toolbar', + items : actions + }); + + this.grid.getSelectionModel().on({ + selectionchange : function(sm, selections) { + if (selections.length) { + for ( var i = 0; i < actions.length; i++) { + if (actions[i].enable) { + actions[i].enable(); + } + } + } else { + for ( var i = 0; i < actions.length; i++) { + if (actions[i].alwaysEnabled == false) { + if (actions[i].disable) { + actions[i].disable(); + } + } + } + } + } + }); + return this.grid; +}; + +ExampleGrid.prototype.input = function() { + return { + proposal : DATADOC.getProposal_10(), + dewars : DATADOC.getDewars_10() + + }; +}; + +ExampleGrid.prototype.test = function(targetId) { + var ExampleGrid = new ExampleGrid({ + height : 150 + }); + BIOSAXS.proposal = new Proposal(ExampleGrid.input().proposal); + var panel = ExampleGrid.getPanel(ExampleGrid.input().dewars); + panel.render(targetId); + +}; + + /** - * See ExperimentGrid - * + * Shows a list of experiment AKA data acquisitions + * @height + * @sorters + * @minHeight + * @gridType: Ext.ux.LiveSearchGridPanel or Ext.grid.Panel + * @tbar true or false + * @grouping true or false + * @width + * @title + * #onEditButtonClicked */ -function TemplateGrid(args) { - - if (args == null) { - args = {}; - } - args.sorters = [ { - property : 'experimentId', - direction : 'DESC' - } ]; - - ExperimentGrid.prototype.constructor.call(this, args); -} - -TemplateGrid.prototype._getFilterTypes = ExperimentGrid.prototype._getFilterTypes; -TemplateGrid.prototype._prettyPrintMacromolecules = ExperimentGrid.prototype._prettyPrintMacromolecules; -TemplateGrid.prototype._getPercentage = ExperimentGrid.prototype._getPercentage; -TemplateGrid.prototype._getPercentageCollected = ExperimentGrid.prototype._getPercentageCollected; -TemplateGrid.prototype.getPercentageMerged = ExperimentGrid.prototype.getPercentageMerged; -TemplateGrid.prototype._prepareData = ExperimentGrid.prototype._prepareData; -TemplateGrid.prototype.getPanel = ExperimentGrid.prototype.getPanel; -TemplateGrid.prototype._renderGrid = ExperimentGrid.prototype._renderGrid; -TemplateGrid.prototype._editExperiment = ExperimentGrid.prototype._editExperiment; -TemplateGrid.prototype._removeExperimentById = ExperimentGrid.prototype._removeExperimentById; - -TemplateGrid.prototype._getTopButtons = function() { - /** Actions buttons * */ - var actions = []; - - actions.push(Ext.create('Ext.Action', { - icon : '../images/add.png', - text : 'Add experiment', - handler : function(widget, event) { - var wizardWidget = new WizardWidget({ - windowMode : true - }); - - wizardWidget.onFinished.attach(function(sender, result) { - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, data) { - var experiment = new Experiment(data); - BIOSAXS.openExperimentByExperiment(experiment); - wizardWidget.window.close(); - }); - wizardWidget.current.setLoading("ISPyB: Creating experiment"); - adapter.createTemplate(result.name, "comments", result.data); - }); - -// wizardWidget.draw(this.targetId, new ExperimentTypeWizardForm()); - wizardWidget.draw(this.targetId, new MeasurementCreatorStepWizardForm(BIOSAXS.proposal.getMacromolecules())); - } - })); - return actions; -}; - -TemplateGrid.prototype.refresh = function(data) { - var filtered = []; - for ( var i = 0; i < data.length; i++) { - if (data[i].experimentType == "TEMPLATE") { - filtered.push(data[i]); - } - } - this.store.loadData(this._prepareData(filtered), false); -}; - -TemplateGrid.prototype._getColumns = function() { - var _this = this; - function actionItemRenderer(value, meta, record, rowIx, ColIx, store) { - if (record.raw.buffer3VOs != null) { - if (record.raw.buffer3VOs.length > 0) { - return 'x-hide-display'; - } - } +function ExperimentGrid(args) { + this.width = "100%"; + this.height = 700; + this.minHeight = 500; - if (record.data.platesCount > 0) { - return 'x-hide-display'; - } - } + this.id = BUI.id(); + this.gridType = 'Ext.grid.Panel'; //'Ext.ux.LiveSearchGridPanel'; + this.tbar = false; + this.hideHeaders = false; + this.grouping = true; + this.title = null; - return [ { - xtype : 'rownumberer', - width : 40 - }, { - text : 'experimentId', - dataIndex : 'experimentId', - name : 'experimentId', - type : 'string', - hidden : true + this.filtered = null; + + /** maximum row count **/ + this.limit = 100; + + this.sessionIdFilter = null; + + /** if not null filtered by date **/ + this.date = null; + + this.sorters = [ { + property : 'date', + direction : 'DESC' }, { - text : 'Name', - dataIndex : 'name', - name : 'name', - type : 'string', - flex : 1 - }, + property : 'time', + direction : 'DESC' + } ]; - { - text : 'Type', - dataIndex : 'type', - name : 'type', - type : 'string', - hidden : true, - flex : 1, - renderer : function(val) { - return val; + this.dates = {}; + if (args != null) { + if (args.height != null) { + this.height = args.height; } - }, { - text : 'Macromolecules', - name : 'macromolecules_names', - dataIndex : 'macromolecules_names', - flex : 1, - renderer : function(val) { - return " " + val + ""; + if (args.limit != null) { + this.limit = args.limit; } - }, { - id : _this.id + 'GO', - width : 80, - sortable : false, - renderer : function(value, metaData, record, rowIndex, colIndex, store) { - return BUI.getGreenButton('EDIT'); + if (args.sorters != null) { + this.sorters = args.sorters; } - }, { - id : _this.id + 'REMOVE', - width : 100, - sortable : false, - renderer : function(value, metaData, record, rowIndex, colIndex, store) { - return BUI.getRedButton('REMOVE'); + if (args.sessionId != null){ + this.sessionIdFilter = args.sessionId; + } + if (args.filtered != null) { + this.filtered = args.filtered; + } + if (args.minHeight != null) { + this.minHeight = args.minHeight; + } + if (args.gridType != null) { + this.gridType = args.gridType; + } + if (args.tbar != null) { + this.tbar = args.tbar; + } + if (args.grouping != null) { + this.grouping = args.grouping; + } + if (args.width != null) { + this.width = args.width; + } + if (args.title != null) { + this.title = args.title; } - } ]; -}; - -TemplateGrid.prototype.input = function() { - var experiments = DATADOC.getExperimentList_10(); - return { - experiments : experiments, - proposal : new MeasurementGrid().input().proposal - - }; -}; - -TemplateGrid.prototype.test = function(targetId) { - var experimentGrid = new TemplateGrid({ - height : 350, - minHeight : 350, - width : 1000, - gridType : 'Ext.grid.Panel', - title : 'Experiments', - grouping : false, - tbar : true - }); - BIOSAXS.proposal = new Proposal(experimentGrid.input().proposal); - var panel = experimentGrid.getPanel(experimentGrid.input().experiments); - experimentGrid.refresh(experimentGrid.input().experiments); - panel.render(targetId); -}; - -function VolumeGrid() { - this.id = BUI.id(); + } + /** Events **/ + this.onEditButtonClicked = new Event(this); } -VolumeGrid.prototype.getPanel = function(experiment) { - this.experiment = experiment; - return this.render(); +ExperimentGrid.prototype._getFilterTypes = function() { + return []; }; -VolumeGrid.prototype.getVolumesPanel = function(data, title) { - var _this = this; - var store = Ext.create('Ext.data.Store', { - fields : [ 'name', 'volume', 'macromoleculeId', 'bufferId' ], - data : data - }); - store.sort([ { - property : 'name', - direction : 'ASC' - } ]); - - var grid = Ext.create('Ext.grid.Panel', { - title : title, - height : 400, - maxHeight : 400, - width : 900, - store : store, - margin : '10 0 50 10', - tbar : [ { - text : 'Go to Shipment', - icon : '../images/plane-small.gif', - handler : function() { - window.location = BUI.getCreateShipmentList(); - } - } ], - viewConfig : { - stripeRows : true, - listeners : { - cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { - if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonCreate') { - var stockSolutionWindow = new StockSolutionWindow(); - /** On stock solution SAVED **/ - stockSolutionWindow.onSaved.attach(function(sender, stockSolution) { - BIOSAXS.proposal.onInitialized.attach(function(sender, data) { - _this.refresh(_this.experiment); - }); - BIOSAXS.proposal.init(); - }); - var acronym = "ST"; - if (record.raw.macromoleculeId != null) { - acronym = acronym + "_" + BIOSAXS.proposal.getMacromoleculeById(record.raw.macromoleculeId).acronym; - } - if (record.raw.bufferId != null) { - acronym = acronym + "_" + BIOSAXS.proposal.getBufferById(record.raw.bufferId).acronym; - } - stockSolutionWindow.draw({ - concentration : record.raw.concentration, - macromoleculeId : record.raw.macromoleculeId, - bufferId : record.raw.bufferId, - name : acronym, - volume : record.raw.volume - }); - } - - if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonStockSolutions') { - var stockSolutionGrid = new StockSolutionGrid({ - btnAddVisible : false, - btnEditVisible : false, - btnRemoveVisible : false, - btnAddExisting : false, - isPackedVisible : true, - multiselect : false - }); - - var window = Ext.create('Ext.window.Window', { - title : 'Stock solutions by specimen', - height : 400, - width : 800, - layout : 'fit', - items : [ stockSolutionGrid.getPanel() ], - buttons : [ { - text : 'Close', - handler : function() { - window.close(); - } - } ] - - }).show(); - stockSolutionGrid.refresh(BIOSAXS.proposal.getStockSolutionsBySpecimen(record.raw.macromoleculeId, record.raw.bufferId)); - } - } - } - }, - columns : [ -// { -// text : '', -// dataIndex : 'macromoleculeId', -// width : 20, -// renderer : function(val, y, sample) { -// if (val != null) { -//// return BUI.getRectangleColorDIV(BIOSAXS.proposal.macromoleculeColors[val], 10, 10); -// return BUI.getRectangleColorDIV(_this.experiment.macromoleculeColors[val], 10, 10); -// } -// } -// }, -// { -// text : '', -// dataIndex : 'bufferId', -// width : 20, -// renderer : function(val, y, sample) { -// if (val != null) { -// return BUI.getRectangleColorDIV(_this.experiment.getSpecimenColorByBufferId(val), 10, 10); -// } -// } -// }, - { - text : 'Specimen', - dataIndex : 'name', - flex : 0.5 - }, - { - text : 'Estimated Volume', - dataIndex : 'volume', - tooltip : 'Estimation of the maximum volume needed for making this experiment', - flex : 0.5, - editor : { - allowBlank : true - }, - renderer : function(val, y, sample) { - return BUI.formatValuesUnits(sample.data.volume, 'µl', { - fontSize : 16, - decimals : 2, - unitsFontSize : this.unitsFontSize - }); - } - }, - { - text : 'Stock Solution', - id : _this.id + 'buttonStockSolutions', - dataIndex : 'name', - flex : 0.5, - tooltip : 'Stock Solutions containing this specimen in this proposal', - renderer : function(value, metaData, record, rowIndex, colIndex, store) { - var macromoleculeId = record.raw.macromoleculeId; - var bufferId = record.raw.bufferId; - var stockSolutions = BIOSAXS.proposal.getStockSolutionsBySpecimen(macromoleculeId, bufferId); - if (stockSolutions.length > 0) { - return "
" + stockSolutions.length + " x
"; - } +ExperimentGrid.prototype._prepareData = function(rows) { + var data = []; + var count = 0; + + rows.sort(function(a,b){return b.experimentId - a.experimentId;}); + for ( var i = 0; i < rows.length; i++) { + var row = rows[i]; + this.dates[moment(row.creationDate).format("YYYYMMDD")] = moment(row.creationDate).format("MMM Do YY"); + if ( + ( this.filtered == null || row.experimentType == this.filtered ) && (count < this.limit || this.limit == null ) && (this.sessionIdFilter == null || this.date != null || (this.date == null && this.sessionIdFilter == row.sessionId)) + ){ - } - }, { - id : _this.id + 'buttonCreate', - text : '', - tooltip : 'Create a new stock solution for shipping', - width : 170, - sortable : false, - renderer : function(value, metaData, record, rowIndex, colIndex, store) { - return BUI.getGreenButton('NEW STOCK SOLUTION', { - width : 160 + data.push({ + experimentId : row.experimentId, + status : row.status, + dataAcquisitionFilePath : row.dataAcquisitionFilePath, + type : row.experimentType, + name : row.name, + macromolecules_names : row.macromolecules, + percentageAnalysed : { + value : (row.dataCollectionDoneCount / row.dataCollectionCount) * 100, + text : row.dataCollectionDoneCount + " of " + row.dataCollectionCount + }, + percentageCollected : { + value : (row.measurementDoneCount / row.measurementCount) * 100, + text : row.measurementDoneCount + " of " + row.measurementCount + }, + percentageMerged : { + value : (row.measurementAveragedCount / row.measurementCount) * 100, + text : row.measurementAveragedCount + " of " + row.measurementCount + }, + date : moment(row.creationDate).format("YYYYMMDD"), + time : moment(row.creationDate).format("YYYYMMDDHHmmss"), + creationDate : row.creationDate }); - } - } ] - }); - return grid; + count ++; + } + } + return data; +}; +ExperimentGrid.prototype.getPanel = function(experiments) { + this.features = experiments; + return this._renderGrid(experiments); }; -VolumeGrid.prototype._prepareData = function(experiment) { - var keys = {}; - for ( var i = 0; i < experiment.getSamples().length; i++) { - var sample = experiment.getSamples()[i]; - var key = ""; - if (sample.macromoleculeId == null) { - key = experiment.getBufferById(sample.bufferId).acronym; - if (keys[key] == null) { - keys[key] = { - macromoleculeId : sample.macromoleculeId, - name : key, - bufferId : sample.bufferId, - volume : 0 - }; - } - keys[key].volume = Number(sample.volume) + Number(keys[key].volume); +ExperimentGrid.prototype.refresh = function(experiments) { + this.experiments = experiments; + var filtered = []; + for ( var i = 0; i < experiments.length; i++) { + if (experiments[i].experimentType != "TEMPLATE") { + filtered.push(experiments[i]); } + } - if ((sample.macromolecule3VO != null) || (sample.macromoleculeId != null)) { - macromoleculeId = sample.macromoleculeId; - if (sample.macromoleculeId == null) { - sample.macromoleculeId = sample.macromolecule3VO.macromoleculeId; - } - key = BIOSAXS.proposal.getMacromoleculeById(sample.macromoleculeId).acronym + " + " + experiment.getBufferById(sample.bufferId).acronym; - if (keys[key] == null) { - keys[key] = { - macromoleculeId : sample.macromoleculeId, - name : key, - bufferId : sample.bufferId, - volume : 0 - }; - } - keys[key].volume = Number(sample.volume) + Number(keys[key].volume); + this.parsedData = this._prepareData(filtered); + + var day = {}; + var dates = []; + for ( var i = 0; i < this.experiments.length; i++) { + var date = moment(this.experiments[i].creationDate).format("MMM Do YYYY"); + if (day[date] == null){ + dates.push({ + date : date, + value : moment(this.experiments[i].creationDate) + }); + day[date] = true; } } - var data = []; - for (var keyId in keys) { - data.push(keys[keyId]); + + this.storeDate.loadData(dates, false); + this.store.loadData(this.parsedData, false); + + /** If it has already been filtered by date we keep the filter **/ + if (this.date != null){ + this._filterByDate(this.date); } - - return data; }; -VolumeGrid.prototype.refresh = function(experiment) { - this.experiment = experiment; - this.macromoleculeGrid.getStore().loadData(this._prepareData(this.experiment), false); -}; +ExperimentGrid.prototype._getTopButtons = function() { + var _this = this; + /** Actions buttons **/ + var actions = []; -VolumeGrid.prototype.render = function() { - this.macromoleculeGrid = this.getVolumesPanel(this._prepareData(this.experiment), "Estimation of required Volume"); + actions.push(Ext.create('Ext.Action', { + icon : '../images/calendar_icon.png', + text : 'Show Calendar', + disabled : false, + handler : function(widget, event) { + var window = Ext.create('Ext.window.Window', { + title : 'Calendar', + width : 600, + height : 600, + modal : true, + closable : true, + layout : { + type : 'vbox', + align : 'stretch' + }, + items : [ { + xtype : 'label', + html : 'Click on a data acquisition to select:', + margin : '5 5 5 5' + }, { + html : '
', + margin : '5 5 5 5' + } - return { - xtype : 'container', - layout : 'vbox', - margin : "0, 0, 0, 5", - items : [ { - xtype : 'container', - layout : 'hbox', - margin : "0, 0, 0, 0", - items : [ this.macromoleculeGrid ] - } ] - }; + ] + }).show(); + + var calendarWidget = new CalendarWidget({ + height : 450 + }); + var aux = _this.limit; + /** we remove the limit temporarily **/ + _this.limit = null; + _this.sessionIdFilter = null; + calendarWidget.loadData(_this._prepareData(_this.experiments)); + _this.limit = aux; + calendarWidget.draw('calendar'); + calendarWidget.onClick.attach(function(sender, date) { + date = moment(date, "YYYY-MM-DD"); + _this._filterByDate(date); + window.close(); + + }); + + } + })); + this.storeDate = Ext.create('Ext.data.ArrayStore', { + fields: ['date', 'value'], + data : [] + }); + + this.dateMenu = Ext.create('Ext.form.field.ComboBox', { + hideLabel: true, + store: this.storeDate, + displayField: 'date', + typeAhead: true, + queryMode: 'local', + margin : '0 0 0 30', + triggerAction: 'all', + emptyText:'Select a date...', + selectOnFocus:true, + width:135, + listeners:{ + scope: this, + 'select': function (a,b,c){ + _this.limit = null; + _this._filterByDate(moment(b[0].raw.value, "YYYY-MM-DD")); + } + } + }); + + actions.push(this.dateMenu); + + actions.push("->"); + if (_this.filtered != null){ + actions.push({ + html : "Experiment Type: " + _this.filtered +"" + }); + } + else{ + actions.push({ + html : "Experiment Type: ALL" + }); + } + return actions; }; -VolumeGrid.prototype.input = function(experiment) { - return { - experiment : DATADOC.getExperiment_10() - }; +/** + * Date format: "YYYY-MM-DD" + */ +ExperimentGrid.prototype._filterByDate = function(date) { + var experimentsFiltered = []; + /** Getting all the experiments of date**/ + for ( var i = 0; i < this.experiments.length; i++) { + var experiment = this.experiments[i]; + if (experiment.creationDate != null) { + var experimentDate = moment(experiment.creationDate); + if (experimentDate.year() == date.year()) { + if (experimentDate.month() == date.month()) { + if (experimentDate.date() == date.date()) { + experimentsFiltered.push(experiment); + } + } + } + } + } + var parsedData = this._prepareData(experimentsFiltered); + this.store.loadData(parsedData); + this.date = date; }; -VolumeGrid.prototype.test = function(targetId) { - var volumeGrid = new VolumeGrid(); - BIOSAXS.proposal = new Proposal(new MeasurementGrid().input().proposal); - var panel = volumeGrid.getPanel(new Experiment(new VolumeGrid().input().experiment)); - Ext.create('Ext.panel.Panel', { - height : 500, - width : 1000, - renderTo : targetId, - items : [ panel ] +/** Only for templates **/ +ExperimentGrid.prototype._removeExperimentById = function(experimentId) { + var _this = this; + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(evt, args) { + _this.grid.setLoading(false); + document.getElementById(BIOSAXS.targetId).innerHTML = ""; + BIOSAXS.start(BIOSAXS.targetId); }); + this.grid.setLoading("Removing experiment "); + adapter.removeExperimentById(experimentId); }; - -/** - * Example of a tab panel to be populated with widgets - * - * @width width in pixels - * @height height in pixels - * - * #myEvent event that this class is supposed to throw - * - **/ -function ExampleTabs(args) { - this.width = 500; - this.height = 500; + +ExperimentGrid.prototype._renderGrid = function() { + var _this = this; + + /** Store **/ + this.store = Ext.create('Ext.data.Store', { + fields : this._getColumns(), + groupField : 'date', + autoload : true, + data : [], + remoteSort: false, + sorters : this.sorters + }); + - if (args != null){ - if (args.width != null){ - this.width= args.width; - } - if (args.height != null){ - this.height= args.height; - } - } - /** Events **/ - this.myEvent = new Event(); -} - -/** Populate the widget **/ -ExampleTabs.prototype.refresh = function(macromolecule) { -}; + var groupingFeature = Ext.create('Ext.grid.feature.Grouping', { + groupHeaderTpl : Ext.create('Ext.XTemplate', + "
{name:this.formatName}
", { + formatName : function(name) { + return _this.dates[name]; + } + }), + hideGroupedHeader : true, + startCollapsed : false + }); -/** It creates a tab panel with the specified with and height **/ -ExampleTabs.prototype.getPanel = function() { - this.panel = Ext.createWidget('tabpanel', { - height : this.height, - width : this.width, - style : { - padding : 2 + this.features = []; + if (this.grouping) { + this.features.push(groupingFeature); + } + + /** Grid **/ + this.grid = Ext.create(this.gridType, { + hideHeaders : this.hideHeaders, + resizable : true, + title : this.title, + width : this.width, + minHeight : this.minHeight, + height : this.height, + features : this.features, + store : this.store, + columns : this._getColumns(), + selModel : { + mode : 'SINGLE' }, - items : [ { - tabConfig : { - title : 'Test', - icon : '/ispyb/images/plane-small.gif' + viewConfig : { + stripeRows : true, + getRowClass : function(record, rowIdx, params, store) { + if (record.raw.type == "TEMPLATE") { + return "template-color-row"; + } + if ((record.raw.type == "CALIBRATION") && (record.raw.status == "FINISHED")) { + return "blue-row"; + } }, - items : [ { - xtype : 'container', - margin : '5 5 5 5', - items : [ ] - } ] - } ] + listeners : { + itemdblclick : function(dataview, record, item, e) { + _this._editExperiment(record.raw.experimentId); + }, + cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'GO') { + _this._editExperiment(record.raw.experimentId); + } + + if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'REMOVE') { + _this._removeExperimentById(record.raw.experimentId); + } + + } + } + } }); - return this.panel; -}; - -/** - * Shows an experiments with the specimens, measurements, analysis tabs where - * results are shown and the frames widget - * - * @targetId - */ -function ExperimentTabs(targetId) { - this.height = 900; - this.targetId = targetId; + var actions = _this._getTopButtons(); - this.id = BUI.id(); - var _this = this; + if (this.tbar) { + this.grid.addDocked({ + xtype : 'toolbar', + items : actions + }); + this.grid.getSelectionModel().on({ + selectionchange : function(sm, selections) { + if (selections.length) { + for ( var i = 0; i < actions.length; i++) { + if (actions[i].enable) { + actions[i].enable(); + } + } + } else { + for ( var i = 0; i < actions.length; i++) { + if (actions[i].alwaysEnabled == false) { + if (actions[i].disable) { + actions[i].disable(); + } + } + } + } + } + }); + } - this.INTERVAL_UPDATE = BUI.getUpdateInterval(); + return this.grid; +}; - this.gridHeight = 1000; - /** data * */ - this.experiment = null; +ExperimentGrid.prototype._getColumns = function() { + var _this = this; + function actionItemRenderer(value, meta, record, rowIx, ColIx, store) { + if (record.raw.buffer3VOs != null) { + if (record.raw.buffer3VOs.length > 0) { + return 'x-hide-display'; + } + } - - /** Specimen Widget contains a specimenGrid and a sampleChangerWidget than can be displayed with are vertical or horizontal layout **/ - this.specimenWidget = new SpecimenWidget({ - height : 600 - }); - - - /** For Overview * */ - /*this.samplePlateGroupWidget = new SamplePlateGroupWidget({ - showTitle : false, - height : 250, - margin : 5, - border : 0 + if (record.data.platesCount > 0) { + return 'x-hide-display'; + } + } - });*/ + return [ + { + text : 'experimentId', + dataIndex : 'experimentId', + name : 'experimentId', + type : 'string', + hidden : true + }, + { + xtype : 'rownumberer', + width : 40 + }, + { + text : 'Name', + dataIndex : 'name', + name : 'name', + type : 'string', + flex : 1 + }, - /** For Measurements * */ - /*this.measurementSamplePlateGroupWidget = new SamplePlateGroupWidget({ - showTitle : false, - height : 250, - margin : '5 0 0 0', - border : 0 - });*/ + { + text : 'Type', + dataIndex : 'type', + name : 'type', + type : 'string', + flex : 1, + renderer : function(val) { + if (val == "CALIBRATION") { + return "" + val + ""; + } + + return val; + } + }, + { + text : 'Macromolecules', + name : 'macromolecules_names', + dataIndex : 'macromolecules_names', + flex : 1, + renderer : function(val) { + if (val != null) { + return " " + val + ""; + } + return " Information not available"; + } + }, + { + text : 'Buffers', + dataIndex : 'buffer_names', + name : 'buffer_names', + flex : 1, + hidden : true, + renderer : function(val) { + return "Buffer/s: " + val + ""; + } + }, + { + text : 'Status', + dataIndex : 'status', + name : 'status', + type : 'string', + flex : 1, + renderer : function(val, x, sample) { + if (sample.raw.type == "TEMPLATE") { + return "READY"; + } + if (sample.raw.status == "ABORTED") { + return "" + val + ""; + } + return "" + val + ""; + } + }, + { + text : 'Download', + dataIndex : 'creationDate', + name : 'creationDate', + renderer : function(val, x, sample) { + if (sample != null) { + if (sample.raw.type == "HPLC") { + return; + } + return BUI.getZipHTMLByExperimentId(sample.raw.experimentId, sample.raw.name); + } + }, + width : 100 - this.measurementGridDone = new MeasurementGrid({ - height : 600, - minHeight : 400, - maxHeight : 800, - positionColumnsHidden : true, - showTitle : false, - estimateTime : true, - width : 900, - maxWidth : 1500, - addBtnEnable : false, - markDone : true, - removeBtnEnabled : false - }); - this.measurementGridDone.onSelected.attach(function(sender, measurements) { - var specimens = []; - for ( var i = 0; i < measurements.length; i++) { - specimens.push(_this.experiment.getSampleById(measurements[i].specimenId)); - } - //_this.measurementSamplePlateGroupWidget.selectSpecimens(specimens); - }); + }, + { + header : 'Measurements', + dataIndex : 'percentageCollected', + name : 'percentageCollected', + type : 'string', + renderer : function(val, y, sample) { + if ((sample.raw.type == "TEMPLATE") || (sample.raw.type == "HPLC")) { + return; + } + return "
" + BUI.getProgessBar(sample.raw.percentageCollected.value, sample.raw.percentageCollected.text) + "
"; + }, + width : 100 + }, + { + header : 'Averaged', + dataIndex : 'percentageMerged', + name : 'percentageMerged', + type : 'string', + renderer : function(val, y, sample) { + if ((sample.raw.type == "TEMPLATE") || (sample.raw.type == "HPLC")) { + return; + } + return "
" + BUI.getProgessBar(sample.raw.percentageMerged.value, sample.raw.percentageMerged.text) + "
"; + }, + width : 100 + }, + { + header : 'Subtractions', + dataIndex : 'percentageAnalysed', + name : 'percentageAnalysed', + type : 'string', + renderer : function(val, y, sample) { + if ((sample.raw.type == "TEMPLATE") || (sample.raw.type == "HPLC")) { + return; + } + return "
" + BUI.getProgessBar(sample.raw.percentageAnalysed.value, sample.raw.percentageAnalysed.text) + "
"; + }, + width : 100 + }, - /** AnalysisGrid * */ - this.analysisGrid = new AnalysisGrid({ - height : Ext.getBody().getViewSize().height * 0.9 - 300, - positionColumnsHidden : true, - sorters : [ { - property : 'priorityLevelId', - direction : 'ASC' - } ] - }); - - - /** Queue * */ - this.queueGrid = new QueueGrid({ - url : BUI.getQueueUrlByExperiment(), - height : Ext.getBody().getViewSize().height * 0.9 - 300, - width : Ext.getBody().getViewSize().width * 0.9 - 300, - isGrouped : true, - tbar : false, - bbar : false, - sorter : [{ - property: 'measurementId', - direction: 'DESC' - }] - }); + { + text : 'time', + dataIndex : 'time', + name : 'time', + hidden : true, + renderer : function(val) { + return val; + }, + width : 100 -} + }, { + text : 'Date', + dataIndex : 'date', + name : 'date', + renderer : function(val) { + return val; + }, + width : 100 -ExperimentTabs.prototype.error = function(error) { - var e = JSON.parse(error); - showError(e); - this.panel.setLoading(false); -}; + }, -ExperimentTabs.prototype.draw = function(experiment) { - this.renderDataAcquisition(experiment); -}; + { + text : 'Time', + dataIndex : 'creationDate', + name : 'creationDate', + renderer : function(val) { + return moment(val).format(" HH:mm:ss"); + }, + width : 100 -ExperimentTabs.prototype.refreshAnalysisData = function() { - var _this = this; - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, data) { - _this.analysisGrid.refresh(data, {experiment : _this.experiment}); - }); - adapter.getAnalysisInformationByExperimentId(this.experiment.experimentId); - - /** Auto load **/ - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, data) { - _this.queueGrid.refresh(data); - }); - adapter.getCompactAnalysisByExperimentId(this.experiment.experimentId); - this.queueGrid.store.proxy.url = BUI.getQueueUrlByExperiment(this.experiment.experimentId); - this.queueGrid.refresh(); + }, { + id : _this.id + 'GO', + width : 80, + sortable : false, + renderer : function(value, metaData, record, rowIndex, colIndex, store) { + return BUI.getGreenButton('GO'); + } + } ]; }; -ExperimentTabs.prototype.getSpecimenContainerHeight = function(experiment) { - var maxItems = 0; - if (maxItems < experiment.getSamples().length + 1) { - maxItems = experiment.getSamples().length + 1; - } - - var height = (maxItems + 1) * 40 + 40; - if (height > 400) { - height = 400; +/** Changes location.href in order to edit the experiment **/ +ExperimentGrid.prototype._editExperiment = function(experimentId) { + if (Ext.urlDecode(window.location.href).sessionId != null) { + location.href = 'viewProjectList.do?reqCode=display&experimentId=' + experimentId + '&sessionId='+ Ext.urlDecode(window.location.href).sessionId; + } else { + location.href = 'viewProjectList.do?reqCode=display&experimentId=' + experimentId; } - return height; -}; - -ExperimentTabs.prototype.getExperimentTitle = function() { - var experimentHeaderForm = new ExperimentHeaderForm(); - return experimentHeaderForm.getPanel(this.experiment); }; -ExperimentTabs.prototype.renderDataAcquisition = function(experiment) { - var _this = this; - this.experiment = experiment; +ExperimentGrid.prototype.input = function() { + var experiments = DATADOC.getExperimentList_10(); + return { + experiments : experiments, + proposal : new MeasurementGrid().input().proposal - var specimenGrid = new SpecimenGrid({ - height : 400, - maxHeight : 500, - width : 890 - }); + }; +}; - specimenGrid.onClick.attach(function(sender, args) { - }); +ExperimentGrid.prototype.test = function(targetId) { + var experimentGrid = new ExperimentGrid({ + height : 350, + minHeight : 350, + width : 1000 - specimenGrid.onSelected.attach(function(sender, specimens) { - //_this.samplePlateGroupWidget.selectSpecimens(specimens); - }); - var specimenContainer = this.specimenWidget.getPanel(); - this.specimenWidget.refresh(experiment); - /* - var specimenContainer = Ext.create('Ext.container.Container', { - layout : 'hbox', - width : 900, - padding : '10 0 0 2', - items : [ specimenGrid.getPanel() ] }); + BIOSAXS.proposal = new Proposal(experimentGrid.input().proposal); + var panel = experimentGrid.getPanel(experimentGrid.input().experiments); + experimentGrid.refresh(experimentGrid.input().experiments); + panel.render(targetId); +}; + + +function FitStructureToExperimentDataGrid(args) { + this.height = null; + this.width = null; + this.id = BUI.id(); + + if (args != null) { + if (args.height != null) { + this.height = args.height; + + if (args.width != null) { + this.width = args.width; + } + } + } + + this.onSelected = new Event(this); +} + +FitStructureToExperimentDataGrid.prototype.getStructuresByMacromolecule = function(macromolecule) { + var structures = []; + if (macromolecule.structure3VOs != null) { + if (macromolecule.structure3VOs.length > 0) { + for (var i = 0; i < macromolecule.structure3VOs.length; i++) { + structures.push(macromolecule.structure3VOs[i]); + } + } + } + return structures; +}; + +FitStructureToExperimentDataGrid.prototype._prepareData = function(subtractions) { + var data = []; + for (var i = 0; i < subtractions.length; i++) { + + for (var j = 0; j < subtractions[i].fitStructureToExperimentalData3VOs.length; j++) { + var fit = subtractions[i].fitStructureToExperimentalData3VOs[j]; + data.push({ + fit : fit.fitFilePath, + fitStructureToExperimentalDataId : fit.fitStructureToExperimentalDataId, + mixtureToStructure3VOs : fit.mixtureToStructure3VOs, + subtractedFile : subtractions[i].substractedFilePath + }); + } + } + return data; + +}; + +FitStructureToExperimentDataGrid.prototype.refresh = function(subtractions) { + this.store.loadData(this._prepareData(subtractions), false); +}; + +FitStructureToExperimentDataGrid.prototype.getPanel = function() { + var _this = this; + this.store = Ext.create('Ext.data.Store', { + fields : [ 'fit', 'subtractedFile', 'structureId', 'fitFilePath', 'mixtureToStructure3VOs' ], + data : [] + }); + + this.store.sort([ { + property : 'name', + direction : 'ASC' + } ]); + + this.selModel = Ext.create('Ext.selection.RowModel', { + allowDeselect : true, + mode : 'MULTI', + listeners : { + selectionchange : function(sm, selections) { + var selected = []; + for (var i = 0; i < selections.length; i++) { + selected.push(selections[i].raw); + } + _this.onSelected.notify(selected); + } + } + }); + + this.panel = Ext.create('Ext.grid.Panel', { + store : this.store, + deferredRender : false, + width : this.width, + height : this.height, + margin : 10, + selModel : this.selModel, + columns : [ { + text : 'Name', + dataIndex : 'fit', + flex : 1, + renderer : function(val, b, record) { + return BUI.getFileName(val); + } + }, { + text : 'PDB', + dataIndex : 'mixtureToStructure3VOs', + flex : 1, + renderer : function(values, b, record) { + var html = ""; + for (var i = 0; i < values.length; i++) { + var structure = BIOSAXS.proposal.getStructuresById(values[i].structureId); + html = html + ""; + if (structure != null){ + html = html + ""; + } + html = html + ""; + } + return html + "
" + structure.name + "
"; + } + }, { + text : 'Volume Fraction', + dataIndex : 'mixtureToStructure3VOs', + flex : 1, + renderer : function(values, b, record) { + var html = ""; + for (var i = 0; i < values.length; i++) { + html = html + ""; + html = html + ""; + html = html + ""; + } + + return html + "
" + values[i].volumeFraction + "
"; + } + },{ + text : 'Subtraction', + dataIndex : 'subtractedFile', + flex : 1, + renderer : function(values, b, record) { + return values.split("/")[values.split("/").length - 1]; + } + },], + + viewConfig : { + enableTextSelection : true, + preserveScrollOnRefresh : true + }, + listeners : { + cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + }, + afterrender : function() { + } + } + }); + return this.panel; +}; + +/** Static method **/ +FitStructureToExperimentDataGrid.prototype.RUNFitScattering = function(subtractionId, structureId) { + var _this = this; + /** Add to Workflow **/ + var adapter = new BiosaxsDataAdapter(); + var workflow = { + 'workflowTitle' : 'FitExperimentalDatatoStructure', + 'comments' : 'FitExperimentalDatatoStructure run from ISPyB for subtractionId: ' + subtractionId + ' and structureId ' + structureId + } + + var inputs = [ { + name : 'subtractionId', + value : subtractionId + }, { + name : 'structureId', + value : structureId + } ]; + + adapter.onSuccess.attach(function(sender, data) { + /** Add to Fit **/ + var adapter2 = new BiosaxsDataAdapter(); + var fit = { + 'workflowId' : data.workflowId, + 'subtractionId' : subtractionId, + 'structureId' : structureId, + 'comments' : 'Sent to workflow engine' + } + adapter2.onSuccess.attach(function(sender, fit) { + _this.refresh(_this.subtractionId, _this.macromolecule); + }); + adapter2.addFitStructureData(fit); + + }); + adapter.addWorkflow(workflow, inputs); + +}; + +/** + * It shows buffer grid with a top bar with "Add" button + * + * @height + * @searchBar + * @collapsed + * @width + */ +function FrameGrid(args) { + this.height = 500; + this.width = 500; + this.searchBar = false; + this.tbar = false; + this.collapsed = false; - specimenGrid.refresh(experiment);*/ - var experimentList = new ExperimentList([ _this.experiment ]); - var measurementContainer = Ext.create('Ext.container.Container', { - layout : 'vbox', - padding : '5px 0px 0px 10px', - items : [] - }); - measurementContainer.insert(0, _this.measurementGridDone.getPanel(this.experiment.getMeasurements(), experimentList)); -// measurementContainer.insert(1, _this.measurementSamplePlateGroupWidget.getPanel(experiment)); + if (args != null) { + if (args.height != null) { + this.height = args.height; + } + + if (args.searchBar != null) { + this.searchBar = args.searchBar; + } - // this.dataCollectionCurveVisualizer = new DataCollectionCurveVisualizer(); - // this.dataCollectionCurveVisualizer.experiments = [experiment]; - // this.dataCollectionCurveVisualizer.dataCollectionFrameTree.experiments = - // [experiment]; - // this.dataCollectionCurveVisualizer.dataCollections = - // experiment.getDataCollections(); + if (args.tbar != null) { + this.tbar = args.tbar; + } - this.panel = Ext.createWidget('tabpanel', { - plain : true, - style : { - padding : 2 - }, - items : [ { - tabConfig : { - id : 'genralTabl', - title : "Overview" - }, - items : [ specimenContainer ] - }, { - tabConfig : { - title : 'Measurements' - }, - items : [ measurementContainer] - }, { - tabConfig : { - id : 'SpecimenTab', - title : 'Analysis', - hidden : this.isTemplate() - }, - items : [ { - xtype : 'container', - layout : 'vbox', - padding : '5px 0px 10px 10px', - items : [ _this.analysisGrid.getPanel([]) ] - } ] - }, - { - tabConfig : { - id : 'newAnalysisTab', - title : 'Analysis BETA', - hidden : this.isTemplate() - }, - items : [ { - xtype : 'container', - layout : 'vbox', - padding : '5px 0px 10px 10px', - items : [ _this.queueGrid.getPanel([]) ] - } ] + if (args.collapsed != null) { + this.collapsed = args.collapsed; } - ] + + if (args.width != null) { + this.width = args.width; + } + } +} + +FrameGrid.prototype._edit = function(bufferId) { + var _this = this; + var window = new BufferWindow(); + window.onSuccess.attach(function(sender, proposal) { + _this.store.loadData(BIOSAXS.proposal.getBuffers()); }); + window.draw(BIOSAXS.proposal.getBufferById(bufferId)); +}; - return this.getPanel(this.panel); +FrameGrid.prototype.refresh = function(buffers, experimentId) { + this.experimentId = experimentId; + this.store.loadData(this._prepareData(buffers), false); }; -ExperimentTabs.prototype.isTemplate = function() { - if (this.experiment.json.type == "TEMPLATE") { - return true; - } - return false; +FrameGrid.prototype._prepareData = function(buffers) { + return buffers; }; -ExperimentTabs.prototype.update = function() { +FrameGrid.prototype._getTbar = function() { var _this = this; - var inter; - if (!_this.isTemplate()) { - function updateExperiments() { - _this.refreshAnalysisData(); - window.clearInterval(inter); - inter = setInterval(function() { - updateExperiments(); - }, _this.INTERVAL_UPDATE); - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, data) { - var experiment = new Experiment(data); - _this.measurementGridDone.refresh(experiment.getMeasurements(), new ExperimentList([ experiment ])); + var actions = []; + + actions.push(Ext.create('Ext.Action', { + icon : '../images/add.png', + text : 'Add Buffer', + disabled : false, + handler : function(widget, event) { + var window = new BufferWindow(); + window.onSuccess.attach(function(sender) { + _this.store.loadData(BIOSAXS.proposal.getBuffers()); }); - adapter.getExperimentById(_this.experiment.json.experimentId, "MEDIUM"); + window.draw({}); } - inter = setInterval(function() { - updateExperiments(); - }, _this.INTERVAL_UPDATE); - } + })); + return actions; }; -ExperimentTabs.prototype.getPanel = function(panel) { +FrameGrid.prototype.getPanel = function(buffers) { var _this = this; - if (this.experimentPanel == null) { - this.experimentPanel = Ext.create('Ext.container.Container', { - bodyPadding : 2, - width : Ext.getBody().getViewSize().width * 0.9, - renderTo : this.targetId, - style : { - padding : 2 - }, - items : [ this.getExperimentTitle(), this.panel ], - listeners : { - afterrender : function() { - _this.refreshAnalysisData(); - _this.update(); - } - } - }); - } - - return this.experimentPanel; -}; - -ExperimentTabs.prototype.input = function() { - return { - experiment : DATADOC.getExperiment_10(), - proposal : DATADOC.getProposal_10() - }; -}; - -ExperimentTabs.prototype.test = function(targetId) { - var experimentTabs = new ExperimentTabs(targetId); - BIOSAXS.proposal = new Proposal(experimentTabs.input().proposal); - experimentTabs.draw(new Experiment(experimentTabs.input().experiment)); -}; + this.store = Ext.create('Ext.data.Store', { + fields : [ 'frameNumber', 'I0', 'Rg', 'Mass', 'Vc', 'Qr', 'quality'], + data : buffers + }); - -/** - * Shows an experiments with the specimens, measurements, analysis tabs where results are shown and the frames widget - * - * @targetId - */ -function HPLCTabs(targetId) { - this.height = 1400; - this.targetId = targetId; + this.store.sort('frameNumber'); - this.id = BUI.id(); + var type = 'Ext.grid.Panel'; + if (this.searchBar == true) { + type = 'Ext.ux.LiveSearchGridPanel'; + } - this.gridHeight = 600; - this.pointsCount = 1036; - this.plotHeight = 350; - this.plotWidth = 800; - this.plotPanelPadding = 5; + this.grid = Ext.create(type, { + store : this.store, + height : this.height, + width : this.width, + margin : 5, + columns : [{ + text : 'Frame', + dataIndex : 'frameNumber', + flex : 1 + },{ + text : 'I0', + dataIndex : 'I0', + flex : 1, + renderer : function(val, y, sample) { + return BUI.formatValuesUnits(sample.data.I0, "", 12, 3); + } + },{ + text : 'Rg', + dataIndex : 'Rg', + flex : 1, + renderer : function(val, y, sample) { + return BUI.formatValuesUnits(sample.data.Rg, "nm", 12, 3); + } + },{ + text : 'Mass', + dataIndex : 'Mass', + flex : 1, + renderer : function(val, y, sample) { + return BUI.formatValuesUnits(sample.data.Mass, "", 12, 3); + } + },{ + text : 'Vc', + dataIndex : 'Vc', + flex : 1, + renderer : function(val, y, sample) { + return BUI.formatValuesUnits(sample.data.Vc, "", 12, 3); + } + },{ + text : 'Qr', + dataIndex : 'Qr', + flex : 1, + renderer : function(val, y, sample) { + return BUI.formatValuesUnits(sample.data.Qr, "", 12, 3); + } + },{ + text : 'Quality', + dataIndex : 'quality', + flex : 1, + renderer : function(val, y, sample) { + return (Number(sample.data.quality) * 100).toFixed(2) + "%"; + } + }, + { + text : '', + renderer : function(val, x, sample) { + if (sample != null) { + return BUI.getZipHTMLByFrameRangeId(_this.experimentId, sample.raw.frameNumber, sample.raw.frameNumber); + } + }, + width : 100 + }], + flex : 1, + viewConfig : { + stripeRows : true + } + }); - var _this = this; + /** Adding the tbar **/ + if (this.tbar) { + this.grid.addDocked({ + xtype : 'toolbar', + items : this._getTbar() + }); + } + return this.grid; +}; - this.analysisGrid = new HPLCAnalysisGrid({ - height : Ext.getBody().getViewSize().height * 0.9 - 300, - positionColumnsHidden : true, - sorters : [ { - property : 'priorityLevelId', - direction : 'ASC' - } ] - }); +FrameGrid.prototype.input = function() { + return []; +}; - this.frameGrid = new FrameGrid({ - width : 600, - height : 60, - collapsed : false, - tbar : false - }); - - this.peakGrid = new PeakGrid({ - width : 550, - height : 500, - collapsed : false, - tbar : false - }); - - this.peakGrid2 = new PeakGrid({ - width : 102, - height : this.plotHeight, +FrameGrid.prototype.test = function(targetId) { + var frameGrid = new FrameGrid({ + width : 800, + height : 350, collapsed : false, - tbar : false, - showExtendedColumns : true + tbar : true }); - - this.mainPlotPanel = new HPLCGraph({ - title : 'I0', - width : this.plotWidth - 110, - height : this.plotHeight, - bbar : true, - plots : { - "I0" : true, - "Rg" : true - }, - xlabel : "HPLC Frames", - scaled : true, - interactionModel : { - 'dblclick' : function(event, g, context) { - _this._selectFrame(g.lastx_); - var annotations = []; - annotations.push({ - series : g.selPoints_[0].name, - x : g.lastx_, - width : 100, - height : 23, - tickHeight : 4, - shortText : g.lastx_, - text : g.lastx_, - attachAtBottom : true - }); - g.setAnnotations(annotations); - } + var panel = frameGrid.getPanel([]); + panel.render(targetId); +}; + + +function PeakGrid(args) { + this.height = 500; + this.searchBar = false; + this.tbar = false; + this.collapsed = false; + this.width = 500; + this.showExtendedColumns = false; + + if (args != null) { + if (args.showExtendedColumns != null) { + this.showExtendedColumns = args.showExtendedColumns; + } + if (args.height != null) { + this.height = args.height; + } + if (args.searchBar != null) { + this.searchBar = args.searchBar; } - }); - this.intensityPlotPanel = new MergesHPLCGraph({ - title : 'Scattering', - width : this.plotWidth, - height : 500, - showRangeSelector : false, - xParam : 0, - xlabel : "scattering_I", - plots : { - "scattering_I" : true, - "subtracted_I" : true, - "buffer_I" : true + if (args.tbar != null) { + this.tbar = args.tbar; } - }); -} + if (args.collapsed != null) { + this.collapsed = args.collapsed; + } -HPLCTabs.prototype._selectFrame = function(frameNumber) { - try{ - this._renderScatteringCurve(frameNumber); - this.frameGrid.refresh([this.mainPlotPanel.getDataByFrameNumber(frameNumber)], this.experiment.experimentId); - } - catch(e){ - console.log(e); + if (args.width != null) { + this.width = args.width; + } } -}; +} -HPLCTabs.prototype.error = function(error) { - var e = JSON.parse(error); - showError(e); - this.panel.setLoading(false); -}; -HPLCTabs.prototype.loadDataMainPlot = function(data) { - var zeroArray = []; - for ( var i = 0; i < data.I0.length; i++) { - zeroArray.push(0); - } - data = [ { - param : "I0", - data : data.I0, - std : data.I0_Stdev, - color : '#0066CC', - label : "I0" - }, - { - param : "sum_I", - label : "sum_I", - color : "#00FF00", - data : data.sum_I, - std : zeroArray - }, - { - param : "Rg", - label : "Rg", - color : "#21610B", - data : data.Rg, - std : data.Rg_Stdev - }, { - param : "Mass", - data : data.mass, - std : data.mass_Stdev, - color : '#FF9900', - label : "Mass" - }, { - param : "Vc", - data : data.Vc, - std : data.Vc_Stdev, - color : '#990099', - label : "Vc" - }, { - param : "Qr", - data : data.Qr, - std : data.Qr_Stdev, - color : '#FF0066', - label : "Qr" - }, { - param : "quality", - label : "quality", - color : "#FF00FF", - data : data.quality, - std : zeroArray - } ]; - this.data = data; - this.mainPlotPanel.loadData(data); +PeakGrid.prototype.refresh = function(buffers) { + this.store.loadData(this._prepareData(buffers), false); }; -HPLCTabs.prototype._loadIntensityPlotByFrameNumber = function(frameNumber) { - var _this = this; +PeakGrid.prototype._prepareData = function(buffers) { - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, data) { - var array = []; - data = [ { - param : "q", - data : data[frameNumber].q, - std : data[frameNumber].q, - fstd : function(a) { - return parseFloat(a); - }, - color : '#green', - label : "q", - showOnMenu : false - }, - { - param : "scattering_I", - data : data[frameNumber].scattering_I, - fdata : function(a) { - return Math.log(parseFloat(a)); - }, - std : data[frameNumber].scattering_Stdev, - fstd : function(a) { - return Math.log(Math.abs(parseFloat(a))); - }, - color : 'green', - label : "Log(I) Sample" - }, - { - param : "buffer_I", - data : data[frameNumber].buffer_I, - fdata : function(a) { - return Math.log(parseFloat(a)); - }, - fstd : function(a) { - return Math.log(Math.abs(parseFloat(a))); - }, - std : data[frameNumber].subtracted_Stdev, - color : '#0000FF', - label : "Log(I) Avg Buf." - }, - { - param : "subtracted_I", - data : data[frameNumber].subtracted_I, - fdata : function(a) { - var value = (Math.log(parseFloat(a))); - if (isNumber(value)) - return value; - }, - fstd : function(a) { - var value = Math.log(Math.abs(parseFloat(a))); - if (isNumber(value)) - return value; - }, - std : data[frameNumber].subtracted_Stdev, - color : 'red', - label : "Log(I) Sub." - } - ]; +// for ( var i = 0; i < buffers.length; i++) { +// buffers[i].name = "Peak #" + (i+1); +// } + /** Adding information of buffer **/ + if (buffers.length > 0){ + buffers.unshift({ + name : "BUFFER", + start : 0, + experimentId : buffers[0].experimentId, + end : buffers[0].start - 1 + }); + } + + + return buffers; +}; - _this.intensityPlotPanel.xlabel = "Frame " + frameNumber + " (q, nm-1)"; +PeakGrid.prototype._getTbar = function() { + var _this = this; + var actions = []; - if (_this.intensityPlotPanel.hplcData == null) { - /** It creates also top bar **/ - _this.intensityPlotPanel.loadData(data); - } else { - _this.intensityPlotPanel.reloadData(data); - + actions.push(Ext.create('Ext.Action', { + icon : '../images/add.png', + text : 'Add Buffer', + disabled : false, + handler : function(widget, event) { + var window = new BufferWindow(); + window.onSuccess.attach(function(sender) { + _this.store.loadData(BIOSAXS.proposal.getBuffers()); + }); + window.draw({}); } - _this.intensityPlotPanel.panel.setLoading(false); - }); - adapter.onError.attach(function(sender, data) { - _this.intensityPlotPanel.panel.setLoading(false); - }); - this.intensityPlotPanel.panel.setLoading("Retrieving Frame " + frameNumber); - adapter.getH5FrameScattering(this.experiment.json.experimentId, frameNumber); + })); + return actions; }; -HPLCTabs.prototype._renderScatteringCurve = function(frameNumber) { +PeakGrid.prototype.getPanel = function(buffers) { var _this = this; + this.peakCount = 0; + this.store = Ext.create('Ext.data.Store', { + fields : ['name', {name:'start', type : 'int'}, {name:'end', type : 'int'}], + data : buffers + + }); - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, data) { - _this.intensityPlotPanel.setPeaks(data); - _this._loadIntensityPlotByFrameNumber(frameNumber); - try{ - var peaks = []; - for(key in data){ - peaks.push({ - start : key.split("-")[0], - end : key.split("-")[1], - experimentId : _this.experiment.json.experimentId - }); + this.store.sort('start'); + + var type = 'Ext.grid.Panel'; + if (this.searchBar == true) { + type = 'Ext.ux.LiveSearchGridPanel'; + } + this.grid = Ext.create(type, { + store : this.store, + height : this.height, + width : this.width, + margin : 5, + columns : [ + { + text : '', + dataIndex : 'name', + width : 100, + hidden : _this.showExtendedColumns, + renderer : function(val, y, sample) { + if (sample.data.name == "BUFFER") + return "BUFFER"; + _this.peakCount++; + return "Peak #" + _this.peakCount; + } + + + }, + { + text : 'Peaks', + dataIndex : 'start', + sortable : true, + width : 100, + hidden: !_this.showExtendedColumns, + renderer : function(val, y, sample) { + return "From " + Number(sample.raw.start) + " to " + Number(sample.raw.end); + } + }, + { + text : 'Frames', + hidden : _this.showExtendedColumns, + columns : [ + { + text : 'Start', + dataIndex : 'start', + sortable : true, + hidden : _this.showExtendedColumns, + width : 100, + renderer : function(val, y, sample) { + return Number(val); + } + },{ + text : 'End', + dataIndex : 'end', + width : 100, + hidden : _this.showExtendedColumns, + renderer : function(val, y, sample) { + return Number(val); + } + },{ + text : 'Total', + dataIndex : 'end', + width : 100, + hidden : _this.showExtendedColumns, + renderer : function(val, y, sample) { + return "" + (sample.raw.end - sample.raw.start) + " frames"; + } + } + ] + } +// , +// { +// text : '', +// hidden : _this.showExtendedColumns, +// renderer : function(val, x, sample) { +// if (sample != null) { +// return BUI.getZipHTMLByFrameRangeId(sample.raw.experimentId, sample.raw.start, sample.raw.end); +// } +// }, +// width : 100 +// } + ], + flex : 1, + viewConfig : { + stripeRows : true, + getRowClass : function(record, rowIdx, params, store) { + if (record.raw.name == "BUFFER") { + return "blue-row"; + } } - _this.peakGrid2.refresh(JSON.parse(JSON.stringify(peaks))); - _this.peakGrid.refresh(peaks); -// console.log(peaks); - } - catch(e){ - showError(e); } }); - adapter.onError.attach(function(sender, data) { - _this.intensityPlotPanel.setLoading(false); + + /** Adding the tbar **/ + if (this.tbar) { + this.grid.addDocked({ + xtype : 'toolbar', + items : this._getTbar() + }); + } + return this.grid; +}; + +PeakGrid.prototype.input = function() { + return []; +}; + +PeakGrid.prototype.test = function(targetId) { + var PeakGrid = new PeakGrid({ + width : 800, + height : 350, + collapsed : false, + tbar : true }); - this.intensityPlotPanel.panel.setLoading("Reading HDF5 File "); - adapter.getH5FramesMerge(this.experiment.json.experimentId); + + var panel = PeakGrid.getPanel([]); + panel.render(targetId); }; + +/** + * Macromolecule Grid showing macromolecules and adding anb updating buttons + * + * @height + * @maxHeight + * @width + * @cssFontStyle + * @searchBar makes this grid as Ext.ux.LiveSearchGridPanel + * @tbar top bar containing "Add" and "Update From SMIS" button + * @collapsed + * @collapsible + * @btnEditVisible + * @btnRemoveVisible + * @multiselect makes it multiselect using Ext.selection.CheckboxModel + * + * #onSelected + * #onMacromoleculesChanged + */ +function MacromoleculeGrid(args) { + this.height = 500; + this.width = 500; + this.id = BUI.id(); + this.maxHeight = this.height; -HPLCTabs.prototype._postRenderOverviewPanel = function() { - var _this = this; - var adapter = new BiosaxsDataAdapter(); - adapter.onError.attach(function(sender, data) { - data = data.replace(/NaN/g, '0'); - _this.loadDataMainPlot(JSON.parse(data)); - + this.searchBar = false; + this.tbar = false; + + this.collapsible = true; + this.collapsed = true; + + this.btnEditVisible = true; + this.btnRemoveVisible = false; + this.multiselect = false; + + /** Font style applied to the acronym column **/ + this.cssFontStyle = null; + + if (args != null) { + if (args.height != null) { + this.height = args.height; + this.maxHeight = this.height; + } + if (args.maxHeight != null) { + this.maxHeight = args.maxHeight; + } + if (args.width != null) { + this.width = args.width; + } + if (args.cssFontStyle != null) { + this.cssFontStyle = args.cssFontStyle; + } + + if (args.searchBar != null) { + this.searchBar = args.searchBar; + } + if (args.tbar != null) { + this.tbar = args.tbar; + } + if (args.collapsible != null) { + this.collapsible = args.collapsible; + } + if (args.collapsed != null) { + this.collapsed = args.collapsed; + } + if (args.btnEditVisible != null) { + this.btnEditVisible = args.btnEditVisible; + } + if (args.btnRemoveVisible != null) { + this.btnRemoveVisible = args.btnRemoveVisible; + } + if (args.multiselect != null) { + this.multiselect = args.multiselect; + } + } - }); - adapter.onSuccess.attach(function(sender, data) { - _this.loadDataMainPlot(data); - _this._renderScatteringCurve(0); - }); - adapter.getH5fileParameters(this.experiment.json.experimentId, [ - "I0", "I0_Stdev", "sum_I","Rg", "Rg_Stdev", "Vc", "Vc_Stdev", "Qr", "Qr_Stdev", "mass", "mass_Stdev", "quality" ]); + this.onSelected = new Event(); -}; + this.onMacromoleculesChanged = new Event(); +} -HPLCTabs.prototype._postRenderDetailsPanel = function() { - if (this.data != null) { - this.I0PlotPanel.loadData(this.data); - this.RgPlotPanel.loadData(this.data); - this.MassPlotPanel.loadData(this.data); - this.VcPlotPanel.loadData(this.data); - } -}; -HPLCTabs.prototype.draw = function(experiment) { - var _this = this; - this.renderDataAcquisition(experiment); -}; -HPLCTabs.prototype.getExperimentTitle = function() { - var experimentHeaderForm = new ExperimentHeaderForm(); - return experimentHeaderForm.getPanel(this.experiment); -}; -HPLCTabs.prototype.renderDataAcquisition = function(experiment) { +MacromoleculeGrid.prototype.edit = function(macromolecule) { var _this = this; - this.experiment = experiment; - if (this.experimentPanel == null) { - this.experimentPanel = Ext.create('Ext.container.Container', { - bodyPadding : 2, - height : this.height, - width : Ext.getBody().getViewSize().width * 0.9, - renderTo : this.targetId, - style : { - padding : 2 - }, - items : [ this.getExperimentTitle(), this.getPanel() ], - listeners : { - afterrender : function() { - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, data) { - _this.analysisGrid.refresh(data); - }); - adapter.getAnalysisInformationByExperimentId(_this.experiment.experimentId); - } - } - }); - } - return this.experimentPanel; + var window = new MacromoleculeWindow(); + window.onSave.attach(function(sender) { + _this.store.loadData(BIOSAXS.proposal.getMacromolecules()); + _this.onMacromoleculesChanged.notify(); + }); + window.draw(macromolecule); }; -HPLCTabs.prototype.getPanel = function() { +MacromoleculeGrid.prototype.getTbar = function() { var _this = this; - this.panel = Ext.createWidget('tabpanel', - { - plain : true, - height : this.height, -// width : 900, - style : { - padding : 2 - }, - items : [ - { - tabConfig : { - title : "Overview" + var actions = []; - }, - items : [ { - xtype : 'container', - layout : 'hbox', - border : 1, - flex : 1, - items : [ { - xtype : 'container', - layout : 'vbox', - border : 1, - items : [ { - xtype : 'container', - margin : '10px', - border : 1, - items : [ - { - xtype : 'container', - margin : '0px', - layout : 'hbox', - border : 1, - items : [ - this.peakGrid2.getPanel([]), - this.mainPlotPanel.getPanel() - ] - }, - { - xtype : 'container', - layout : 'vbox', - border : 1, - items : [ - { - html: '
Select a frame by double-clicking on the HPLC Frames plot
', - margin: 5, - border : 0 - }, - this.frameGrid.getPanel([]), - this.intensityPlotPanel.getPanel() - ] - } - - ] - }], - listeners : { - afterrender : function() { - _this._postRenderOverviewPanel(); - } - } - } ] - } ] - }, -// { -// tabConfig : { -// title : "Details" -// }, -// items : [ { -// xtype : 'container', -// layout : 'hbox', -// border : 1, -// flex : 1, -// items : [ { -// xtype : 'container', -// layout : 'vbox', -// border : 1, -// items : [ { -// xtype : 'container', -// padding : '1px', -// items : [ -// this.I0PlotPanel.getPanel(), this.RgPlotPanel.getPanel(), this.MassPlotPanel.getPanel(), -// this.VcPlotPanel.getPanel() ] -// } ], -// listeners : { -// afterrender : function() { -// _this._postRenderDetailsPanel(); -// } -// } -// } ] -// } ] -// }, - { - tabConfig : { - title : "Analysis" - }, - items : [ { - xtype : 'container', - layout : 'vbox', - padding : '5px 0px 10px 10px', - items : [ _this.analysisGrid.getPanel([]) ] - } ] - }, - { - tabConfig : { - title : "File Manager" - }, - items : [ { - xtype : 'container', - layout : 'vbox', - padding : '5px 0px 10px 10px', - items : [ - { - html: '
Custom Download
', - margin : '10 0 10 5', - border : 0 - }, - { - html: '
Download has been temporary disabled. Contact your labcontact if you want to extract the frames from the HDF5 file
', - margin : '10 0 10 5', - hidden : _this.experiment.experimentId == 2602, - border : 0 - }, - { - xtype : 'container', - layout : 'hbox', - margin : '0 0 0 20', - flex : 1, - items :[ - { - xtype: 'numberfield', - id: 'field_start', - fieldLabel: 'Frames from', - value: 0, - minValue: 0 - }, - { - xtype: 'numberfield', - id: 'field_end', - fieldLabel: 'to', - labelWidth : 20, - margin : '0 0 0 10', - minValue: 0 - }, - { - xtype: 'button', - /** allowing test account to download frames **/ - disabled : _this.experiment.experimentId != 2602, - text : 'Download', - margin : '0 0 0 10', - handler : function() { - var experimentId = _this.experiment.experimentId; - var start = Ext.getCmp("field_start").getValue(); - var end = Ext.getCmp("field_end").getValue(); - /** Checking if start is bigger than end **/ - if (start > end){ - var aux = end; - end = start; - start = aux; - } - var url = BUI.getZipFrameHPLCUrl(experimentId, start, end); - window.open(url) - } - } - ] - }, - _this.peakGrid.getPanel([]) - - ] - } - ] - } - ] + actions.push(Ext.create('Ext.Action', { + icon : '../images/add.png', + text : 'Add macromolecule', + disabled : false, + handler : function(widget, event) { + _this.edit(); + } + })); + actions.push("->"); + actions.push(Ext.create('Ext.Action', { + icon : '../images/folder_go.png', + text : 'Update From SMIS', + tooltip : "Retrieve all the macromolecules of your proposal from SMIS database", + disabled : false, + handler : function(widget, event) { + _this.grid.setLoading("Connecting to SMIS"); + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, data) { + BIOSAXS.proposal.setMacromolecules(data.macromolecules); + _this.refresh(BIOSAXS.proposal.macromolecules); + _this.grid.setLoading(false); + }); + adapter.onError.attach(function(sender, data) { + _this.grid.setLoading(false); }); + adapter.updateDataBaseFromSMIS(); + } + })); + return actions; +}; - return this.panel; +MacromoleculeGrid.prototype.deselectAll = function() { + this.grid.getSelectionModel().deselectAll(); +}; + +MacromoleculeGrid.prototype.selectById = function(macromoleculeId) { + this.grid.getSelectionModel().deselectAll(); + for ( var i = 0; i < this.grid.getStore().data.items.length; i++) { + var item = this.grid.getStore().data.items[i].raw; + if (item.macromoleculeId == macromoleculeId) { + this.grid.getSelectionModel().select(i); + } + } +}; + +MacromoleculeGrid.prototype.refresh = function(macromolecules) { + this.store.loadData(macromolecules, false); +}; + +MacromoleculeGrid.prototype.getColumns = function() { + var _this = this; + var columns = [ + { + text : 'Acronym', + dataIndex : 'acronym', + id : this.id + "acronym", + flex : 1, + renderer : function(value, metaData, record, rowIndex, colIndex, store) { + if (_this.cssFontStyle != null) { + return "" + value + ""; + } + return value; + } + }, { + text : 'Name', + dataIndex : 'name', + id : this.id + "name", + flex : 1, + hidden : true + } ]; + + if (this.btnEditVisible) { + columns.push({ + id : _this.id + 'buttonEditMacromolecule', + width : 85, + sortable : false, + renderer : function(value, metaData, record, rowIndex, colIndex, store) { + if (_this.btnEditVisible) { + return BUI.getGreenButton('EDIT'); + } + return null; + } + }); + } + if (this.btnRemoveVisible) { + columns.push({ + id : _this.id + 'buttonRemoveMacromolecule', + width : 85, + sortable : false, + renderer : function(value, metaData, record, rowIndex, colIndex, store) { + if (_this.btnRemoveVisible) { + return BUI.getRedButton('REMOVE'); + } + return null; + } + }); + } + + return columns; +}; + + +/** Returns the grid **/ +MacromoleculeGrid.prototype.getPanel = function() { + var _this = this; + + this.store = Ext.create('Ext.data.Store', { + fields : [ 'macromoleculeId', 'name', 'acronym' ] + }); + + var type = 'Ext.grid.Panel'; + if (this.searchBar == true) { + type = 'Ext.ux.LiveSearchGridPanel'; + } + + this.selModel = Ext.create('Ext.selection.RowModel', { +// allowDeselect : true, +// mode : 'MULTI', + listeners : { + selectionchange : function(sm, selections) { + var selected = []; + for (var i = 0; i < selections.length; i++) { + selected.push(selections[i].raw); + } + _this.onSelected.notify(selected); + } + } + }); + + this.grid = Ext.create(type, { + id : this.id, + title : 'Macromolecules', + collapsible : this.collapsible, + collapsed : this.collapsed, + store : this.store, + height : this.height, + maxHeight : this.maxHeight, + selModel : this.selModel, + columns : this.getColumns(), + width : this.width, + viewConfig : { + stripeRows : true, + listeners : { + 'celldblclick' : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + _this.edit(BIOSAXS.proposal.getMacromoleculeById(record.data.macromoleculeId)); + }, + 'cellclick' : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonEditMacromolecule') { + _this.edit(BIOSAXS.proposal.getMacromoleculeById(record.data.macromoleculeId)); + } + if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonRemoveMacromolecule') { + BUI.showBetaWarning(); + } + } + + } + } + }); + + /** Adding the tbar **/ + if (this.tbar) { + this.grid.addDocked({ + xtype : 'toolbar', + items : this.getTbar() + }); + } + return this.grid; +}; + +MacromoleculeGrid.prototype.input = function() { + return { + proposal : DATADOC.getProposal_10() + }; +}; + +MacromoleculeGrid.prototype.test = function(targetId) { + var macromoleculeGrid = new MacromoleculeGrid({ + width : 800, + height : 350, + collapsed : false, + tbar : true + }); + + BIOSAXS.proposal = new Proposal(macromoleculeGrid.input().proposal); + var panel = macromoleculeGrid.getPanel(BIOSAXS.proposal.macromolecules); + panel.render(targetId); }; /** - * Main form tab for macromolecule + * Shows measurements with their attributes as specimen, exposure temperature, + * volume to load, transmission etc... * - * @width + * @addBtnMultipleEdit: if true add a button for changing measurements' + * parameters by choosing multiple measurements. It opens + * MultipleEditMeasurementGridWindow + * @collapsed: if true it doesn't show buffer's measurements + * @editor: hashmap containing columns to be edited. It is an array of a json + * like editor of Ext + * @selModel + * @collapseBtnEnable + * @removeBtnEnabled Ext.selection.RowModel + * @addBtnEnable + * @updateRowEnabled true/false if update plugin is set to the grid + * @showTitle + * @title + * @isStatusColumnHidden + * @isPriorityColumnHidden + * @isTimeColumnHidden + * @estimateTime + * @margin + * @tbar * @height + * @maxHeight + * @minHeight + * @width + * @maxWidth + * @resizable + * @experimentColorBased when colors for buffers and macromolecules are not + * selected by the proposal but by experiment, so the + * number of colors is smaller * - * #onClose when user clicks on close button in the MacromoleculeForm - * #onSave when macromole is saved by macromoleculeForm + * #onClick #onSelected #onRemoved #onUpdateTime #onMeasurementChanged + * #onExperimentChanged */ -function MacromoleculeTabs(args) { - this.width = 500; +function MeasurementGrid(args) { + this.id = BUI.id(); + this.height = 500; + this.width = 900; - if (args != null) { - if (args.width != null) { - this.width = args.width; + this.maxWidth = 1200; + this.maxHeight = 600; + this.minHeight = 500; + + this.unitsFontSize = 9; + this.title = "Measurements"; + this.estimateTime = false; + this.collapsed = true; + this.tbar = true; + + this.showTitle = true; + this.resizable = true; + this.updateRowEnabled = true; + + /** + * Hash map containing the keys of the editable columns. Ex: + * 'exposureTemperature' * + */ + this.editor = { + comments : { + xtype : 'textfield', + allowBlank : true } - if (args.height != null) { - this.height = args.height; + }; + + this.isTimeColumnHidden = false; + this.isStatusColumnHidden = false; + this.isPriorityColumnHidden = true; + this.margin = "0 0 0 0"; + + this.addBtnEnable = true; + this.sorter = [ { + property : 'priority', + direction : 'ASC' + } ]; + + this.removeBtnEnabled = false; + this.collapseBtnEnable = true; + this.addBtnMultipleEdit = false; + this.sortingBtnEnable = false; + + var _this = this; + this.selModel = Ext.create('Ext.selection.RowModel', { + allowDeselect : true, + mode : 'MULTI', + listeners : { + selectionchange : function(sm, selections) { + var selected = []; + for (var i = 0; i < selections.length; i++) { + selected.push(selections[i].raw); + } + _this.onSelected.notify(selected); + } } - } + }); - var _this = this; + if (args != null) { - /** Widgets **/ + if (args.selModel != null) { + this.selModel = args.selModel; + } - /** Macromolecule Form **/ - this.macromoleculeForm = new MacromoleculeForm({ - width : this.width - 30, - height : this.height - 50, - }); + if (args.removeBtnEnabled != null) { + this.removeBtnEnabled = args.removeBtnEnabled; + } - this.macromoleculeForm.onClose.attach(function(sender) { - _this.onClose.notify(); - }); + if (args.addBtnMultipleEdit != null) { + this.addBtnMultipleEdit = args.addBtnMultipleEdit; + } - this.macromoleculeForm.onSave.attach(function(sender, macromolecule) { - _this.onSave.notify(macromolecule); - }); + // if (args.experimentColorBased != null) { + // this.experimentColorBased = args.experimentColorBased; + // } - this.assemblyForm = new AssemblyForm({ - width : this.width - 30, - height : this.height - 50, - }); - - this.rigibBodyModelingForm = new RigibBodyModelingForm({ - width : this.width - 30, - height : this.height - 50, - }); - - this.rigibBodyModelingForm.onSave.attach(function(sender, macromolecule) { - _this.onSave.notify(macromolecule); - }); - + if (args.collapsed != null) { + this.collapsed = args.collapsed; + } + if (args.resizable != null) { + this.resizable = args.resizable; + } - /** Events **/ - this.onClose = new Event(this); - this.onSave = new Event(this); -} + if (args.editor != null) { + this.editor = args.editor; + } -/** Populate the widget **/ -MacromoleculeTabs.prototype.refresh = function(macromolecule) { - this.macromoleculeForm.refresh(macromolecule); - this.assemblyForm.refresh(macromolecule); - this.rigibBodyModelingForm.refresh(macromolecule); + if (args.collapseBtnEnable != null) { + this.collapseBtnEnable = args.collapseBtnEnable; + } - if (macromolecule != null){ - if (macromolecule.macromoleculeId == null){ - Ext.getCmp(this.id + "_advancedTab").disable(); - Ext.getCmp(this.id + "_assemblyTab").disable(); + if (args.addBtnEnable != null) { + this.addBtnEnable = args.addBtnEnable; } - else{ - Ext.getCmp(this.id + "_advancedTab").enable(); - Ext.getCmp(this.id + "_assemblyTab").enable(); + if (args.sortingBtnEnable != null) { + this.sortingBtnEnable = args.sortingBtnEnable; } - } - else{ - Ext.getCmp(this.id + "_advancedTab").disable(); - Ext.getCmp(this.id + "_assemblyTab").disable(); - } -}; -MacromoleculeTabs.prototype.getItems = function() { - return [ { - tabConfig : { - title : 'General' - }, - items : [ { - xtype : 'container', - items : [ this.macromoleculeForm.getPanel() ] - } ] - }, { - id : this.id + "_assemblyTab", - tabConfig : { - title : 'Assembly' - }, - items : [ { - xtype : 'container', - items : [ this.assemblyForm.getPanel() ] - } ] - },{ - id : this.id + "_advancedTab", - tabConfig : { - title : 'Advanced' - }, - items : [ this.rigibBodyModelingForm.getPanel() ] - } ]; -}; + if (args.isPriorityColumnHidden != null) { + this.isPriorityColumnHidden = args.isPriorityColumnHidden; + } -MacromoleculeTabs.prototype.getPanel = function() { - this.panel = Ext.createWidget('tabpanel', { - height : this.height, - width : this.width, - plain : true, - margin : 5, - border : 0, - items : this.getItems() - }); - return this.panel; -}; - -function ResultTabs() { -} + if (args.width != null) { + this.width = args.width; + } -ResultTabs.prototype.draw = function(targetId, data, macromoleculeId) { - var panel = this.getPanel(targetId, data, macromoleculeId); - return Ext.create('Ext.container.Container', { - renderTo : targetId, - items : [ BUI.getMacromoleculeHeader(macromoleculeId), panel ] - }); + if (args.updateRowEnabled != null) { + this.updateRowEnabled = args.updateRowEnabled; + } -}; + if (args.showTitle != null) { + this.showTitle = args.showTitle; + if (this.showTitle == false) { + this.title = null; + } + } -ResultTabs.prototype._splitBySpecimen = function(data) { - var splitted = {}; + if (args.height != null) { + this.height = args.height; + } - for ( var i = 0; i < data.length; i++) { - var row = data[i]; - if (splitted[row.macromoleculeId + "_" + row.bufferId] == null) { - splitted[row.macromoleculeId + "_" + row.bufferId] = []; + if (args.maxHeight != null) { + this.maxHeight = args.maxHeight; } - splitted[row.macromoleculeId + "_" + row.bufferId].push(row); - } - return splitted; -}; + if (args.minHeight != null) { + this.minHeight = args.minHeight; + } -ResultTabs.prototype._getTabTitle = function(key, data) { - var macromoleculeId = key.split("_")[0]; - var bufferId = key.split("_")[1]; + if (args.maxWidth != null) { + this.maxWidth = args.maxWidth; + } - return BIOSAXS.proposal.getMacromoleculeById(macromoleculeId).acronym + " + " + BIOSAXS.proposal.getBufferById(bufferId).acronym + "(" + data.length + ")"; -}; + if (args.isStatusColumnHidden != null) { + this.isStatusColumnHidden = args.isStatusColumnHidden; + } + if (args.isTimeColumnHidden != null) { + this.isTimeColumnHidden = args.isTimeColumnHidden; + } -ResultTabs.prototype.getPanel = function(targetId, data, macromoleculeId) { + if (args.title != null) { + this.title = args.title; + } + if (args.estimateTime != null) { + this.estimateTime = args.estimateTime; + } - var dataFiltered = new AnalysisGrid({ - hideNulls : true, - grouped : true, - sorters : [ { - property : 'quality', - direction : 'DESC' - } ] - })._prepareData(data); + if (args.margin != null) { + this.margin = args.margin; + } - var items = [ { - tabConfig : { - title : 'Analysis (' + dataFiltered.length + ')' - }, - items : [ { - xtype : 'container', - layout : 'vbox', - padding : 10, - items : [ new AnalysisGrid({ - isScatteringPlotVisible : false - }).getPanel(dataFiltered) ] - } ] - }, { - tabConfig : { - title : 'Concentration Effects' - }, - items : [ new ResultSummaryForm().getPanel(data) ] - } ]; + if (args.tbar != null) { + this.tbar = args.tbar; + } + + if (args.sorter != null) { + this.sorter = args.sorter; + } - var splitted = this._splitBySpecimen(dataFiltered); - for ( var key in splitted) { - items.push({ - tabConfig : { - title : this._getTabTitle(key, splitted[key]) - }, - items : [ new ResultSummaryForm().getPanel(splitted[key]) ] - }); } + this.onClick = new Event(this); + this.onSelected = new Event(this); + this.onRemoved = new Event(this); + this.onUpdateTime = new Event(this); + this.onMeasurementChanged = new Event(this); + this.onExperimentChanged = new Event(this); +} - this.panel = Ext.createWidget('tabpanel', { - style : { - padding : 2 - }, - items : items +MeasurementGrid.prototype._sortBy = function(sort) { + var _this = this; + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, data) { + _this.onExperimentChanged.notify(data); + _this.grid.setLoading(false); + // wizardWidget.window.close(); }); - return this.panel; - + adapter.onError.attach(function(sender, data) { + _this.grid.setLoading(false); + alert("Oops, there was a problem"); + }); + _this.grid.setLoading("Sorting"); + adapter.sortMeasurements(this.experiments.experiments[0].experimentId, sort); }; - - -function ShipmentTabs(targetId) { - this.targetId = targetId; +MeasurementGrid.prototype._getMenu = function() { var _this = this; - this.gridHeight = 500; - /** data **/ - this.shipment = null; - - /** Shipment Form **/ - this.shipmentForm = new ShipmentForm({ - creationMode : false, - showTitle : false - }); - this.shipmentForm.onSaved.attach(function(sender, data) { - _this.refresh(data); + if (this.tbar) { - }); + var items = []; + if (_this.addBtnEnable) { + items.push({ + icon : '../images/add.png', + text : 'Add measurements', + handler : function() { + _this._openAddMeasurementWindow(); + } + }); + } + if (_this.addBtnMultipleEdit) { + items.push({ + icon : '../images/Edit_16x16_01.png', + text : 'Multiple Edit', + handler : function() { + var multipleEditMeasurementGridWindow = new MultipleEditMeasurementGridWindow(); + multipleEditMeasurementGridWindow.onExperimentChanged.attach(function(sender, data) { + _this.onExperimentChanged.notify(data); + }); - /** Cases grid **/ - this.caseGrid = new CaseGrid({ - height : this.gridHeight - }); + multipleEditMeasurementGridWindow.draw(_this.measurements, _this.experiments); - this.caseGrid.onAddButtonClicked.attach(function(sender, dewar) { - _this.caseGrid.grid.setLoading("ISPyB: Creating a new case"); - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, shipment) { - /** updateing shipment on proposal **/ - for ( var i = 0; i < BIOSAXS.proposal.shippings.length; i++) { - if (BIOSAXS.proposal.shippings[i].shippingId == shipment.shippingId) { - BIOSAXS.proposal.shippings[i] = shipment; } - } - _this.refresh(shipment); - }); - adapter.onError.attach(function(sender, shipment) { - _this.caseGrid.grid.setLoading(false); - }); - adapter.addCase(_this.shipment.json.shippingId); - }); + }); + } - this.caseGrid.onRemoveButtonClicked.attach(function(sender, dewarId) { - _this.panel.setLoading("ISPyB: removing case"); - _this.shipment.onSaved.attach(function(sender, shipment) { - _this.refresh(shipment); + items.push("->"); - }); - _this.shipment.removeCase(dewarId); - }); -} + if (_this.sortingBtnEnable) { + var split = Ext.create('Ext.button.Split', { + text : 'Sort by', + // handle a click on the button itself + handler : function() { + // alert("The button was clicked"); + }, + menu : new Ext.menu.Menu({ + items : [ + { + text : 'First Created First Measured', + handler : function() { + _this._sortBy("FIFO"); + } + }, "-", { + text : 'Default', + handler : function() { + _this._sortBy("DEFAULT"); + } + } ] + }) + }); + items.push(split); + } -ShipmentTabs.prototype.refresh = function(shipment) { + if (_this.collapseBtnEnable) { + items.push({ + text : 'Collapse buffers', + enableToggle : true, + scope : this, + toggleHandler : function(item, pressed) { + this.collapsed = pressed; + this.grid.getStore().loadData(this._prepareData(this.measurements, this.experiments), false); + }, + pressed : this.collapsed + }); + } + var tb = Ext.create('Ext.toolbar.Toolbar', { + items : items + }); + return tb; + } + return null; +}; +/** Opens WizardWidget for adding new measurements * */ +MeasurementGrid.prototype._openAddMeasurementWindow = function(measurements, experiments) { var _this = this; - this.shipment = shipment; - var proposal = new Proposal(); - proposal.onDataRetrieved.attach(function(sender, plates) { - - _this.refreshWithPlates(shipment, plates); - _this.caseGrid.grid.setLoading(false); + var wizardWidget = new WizardWidget(); + wizardWidget.onFinished.attach(function(sender, result) { + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, data) { + _this.onExperimentChanged.notify(data); + _this.grid.setLoading(false); + wizardWidget.window.close(); + }); + wizardWidget.current.setLoading("ISPyB: Adding measurements"); + adapter.addMeasurements(result.name, "comments", result.data, _this.experiments.experiments[0].experimentId); }); - proposal.getPlatesByProposal(); + + wizardWidget.draw(null, new MeasurementCreatorStepWizardForm(BIOSAXS.proposal.getMacromolecules(), { + noNext : true + })); }; -ShipmentTabs.prototype.refreshWithPlates = function(shipment, plates) { - this.shipment = new Shipment(shipment); +/******************************************************************************* + * Opens WizardWidget for adding new measurements + * + * @Measurements + * @Experiments experimentList Object + ******************************************************************************/ +MeasurementGrid.prototype._prepareData = function(measurements, experiments) { + var data = []; + for (var i = 0; i < measurements.length; i++) { + var measurement = measurements[i]; + var specimen = experiments.getSampleById(measurement.specimenId); + var buffer = experiments.getBufferById(specimen.bufferId); + measurement.buffer_acronym = buffer.acronym; + measurement.bufferId = buffer.bufferId; + measurement.volume = specimen.volume; + if (specimen.macromolecule3VO != null) { + measurement.acronym = specimen.macromolecule3VO.acronym; + measurement.macromoleculeId = specimen.macromolecule3VO.macromoleculeId; + } + measurement.concentration = specimen.concentration; + if (measurement.run3VO != null) { + measurement.energy = measurement.run3VO.energy; + measurement.expExposureTemperature = measurement.run3VO.exposureTemperature; + measurement.storageTemperature = measurement.run3VO.storageTemperature; + measurement.timePerFrame = measurement.run3VO.timePerFrame; + measurement.radiationAbsolute = measurement.run3VO.radiationAbsolute; + measurement.radiationRelative = measurement.run3VO.radiationRelative; + measurement.status = "DONE"; - this.caseGrid.refresh(this.shipment.getDewars(), plates); - this.panel.setLoading(false); + try { + if (measurement.run3VO.timeStart != null) { + if (measurement.run3VO.timeStart != "") { + measurement.miliseconds = moment(measurement.run3VO.timeStart).format("X"); + } + } + } catch (E) { + console.log(E); + } + } + + if (experiments.getDataCollectionByMeasurementId(measurement.measurementId).length > 0) { + var measurementtodatacollection3VOs = experiments.getDataCollectionByMeasurementId(measurement.measurementId)[0].measurementtodatacollection3VOs; + for (var k = 0; k < measurementtodatacollection3VOs.length; k++) { + if (measurementtodatacollection3VOs[k].dataCollectionOrder == 1) { + var specimenBuffer = experiments.getSampleById(experiments.getMeasurementById(measurementtodatacollection3VOs[k].measurementId).specimenId); + if (specimenBuffer.sampleplateposition3VO != null) { + measurement.bufferSampleplateposition3VO = specimenBuffer.sampleplateposition3VO; + measurement.bufferSampleplate = (experiments.getSamplePlateById(specimenBuffer.sampleplateposition3VO.samplePlateId)); + } + } + } + } + + if (this.collapsed) { + /** If collapsed only the samples * */ + if (specimen.macromolecule3VO != null) { + data.push(measurement); + } + } else { + data.push(measurement); + } + + } + return data; }; -ShipmentTabs.prototype.error = function(error) { - var e = JSON.parse(error); - showError(e); - this.panel.setLoading(false); +/** + * Refresh data grid with the measurements and the experiments + * + * @measurements array with measurement3VO objects + * @experiments array with experiments objects + */ +MeasurementGrid.prototype.refresh = function(measurements, experiments) { + this.experiments = experiments; + this.measurements = measurements; + this.store.loadData(this._prepareData(measurements, experiments), false); }; -//ShipmentTabs.prototype.refreshTabTitles = function() { -/*Ext.getCmp("MacromoleculeTab").setText(this.getMacromoleculeTitle()); -Ext.getCmp("BufferTab").setText(this.getBuffersTitle()); -Ext.getCmp("SpecimenTab").setText(this.getSpecimenTitle()); -Ext.getCmp("PlatesTab").setText(this.getPlatesTitle()); -Ext.getCmp("AssembliesTab").setText(this.getShipmentTitle()); -Ext.getCmp("PlateGroupsTab").setText(this.getPlateGroupsTitle());*/ -//}; -ShipmentTabs.prototype.draw = function(shipment) { - var _this = this; - _this.plates = []; - _this.shipment = shipment; - _this.render(shipment); +/** + * Set status bar to busy (refreshing icon) + * + * @msg message to be displayed on the bar + */ +MeasurementGrid.prototype._showStatusBarBusy = function(msg) { + var statusBar = Ext.getCmp(this.id + 'basic-statusbar'); + statusBar.setStatus({ + text : msg, + iconCls : 'x-status-busy', + clear : false + }); +}; - // var proposal = new Proposal(); - // proposal.onDataRetrieved.attach(function(sender, plates){ - // _this.plates = plates; - // _this.shipment = shipment; - // _this.render(shipment); - // - // }); - // proposal.getPlatesByProposal(); +/** + * Set status bar to ready (ok icon) + * + * @msg message to be displayed on the bar + */ +MeasurementGrid.prototype._showStatusBarReady = function(msg) { + var statusBar = Ext.getCmp(this.id + 'basic-statusbar'); + statusBar.setStatus({ + text : msg, + iconCls : 'x-status-valid', + clear : false + }); }; -//ShipmentTabs.prototype.getShipmentTitle = function() { -// return 'Shipment'; -//}; +/** + * If updateRowEnabled returns an array with Ext.grid.plugin.RowEditing + */ +MeasurementGrid.prototype._getPlugins = function() { + var _this = this; + var plugins = []; + if (this.updateRowEnabled) { + plugins.push(Ext.create('Ext.grid.plugin.RowEditing', { + clicksToEdit : 1, + listeners : { + validateedit : function(grid, e) { + /** Setting values * */ + for ( var key in _this.editor) { + e.record.raw[key] = e.newValues[key]; + } + /** Comments are always updatable* */ + e.record.raw.comments = e.newValues.comments; -//ShipmentTabs.prototype.getMacromoleculeTitle = function() { -// return 'Macromolecules (' + this.experiment.getMacromolecules().length + ')'; -//}; + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, measurement) { + _this.onMeasurementChanged.notify(measurement); + _this.grid.setLoading(false); + }); + adapter.onError.attach(function() { + alert("Error"); + _this.grid.setLoading(false); + }); -//ShipmentTabs.prototype.getBuffersTitle = function() { -// return 'Buffers (' + this.experiment.getBuffers().length + ')'; -//}; + _this.grid.setLoading(); + adapter.saveMeasurement(e.record.raw, _this.experiments.experiments[0]); + } + } + })); + } + return plugins; +}; -//ShipmentTabs.prototype.getPlateGroupsTitle = function() { -// return 'Plate Groups (' + this.experiment.getPlateGroups().length + ')'; -//}; +/** + * @key name of the columns mathing the this.editor[key] + */ +MeasurementGrid.prototype._getEditor = function(key) { + if (this.editor[key] != null) { + return this.editor[key]; + } + return null; +}; -//ShipmentTabs.prototype.getSampleChangerTitle = function() { -// return 'Sample Changer'; -//}; +MeasurementGrid.prototype.getPanel = function(measurements, experiments) { + this.experiments = experiments; + this.measurements = measurements; + var _this = this; + this.store = Ext.create('Ext.data.Store', { + fields : [ { + name : 'miliseconds', + type : 'int' + }, 'priority', 'bufferId', { + name : 'exposureTemperature', + type : 'numeric' + }, 'volumeToLoad','code', 'transmission', 'viscosity', 'waitTime', 'flow', 'buffer_acronym', 'macromoleculeId', 'acronym', 'concentration', 'extraFlowTime', 'volume', 'energy', + 'expExposureTemperature', 'storageTemperature', 'timePerFrame', 'radiationAbsolute', 'radiationRelative', 'status', 'comments' ], + data : this._prepareData(measurements, experiments) + }); + this.store.sort(this.sorter); + var bbar = {}; + try { + bbar = Ext.create('Ext.ux.StatusBar', { + id : _this.id + 'basic-statusbar', + defaultText : 'Ready', + text : 'Ready', + iconCls : 'x-status-valid', + items : [] + }); + } catch (exp) { + console.log("bbar error"); + } -//ShipmentTabs.prototype.getSpecimenTitle = function() { -// return 'Specimens(' + this.experiment.getSpecimenCount() + ')'; -//}; + this.grid = Ext + .create( + 'Ext.grid.Panel', + { + id : this.id, + title : this.title, + store : this.store, + selModel : this.selModel, + plugins : this._getPlugins(), + resizable : this.resizable, + margin : this.margin, + maxHeight : this.maxHeight, + minHeight : this.minHeight, + maxWidth : this.maxWidth, + width : this.width, + tbar : this._getMenu(), + columns : [ + { + text : 'Order', + dataIndex : 'priority', + width : 50, + hidden : _this.isPriorityColumnHidden, + sortable : true -//ShipmentTabs.prototype.getPlatesTitle = function() { -// return 'Plates(' + this.experiment.getSamplePlates().length + ')'; -//}; + }, + { + text : 'Run Number', + dataIndex : 'code', + width : 50, + hidden : true, + sortable : true -//ShipmentTabs.prototype.getBuffersTip = function() { -/*if (this.experiment.getBuffers().length == 0){ - return BUI.getWarningHTML("There are no buffers. Click on add to create new ones. Click on edit button or double click to edit them"); - -} -else{ - return BUI.getTipHTML("Click on edit button or double click to edit them. Click on duplicate to create an identical buffer including its additives") -}*/ -//}; + }, -//ShipmentTabs.prototype.refreshTips = function() { -/*Ext.getCmp("BufferTabTip").update(this.getBuffersTip()); -Ext.getCmp("SpecimenTabTip").update(this.getSpecimensTip());*/ -//}; + { + text : 'Specimen', + columns : [ -ShipmentTabs.prototype.render = function(shipment) { - return this.getPanel(shipment); -}; + { + text : '', + dataIndex : 'macromoleculeId', + width : 30, + renderer : function(val, y, sample) { + if (val != null) { + if (_this.experiments == null) { + return BUI.getRectangleColorDIV(BIOSAXS.proposal.macromoleculeColors[val], 10, 10); + } else { + return BUI.getRectangleColorDIV(_this.experiments.macromoleculeColors[val], 10, 10); + } + } + }, + sortable : true + }, + { + text : 'Macromo.', + dataIndex : 'acronym', + width : 80, + renderer : function(val, y, sample) { + return val; + }, + sortable : true + }, + { + text : 'Conc. ', + dataIndex : 'concentration', + width : 80, + renderer : function(val, y, sample) { + if (sample.raw.macromoleculeId == null) { + return ""; + } + if (isNaN(val)) + return val; -ShipmentTabs.prototype.getShipmentHeader = function(shipment) { - var _this = this; - function getHTMLSource() { - var name = shipment.json.shippingName; - var status = shipment.json.shippingStatus; - var creationDate = shipment.json.creationDate; - var html = BUI.createFormLabel("Name :", name, 75, 400); - html = html + BUI.createFormLabel("Status :", status, 75, 400); - html = html + BUI.createFormLabel("Date :", creationDate, 75, 400); - return html; - } + if (val != 0) { + return BUI.formatValuesUnits(val, '', { + fontSize : 16, + decimals : 3, + unitsFontSize : this.unitsFontSize + }); + } else { + return; + } - return Ext.create('Ext.container.Container', { - frame : false, - layout : 'hbox', - title : 'General', - padding : 5, - bodyPadding : '5 5 0 0', - width : 890, - margin : '0 0 10 0', - height : 100, - style : { - borderColor : '#BDBDBD', - borderStyle : 'solid', - borderWidth : '1px' - }, - fieldDefaults : { - msgTarget : 'side', - labelWidth : 100 - }, - items : [ { - margin : "0 0 0 0", - width : 475, - border : 0, - html : getHTMLSource() - } - ] - }); -}; + }, + sortable : true + }, + { + text : '', + dataIndex : 'bufferId', + width : 30, + hidden : false, + renderer : function(val, y, sample) { + if (val != null) { + var color = '#FFCCFF'; + if (_this.experiments != null) { + var dc = _this.experiments.getDataCollectionByMeasurementId(sample.raw.measurementId); + if (dc != null) { + if (dc.length > 0) { + color = _this.experiments.getSpecimenColorByBufferId(_this.experiments + .getMeasurementById(dc[0].measurementtodatacollection3VOs[0].measurementId).specimenId); + } + } + } else { + color = BIOSAXS.proposal.bufferColors[val]; + } + return BUI.getRectangleColorDIV(color, 10, 10); + } + }, + sortable : true + }, + { + text : 'Buffer', + dataIndex : 'buffer_acronym', + width : 120, + renderer : function(val, y, sample) { + if (sample.raw.bufferSampleplateposition3VO != null) { + return BIOSAXS.proposal.getBufferById(sample.raw.bufferId).acronym + " Plate: [" + + sample.raw.bufferSampleplate.slotPositionColumn + ", " + + BUI.getSamplePlateLetters()[sample.raw.bufferSampleplateposition3VO.rowNumber - 1] + "-" + + sample.raw.bufferSampleplateposition3VO.columnNumber + "]"; + } + return val; + }, + sortable : true + }, { + text : 'Position', + width : 100, + hidden : true, + renderer : function(val, y, sample) { + if (_this.experiments != null) { + return BUI.getSamplePositionHTML(_this.experiments.getSampleById(sample.raw.specimenId), _this.experiments.experiments[0]); + } + } + } ] + }, + { + text : 'Parameters', + columns : [ + { + text : 'Ex. Flow. time (s)', + dataIndex : 'extraFlowTime', + width : 100, + hidden : true, + renderer : function(val, y, sample) { + return BUI.formatValuesUnits(val, 's', { + fontSize : 12, + decimals : 0, + unitsFontSize : this.unitsFontSize + }); + } + }, + { + text : 'Exp. Temp.', + dataIndex : 'exposureTemperature', + width : 70, + renderer : function(val, y, sample) { + if (Number(val)) { + return BUI.formatValuesUnits(val, 'C', { + fontSize : 12, + decimals : 2, + unitsFontSize : this.unitsFontSize + }); + } + return null; + }, + sortable : true, + editor : this._getEditor("exposureTemperature") + }, + { + text : 'Vol. Load', + dataIndex : 'volumeToLoad', + width : 60, + hidden : false, + editor : this._getEditor("volumeToLoad"), + renderer : function(val, y, sample) { + // return val+ " µl"; + return BUI.formatValuesUnits(val, 'µl', { + fontSize : 12, + decimals : 2, + unitsFontSize : this.unitsFontSize + }); + } + }, + { + text : 'Volume in Well', + dataIndex : 'volume', + hidden : true, + editor : this._getEditor("volume"), + width : 80, + renderer : function(val, y, sample) { + // return val + "(µl)"; + return BUI.formatValuesUnits(val, 'µl', { + fontSize : 12, + decimals : 2, + unitsFontSize : this.unitsFontSize + }); + } + }, + { + text : 'Trans.', + dataIndex : 'transmission', + width : 60, + editor : this._getEditor("transmission"), + renderer : function(val, y, sample) { + return BUI.formatValuesUnits(val, '%', { + fontSize : 12, + decimals : 0, + unitsFontSize : this.unitsFontSize + }); + } + }, + { + text : 'Wait T.', + dataIndex : 'waitTime', + editor : this._getEditor("waitTime"), + width : 50, + renderer : function(val, y, sample) { + // if (val != 0) { + return BUI.formatValuesUnits(val, 's', { + fontSize : 12, + decimals : 0, + unitsFontSize : this.unitsFontSize + }); + // } + } + }, + { + text : 'Flow', + dataIndex : 'flow', + editor : this._getEditor("flow"), + width : 50, + renderer : function(val, y, sample) { + if (val == true) { + return "yes"; + } + return null; + } + }, + { + text : 'Viscosity', + dataIndex : 'viscosity', + tooltip : 'The viscosity of a fluid is a measure of its resistance to gradual deformation by shear stress or tensile stress. For liquids, it corresponds to the informal notion of "thickness"', + editor : this._getEditor("viscosity"), + width : 50, + renderer : function(val, y, sample) { + return val; + } + } ] + }, { + text : 'Status', + dataIndex : 'status', + width : 50, + hidden : _this.isStatusColumnHidden, + renderer : function(val, y, sample) { + if (val != null) { + if (val == 'DONE') { + return "" + val + " "; + } + } + } + }, { + text : 'Time', + dataIndex : 'time', + width : 80, + hidden : _this.isTimeColumnHidden, + renderer : function(val, y, sample) { + if (sample.raw.run3VO != null) { + if (sample.raw.run3VO.timeStart != null) { + if (sample.raw.run3VO.timeStart != "") { + var m = moment(sample.raw.run3VO.timeStart); + return m.format("hh:mm:ss a"); + } + } + } + } + }, { + text : 'Energy', + dataIndex : 'energy', + width : 100, + hidden : true + }, { + text : 'Real Exp. Temp.(C)', + width : 100, + dataIndex : 'expExposureTemperature', + hidden : true + }, { + text : 'Storage Temp.(C)', + width : 100, + dataIndex : 'storageTemperature', + hidden : true + }, { + text : 'Time/Frame (s)', + width : 100, + dataIndex : 'timePerFrame', + hidden : true + }, { + text : 'Radiation Relative', + dataIndex : 'radiationRelative', + width : 100, + hidden : true + }, { + text : 'Radiation Absolute', + dataIndex : 'radiationAbsolute', + width : 100, + hidden : true + }, { + text : 'Comments', + dataIndex : 'comments', + flex : 1, + hidden : false, + editor : this._getEditor("comments") -ShipmentTabs.prototype.getTabPanel = function(shipment) { - this.panel = Ext.createWidget('tabpanel', { - height : 600, - style : { - padding : 2 - }, - items : [ { - tabConfig : { - id : 'Shipment', - title : 'Shipment', - icon : '/ispyb/images/plane-small.gif' - }, - items : [ { - xtype : 'container', - margin : '5 5 5 5', - items : [ this.shipmentForm.getPanel(shipment) ] - } ] - }, { - tabConfig : { - id : 'Cases', - title : 'Cases', - icon : '../images/box-icon-very-small.png' - }, - items : [ { - xtype : 'container', - margin : '5 5 5 5', - items : [ this.caseGrid.getPanel(shipment.getDewars(), this.plates) ] - } ] - } ] - }); - return this.panel; -}; + }, { + id : _this.id + 'buttonRemoveSample', + text : '', + hidden : !_this.removeBtnEnabled, + width : 100, + sortable : false, + renderer : function(value, metaData, record, rowIndex, colIndex, store) { + if (record.raw.macromoleculeId != null) { + if (_this.removeBtnEnabled) { + return BUI.getRedButton('REMOVE'); + } + } + } + } ], + bbar : bbar, + viewConfig : { + stripeRows : true, + getRowClass : function(record, index, rowParams, store) { + if (record.data.status == "DONE") { + return 'green-row'; + } -ShipmentTabs.prototype.getPanel = function(shipment) { - var _this = this; - this.shipment = shipment; - if (this.plates == null) { - this.plates = []; - } + }, + listeners : { + 'itemclick' : function(grid, record, item, index, e, eOpts) { + _this.onClick.notify({ + specimen : record.raw + }); + }, + 'cellclick' : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonRemoveSample') { + grid.getStore().removeAt(rowIndex); - if (this.shipPanel == null) { - this.shipPanel = Ext.create('Ext.container.Container', { - bodyPadding : 2, - width : Ext.getBody().getViewSize().width * 0.9, - renderTo : this.targetId, - style : { - padding : 2 - }, - items : [ this.getShipmentHeader(shipment), this.getTabPanel(shipment) ] - }); - } + if (record.raw.measurementId != null) { + /** For testing * */ + grid.setLoading("ISPyB: Removing measurement"); + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, data) { + grid.setLoading(false); + /** + * We get and refresh experiment + * because specimens has changed * + */ + var adapter2 = new BiosaxsDataAdapter(); + adapter2.onSuccess.attach(function(sender, experiment) { + _this.onRemoved.notify(experiment); + _this._showStatusBarReady('Ready'); + }); + if (_this.experiments.experiments[0].experimentId != null) { + adapter2.getExperimentById(_this.experiments.experiments[0].experimentId, "MEDIUM"); + _this._showStatusBarBusy("ISPyB: Removing Unused Specimens"); + } + }); - return this.shipPanel; -}; - -/** - * Shows an template with the specimens, measurements and the experiment's - * requirement - * - * @targetId - */ -function TemplateTabs(targetId) { - this.height = 600; - this.targetId = targetId; + adapter.onError.attach(function(sender, data) { + alert("Error: " + data); + grid.setLoading(false); + }); - this.id = BUI.id(); - var _this = this; + adapter.removeMeasurement(record.raw); + } + } - this.gridHeight = 1000; - /** data * */ - this.experiment = null; + } - this.specimenSelected = null; + } + } + }); - /** Specimen Widget contains a specimenGrid and a sampleChangerWidget than can be displayed with are vertical or horizontal layout **/ - this.specimenWidget = new SpecimenWidget({ - height : this.height - }); - - this.samplePlateGroupWidget = new SamplePlateGroupWidget({ - showTitle : false, - height : 250, - margin : 5, - bbar : true - }); - - this.samplePlateGroupWidget.onExperimentChanged.attach(function(sender, experiment) { - _this.refresh(experiment); - }); + this.grid.on("afterrender", function() { - this.samplePlateGroupWidget.onClick.attach(function(sender, args) { - }); + function updateTime() { + try { + _this.estimatedTime = _this.estimatedTime - 1; - this.volumePlanificator = new VolumeGrid(); + _this.onUpdateTime.notify({ + hours : (Number(_this.estimatedTime / 3600).toFixed()), + minutes : (Number((_this.estimatedTime / 60) % 60).toFixed()), + seconds : (Number(_this.estimatedTime % 60).toFixed()) - /** For Measurements * */ - var storeViscosity = Ext.create('Ext.data.Store', { - fields : [ 'name' ], - data : [ { - "name" : "low" - }, { - "name" : "medium" - }, { - "name" : "high" - } ] - }); + }); - // Create the combo box, attached to the states data store - var viscosityEditor = Ext.create('Ext.form.ComboBox', { - fieldLabel : '', - store : storeViscosity, - queryMode : 'local', - displayField : 'name', - valueField : 'name' - }); + if (Number(_this.estimatedTime) < 0) { + _this.timer = null; + grid.setTitle(_this.title); + } - this.measurementGrid = new MeasurementGrid({ - maxWidth : 1500, - estimateTime : false, - positionColumnsHidden : true, - isPriorityColumnHidden : true, - isStatusColumnHidden : true, - isTimeColumnHidden : true, - updateRowEnabled : true, - collapsed : true, - removeBtnEnabled : true, - showTitle : false, - collapseBtnEnable : false, - addBtnMultipleEdit : true, - sortingBtnEnable : true, - editor : { - exposureTemperature : { - xtype : 'textfield', - allowBlank : true - }, - comments : { - xtype : 'textfield', - allowBlank : true - }, - volumeToLoad : { - xtype : 'numberfield', - allowBlank : true - }, - transmission : { - xtype : 'numberfield', - allowBlank : true - }, - viscosity : viscosityEditor, - waitTime : { - xtype : 'numberfield', - allowBlank : true - }, - flow : { - xtype : 'checkbox', - allowBlank : true + } catch (e) { + console.log(e); + _this.timer = null; } } - }); - - this.measurementGrid.onSelected.attach(function(sender, measurements) { - var specimens = []; - for ( var i = 0; i < measurements.length; i++) { - specimens.push(_this.experiment.getSampleById(measurements[i].specimenId)); - } - }); - this.measurementGrid.onMeasurementChanged.attach(function(sender, measurement) { - _this.experiment.setMeasurement(measurement); - _this.refresh(_this.experiment); - }); + if (_this.estimateTime) { + var experimentList = _this.experiments; + var collected = experimentList.getMeasurementsCollected(); + if (collected.length > 0) { + if (collected[0].run3VO != null) { + try { + var end = collected[0].run3VO.timeEnd; + var start = collected[0].run3VO.timeStart; + var dstart = moment(start); + var dend = moment(end); + var seconds = Number(dend.diff(dstart) / 1000).toFixed(); - this.measurementGrid.onExperimentChanged.attach(function(sender, json) { - _this.refresh(new Experiment(json)); - }); + _this.estimatedTime = (seconds * experimentList.getMeasurementsNotCollected().length); - this.measurementGrid.onRemoved.attach(function(sender, experiment) { - _this.refreshSpecimen(new Experiment(experiment)); - }); + if (_this.estimatedTime > 0) { + updateTime(); + _this.timer = setInterval(function() { + updateTime(); + }, 1000); + } - this.measurementGrid.onUpdateTime.attach(function(sender, args) { - document.getElementById(_this.id + "_counter").innerHTML = args.hours + 'h, ' + args.minutes + 'min and ' + args.seconds + ' seconds'; + } catch (e) { + } + } + } + } }); - -} - -TemplateTabs.prototype.refreshMeasurement = function(experiment) { - this.experiment = experiment; - this.experiment.onSaved = new Event(this); - this.experiment.onSpecimenSaved = new Event(this); - var experimentList = new ExperimentList([ this.experiment ]); - this.measurementGrid.refresh(experimentList.getMeasurementsNotCollected(), experimentList); -}; -TemplateTabs.prototype.refreshSpecimen = function(experiment) { - this.experiment = experiment; - this.experiment.onSaved = new Event(this); - this.experiment.onSpecimenSaved = new Event(this); - this.samplePlateGroupWidget.refresh(this.experiment); - this.specimenWidget.refresh(this.experiment); - this.volumePlanificator.refresh(this.experiment); + return this.grid; }; -TemplateTabs.prototype.refresh = function(experiment) { - // var start = new Date().getTime(); - this.experiment = experiment; - this.experiment.onSaved = new Event(this); - this.experiment.onSpecimenSaved = new Event(this); - - // var experimentList = new ExperimentList([this.experiment]); - this.refreshMeasurement(experiment); - this.refreshSpecimen(experiment); - /** Refreshing grids * */ - this.panel.setLoading(false); - +/** Method for testing * */ +MeasurementGrid.prototype.input = function() { + var experiment = DATADOC.getExperiment_10(); + var measurements = DATADOC.getMeasurements_10(); + var proposal = DATADOC.getProposal_10(); + return { + experiment : experiment, + measurements : measurements, + proposal : proposal + }; }; -TemplateTabs.prototype.error = function(error) { - var e = JSON.parse(error); - showError(e); - this.panel.setLoading(false); +MeasurementGrid.prototype.test = function(targetId) { + var measurementGrid = new MeasurementGrid({ + tbar : true + }); + BIOSAXS.proposal = new Proposal(measurementGrid.input().proposal); + var panel = measurementGrid.getPanel(measurementGrid.input().measurements, new ExperimentList([ new Experiment(measurementGrid.input().experiment) ])); + panel.render(targetId); }; + +/** + * A shipment may contains one or more cases where stock solutions and sample + * plates are stored + * + * @height + * @btnEditVisible + * @btnRemoveVisible + * + * #onEditButtonClicked + */ +function MolarityGrid(args) { + this.height = 100; + this.width = 100; + + if (args != null) { + if (args.height != null) { + this.height = args.height; + } + if (args.width != null) { + this.width = args.width; + } + } + + var _this = this; + + this.molarityForm = new MolarityForm({height : 180, width : 455}); + + this.molarityForm.onSave.attach(function(sender){ + _this.molarityWindow.destroy(); + _this.updateProposal(); + + }); + + this.molarityForm.onClose.attach(function(sender){ + _this.molarityWindow.destroy(); + + }); + + /** Events * */ + this.onEditButtonClicked = new Event(this); +} + +MolarityGrid.prototype._getColumns = function() { + return [ { + text : 'Subunit', + columns : [ { + text : "Acronym", + width : 100, + hidden : false, + dataIndex : 'acronym', + sortable : true + }, { + text : "Name", + width : 125, + hidden : false, + dataIndex : 'name', + sortable : true + }, { + text : "MM Est.", + width : 100, + dataIndex : 'molecularMass', + sortable : true, + renderer : function(grid, cls, record){ + return BUI.formatValuesUnits(record.data.molecularMass , "Da", 10, 2); + + } + } ] + }, { +// text : "Number
in assymmetric units", + text : "Ratio", + width : 100, + dataIndex : 'ratio', + tooltip : 'Number of times the subunit is present in the macromolecule', + sortable : true + }, { + id : this.id + 'MOLARITY_REMOVE', + flex : 0.1, + sortable : false, + renderer : function(value, metaData, record, rowIndex, colIndex, store) { + return BUI.getRedButton('REMOVE'); + } + } ]; +}; + +MolarityGrid.prototype._openMolarityWindow = function() { + this.molarityWindow = Ext.create('Ext.window.Window', { + title : 'Molarity', + height : 220, + width : 500, + modal : true, + items : [this.molarityForm.getPanel() ] + }).show(); +}; + +MolarityGrid.prototype._getButtons = function() { + var _this = this; + return [ { + text : 'Add subunit', + icon : '../images/add.png', + handler : function() { + _this._openMolarityWindow(); + } + }]; +}; + + +MolarityGrid.prototype.updateProposal = function() { + var _this = this; + this.panel.setLoading(); + BIOSAXS.proposal.onInitialized.attach(function() { + if (BIOSAXS.proposal != null) { + var macromolecules = BIOSAXS.proposal.macromolecules; + for (var i = 0; i < macromolecules.length; i++) { + + if (macromolecules[i].macromoleculeId == _this.macromolecule.macromoleculeId) { + _this.refresh(macromolecules[i]); + _this.panel.setLoading(false); + } + } + } + }); + BIOSAXS.proposal.init(); +}; + + +MolarityGrid.prototype.getPanel = function() { + var _this = this; + + this.molarityStore = Ext.create('Ext.data.Store', { + fields : [ 'acronym', 'ratio', 'comments', 'stoichiometryId', 'name', 'molecularMass' ], + sorters : { + property : 'ratio', + direction : 'DESC' + } + }); + + this.panel = Ext.create('Ext.grid.Panel', { + store : this.molarityStore, + height : this.height, + padding : 5, + columns : this._getColumns(), + listeners : { + cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + + /** Remove entry * */ + if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'MOLARITY_REMOVE') { + var dataAdapter = new BiosaxsDataAdapter(); + dataAdapter.onSuccess.attach(function(sender) { + _this.updateProposal(); + + }); + dataAdapter.removeStoichiometry(record.data.stoichiometryId); + _this.panel.setLoading("Removing Structure"); + } + } + }, + tbar : this._getButtons() + }); + return this.panel; +}; + +MolarityGrid.prototype._prepareData = function(macromolecule) { + /** Return an array of [{ratio,acronym, stoichiometryId, name}] **/ + var data = []; + if (macromolecule.stoichiometry != null) { + for (var i = 0; i < macromolecule.stoichiometry.length; i++) { + var hostMacromolecule = BIOSAXS.proposal.getMacromoleculeById(macromolecule.stoichiometry[i].macromoleculeId); + data.push({ + ratio : macromolecule.stoichiometry[i].ratio, + acronym : hostMacromolecule.acronym, + comments : hostMacromolecule.comments, + molecularMass : hostMacromolecule.molecularMass, + stoichiometryId : macromolecule.stoichiometry[i].stoichiometryId, + name : hostMacromolecule.name + }); + } + } + return data; +}; + +MolarityGrid.prototype.refresh = function(macromolecule) { + if (macromolecule != null){ + this.molarityStore.loadData(this._prepareData(macromolecule)); + this.molarityForm.refresh(macromolecule); + this.macromolecule = macromolecule; + } +}; + +MolarityGrid.prototype.input = function() { + return { + proposal : DATADOC.getProposal_10(), + dewars : DATADOC.getDewars_10() + + }; +}; + +MolarityGrid.prototype.test = function(targetId) { + var MolarityGrid = new MolarityGrid({ + height : 150 + }); + BIOSAXS.proposal = new Proposal(MolarityGrid.input().proposal); + var panel = MolarityGrid.getPanel(MolarityGrid.input().dewars); + panel.render(targetId); + +}; + + +/** + * Given data analysis parsed with ResultsAssemblyWidget makes a selector grid with buffer/protein + **/ +function SpecimenSelectorResultGrid() { + this.id = BUI.id(); +} -TemplateTabs.prototype.draw = function(experiment) { - this.render(experiment); +SpecimenSelectorResultGrid.prototype._prepareData = function(data) { + var parsed = []; + for ( var i = 0; i < data.length; i++) { + var row = data[i]; + for ( var j = 0; j < row.conditions.length; j++) { + parsed.push({ + bufferId : row.conditions[j].bufferId, + macromoleculeId : row.macromoleculeId, + macromoleculeAcronym : BIOSAXS.proposal.getMacromoleculeById(row.macromoleculeId).acronym, + bufferAcronym : BIOSAXS.proposal.getBufferById(row.conditions[j].bufferId).acronym + }); + } + } + return parsed; }; - -TemplateTabs.prototype.getExperimentTitle = function() { - var _this = this; - var experimentHeaderForm = new ExperimentHeaderForm(); - return experimentHeaderForm.getPanel(this.experiment); +SpecimenSelectorResultGrid.prototype.refresh = function(data) { + this.store.loadData(this._prepareData(data)); }; -TemplateTabs.prototype.render = function(experiment) { +SpecimenSelectorResultGrid.prototype.getPanel = function(data) { var _this = this; - this.experiment = experiment; + this.store = Ext.create('Ext.data.Store', { + fields : [ 'macromoleculeId', 'bufferId', 'macromoleculeAcronym', 'bufferAcronym', 'concentration' ], + data : this._prepareData(data), + groupField : 'macromoleculeAcronym' + }); - /** - * - * Depending on the sample Changer configuration we want to display the plates vertically or horizontally - * Default is vertical - * - * */ - - var specimenContainer = this.specimenWidget.getPanel(); - this.specimenWidget.refresh(experiment); - - var experimentList = new ExperimentList([ _this.experiment ]); - var measurementContainer = Ext.create('Ext.container.Container', { - layout : { - type : 'vbox' - }, - defaults : { - style : { - padding : '5px 0px 0px 10px' + this.store.sort('concentration'); + this.grid = Ext.create('Ext.grid.Panel', { + id : this.id, + store : this.store, + width : this.width, + height : this.height, + maxHeight : this.maxHeight, + border : 1, + features : [ { + ftype : 'grouping', + groupHeaderTpl : '{name}', + hideGroupedHeader : true, + startCollapsed : false + } ], + selModel : Ext.create('Ext.selection.CheckboxModel', { + allowDeselect : true, + mode : 'MULTI', + listeners : { + selectionchange : function(sm, selections) { + _this.selected = []; + for ( var i = 0; i < selections.length; i++) { + _this.selected.push(selections[i].raw); + } + } } - }, - items : [ _this.measurementGrid.getPanel(experimentList.getMeasurementsNotCollected(), experimentList) ] + }), + margin : 10, + sortableColumns : true, + columns : [ { + text : 'Macromolecule', + dataIndex : 'macromoleculeAcronym', + flex : 1 + }, { + text : '', + dataIndex : 'bufferId', + width : 20, + hidden : false, + renderer : function(val, y, sample) { + return BUI.getRectangleColorDIV(BIOSAXS.proposal.bufferColors[val], 10, 10); + } + }, { + text : 'Buffer', + dataIndex : 'bufferAcronym', + flex : 1 + }, { + text : 'Concentration', + dataIndex : 'concentration', + flex : 1, + renderer : function(val, y, sample) { + return val + " mg/ml"; + } + } ] }); - this.panel = Ext - .createWidget( - 'tabpanel', - { - plain : true, - items : [ - { - tabConfig : { - title : 'Measurements' - }, - items : [ { - xtype : 'container', - layout : 'vbox', - border : 1, -// height : _this.gridHeight, - margin : "0 0 10 0", - items : [ measurementContainer ] - } - - ] - }, - { - tabConfig : { - id : 'genralTabl', - title : "Specimens" - // width : 900, - // border : 3 - - }, - items : [ specimenContainer ] - }, - { - tabConfig : { - title : "Requirements" + /** Adding the tbar **/ + if (this.tbar) { + this.grid.addDocked({ + xtype : 'toolbar', + items : this.getTbar() + }); + } + return this.grid; +}; - }, - items : [ - { - html : BUI.getTipHTML("Estimated volume is the maximum volume required. Depending on the order of your measurements you may use less. Click on create stock solutions if you plan to ship these stock solutions"), - margin : "10 10 10 10", - border : 0 - }, this.volumePlanificator.getPanel(experiment) ] - } ] - }); - // ); - return this.getPanel(this.panel); +SpecimenSelectorResultGrid.prototype.input = function() { + return { + data : new ResultsAssemblyGrid()._prepareData(new ResultsAssemblyGrid().input().data), + proposal : new ResultsAssemblyGrid().input().proposal + }; }; -TemplateTabs.prototype.isTemplate = function() { - if (this.experiment.json.type == "TEMPLATE") { - return true; - } - return false; +SpecimenSelectorResultGrid.prototype.test = function(targetId) { + var specimenSelectorResultGrid = new SpecimenSelectorResultGrid(); + BIOSAXS.proposal = new Proposal(specimenSelectorResultGrid.input().proposal); + var panel = specimenSelectorResultGrid.getPanel(specimenSelectorResultGrid.input().data); + panel.render(targetId); }; +/** + * Show all buffer conditions for each macromolecules pointing out the measurements quality + * + * @height + * @maxHeight + * @width + * @searchBar + * @tbar + * @btnResultVisible + * + * #onClick + */ +function ResultsAssemblyGrid(args) { + this.height = 500; + this.id = BUI.id(); + this.maxHeight = this.height; -TemplateTabs.prototype.getPanel = function(panel) { - var _this = this; - if (this.experimentPanel == null) { - this.experimentPanel = Ext.create('Ext.container.Container', { - bodyPadding : 2, - width : 1000,//Ext.getBody().getViewSize().width * 0.9, - renderTo : this.targetId, - height : 500, -// style : { -// padding : 2 -// }, - items : [ - this.getExperimentTitle(), - this.panel - ], - listeners : { - afterrender : function(thisCmp) { - $("#SchemeReport" + _this.experiment.experimentId).click(function() { - $(this).target = "_blank"; - window.open('viewProjectList.do?reqCode=display&menu=platescheme&experimentId=' + _this.experiment.experimentId); - return false; - }); + this.width = 900; + this.searchBar = false; + this.tbar = false; + this.btnResultVisible = false; - } - } - }); - } - return this.experimentPanel; -}; + /** For processing **/ + this.processed = {}; + this.renderedPlotIndex = 0; -TemplateTabs.prototype.input = function(targetId) { - return new ExperimentTabs().input(); -}; + this.plotWidth = 210; + this.plotHeight = 80; -TemplateTabs.prototype.test = function(targetId) { - var experimentTabs = new TemplateTabs(targetId); - BIOSAXS.proposal = new Proposal(experimentTabs.input().proposal); - experimentTabs.draw(new Experiment(experimentTabs.input().experiment)); -}; - -var BUI = { - //interval : 60000, - interval : 40000, - rainbow : function(numOfSteps, step) { - // This function generates vibrant, "evenly spaced" colours (i.e. no clustering). This is ideal for creating easily distinguishable vibrant markers in Google Maps and other apps. - // Adam Cole, 2011-Sept-14 - // HSV to RBG adapted from: http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript - var r, g, b; - var h = step / numOfSteps; - var i = ~~(h * 6); - var f = h * 6 - i; - var q = 1 - f; - switch (i % 6) { - case 0: - r = 1, g = f, b = 0; - break; - case 1: - r = q, g = 1, b = 0; - break; - case 2: - r = 0, g = 1, b = f; - break; - case 3: - r = 0, g = q, b = 1; - break; - case 4: - r = f, g = 0, b = 1; - break; - case 5: - r = 1, g = 0, b = q; - break; + /** Colors **/ + this.validColor = BUI.getValidColor(); + this.warningColor = BUI.getWarningColor(); + this.notValidColor = BUI.getErrorColor(); + + /** Show warning if guinier quality less than **/ + this.guinierQuality = BUI.getQualityThreshold(); + this.framePercentageThreshold = BUI.getRadiationDamageThreshold(); + + if (args != null) { + if (args.height != null) { + this.height = args.height; + this.maxHeight = this.height; } - var c = "#" + ("00" + (~~(r * 255)).toString(16)).slice(-2) + ("00" + (~~(g * 255)).toString(16)).slice(-2) - + ("00" + (~~(b * 255)).toString(16)).slice(-2); - return (c); - }, - getFileNameByPath : function(filePath) { - if (filePath != null){ - var split = filePath.split("/"); - if (split.length > 0){ - return split[split.length - 1]; - } + if (args.maxHeight != null) { + this.maxHeight = args.maxHeight; + } + if (args.width != null) { + this.width = args.width; + } + if (args.searchBar != null) { + this.searchBar = args.searchBar; } - return "Not file"; - }, - getUpdateInterval : function() { - this.interval = this.interval + 2000; - return this.interval; - }, - getRadiationDamageThreshold : function() { - return 0.7; - }, - getQualityThreshold : function() { - return 0.7; - }, - getCreateShipmentURL : function() { - return '/ispyb/user/viewProjectList.do?reqCode=display&menu=create_shipment'; - }, - getCreateShipmentList : function() { - return '/ispyb/user/viewProjectList.do?reqCode=display&menu=list_shipment'; - }, - getShippingURL : function(shippingId) { - return '/ispyb/user/viewProjectList.do?reqCode=display&menu=shipment&shippingId=' + shippingId; - }, - getMacromoleculeResultsURLByMultipleSearch : function(array) { - return '/ispyb/user/viewProjectList.do?reqCode=display&menu=macromolecule&search=' - + JSON.stringify(array).replace(new RegExp("\"", 'g'), "'"); - }, - getMacromoleculeResultsURL : function(macromoleculeId) { - return '/ispyb/user/viewProjectList.do?reqCode=display&menu=macromolecule¯omoleculeId=' + macromoleculeId; - }, - getMacromoleculeBufferResultsURL : function(macromoleculeId, bufferId) { - return '/ispyb/user/viewProjectList.do?reqCode=display&menu=macromolecule&bufferId=' + bufferId + '¯omoleculeId=' + macromoleculeId; - }, + if (args.tbar != null) { + this.tbar = args.tbar; + } + if (args.btnResultVisible != null) { + this.btnResultVisible = args.btnResultVisible; + } - getMacromoleculeHeader : function(macromoleculeId) { + } - function getHTMLSource(macromoleculeId) { - if (macromoleculeId != null) { - var html = BUI.createFormLabel("Name :", BIOSAXS.proposal.getMacromoleculeById(macromoleculeId).name, 75, 400); - html = html + BUI.createFormLabel("Acronym :", BIOSAXS.proposal.getMacromoleculeById(macromoleculeId).acronym, 75, 400); - if (BIOSAXS.proposal.getMacromoleculeById(macromoleculeId).comments != null) { - html = html + BUI.createFormLabel("Comments :", BIOSAXS.proposal.getMacromoleculeById(macromoleculeId).comments, 75, 400); - } - return html; - } - } + this.onClick = new Event(); +} - return Ext.create('Ext.container.Container', { - frame : false, - layout : 'hbox', - title : 'Macromolecule', - bodyPadding : '5', - width : 890, - margin : '0 0 10 0', - height : 100, - style : { - borderColor : '#BDBDBD', - borderStyle : 'solid', - borderWidth : '1px' - }, - fieldDefaults : { - msgTarget : 'side', - labelWidth : 100 - }, - items : [ { - margin : "10 0 0 10", - width : 475, - border : 0, - html : getHTMLSource(macromoleculeId) - }, { - margin : "10 0 0 10", - width : 475, - border : 0, - html : BUI.getZipHTMLByMacromoleculeId(macromoleculeId) - } - ] - }); - }, +ResultsAssemblyGrid.prototype.edit = function(macromoleculeId) { + var _this = this; + var window = new MacromoleculeWindow(); + window.onSuccess.attach(function(sender, proposal) { + _this.store.loadData(_this._prepareData(BIOSAXS.proposal.getMacromolecules())); + }); + window.draw(BIOSAXS.proposal.getMacromoleculeById(macromoleculeId)); +}; - getZipHTMLByMacromoleculeId : function(macromoleculeId) { - if (macromoleculeId != null) { - var fileName = BIOSAXS.proposal.getMacromoleculeById(macromoleculeId).acronym; - return ""; - } - }, - getZipHTMLByExperimentId : function(experimentId, filename) { - if (filename == null){ - filename = "experiment"; - } - return ""; - }, - - getZipURLByAverageId : function(averageId, filename) { - if (filename == null){ - filename = "experiment"; - } - return "/ispyb/user/dataadapter.do?reqCode=getZipFileByAverageListId&f&mergeIdsList=" + averageId + "&fileName=" + filename; - }, - getZipURLBySubtractionId : function(subtractionId, filename) { - if (filename == null){ - filename = "experiment"; +ResultsAssemblyGrid.prototype.getTbar = function() { + var _this = this; + var actions = []; + + actions.push(Ext.create('Ext.Action', { + icon : '../images/add.png', + text : 'Add Macromolecule', + disabled : false, + handler : function(widget, event) { + var window = new MacromoleculeWindow(); + window.onSuccess.attach(function(sender) { + _this.refresh(); + }); + window.draw({}); } - return "/ispyb/user/dataadapter.do?reqCode=getZipFileByAverageListId&f&subtractionIdList=" + subtractionId + "&fileName=" + filename; - }, - - - getZipHTMLByFrameRangeId : function(experimentId, start, end) { - var fileName = "experiment"; - return ""; - }, - getZipFrameHPLCUrl : function(experimentId, start, end) { - return "/ispyb/user/dataadapter.do?reqCode=getZipFileH5ByFramesRange&f&experimentId=" + experimentId + "&start=" + Number(start) +"&end="+ Number(end); - }, - - getQueueUrl : function() { - return "/ispyb/user/dataadapter.do?reqCode=getPagingCompactAnalysisByProposalId"; - }, - - getQueueUrlByExperiment: function(experimentId) { - return "/ispyb/user/dataadapter.do?reqCode=getPagingCompactAnalysisByExperimentId&f&experimentId=" + experimentId; - }, - getStandardDeviation : function(values) { - var sum = 0; - var count = 0; - var avg = null; + })); + + actions.push(Ext.create('Ext.Action', { + icon : '../images/add.png', + text : 'Define an Assembly', + disabled : false, + handler : function(widget, event) { + var createAssemblywindow = new CreateAssemblyWindow(); + createAssemblywindow.onSaved.attach(function(evt, args) { + if (args.macromoleculeIds.length > 0) { + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, proposal) { + _this.refresh(); + }); + adapter.saveAssembly(args.assemblyId, args.macromoleculeIds); - var curatedValues = new Array(); - for ( var i = 0; i < values.length; i++) { - var value = values[i]; - if (value != null) { - if (!isNaN(value)) { - count = count + 1; - sum = sum + Number(value); - curatedValues.push(Number(value)); } - } - } - if (count > 0) { - avg = sum / count; - } else { - avg = sum; - } - var aux = 0; - for ( var i = 0; i < curatedValues.length; i++) { - var value = curatedValues[i]; - aux = aux + Math.pow(value - avg, 2); + }); + createAssemblywindow.draw(_this.experiment); } - /** std **/ - var std = Math.sqrt(aux / count); + })); + + return actions; +}; + +ResultsAssemblyGrid.prototype.refresh = function() { + this.store.loadData(this._prepareData()); +}; + +ResultsAssemblyGrid.prototype.addCondition = function(record, data_parsed, i) { + function getCondition(record) { return { - std : (std), - sum : (sum), - avg : (avg), - validNumber : count, - totalNumber : values.length, - values : values + concentration : record.conc, + quality : record.quality, + bufferBeforeFramesMerged : record.bufferBeforeFramesMerged, + bufferAfterFramesMerged : record.bufferAfterFramesMerged, + framesCount : record.framesCount, + framesMerge : record.framesMerge }; - }, - - getHTMLTableForFrameAveraged : function(bufferAcronym, macromoleculeAcronym, bbmerges, molmerges, bamerges, totalframes, bufferId,macromoleculeId, macromoleculeColor) { - - function getFrameSpan(framesMerged, total) { - return "(" + framesMerged + " of " + total + ")"; - } + } - function getColorFrame(framesMerged, total) { - if (framesMerged / total < 0.5) { - return "#FA5858"; - } - if ((framesMerged / total >= 0.5) && (framesMerged / total < 0.8)) { - return "#FF9900"; + if (data_parsed[i].conditions != null) { + var bufferFound = false; + for ( var index in data_parsed[i].conditions) { + condition = data_parsed[i].conditions[index]; + + if ((condition.macromoleculeId == record.macromoleculeId) && (condition.bufferId == record.bufferId)) { + data_parsed[i].conditions[index].concentrations.push(getCondition(record)); + bufferFound = true; } - return "white"; } - - function getRow(color, acroynm, framesMerged, totalframes) { - return " " - + BUI.getRectangleColorDIV(color, 10, 10) - + " " + acroynm + "" - + getFrameSpan(framesMerged, totalframes) + ""; + if (!bufferFound) { + data_parsed[i].conditions.push({ + macromoleculeId : record.macromoleculeId, + bufferId : record.bufferId, + concentrations : [ getCondition(record) ] + }); } + } +}; - var html = ""; +ResultsAssemblyGrid.prototype.process = function(record, data_parsed) { + if (this.processed[record.macromoleculeId] == null) { + this.processed[record.macromoleculeId] = true; + record.measurementCount = 1; + record.subtractionCount = 1; + record.averageCount = 1; + record.conditions = []; + data_parsed.push(record); + } - /** Buffer Before **/ - if (bufferAcronym != null) { - if (bbmerges != null) { - color = BIOSAXS.proposal.bufferColors[bufferId]; - html = html + getRow(color, bufferAcronym, bbmerges, totalframes); + for ( var i = 0; i < data_parsed.length; i++) { + if (data_parsed[i].macromoleculeId == record.macromoleculeId) { + data_parsed[i].measurementCount = data_parsed[i].measurementCount + 1; + + if (record.subtractionId != null) { + data_parsed[i].subtractionCount = data_parsed[i].subtractionCount + 1; + this.addCondition(record, data_parsed, i); } - } - /** Molecule **/ - if (macromoleculeAcronym != null) { - if (molmerges != null) { - if (macromoleculeColor == null){ - color = BIOSAXS.proposal.macromoleculeColors[macromoleculeId]; - } - else{ - color = macromoleculeColor; //BIOSAXS.proposal.macromoleculeColors[macromoleculeId]; + if (record.framesMerge != null) { + data_parsed[i].averageCount = data_parsed[i].averageCount + 1; + } + + if (record.timeStart != null) { + if (data_parsed[i].timeStart != null) { + if (moment(data_parsed[i].timeStart).format("X") > moment(record.timeStart).format("X")) { + data_parsed[i].timeStart = record.timeStart; + } } - html = html + getRow(color, macromoleculeAcronym, molmerges, totalframes); } } + } - /** Buffer After **/ - if (bufferAcronym != null) { - if (bamerges != null) { - color = BIOSAXS.proposal.bufferColors[bufferId]; - html = html + getRow(color, bufferAcronym, bamerges, totalframes); + return data_parsed; + +}; + +ResultsAssemblyGrid.prototype._prepareData = function(data) { + var data_parsed = []; + for ( var i = 0; i < data.length; i++) { + data_parsed = this.process(data[i], data_parsed); + } + this.data = data_parsed; + return data_parsed; +}; + +/** Given an array of conditions it returns distinct(concentrations) order by concentration and hash map with number of ocurrences**/ +ResultsAssemblyGrid.prototype.parseConcentrations = function(val, conditions, differentConcentration, quality) { + var conditions = []; + var differentConcentration = {}; + var quality = []; + for ( var i = 0; i < val.length; i++) { + var conc = Number(val[i].concentration).toFixed(1); + var quality = Number(val[i].quality).toFixed(2); + if (differentConcentration[conc] == null) { + differentConcentration[conc] = 0; + conditions.push({ + concentration : conc, + quality : [ quality ], + frames : [ { + bufferAfterFramesMerged : val[i].bufferAfterFramesMerged, + bufferBeforeFramesMerged : val[i].bufferBeforeFramesMerged, + framesMerge : val[i].framesMerge, + framesCount : val[i].framesCount + } ] + }); + } else { + /** Add quality **/ + for ( var j = 0; j < conditions.length; j++) { + if (conditions[j].concentration == conc) { + conditions[j].quality.push(quality); + conditions[j].frames.push({ + bufferAfterFramesMerged : val[i].bufferAfterFramesMerged, + bufferBeforeFramesMerged : val[i].bufferBeforeFramesMerged, + framesMerge : val[i].framesMerge, + framesCount : val[i].framesCount + }); + } } } - return html + "
"; - }, - isWebGLEnabled : function(return_context) { - if (!!window.WebGLRenderingContext) { - var canvas = document.createElement("canvas"); - names = [ "webgl", "experimental-webgl", "moz-webgl", "webkit-3d" ]; - context = false; - for ( var i = 0; i < 4; i++) { - try { - context = canvas.getContext(names[i]); - if (context && typeof context.getParameter == "function") { - // WebGL is enabled - if (return_context) { - // return WebGL object if the function's argument is present - return { - name : names[i], - gl : context - }; - } - // else, return just true - return true; - } - } catch (e) { - } + + differentConcentration[conc] = differentConcentration[conc] + 1; + } + /** sorting concentrations **/ + conditions.sort(function(a, b) { + return a.concentration - b.concentration; + }); + return { + concentrations : conditions, + differentConcentration : differentConcentration + }; +}; + +ResultsAssemblyGrid.prototype.getConditionWarnings = function(condition) { + + var withWarnings = 0; + + for ( var i = 0; i < condition.frames.length; i++) { + if (condition.quality[i] == null) { + withWarnings = withWarnings + 1; + continue; + } else { + if (Number(condition.quality[i]) < this.guinierQuality) { + withWarnings = withWarnings + 1; + continue; } - // WebGL is supported, but disabled - return false; } - // WebGL not supported28. - return false; - }, - getHTMLTableForPrefixes : function(bufferBeforeaverageFilePath, averageFilePath, bufferAfterAverageFilePath) { - function getRow(bufferBeforeaverageFilePath) { - file = bufferBeforeaverageFilePath; - try { - file = bufferBeforeaverageFilePath.split("/")[bufferBeforeaverageFilePath.split("/").length - 1]; - } catch (e) { - file = "NA"; + if (condition.frames[i].bufferBeforeFramesMerged / condition.frames[i].framesCount < this.framePercentageThreshold|| condition.frames[i].framesMerged / condition.frames[i].framesCount < this.framePercentageThreshold|| condition.frames[i].bufferAfterFramesMerged / condition.frames[i].framesCount < this.framePercentageThreshold) { + withWarnings = withWarnings + 1; + continue; + } + } + + return { + withWarnings : withWarnings, + withoutWarnings : condition.frames.length - withWarnings + }; +}; + +ResultsAssemblyGrid.prototype.getFrameHTMLTable = function(warnings) { + var html = ""; + if (warnings.withWarnings > 0) { + html = html + "
" + warnings.withWarnings + + "x
"; + } + + if (warnings.withoutWarnings > 0) { + html = html + "
" + warnings.withoutWarnings + + "x
"; + } + return html; +}; + +ResultsAssemblyGrid.prototype.createConcentrationRow = function(numberOcu, condition, warnings){ + var html = ""; + if (numberOcu > 1){ + html = html + "" +numberOcu + "x " + BUI.formatConcentration(condition.concentration); + } + else{ + html = html + BUI.formatConcentration(condition.concentration); + } + html = html + ""; + html = html + this.getFrameHTMLTable(warnings); + ""; + html = html + ""; + return html; +}; + + +ResultsAssemblyGrid.prototype.getConditionHTMLTable = function(val, style, record) { + var maxNumberColumns = 2; + var html = "
"; + var nColumns = 0; + for ( var r = 0; r < val.length; r++) { + if (nColumns == maxNumberColumns) { + nColumns = 0; + html = html + ""; + } + html = html + ""; } - var html = "
"; + var value = val[r]; + + var bufferAcronym = (BIOSAXS.proposal.getBufferById(value.bufferId).acronym); + + var parsed = (this.parseConcentrations(value.concentrations)); + var conditions = parsed.concentrations; + var differentConcentration = parsed.differentConcentration; + + /** Checking warnings **/ + var warnings = []; + var concentrationValidPerconcentration = 0; + var measurements = 0; + for ( var i = 0; i < conditions.length; i++) { + measurements = measurements + conditions[i].frames.length; + var warning = this.getConditionWarnings(conditions[i]); + warnings.push(warning); + if (warning.withoutWarnings > 0) { + concentrationValidPerconcentration = concentrationValidPerconcentration + 1; } - return "
" + file + "
"; - /** Buffer Before **/ - if (bufferBeforeaverageFilePath != null) { - html = html + getRow(bufferBeforeaverageFilePath); - } + this.validColor = '#E0F8E0'; + this.warningColor = '#F5DA81'; + this.notValidColor = '#F6CED8'; - /** Molecule **/ - if (averageFilePath != null) { - html = html + getRow(averageFilePath); + var color = this.warningColor; + if (concentrationValidPerconcentration > 2) { + color = this.validColor; + } + /** More measurement need to be done **/ + if (measurements < 3) { + color = this.notValidColor; } - /** Buffer After **/ - if (bufferAfterAverageFilePath != null) { - html = html + getRow(bufferAfterAverageFilePath); + html = html + "
"; + html = html + ""; + for ( var i = 0; i < conditions.length; i++) { + html = html + this.createConcentrationRow(differentConcentration[conditions[i].concentration], conditions[i], warnings[i]); } - return html + "
" + bufferAcronym.toUpperCase() + + "
"; - }, - getBaseURL : function() { - return '/ispyb/user/dataadapter.do'; - }, + html = html + ""; + html = html + ""; - getPrintcomponentURL : function(dewarId) { - return '/ispyb/user/viewDewarAction.do?reqCode=generateLabels&dewarId=' + dewarId; + html = html + ""; + nColumns = nColumns + 1; + } + html = html + "
"; + return html; +}; - }, - getPDBVisualizerURL : function(modelId, subtractionId, experimentId) { - return '/ispyb/user/viewProjectList.do?reqCode=display&menu=PDBVisualizer&modelId=' + modelId + '&experimentId=' + experimentId - + '&subtractionId=' + subtractionId; - }, +ResultsAssemblyGrid.prototype._getTbar = function() { + function goTo(url) { + window.location = url; + } - getURL : function() { - return this.getBaseURL() + '?reqCode=getImage'; + var _this = this; + return [ { + icon : '../images/application_view_list.png', + text : 'Multiple Select', + handler : function() { + var specimenSelectorResultGrid = new SpecimenSelectorResultGrid(); + var window = Ext.create('Ext.window.Window', { + title : 'Multiple select', + height : 600, + width : 600, + layout : 'fit', + items : [ specimenSelectorResultGrid.getPanel(_this.data) ], + buttons : [ { + text : 'Go', + handler : function() { + var array = []; + for ( var i = 0; i < specimenSelectorResultGrid.selected.length; i++) { + var row = specimenSelectorResultGrid.selected[i]; + array.push({ + macromoleculeId : row.macromoleculeId, + bufferId : row.bufferId + }); + } + goTo(BUI.getMacromoleculeResultsURLByMultipleSearch(array)); + + } + }, { + text : 'Cancel', + handler : function() { + window.close(); + } + } ] + + }).show(); - }, - getAbinitioImageURL : function() { - return this.getBaseURL() + '?reqCode=getAbinitioImage'; - }, - getNSDImageURL : function(modelListId) { - return BUI.getAbinitioImageURL() + '&type=NSD&modelListId=' + modelListId; - }, - getCHI2ImageURL : function(modelListId) { - return BUI.getAbinitioImageURL() + '&type=CHI2&modelListId=' + modelListId; - }, - getModelFile : function(type, modelId, format) { - return this.getBaseURL() + '?reqCode=getModelFile' + "&type=" + type + "&modelId=" + modelId + "&format=" + format; - }, - getPdbURL : function() { - return this.getBaseURL() + '?reqCode=getPdbFiles'; - }, - getStvArray : function(value, error) { - value = Number(value); - error = Number(error); - return [ value - error, value, value + error ]; - }, - getPointArrayForDygraph : function(x, y, error) { - return [ x, BUI.getStvArray(y, error) ]; - }, - createDIV : function(text, width, className, backgroundColor) { - var nameContainer = document.createElement("div"); - var nameSpan = document.createElement("span"); - if (className != null) { - nameSpan.setAttribute("class", className); - } - if (backgroundColor != null) { - nameSpan.setAttribute("style", "float:left;width:" + width + "px;height:18px;background-color:" + backgroundColor); - } else { - nameSpan.setAttribute("style", "float:left;width:" + width + "px;height:18px;"); } - nameSpan.appendChild(document.createTextNode(text)); - nameContainer.appendChild(nameSpan); - return nameContainer; - }, + } ]; +}; - createFormLabel : function(labelText, text, labelWidth, textWidth, backgroundColor) { - var div = document.createElement("div"); +ResultsAssemblyGrid.prototype.getLegendPanel = function() { + return { + html : '
' + BUI.getRectangleColorDIV(this.validColor, 10, 10) + + 'Good quality measurements' + + BUI.getRectangleColorDIV(this.warningColor, 10, 10) + + 'Probably valid with manual processing' + + BUI.getRectangleColorDIV(this.notValidColor, 10, 10) + + 'More measurements need to be done
' + }; +}; +/** Returns the grid **/ +ResultsAssemblyGrid.prototype.getPanel = function(macromolecules) { + var _this = this; - div.appendChild(BUI.createDIV(labelText, labelWidth, "bLabel", backgroundColor)); - div.appendChild(BUI.createDIV(text, textWidth, "btext", backgroundColor)); - return div.innerHTML; - }, + this.store = Ext.create('Ext.data.Store', { + fields : [ + 'macromoleculeId', 'macromoleculeAcronym', 'measurementCount', 'subtractionCount', 'averageCount', 'timeStart', 'concentrationArray', + 'bufferConditions', 'conditions' ], + data : this._prepareData(macromolecules) + }); - createTextArea : function(labelText, text, labelWidth, rows, cols) { - var div = document.createElement("div"); - div.appendChild(BUI.createDIV(labelText, labelWidth, "bLabel")); - var textArea = document.createElement("textarea"); - textArea.setAttribute("rows", rows); - textArea.setAttribute("cols", cols); - textArea.appendChild(document.createTextNode(text)); - div.appendChild(textArea); - return div.innerHTML; - }, + this.store.sort('macromoleculeAcronym'); - showBetaWarning : function() { - alert("ISPyB for Biosaxs version Beta has not this functionality enabled"); - }, + this.grid = Ext.create('Ext.grid.Panel', { + id : this.id, + title : 'Macromolecules', + store : this.store, + tbar : this._getTbar(), + bbar : [ this.getLegendPanel() ], + width : this.width, + height : this.height, + maxHeight : this.maxHeight, + sortableColumns : true, + columns : [ + { + text : '', + dataIndex : 'macromoleculeId', + width : 20, + hidden : false, + renderer : function(val, y, sample) { + return BUI.getRectangleColorDIV(BIOSAXS.proposal.macromoleculeColors[val], 10, 10); + } + }, + { + text : 'Macromolecule', + dataIndex : 'macromoleculeAcronym', + width : 200 + }, + { + text : 'Buffer Conditions', + dataIndex : 'conditions', + flex : 1, + renderer : function(val, style, record) { + return _this.getConditionHTMLTable(val, style, record); + } + }, + { + header : 'Average', + dataIndex : 'percentageCollected', + name : 'percentageCollected', + type : 'string', + hidden : true, + renderer : function(val, y, sample) { + return "
" + + BUI.getProgessBar((sample.raw.averageCount / sample.raw.measurementCount) * 100, sample.raw.averageCount + "/" + + sample.raw.measurementCount) + "
"; + }, + width : 100, + sorter : false + }, + { + header : 'Subtractions', + dataIndex : 'percentageCollected', + name : 'percentageCollected', + type : 'string', + hidden : true, + renderer : function(val, y, sample) { + return "
"+ BUI.getProgessBar((sample.raw.subtractionCount / sample.raw.measurementCount) * 100, sample.raw.subtractionCount + "/" + + sample.raw.measurementCount) + "
"; + }, + width : 100 + }, { + header : 'Date', + dataIndex : 'timeStart', + name : 'timeStart', + type : 'string', + renderer : function(value, metaData, record, rowIndex, colIndex, store) { + if (record.raw.timeStart != null) { + return moment(record.raw.timeStart).format("MMM Do YY"); + } + } + }, { + header : 'Download', + dataIndex : 'percentageCollected', + name : 'percentageCollected', + type : 'string', + renderer : function(val, y, sample) { + return BUI.getZipHTMLByMacromoleculeId(sample.raw.macromoleculeId); + }, + width : 100 + }, - formatValuesErrorUnitsScientificFormat : function(val, error, unit, args) { - var fontSize = 14; - var decimals = 2; - var errorFontSize = 10; - /** line break **/ - var lineBreak = true; - if (args != null) { - if (args.fontSize != null) { - fontSize = args.fontSize; - } - if (args.decimals != null) { - decimals = args.decimals; - } - if (args.errorFontSize != null) { - errorFontSize = args.errorFontSize; - } - if (args.lineBreak != null) { - lineBreak = args.lineBreak; + { + id : 'btnResultVisible', + width : 85, + sortable : false, + renderer : function(value, metaData, record, rowIndex, colIndex, store) { + return BUI.getGreenButton('GO'); + } + } ], + viewConfig : { + stripeRows : true, + listeners : { + afterrender : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + }, + celldblclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + _this.edit(record.data.macromoleculeId); + }, + cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + if (grid.getGridColumns()[cellIndex].getId() == 'buttonEditMacromolecule') { + _this.edit(record.data.macromoleculeId); + } + if (grid.getGridColumns()[cellIndex].getId() == 'buttonRemoveMacromolecule') { + BUI.showBetaWarning(); + } + if (grid.getGridColumns()[cellIndex].getId() == 'btnResultVisible') { + window.location = BUI.getMacromoleculeResultsURL(record.data.macromoleculeId); + } + } } - } + }); - if (val == "") { - return ""; - } - if (error == null) { - return "" + Number(val).toFixed(decimals) + ""; - } - var html = "" + Number(val).toFixed(decimals) + " " + unit + ""; - if (lineBreak) { - html = html + "
"; - } - return html + " ± " + Number(Number(error).toFixed(3)).toExponential() - + ""; - }, + /** Adding the tbar **/ + if (this.tbar) { + this.grid.addDocked({ + xtype : 'toolbar', + items : this.getTbar() + }); + } + return this.grid; +}; - formatValuesErrorUnits : function(val, error, unit, args) { - var fontSize = 16; - var decimals = 2; - var errorFontSize = 10; - /** line break **/ - var lineBreak = true; - if (args != null) { - if (args.fontSize != null) { - fontSize = args.fontSize; - } - if (args.decimals != null) { - decimals = args.decimals; - } - if (args.errorFontSize != null) { - errorFontSize = args.errorFontSize; - } - if (args.lineBreak != null) { - lineBreak = args.lineBreak; - } +ResultsAssemblyGrid.prototype.input = function(targetId) { + return { + data : DATADOC.getData_3367(), + proposal : DATADOC.getProposal_3367() + }; +}; - } +ResultsAssemblyGrid.prototype.test = function(targetId) { + var grid = new ResultsAssemblyGrid({ + height : 600, + searchBar : false, + tbar : false, + btnResultVisible : true, + width : 1000 + }); + BIOSAXS.proposal = new Proposal(grid.input().proposal); + var panel = grid.getPanel(grid.input().data); + panel.render(targetId); - if (val == "") { - return ""; +}; + + +function RigidModelGrid(args) { + this.height = null; + this.width = null; + this.id = BUI.id(); + + if (args != null) { + if (args.height != null) { + this.height = args.height; + + if (args.width != null) { + this.width = args.width; + } + } + } + + this.onSelected = new Event(this); +} + + +RigidModelGrid.prototype.refresh = function(subtractions) { + var data = []; + if (subtractions != null){ + if (subtractions.length > 0){ + for (j in subtractions){ + var subtraction = subtractions[j]; + if (subtraction.rigidBodyModeling3VOs != null){ + for (i in subtraction.rigidBodyModeling3VOs){ + data.push(subtraction.rigidBodyModeling3VOs[i]); + } + } + } + } + } + this.store.loadData(data); +}; + +RigidModelGrid.prototype.getPanel = function() { + var _this = this; + this.store = Ext.create('Ext.data.Store', { + fields : [ 'symmetry', 'subtractionId', 'subUnitConfigFilePath', 'rigidBodyModelingId', 'rigidBodyModelFilePath', 'logFilePath', 'fitFilePath', 'curveConfigFilePath', + 'crossCorrConfigFilePath', 'contactDescriptionFilePath' ], + data : [] + }); + + this.store.sort([ { + property : 'name', + direction : 'ASC' + } ]); + + this.panel = Ext.create('Ext.grid.Panel', { + store : this.store, + width : this.width, + height : this.height, + margin : 10, + deferredRender : false, + columns : [ { + text : 'RBM', + dataIndex : 'rigidBodyModelFilePath', + hidden : false, + flex : 1, + renderer : function(val){ + return BUI.getFileName(val); + } + }, { + text : 'Sub Unit Conf.', + dataIndex : 'subUnitConfigFilePath', + hidden : true, + flex : 1, + renderer : function(val){ + return BUI.getFileName(val); + } + }, { + text : 'Log', + dataIndex : 'logFilePath', + hidden : true, + flex : 1, + renderer : function(val){ + return BUI.getFileName(val); + } + }, { + text : 'Fit', + dataIndex : 'fitFilePath', + hidden : false, + flex : 1, + renderer : function(val){ + return BUI.getFileName(val); + } + }, { + text : 'Curve Conf.', + dataIndex : 'curveConfigFilePath', + hidden : false, + flex : 1, + renderer : function(val){ + return BUI.getFileName(val); + } + }, { + text : 'Cross Corr.', + dataIndex : 'crossCorrConfigFilePath', + hidden : false, + flex : 1, + renderer : function(val){ + return BUI.getFileName(val); + } + }, { + text : 'Contact Desc.', + dataIndex : 'contactDescriptionFilePath', + hidden : true, + flex : 1, + renderer : function(val){ + return BUI.getFileName(val); + } + } ], + + viewConfig : { + enableTextSelection : true, + preserveScrollOnRefresh : true + }, + listeners : { + cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + + }, + afterrender : function() { + } + } + }); + return this.panel; +}; + + +/** + * shows shipments + * + * @height + * @width + * @minHeight + * @btnEditVisible + */ +function ShipmentGrid(args) { + this.id = BUI.id(); + this.height = 100; + this.width = null; + this.minHeight = null; + this.btnEditVisible = true; + + if (args != null) { + if (args.height != null) { + this.height = args.height; } - if (error == null) { - return "" + Number(val).toFixed(decimals) + ""; + if (args.width != null) { + this.width = args.width; } - var html = "" + Number(val).toFixed(decimals) + " " + unit + ""; - if (lineBreak) { - html = html + "
"; + if (args.minHeight != null) { + this.minHeight = args.minHeight; } - return html + " ± " + Number(Number(error).toFixed(8)).toExponential() - + ""; - }, - - formatValuesUnits : function(val, unit, args) { - var fontSize = 12; - var decimals = 2; - var unitsFontSize = 10; - if (args != null) { - if (args.fontSize != null) { - fontSize = args.fontSize; - } - if (args.decimals != null) { - decimals = args.decimals; - } - if (args.unitsFontSize != null) { - unitsFontSize = args.unitsFontSize; - } - + if (args.btnEditVisible != null) { + this.btnEditVisible = args.btnEditVisible; } + } +} - if (val === "") { - return ""; - } - if (unit == null) { - return "" + Number(val).toFixed(decimals) + ""; - } - return "" + Number(val).toFixed(decimals) + " " + unit + ""; - }, +ShipmentGrid.prototype._getColumns = function() { + var _this = this; + var columns = [ + { + text : 'Name', + dataIndex : 'shippingName', + flex : 1 + }, + { + header : 'Type', + dataIndex : 'shippingType', + flex : 1, + hidden : true, + renderer : function(val, comp, record) { + if (val != null) { + return val.toUpperCase(); + } - getGreenButton : function(text, args) { - var width = 70; - var height = 20; - if (args != null) { - if (args.width != null) { - width = args.width; } - if (args.height != null) { - height = args.height; + }, + { + header : 'Status', + type : 'string', + flex : 1, + hidden : false, + renderer : function(comp, val, record) { + if (record.raw.shippingStatus != null) { + if (new String(record.raw.shippingStatus).toUpperCase() == 'OPENED') { + return "" + new String(record.raw.shippingStatus).toUpperCase() + ""; + } + return "" + new String(record.raw.shippingStatus).toUpperCase() + ""; + } + } + }, + { + text : 'Cases', + flex : 1, + hidden : false, + renderer : function(comp, val, record) { + var shipment = BIOSAXS.proposal.getShipmentById(record.data.shippingId); + var container = ""; + if (shipment.dewarVOs.length > 0) { + container = container + ""; + } else { + return "Empty"; + } + return container + "
" + shipment.dewarVOs.length + "x
"; } - } - - return ''; - }, -// getBlueButton : function(text, args) { -// var width = 70; -// var height = 20; -// if (args != null) { -// if (args.width != null) { -// width = args.width; -// } -// if (args.height != null) { -// height = args.height; -// } -// } -// -// return ''; -// }, + }, { + header : 'Comments', + dataIndex : 'comments', + flex : 1, + hidden : false + }, { + header : 'Creation Date', + dataIndex : 'creationDate', + hidden : true + } ]; - getBlueButton : function(text, args) { - var width = 70; - var height = 20; - var href = null; - if (args != null) { - if (args.width != null) { - width = args.width; - } - if (args.height != null) { - height = args.height; + if (this.btnEditVisible) { + columns.push({ + id : _this.id + 'buttonEdit', + text : '', + hidden : !_this.btnEditVisible, + width : 100, + sortable : false, + renderer : function(value, metaData, record, rowIndex, colIndex, store) { + return BUI.getGreenButton('EDIT'); } - if (args.href != null) { - href = args.href; + }); + } + + columns.push({ + id : _this.id + 'buttonRemove', + text : '', + width : 100, + sortable : false, + renderer : function(value, metaData, record, rowIndex, colIndex, store) { + var shipment = BIOSAXS.proposal.getShipmentById(record.data.shippingId); + if (shipment.dewarVOs.length == 0) { + return BUI.getRedButton('REMOVE'); } + } - if (href != null) { - return ''; - } else { - return ''; -// return ''; - } - }, - - getSubmitGreenButton : function(text) { - return ''; - }, + }); - getRedButton : function(text) { - return ''; + return columns; +}; - }, - getRectangleColorDIV : function(color, height, width) { - return '
'; - }, +ShipmentGrid.prototype._getTopButtons = function() { + var _this = this; + /** Actions buttons **/ + var actions = []; + actions.push(Ext.create('Ext.Action', { + icon : '../images/add.png', + text : 'Add Shipment', + handler : function(widget, event) { + //window.location = BUI.getCreateShipmentURL(); + var _this = this; - openBufferWindow : function(bufferId) { - var window = new BufferWindow(); - window.draw(tabs.experiment.getBufferById(bufferId), tabs.experiment); - }, + var shipmentForm = new ShipmentForm({ + creationMode : true, + showTitle : false + }); + shipmentForm.onSaved.attach(function(sender, shipment) { + _this.showShipmentTabs(shipment, targetId); + }); + + var window = Ext.create('Ext.window.Window', { + title : 'New Shipment', + height : 600, + width : 800, + layout : 'fit', + items : [ shipmentForm.getPanel() ] + }).show(); - /** Render for safety levels on grids **/ - safetyRenderer : function(val, y, specimen) { - var color = val; - if (val == "YELLOW") { - color = "#E9AB17"; } - return '' + val + ''; - }, + })); + return actions; +}; - getSampleColor : function() { - return '#CB181D'; - }, +ShipmentGrid.prototype.refresh = function(shippings) { + this.features = shippings; + this.store.loadData(this._prepareData(), false); +}; - getLightSampleColor : function() { - return '#FCBBA1'; - }, +ShipmentGrid.prototype._prepareData = function() { + var data = []; + for ( var i = 0; i < this.features.length; i++) { + data.push(this.features[i]); + } + return data; +}; - getBufferColor : function() { - return '#6A51A3'; - }, - getLightBufferColor : function() { - return '#BCBDDC'; - }, +ShipmentGrid.prototype.getPanel = function(shipments) { + this.features = shipments; + return this._renderGrid(); +}; - formatConcentration : function(val) { - if (val != null) { - return "" + Number(val).toFixed(2) + " mg/ml "; - } - return val; - }, +ShipmentGrid.prototype.edit = function(shippingId) { + window.location = BUI.getShippingURL(shippingId); +}; - formatVolume : function(sample, volume) { - if (Number(sample.data.volumeToLoad) > Number(sample.data.volume)) { - return "" + volume + " �l"; - } - if (Number(sample.data.volumeToLoad) == Number(sample.data.volume)) { - return "" + volume + " �l"; - } - return "" + volume + " �l"; - }, +ShipmentGrid.prototype._getStoreFields = function(data) { + var _this = this; + return [ { + name : 'shippingId' + }, { + name : 'shippingName' + }, { + name : 'shippingStatus' + }, { + name : 'shippingType' + }, { + name : 'creationDate' + }, { + name : 'comments' + } ]; +}; - getProposal : function() { - return new Proposal(); - }, +ShipmentGrid.prototype._renderGrid = function() { + var _this = this; - getSampleNameRenderer : function(val, y, record) { - var sample = record.data; - if (record.raw.macromolecule3VO == null) { - return '' + sample.code + ''; - } else { - return '' + sample.code + ''; - } - }, + /** Store **/ + var data = this._prepareData(); + this.store = Ext.create('Ext.data.Store', { + fields : this._getStoreFields(data), + autoload : true, + data : data + }); - getSafetyLevels : function() { - var safetyLevels = new Array(); - safetyLevels.push({ - safetyLevelId : "", - name : "UNKNOWN" - }); - safetyLevels.push({ - safetyLevelId : 1, - name : "GREEN" - }); - safetyLevels.push({ - safetyLevelId : 2, - name : "YELLOW" - }); - safetyLevels.push({ - safetyLevelId : 3, - name : "RED" - }); - return safetyLevels; - }, + this.store.loadData(data, false); + + this.grid = Ext.create('Ext.grid.Panel', { + title : "Shipping", + icon : '/ispyb/images/plane.gif', + width : this.width, + minWidth : this.minWidth, + height : this.height, + store : this.store, + columns : this._getColumns(), + viewConfig : { + stripeRows : true, + listeners : { + itemdblclick : function(dataview, record, item, e) { + _this.edit(record.raw.shippingId); + }, + cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonEdit') { + _this.edit(record.data.shippingId); + } - getErrorColor : function() { - return '#F6CED8'; - }, - getWarningColor : function() { - return '#F5DA81'; - }, - getValidColor : function() { - return '#E0F8E0'; - }, - getSamplePlateLetters : function() { - return [ "A", "B", "C", "D", "E", "F", "G", "H" ]; - }, - getSamplePositionHTML : function(sample, experiment) { - var plate = ""; - var row = ""; - var column = ""; - var rows = this.getSamplePlateLetters(); - if (sample.sampleplateposition3VO != null) { - var samplePlate = experiment.getSamplePlateById(sample.sampleplateposition3VO.samplePlateId); - if (samplePlate != null) { - plate = (samplePlate.slotPositionColumn); - row = (sample.sampleplateposition3VO.rowNumber); - column = (sample.sampleplateposition3VO.columnNumber); - // var html = "Plate: " + "" + plate + ""; - // html = html + ", Row: " + "" + rows[row - 1] + ""; - // html = html + ", Column: " + "" + column + ""; - var html = "Plate: " + plate - + "-" + rows[row - 1] + "" + column + ""; - return html; + if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonRemove') { + var shipment = BIOSAXS.proposal.getShipmentById(record.data.shippingId); + if (shipment.dewarVOs.length == 0) { + + var adapter = new BiosaxsDataAdapter(); + _this.grid.setLoading("ISPyB: Removing shipment"); + adapter.onSuccess.attach(function(sender) { + BIOSAXS.proposal.onInitialized.attach(function(sender) { + _this.refresh(BIOSAXS.proposal.getShipments()); + _this.grid.setLoading(false); + }); + BIOSAXS.proposal.init(); + }); + + adapter.onError.attach(function(sender) { + alert("Error"); + _this.grid.setLoading(false); + }); + adapter.removeShipment(record.data.shippingId); + } + } + } } + }, + selModel : { + mode : 'SINGLE' } - return ""; - }, + }); - getSafetyLabelName : function(safetyLevelId) { - var safetyLevels = BUI.getSafetyLevels(); - for ( var i = 0; i < safetyLevels.length; i++) { - if (safetyLevels[i].safetyLevelId == safetyLevelId) { - return safetyLevels[i].name; + var actions = _this._getTopButtons(); + this.grid.addDocked({ + xtype : 'toolbar', + items : actions + }); + + this.grid.getSelectionModel().on({ + selectionchange : function(sm, selections) { + if (selections.length) { + for ( var i = 0; i < actions.length; i++) { + if (actions[i].enable) { + actions[i].enable(); + } + } + } else { + for ( var i = 0; i < actions.length; i++) { + if (actions[i].alwaysEnabled == false) { + if (actions[i].disable) { + actions[i].disable(); + } + } + } } } - return "UNKNOWN"; - }, - /** generate random id **/ - id : function() { - var text = ""; - var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; - - for ( var i = 0; i < 5; i++) - text += possible.charAt(Math.floor(Math.random() * possible.length)); + }); + return this.grid; +}; - return text; - }, - showWarning : function(message) { - Ext.Msg.show({ - title : 'Warning', - msg : message, - icon : Ext.Msg.WARNING, - animEl : 'elId' - }); - }, - showError : function(message) { - Ext.Msg.show({ - title : 'Warning', - msg : message, - icon : Ext.Msg.ERROR, - animEl : 'elId' - }); - }, - getTipHTML : function(message) { - //return "
Tip
" - // + message + "
"; - return "
Tip
" - + message + "
"; - }, +ShipmentGrid.prototype.input = function() { + return { + proposal : new MeasurementGrid().input().proposal, + shippings : DATADOC.getShippings_10() + }; +}; - getWarningHTML : function(message) { - return "
Warning
" - + message + "
"; - }, +ShipmentGrid.prototype.test = function(targetId) { + var shipmentGrid = new ShipmentGrid({ + minHeight : 300, + height : 440 + }); + BIOSAXS.proposal = new Proposal(shipmentGrid.input().proposal); + var panel = shipmentGrid.getPanel(targetId); + panel.render(targetId); + shipmentGrid.refresh(shipmentGrid.input().shippings); +}; + +function SpecimenGrid(args) { + this.id = BUI.id(); + this.height = 500; + this.unitsFontSize = 9; + this.editEnabled = false; + this.isPositionColumnHidden = false; + this.removeBtnEnabled = false; - getErrorHTML : function(message) { - return "
Error
" - + message + "
"; - }, + this.selectionMode = "MULTI"; + this.updateRowEnabled = false; + this.grouped = true; + this.width = 900; + this.title = 'Specimens'; + + this.margin = "0 0 0 0"; +// this.experimentColorBased = false; - getProgessBar : function(percentage, text) { - /** percentage 100% green **/ - var color = "#0a0"; + if (args != null) { + if (args.height != null) { + this.height = args.height; + } - color = "#99CC00"; - if (percentage > 100) { - color = "yellow"; - percentage = 100; + if (args.showTitle == false) { + this.title = null; } - if (isNaN(percentage)) { - color = "white"; - percentage = 0; + + if (args.margin == false) { + this.margin = args.margin; } - var defaultText = percentage + "%"; - if (text != null) { - defaultText = text; + if (args.grouped == false) { + this.grouped = null; } - return "
" + defaultText + "
"; - }, - getFileName : function(filePath){ - if (filePath != null){ - return filePath.split("/")[filePath.split("/").length - 1] + if (args.width != null) { + this.width = args.width; } - return ""; - } -}; -Ext.ux.form.RequiredCombo = Ext.extend(Ext.form.ComboBox, { - config : { - cls : 'custom-field-text-required' - }, - initComponent : function() { - Ext.ux.form.RequiredCombo.superclass.initComponent.apply(this, arguments); - }, - checkChange : function() { - if ((this.getValue() == null) || String(this.getValue()).length == 0) { - this.addCls('custom-field-text-required'); - } else { - this.removeCls('custom-field-text-required'); + if (args.editEnabled != null) { + this.editEnabled = args.editEnabled; + } + if (args.removeBtnEnabled != null) { + this.removeBtnEnabled = args.removeBtnEnabled; + } + if (args.isPositionColumnHidden != null) { + this.isPositionColumnHidden = args.isPositionColumnHidden; + } + if (args.selectionMode != null) { + this.selectionMode = args.selectionMode; + } + if (args.updateRowEnabled != null) { + this.updateRowEnabled = args.updateRowEnabled; } + } -}); + this.onClick = new Event(this); + this.onSelected = new Event(this); + this.onRemoved = new Event(this); + this.onSpecimenChanged = new Event(); +} -Ext.define('Ext.form.field.RequiredNumber', { - extend : 'Ext.form.field.Number', - alias : 'widget.requirednumberfield', - alternateClassName : [ 'Ext.form.RequiredNumberField', 'Ext.form.RequiredNumber' ], - config : { - cls : 'custom-field-text-required' - }, +SpecimenGrid.prototype._prepareData = function(experiment) { + var data = []; - initComponent : function() { - var me = this; - me.callParent(); - me.setMinValue(me.minValue); - me.setMaxValue(me.maxValue); - }, + var samples = experiment.getSamples(); + for ( var i = 0; i < samples.length; i++) { + var sample = samples[i]; + if (sample.macromolecule3VO != null) { + sample.macromolecule = sample.macromolecule3VO.acronym; + sample.exposureTemperature = []; + sample.macromoleculeId = sample.macromolecule3VO.macromoleculeId; + } - onChange : function() { - if ((this.getValue() == null) || String(this.getValue()).length == 0) { - this.addCls('custom-field-text-required'); + if (sample.sampleplateposition3VO != null) { + if (sample.sampleplateposition3VO.samplePlateId != null) { + sample.samplePlateId = sample.sampleplateposition3VO.samplePlateId; + sample.rowNumber = sample.sampleplateposition3VO.rowNumber; + sample.columnNumber = sample.sampleplateposition3VO.columnNumber; + if (experiment.getSamplePlateById(sample.sampleplateposition3VO.samplePlateId).plategroup3VO != null) { + sample.plateGroupName = experiment.getSamplePlateById(sample.sampleplateposition3VO.samplePlateId).plategroup3VO.name; + sample.samplePlateName = experiment.getSamplePlateById(sample.sampleplateposition3VO.samplePlateId).name + " [" + sample.plateGroupName + "]"; + sample.slotPositionColumn = experiment.getSamplePlateById(sample.sampleplateposition3VO.samplePlateId).slotPositionColumn; + } + } } else { - this.removeCls('custom-field-text-required'); + sample.samplePlateName = "Unallocated Specimens"; } - this.toggleSpinners(); - this.callParent(arguments); - } -}); -Ext.define('Ext.form.field.RequiredText', { - extend : 'Ext.form.field.Text', - alias : 'widget.requiredtext', - requires : [ 'Ext.form.field.VTypes', 'Ext.layout.component.field.Text' ], - alternateClassName : [ 'Ext.form.RequiredTextField', 'Ext.form.RequiredText' ], - config : { - cls : 'custom-field-text-required' - }, - initComponent : function() { - var me = this; - if (me.allowOnlyWhitespace === false) { - me.allowBlank = false; + /** For grouping, because sencha has not option for multiple grouping I add a field to your store with a convert function that concatenates these two fields and then group by that field.**/ + sample.groupIndex = sample.bufferId + sample.macromoleculeId; + var macromolecule = BIOSAXS.proposal.getMacromoleculeById(sample.macromoleculeId); + + sample.acronym = "Buffers"; + if (macromolecule != null) { + sample.acronym = BIOSAXS.proposal.getMacromoleculeById(sample.macromoleculeId).acronym; } - me.callParent(); - me.addEvents( - /** - * @event autosize - * Fires when the **{@link #autoSize}** function is triggered and the field is resized according to the - * {@link #grow}/{@link #growMin}/{@link #growMax} configs as a result. This event provides a hook for the - * developer to apply additional logic at runtime to resize the field if needed. - * @param {Ext.form.field.Text} this This text field - * @param {Number} width The new field width - */ - 'autosize', - /** - * @event keydown - * Keydown input field event. This event only fires if **{@link #enableKeyEvents}** is set to true. - * @param {Ext.form.field.Text} this This text field - * @param {Ext.EventObject} e - */ - 'keydown', - /** - * @event keyup - * Keyup input field event. This event only fires if **{@link #enableKeyEvents}** is set to true. - * @param {Ext.form.field.Text} this This text field - * @param {Ext.EventObject} e - */ - 'keyup', - /** - * @event keypress - * Keypress input field event. This event only fires if **{@link #enableKeyEvents}** is set to true. - * @param {Ext.form.field.Text} this This text field - * @param {Ext.EventObject} e - */ - 'keypress'); - me.addStateEvents('change'); - me.setGrowSizePolicy(); - }, - checkChange : function() { - if ((this.getValue() == null) || String(this.getValue()).length == 0) { - this.addCls('custom-field-text-required'); - } else { - this.removeCls('custom-field-text-required'); + sample.buffer = experiment.getBufferById(sample.bufferId); + + sample.volumeToLoad = experiment.getVolumeToLoadBySampleId(sample.sampleId); + data.push(sample); + } + return data; +}; + +SpecimenGrid.prototype.deselectAll = function() { + this.grid.getSelectionModel().deselectAll(); +}; + +SpecimenGrid.prototype.selectById = function(specimenId) { + this.grid.getSelectionModel().deselectAll(); + for ( var i = 0; i < this.grid.getStore().data.items.length; i++) { + var item = this.grid.getStore().data.items[i].raw; + if (item.specimenId == specimenId) { + this.grid.getSelectionModel().select(i); } } -}); +}; -var BIOSAXS_COMBOMANAGER = { +SpecimenGrid.prototype.getStore = function() { + return this.store; +}; - getComboMacromoleculeByMacromolecules : function(macromolecules, args) { - var labelWidth = 150; - var margin = "0 0 5 0"; - var width = 300; +SpecimenGrid.prototype.getPlugins = function() { + var _this = this; - if (args != null) { - if (args.labelWidth != null) { - labelWidth = args.labelWidth; - } - if (args.margin != null) { - margin = args.margin; - } - if (args.width != null) { - width = args.width; - } - } + var plugins = []; - var store = Ext.create('Ext.data.Store', { - fields : [ 'macromoleculeId', 'acronym' ], - data : macromolecules, - sorters : [ 'acronym' ] - }); + if (this.updateRowEnabled) { + plugins.push(Ext.create('Ext.grid.plugin.RowEditing', { + clicksToEdit : 1, + listeners : { + validateedit : function(grid, e) { + var measurements = []; - return Ext.create('Ext.form.ComboBox', { - fieldLabel : 'Macromolecules', - labelWidth : labelWidth, - width : width, - margin : margin, - store : store, - editable: false, - queryMode : 'local', - displayField : 'acronym', - valueField : 'macromoleculeId' - }); - }, - getComboBuffers : function(buffers, args) { - var labelWidth = 150; - var margin = "0 0 5 0"; - var width = 300; - var fieldLabel = 'Buffer'; + if (e.newValues.bufferId != e.record.raw.bufferId) { + /** If buffer has changed we have to change all the specimens sharing same datacollection **/ + var dataCollections = []; + if (e.record.raw.macromoleculeId == null) { + dataCollections = dataCollections.concat(_this.experiment.getDataCollectionsBySpecimenId(e.record.raw.specimenId)); + } else { + var sampleDataCollections = _this.experiment.getDataCollectionsBySpecimenId(e.record.raw.specimenId); + for ( var i = 0; i < sampleDataCollections.length; i++) { + var sampleDataCollection = sampleDataCollections[i]; + if (sampleDataCollection != null) { + for ( var j = 0; j < sampleDataCollection.measurementtodatacollection3VOs.length; j++) { + var measurementTODc = sampleDataCollection.measurementtodatacollection3VOs[j]; + if (measurementTODc.dataCollectionOrder == 1) { + dataCollections = dataCollections.concat(_this.experiment.getDataCollectionsBySpecimenId(_this.experiment + .getMeasurementById(measurementTODc.measurementId).specimenId)); + } + } + } + } + } + var i = null; + for ( i = 0; i < dataCollections.length; i++) { + var dataCollection = dataCollections[i]; + var specimens = _this.experiment.getSpecimenByDataCollectionId(dataCollection.dataCollectionId); + measurements = measurements.concat(specimens); + } - if (args != null) { - if (args.labelWidth != null) { - labelWidth = args.labelWidth; - } - if (args.margin != null) { - margin = args.margin; - } - if (args.width != null) { - width = args.width; - } - if (args.noLabel != null) { - fieldLabel = null; - } - } + for ( i = 0; i < measurements.length; i++) { + var measurement = measurements[i]; + var specimen = _this.experiment.getSpecimenById(measurement.specimenId); + specimen.bufferId = e.newValues.bufferId; + new BiosaxsDataAdapter().saveSpecimen(specimen, _this.experiment); + } + } - var store = Ext.create('Ext.data.Store', { - fields : [ 'bufferId', 'acronym' ], - data : buffers, - sorters : [ 'acronym' ] - }); + /** Setting values **/ + e.record.raw.concentration = e.newValues.concentration; + e.record.raw.volume = e.newValues.volume; - return Ext.create('Ext.form.ComboBox', { - fieldLabel : fieldLabel, - labelWidth : labelWidth, - width : width, - margin : margin, - editable: false, - store : store, - queryMode : 'local', - displayField : 'acronym', - valueField : 'bufferId' - }); - }, - getComboSessions : function(sessions, args) { - var labelWidth = 150; - var margin = "0 0 5 0"; - var width = 300; + /** Position **/ + if (e.record.raw.sampleplateposition3VO != null) { + var samplePlate = _this.experiment.getSamplePlateBySlotPositionColumn(e.newValues.slotPositionColumn); + if (samplePlate != null) { + e.record.raw.sampleplateposition3VO = { + columnNumber : e.newValues.columnNumber, + rowNumber : e.newValues.rowNumber, + samplePlateId : samplePlate.samplePlateId + }; + } + } else { + if (e.newValues.slotPositionColumn != null) { + var samplePlate = _this.experiment.getSamplePlateBySlotPositionColumn(e.newValues.slotPositionColumn); + if (samplePlate != null) { + e.record.raw.sampleplateposition3VO = { + columnNumber : e.newValues.columnNumber, + rowNumber : e.newValues.rowNumber, + samplePlateId : samplePlate.samplePlateId + }; + } + } + } - if (args != null) { - if (args.labelWidth != null) { - labelWidth = args.labelWidth; - } - if (args.margin != null) { - margin = args.margin; - } - if (args.width != null) { - width = args.width; + var macromoleculeId = e.record.data.macromoleculeId; + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, specimen) { + /** Because macromolecule3VO is fecthed LAZY **/ + if (macromoleculeId != null) { + specimen.macromolecule3VO = BIOSAXS.proposal.getMacromoleculeById(macromoleculeId); + } + _this.onSpecimenChanged.notify(specimen); + _this.grid.setLoading(false); + }); + adapter.onError.attach(function() { + alert("Error"); + _this.grid.setLoading(false); + }); + _this.grid.setLoading(); + adapter.saveSpecimen(e.record.raw, _this.experiment); + } } - } - - for ( var i = 0; i < sessions.length; i++) { - sessions[i]["startDateFormatted"] = moment(sessions[i].startDate).format("MMM Do YY"); - sessions[i]["sorter"] = moment(sessions[i].startDate).format("YYYYMMDD"); - } + })); + } + return plugins; +}; - var store = Ext.create('Ext.data.Store', { - fields : [ 'sessionId', 'startDateFormatted', 'beamlineName', 'startDate', 'endDate', 'beamlineOperator' ], - data : sessions, - sorters : [ 'sorter' ] +SpecimenGrid.prototype._getRowCombo = function() { + var data = []; + for ( var i = 1; i <= 8; i++) { + data.push({ + rowNumber : i, + name : BUI.getSamplePlateLetters()[i - 1] }); + } - return Ext.create('Ext.form.ComboBox', { - fieldLabel : 'Sessions', - labelWidth : labelWidth, - width : width, - margin : margin, - store : store, - queryMode : 'local', - // displayField : 'startDate', - valueField : 'sessionId', - // Template for the dropdown menu. - // Note the use of "x-boundlist-item" class, - // this is required to make the items selectable. - tpl : Ext.create('Ext.XTemplate', '', - '
{startDateFormatted} {beamlineName}
', '
'), - // template for the content inside text field - displayTpl : Ext.create('Ext.XTemplate', '', '{startDateFormatted}', '') + var positionsStore = Ext.create('Ext.data.Store', { + fields : [ 'rowNumber', 'name' ], + data : data + }); - }); - } + return Ext.create('Ext.form.ComboBox', { + store : positionsStore, + queryMode : 'local', + displayField : 'name', + valueField : 'rowNumber' + }); }; - - function GenericWindow(args){ - this.title = "title"; - this.width = 700; - this.height = 500; - this.id = BUI.id(); - - this.close = false; - this.draggable = true; - this.modal = true; - - if (args != null){ - if (args.actions != null){ - this.actions = args.actions; - } - if (args.form != null){ - this.form = args.form; - } - if (args.width != null){ - this.width = args.width; - } - if (args.modal != null){ - this.modal = args.modal; - } - - if (args.height != null){ - this.height = args.height; - } - if (args.title != null){ - this.title = args.title; - } - if (args.form != null){ - this.form = args.form; - } - if (args.close != null){ - this.close = args.close; - } - if (args.draggable != null){ - this.draggable = args.draggable; - } - - } - /** Events **/ - this.onSaved = new Event(this); - }; - - -GenericWindow.prototype.getButtons = function() { - var _this = this; - - if (this.close){ - return [ { - text : 'Close', - handler : function() { - _this.panel.close(); - } - } ]; - } - else{ - return [ { - text : 'Save', - handler : function() { - _this.save(); - - } - }, { - text : 'Cancel', - handler : function() { - _this.panel.close(); - } - } ]; +SpecimenGrid.prototype._getColumnCombo = function() { + var data = []; + for ( var i = 1; i <= 12; i++) { + data.push({ + columnNumber : i + }); } -}; -GenericWindow.prototype.save = function (){ - alert("Method save of GenerciWindow class is abstract"); + var positionsStore = Ext.create('Ext.data.Store', { + fields : [ 'columnNumber' ], + data : data + }); + + return Ext.create('Ext.form.ComboBox', { + store : positionsStore, + queryMode : 'local', + displayField : 'columnNumber', + valueField : 'columnNumber' + }); }; -GenericWindow.prototype._postRender = function(data, experiment){ +SpecimenGrid.prototype._getSlotColumBombo = function() { + if (this.experiment){ + var length = this.experiment.getSamplePlates().length; + + var data = []; + for ( var i = 1; i <= length; i++) { + data.push({ + slotPositionColumn : i + }); + } + + var positionsStore = Ext.create('Ext.data.Store', { + fields : [ 'slotPositionColumn' ], + data : data + }); + return Ext.create('Ext.form.ComboBox', { + store : positionsStore, + queryMode : 'local', + displayField : 'slotPositionColumn', + valueField : 'slotPositionColumn' + }); + } }; - -GenericWindow.prototype.draw = function (data, experiment){ - this._render(data, experiment); +SpecimenGrid.prototype.getPanelByExperiment = function(experiment) { + this.experiment = experiment; + var data = this._prepareData(experiment); + return this.getPanel(data); }; -GenericWindow.prototype.refresh = function(data, experiment){ - this.data = data; - this.experiment = experiment; - this.form.refresh(data, experiment); +SpecimenGrid.prototype.refresh = function(experiment) { + this.experiment = experiment; + var data = this._prepareData(experiment); + this.store.loadData(data); }; -GenericWindow.prototype._render = function(data, experiment){ - this.data = data; - var _this = this; - if (this.panel == null){ - this.panel = Ext.create('Ext.Window', { - id : this.id, - title : this.title, - resizable : true, - constrain : true, - border : 1, - modal : this.modal, - frame : false, - draggable : this.draggable, - closable : true, - autoscroll : true, - layout : { type: 'vbox',align: 'stretch'}, - width : this.width, - height : this.form.height, - buttonAlign :'right', - buttons : this.getButtons(), - items : this.form.getPanel(data, experiment), - listeners: { - scope : this, - minimize : function(){ - this.panel.hide(); - }, - destroy : function(){ - delete this.panel; - } - } - }); - this.panel.setLoading(); - } - - this.panel.show(); - this._postRender(); -}; -function CalendarWidget(args) { - this.height = 740; +SpecimenGrid.prototype.getPanel = function() { + var _this = this; + this.store = Ext.create('Ext.data.Store', { + fields : [ + 'buffer', 'bufferId', 'code', 'macromolecule', 'acronym', 'macromoleculeId', 'concentration', 'volume', 'samplePlateId', + 'slotPositionColumn', 'rowNumber', 'columnNumber', 'groupIndex' ], + data : [], + groupField : 'acronym' + }); + this.store.sort([ { + property : 'concentration', + direction : 'ASC' + }, { + property : 'buffer', + direction : 'ASC' + } ]); - if (args != null) { - if (args.height != null) { - this.height = args.height; + var selModel = Ext.create('Ext.selection.RowModel', { + allowDeselect : true, + mode : this.selectionMode, + listeners : { + selectionchange : function(sm, selections) { + var selected = []; + for ( var i = 0; i < selections.length; i++) { + selected.push(selections[i].raw); + } + _this.onSelected.notify(selected); + } } + }); + + var features = []; + + if (this.grouped) { + features.push({ + ftype : 'grouping', + groupHeaderTpl : '{name}', + hideGroupedHeader : false, + startCollapsed : false, + id : 'myGroupedStore' + }); } - this.onClick = new Event(); -} + this.grid = Ext.create( + 'Ext.grid.Panel', + { + title : this.title, + height : this.height, + width : this.width, + selModel : selModel, + store : this.store, + features : features, + margin : this.margin, + plugins : this.getPlugins(), + columns : [ + { + text : '', + dataIndex : 'macromolecule', + width : 20, + renderer : function(val, y, sample) { + var macromoleculeId = null; + if (sample.raw.macromolecule3VO != null) { + macromoleculeId = sample.raw.macromolecule3VO.macromoleculeId; + } + else{ + macromoleculeId = sample.raw.macromoleculeId; + } + + if (macromoleculeId == null) return; + return BUI.getRectangleColorDIV(_this.experiment.macromoleculeColors[macromoleculeId], 10, 10); + } + }, + { + text : 'Macromolecule', + dataIndex : 'macromolecule', + width : 100 + }, + { + text : '', + dataIndex : 'buffer', + width : 20, + renderer : function(val, y, sample) { + var color = "black"; + if (sample.raw.bufferId != null) { + if (_this.experiment.getDataCollectionsBySpecimenId(sample.raw.specimenId)[0] != null){ + color = _this.experiment.getSpecimenColorByBufferId(_this.experiment.getMeasurementById(_this.experiment.getDataCollectionsBySpecimenId(sample.raw.specimenId)[0].measurementtodatacollection3VOs[0].measurementId).specimenId); + } + return BUI.getRectangleColorDIV(color, 10, 10); + } + } + }, { + text : 'Buffer', + dataIndex : 'bufferId', + width : 140, + renderer : function(val, y, sample) { + if (sample.raw.bufferId != null) { + return BIOSAXS.proposal.getBufferById(val).acronym; + } + }, + editor : BIOSAXS_COMBOMANAGER.getComboBuffers(BIOSAXS.proposal.getBuffers(), { + noLabel : true, + width : 300 + }) + }, { + text : 'Conc.', + dataIndex : 'concentration', + width : 100, + editor : { + allowBlank : false + }, + renderer : function(val, meta, sample) { + if (isNaN(val)) { + meta.tdCls = 'yellow-cell'; + return val; + } else { + if (val != 0) { + return BUI.formatValuesUnits(val, 'mg/ml', { + fontSize : 16, + decimals : 3, + unitsFontSize : this.unitsFontSize + }); + } else { + return; + } + } + } + }, { + text : 'Vol. Well', + dataIndex : 'volume', + width : 70, + editor : { + allowBlank : true + }, + renderer : function(val, y, sample) { + return BUI.formatValuesUnits(sample.data.volume, 'µl', { + fontSize : 12, + decimals : 2, + unitsFontSize : this.unitsFontSize + }); + } + }, { + text : 'Position', + hidden : true, + flex : 1, + renderer : function(val, y, sample) { + return BUI.getSamplePositionHTML(sample.raw, _this.experiment); + } + }, { + text : 'samplePlateId', + dataIndex : 'samplePlateId', + hidden : true + }, { + text : 'Plate', + hidden : this.isPositionColumnHidden, + dataIndex : 'slotPositionColumn', + editor : _this._getSlotColumBombo(), + flex : 1, + renderer : function(val, meta, sample) { + if ((val != null) & (val != "")) { + return val; + } else { + meta.tdCls = 'yellow-cell'; + } + } + }, { + text : 'Row', + hidden : this.isPositionColumnHidden, + dataIndex : 'rowNumber', + editor : this._getRowCombo(), + flex : 1, + renderer : function(val, meta, sample) { + if ((val != null) && (val != "")) { + return BUI.getSamplePlateLetters()[val - 1]; + } else { + meta.tdCls = 'yellow-cell'; + } + } + }, { + text : 'Well', + hidden : this.isPositionColumnHidden, + dataIndex : 'columnNumber', + editor : this._getColumnCombo(), + flex : 1, + renderer : function(val, meta, sample) { + if ((val != null) && (val != "")) { + return val; + } else { + meta.tdCls = 'yellow-cell'; + } + } + }, { + id : _this.id + 'buttonEditSample', + text : 'Edit', + width : 80, + sortable : false, + hidden : !_this.editEnabled, + renderer : function(value, metaData, record, rowIndex, colIndex, store) { + if (_this.editEnabled) { + return BUI.getGreenButton('EDIT'); + } + } + }, { + id : _this.id + 'buttonRemoveSample', + text : '', + hidden : !_this.removeBtnEnabled, + width : 100, + sortable : false, + renderer : function(value, metaData, record, rowIndex, colIndex, store) { + if (_this.removeBtnEnabled) { + return BUI.getRedButton('REMOVE'); + } + } + } -CalendarWidget.prototype.loadData = function(data) { - this.events = []; + ], + viewConfig : { + preserveScrollOnRefresh : true, + stripeRows : true, + getRowClass : function(record) { + var specimens = _this.experiment.getSampleByPosition(record.data.samplePlateId, record.data.rowNumber, + record.data.columnNumber); + if (specimens.length > 1) { + return 'red-row'; - for ( var i = 0; i < data.length; i++) { - var date = moment(data[i].creationDate); - var textColor = 'black'; + } + }, + listeners : { + selectionchange : function(grid, selected) { + _this.onClick.notify(record.raw); + }, + cellclick : function(grid, td, cellIndex, record, tr) { + if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonEditSample') { + } + if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonRemoveSample') { + grid.getStore().removeAt(rowIndex); + _this.onRemoved.notify(); + } - if (data[i].status == "FINISHED") { - textColor = 'green'; - } - if (data[i].status == "ABORTED") { - textColor = 'red'; - } - this.events.push({ - title : data[i].name, - start : date.format("YYYY-MM-DD HH:mm:ss"), - end : date.format("YYYY-MM-DD HH:mm:ss"), - date : date, - allDay : false, - color : textColor, - className : date.format("YYYY-MM-DD") - }); - } + } + } + } + }); + return this.grid; }; -CalendarWidget.prototype.draw = function(targetId) { - var _this = this; - $('#' + targetId).fullCalendar({ - eventClick : function(calEvent, jsEvent, view) { - _this.onClick.notify(calEvent.className[0]); - - }, - contentHeight : _this.height, - header : { - left : 'prev,next today', - center : 'title', - right : 'month,basicWeek,basicDay' - }, - editable : false, - events : this.events +SpecimenGrid.prototype.input = function() { + return { + experiment : DATADOC.getExperiment_10(), + proposal : DATADOC.getProposal_10() + }; +}; + +SpecimenGrid.prototype.test = function(targetId) { + var specimenGrid = new SpecimenGrid({ + height : 400, + maxHeight : 400, + width : 1000 }); + BIOSAXS.proposal = new Proposal(specimenGrid.input().proposal); + + var experiment = new Experiment(specimenGrid.input().experiment); + var panel = specimenGrid.getPanelByExperiment(experiment); + panel.render(targetId); }; /** - * It shows a table with the guinier and gnom data as well as passed and - * discarded measurements + * Shows a list of stock solutions with macromolecule, buffer, storage temperature, concentration, shipment and comments * - * @height - * @showBufferColumns + * @multiselect allows multiple selection + * @height + * @minHeight + * @width + * @tbar + * @showTitle + * @isPackedVisible shows is stock solution is in a box + * @btnEditVisible shows edit button + * @btnAddVisible + * @btnAddExisting + * @btnUnpackVisible allows to unpack a stock solution + * @btnRemoveVisible allow to remove a stock solution */ -function ConcentrationHTMLTableWidget(args) { + +function StockSolutionGrid(args) { this.id = BUI.id(); + this.height = 100; + this.width = null; + this.minHeight = null; + this.tbar = true; - this.showBufferColumns = true; + this.title = "Stock Solutions"; + + /** Visible buttons and actions **/ + this.btnEditVisible = true; + this.btnRemoveVisible = true; + this.btnAddVisible = true; + this.btnAddExisting = false; + this.isPackedVisible = true; + this.btnUnpackVisible = false; + + /** Selectors **/ + this.multiselect = false; + this.selectedStockSolutions = []; if (args != null) { + if (args.btnUnpackVisible != null) { + this.btnUnpackVisible = args.btnUnpackVisible; + } + if (args.multiselect != null) { + this.multiselect = args.multiselect; + } if (args.height != null) { this.height = args.height; } - if (args.showBufferColumns != null) { - this.showBufferColumns = args.showBufferColumns; + if (args.btnEditVisible != null) { + this.btnEditVisible = args.btnEditVisible; + } + if (args.btnAddVisible != null) { + this.btnAddVisible = args.btnAddVisible; + } + if (args.btnAddExisting != null) { + this.btnAddExisting = args.btnAddExisting; + } + if (args.width != null) { + this.width = args.width; + } + if (args.minHeight != null) { + this.minHeight = args.minHeight; + } + if (args.tbar != null) { + this.tbar = args.tbar; + } + if (args.btnRemoveVisible != null) { + this.btnRemoveVisible = args.btnRemoveVisible; + } + if (args.isPackedVisible != null) { + this.isPackedVisible = args.isPackedVisible; + } + if (args.showTitle != null) { + this.showTitle = args.showTitle; + if (this.showTitle == false) { + this.title = null; + } } - } -} - -ConcentrationHTMLTableWidget.prototype.refresh = function(parsedData) { - document.getElementById(this.id).innerHTML = this.getPanel(parsedData); -}; - -ConcentrationHTMLTableWidget.prototype.getPanel = function(parsedData) { - var html = "
"; - html = html + ""; - /** Title * */ - html = html + ""; - html = html + ""; - if (this.showBufferColumns) { - html = html + ""; } - html = html + ""; - html = html + ""; - html = html + ""; - html = html + ""; - html = html + ""; - html = html + ""; - html = html + ""; - html = html + ""; - html = html + ""; - html = html + ""; - html = html + ""; - html = html + ""; - html = html + ""; + /** Events **/ + this.onProposalChanged = new Event(this); + this.onStockSolutionSelected = new Event(this); +} - parsedData.concentration.concentrations.sort(function(a, b) { - return a.concentration - b.concentration; - }); - /** Row * */ - for ( var i = 0; i < parsedData.concentration.concentrations.length; i++) { - var row = parsedData[i]; +StockSolutionGrid.prototype._getColumns = function() { + var _this = this; + var columns = [ - var tr = ""; - if (i % 2 == 1) { - tr = ""; + { + header : 'Macromolecule', + dataIndex : 'macromolecule', + id : _this.id + 'macromolecule', + type : 'string', + renderer : function(val, y, specimen) { + return '' + val + ''; + }, + hidden : false, + flex : 1 + }, { + header : 'Buffer', + dataIndex : 'buffer', + name : 'buffer', + hidden : false, + renderer : function(val, y, specimen) { + return '' + val + ''; + }, + type : 'string', + flex : 1 + }, { + header : 'Acronym', + dataIndex : 'name', + name : 'name', + type : 'string', + flex : 1, + hidden : true + }, { + header : 'Storage Temp.', + dataIndex : 'storageTemperature', + name : 'storageTemperature', + type : 'string', + flex : 1, + hidden : false, + renderer : function(val) { + return BUI.formatValuesUnits(val, 'C', { + fontSize : 12, + decimals : 2, + unitsFontSize : 10 + }); } - var goodMeasurements = (parsedData.concentration.concentrations[i].frames.length - parsedData.concentration.concentrations[i].frames_warning); - if (goodMeasurements == 0) { - tr = ""; + }, { + header : 'Volume', + dataIndex : 'volume', + type : 'string', + flex : 1, + hidden : false, + renderer : function(val) { + return BUI.formatValuesUnits(val, 'µl', { + fontSize : 12, + decimals : 2, + unitsFontSize : 10 + }); } - html = html + tr; - html = html + ""; - if (this.showBufferColumns) { - html = html + ""; + }, { + header : 'Concentration', + dataIndex : 'concentration', + name : 'concentration', + type : 'string', + flex : 1, + renderer : function(val) { + return BUI.formatValuesUnits(val, 'mg/ml', { + fontSize : 12, + decimals : 2, + unitsFontSize : 10 + }); } - html = html + ""; - html = html + ""; - html = html + ""; - html = html + ""; - html = html + ""; - html = html + ""; - html = html + ""; - html = html + ""; + }, { + header : 'Packed', + dataIndex : 'comments', + id : _this.id + "box", + type : 'string', + width : 50, + hidden : !this.isPackedVisible, + renderer : function(val, cmp, a) { + if (a.raw.boxId != null) { + return "
"; + } + + } + }, { + header : 'Comments', + dataIndex : 'comments', + type : 'string', + flex : 1 + } ]; + + if (this.btnEditVisible) { + columns.push({ + id : _this.id + 'buttonEdit', + width : 85, + sortable : false, + renderer : function(value, metaData, record, rowIndex, colIndex, store) { + if (_this.btnEditVisible) { + return BUI.getGreenButton('EDIT'); + } + } + }); + } + + if (this.btnRemoveVisible) { + columns.push({ + id : _this.id + 'buttonRemove', + width : 85, + sortable : false, + renderer : function(value, metaData, record, rowIndex, colIndex, store) { + if (_this.btnRemoveVisible) { + return BUI.getRedButton('REMOVE'); + } + } + }); } - return html + "
ConcentrationBufferMeasurementsGuinierGnom
PassedDiscardedRgI0QualityRgdMax
" + parsedData.concentration.concentrations[i].concentration + "" + parsedData.concentration.concentrations[i].bufferAcronym + "" + goodMeasurements + " of " + parsedData.concentration.concentrations[i].frames.length + "" + parsedData.concentration.concentrations[i].frames_warning + "" + BUI.formatValuesErrorUnits(parsedData.concentration.concentrations[i].calculation.rgGuinier.avg, parsedData.concentration.concentrations[i].calculation.rgGuinier.std, "nm", { - lineBreak : false - }) + "" + BUI.formatValuesErrorUnits(parsedData.concentration.concentrations[i].calculation.i0Guinier.avg, parsedData.concentration.concentrations[i].calculation.i0Guinier.std, " ", { - lineBreak : false - }) + "" + BUI.formatValuesErrorUnits(parsedData.concentration.concentrations[i].calculation.quality.avg, parsedData.concentration.concentrations[i].calculation.quality.std, " ", { - lineBreak : false - }) + "" + BUI.formatValuesErrorUnits(parsedData.concentration.concentrations[i].calculation.rgGnom.avg, parsedData.concentration.concentrations[i].calculation.rgGnom.std, " ", { - lineBreak : false - }) + "" + BUI.formatValuesErrorUnits(parsedData.concentration.concentrations[i].calculation.dMax.avg, parsedData.concentration.concentrations[i].calculation.dMax.std, " ", { - lineBreak : false - }) + "
"; + + if (this.btnUnpackVisible) { + columns.push({ + id : _this.id + 'buttonUnpack', + width : 85, + sortable : false, + renderer : function(value, metaData, record, rowIndex, colIndex, store) { + if (_this.btnUnpackVisible) { + return BUI.getBlueButton('UNPACK'); + } + } + }); + } + return columns; }; -ConcentrationHTMLTableWidget.prototype.input = function() { - return { - data : { - "dataCollectionCount" : 4, - "buffers" : [ { - "bufferId" : "422" - } ], - "concentration" : { - "concentrations" : [ { - "concentration" : "7.17", - "id" : "7.1699999999999999", - "bufferId" : "422.00", - "bufferAcronym" : "B1", - "rgGuinier" : [ "5.12723" ], - "frames" : [ { - "total" : "0.515705406286", - "bufferBeforeMeasurementId" : 15045, - "sampleMergeId" : 8176, - "averagedModelId" : null, - "I0" : "183.457461646", - "proposalCode" : "OPD", - "averagedModel" : null, - "bufferAfterMergeId" : 8177, - "framesCount" : "10", - "bufferBeforeMergeId" : 8175, - "averageFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_065_ave.dat", - "bufferAfterMeasurementId" : 15048, - "rgGuinier" : "5.12723", - "subtractedFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_065_sub.dat", - "firstPointUsed" : "17", - "chi2RgFilePath" : null, - "abInitioModelId" : null, - "macromoleculeId" : 649, - "code" : "_4.0_7.17", - "transmission" : "100.0", - "timeStart" : "2013-06-05 15:43:54.348723", - "bufferAcronym" : "B1", - "quality" : "0.853011", - "shapeDeterminationModelId" : null, - "expermientComments" : "[BsxCube] Generated from BsxCube", - "bufferId" : 422, - "isagregated" : "False", - "dmax" : "25.63615", - "bufferBeforeAverageFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_064_ave.dat", - "exposureTemperature" : "4.0", - "subtractionId" : 3377, - "conc" : "7.1699999999999999", - "rapidShapeDeterminationModel" : null, - "experimentCreationDate" : "Jun 5, 2013 3:30:30 PM", - "lastPointUsed" : "36", - "modelListId" : null, - "framesMerge" : "9", - "rapidShapeDeterminationModelId" : null, - "bufferAfterAverageFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_066_ave.dat", - "nsdFilePath" : null, - "bufferAfterFramesMerged" : "10", - "experimentId" : 700, - "macromoleculeAcronym" : "L9", - "i0stdev" : "0.139364435146", - "sampleMeasurementId" : 15047, - "measurementComments" : "[5]", - "priorityLevelId" : 10, - "bufferBeforeFramesMerged" : "10", - "substractionCreationTime" : "Jun 5, 2013 3:46:31 PM", - "proposalNumber" : "29", - "rgGnom" : "5.40297914939", - "volume" : "475.302", - "shapeDeterminationModel" : null, - "comments" : null, - "groupeField" : "L9 B1" - } ], - "frames_warning" : 0, - "calculation" : { - "rgGuinier" : { - "std" : 0, - "sum" : 5.12723, - "avg" : 5.12723, - "validNumber" : 1, - "totalNumber" : 1, - "values" : [ "5.12723" ] - }, - "i0Guinier" : { - "std" : 0, - "sum" : 183.457461646, - "avg" : 183.457461646, - "validNumber" : 1, - "totalNumber" : 1, - "values" : [ "183.457461646" ] - }, - "quality" : { - "std" : 0, - "sum" : 0.853011, - "avg" : 0.853011, - "validNumber" : 1, - "totalNumber" : 1, - "values" : [ "0.853011" ] - }, - "rgGnom" : { - "std" : 0, - "sum" : 5.40297914939, - "avg" : 5.40297914939, - "validNumber" : 1, - "totalNumber" : 1, - "values" : [ "5.40297914939" ] - }, - "dMax" : { - "std" : 0, - "sum" : 25.63615, - "avg" : 25.63615, - "validNumber" : 1, - "totalNumber" : 1, - "values" : [ "25.63615" ] - } - } - }, { - "concentration" : "3.53", - "id" : "3.5299999999999998", - "bufferId" : "422.00", - "bufferAcronym" : "B1", - "rgGuinier" : [ "4.38278" ], - "frames" : [ { - "total" : "0.497164741078", - "bufferBeforeMeasurementId" : 15048, - "sampleMergeId" : 8178, - "averagedModelId" : null, - "I0" : "160.023229462", - "proposalCode" : "OPD", - "averagedModel" : null, - "bufferAfterMergeId" : 8179, - "framesCount" : "10", - "bufferBeforeMergeId" : 8177, - "averageFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_067_ave.dat", - "bufferAfterMeasurementId" : 15051, - "rgGuinier" : "4.38278", - "subtractedFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_067_sub.dat", - "firstPointUsed" : "36", - "chi2RgFilePath" : null, - "abInitioModelId" : null, - "macromoleculeId" : 649, - "code" : "_4.0_3.53", - "transmission" : "100.0", - "timeStart" : "2013-06-05 15:47:04.630129", - "bufferAcronym" : "B1", - "quality" : "0.742825", - "shapeDeterminationModelId" : null, - "expermientComments" : "[BsxCube] Generated from BsxCube", - "bufferId" : 422, - "isagregated" : "False", - "dmax" : "15.33973", - "bufferBeforeAverageFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_066_ave.dat", - "exposureTemperature" : "4.0", - "subtractionId" : 3378, - "conc" : "3.5299999999999998", - "rapidShapeDeterminationModel" : null, - "experimentCreationDate" : "Jun 5, 2013 3:30:30 PM", - "lastPointUsed" : "61", - "modelListId" : null, - "framesMerge" : "10", - "rapidShapeDeterminationModelId" : null, - "bufferAfterAverageFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_068_ave.dat", - "nsdFilePath" : null, - "bufferAfterFramesMerged" : "10", - "experimentId" : 700, - "macromoleculeAcronym" : "L9", - "i0stdev" : "0.1499898017", - "sampleMeasurementId" : 15050, - "measurementComments" : "[6]", - "priorityLevelId" : 12, - "bufferBeforeFramesMerged" : "10", - "substractionCreationTime" : "Jun 5, 2013 3:49:42 PM", - "proposalNumber" : "29", - "rgGnom" : "4.40615474861", - "volume" : "372.656", - "shapeDeterminationModel" : null, - "comments" : null, - "groupeField" : "L9 B1" - } ], - "frames_warning" : 0, - "calculation" : { - "rgGuinier" : { - "std" : 0, - "sum" : 4.38278, - "avg" : 4.38278, - "validNumber" : 1, - "totalNumber" : 1, - "values" : [ "4.38278" ] - }, - "i0Guinier" : { - "std" : 0, - "sum" : 160.023229462, - "avg" : 160.023229462, - "validNumber" : 1, - "totalNumber" : 1, - "values" : [ "160.023229462" ] - }, - "quality" : { - "std" : 0, - "sum" : 0.742825, - "avg" : 0.742825, - "validNumber" : 1, - "totalNumber" : 1, - "values" : [ "0.742825" ] - }, - "rgGnom" : { - "std" : 0, - "sum" : 4.40615474861, - "avg" : 4.40615474861, - "validNumber" : 1, - "totalNumber" : 1, - "values" : [ "4.40615474861" ] - }, - "dMax" : { - "std" : 0, - "sum" : 15.33973, - "avg" : 15.33973, - "validNumber" : 1, - "totalNumber" : 1, - "values" : [ "15.33973" ] +StockSolutionGrid.prototype._getTopButtons = function() { + var _this = this; + /** Actions buttons **/ + var actions = []; + + /** ADD BUTTON **/ + if (this.btnAddVisible) { + actions.push(Ext.create('Ext.Action', { + icon : '../images/add.png', + text : 'Add Stock Solution', + tooltip : 'Will create a new stock solution', + disabled : false, + alwaysEnabled : true, + handler : function(widget, event) { + _this.edit(); + } + })); + } + + if (this.btnAddExisting) { + actions.push(Ext.create('Ext.Action', { + icon : '../images/add.png', + text : 'Add Existing', + tooltip : 'Allows to select upacked stock solutions', + disabled : false, + alwaysEnabled : true, + handler : function(widget, event) { + var stockSolutionGrid = new StockSolutionGrid({ + btnAddVisible : false, + btnEditVisible : false, + btnRemoveVisible : false, + btnAddExisting : false, + isPackedVisible : true, + multiselect : true + }); + + var window = Ext.create('Ext.window.Window', { + title : 'Select', + height : 400, + width : 800, + layout : 'fit', + items : [ stockSolutionGrid.getPanel() ], + buttons : [ { + text : 'Pack', + handler : function() { + _this.onStockSolutionSelected.notify(stockSolutionGrid.selectedStockSolutions); + window.close(); + } + }, { + text : 'Cancel', + handler : function() { + window.close(); } + } ] + + }).show(); + + stockSolutionGrid.refresh(BIOSAXS.proposal.getUnpackedStockSolutions()); + } + })); + } + + return actions; +}; + +StockSolutionGrid.prototype.refresh = function(stockSolutions) { + this.features = stockSolutions; + this.store.loadData(this._prepareData(), false); +}; + +StockSolutionGrid.prototype._prepareData = function() { + var data = []; + for ( var i = 0; i < this.features.length; i++) { + var stockSolution = this.features[i]; + stockSolution.buffer = BIOSAXS.proposal.getBufferById(stockSolution.bufferId).acronym; + if (stockSolution.macromoleculeId != null) { + stockSolution.macromolecule = BIOSAXS.proposal.getMacromoleculeById(stockSolution.macromoleculeId).acronym; + } + data.push(stockSolution); + } + return data; +}; + +StockSolutionGrid.prototype.getPanel = function() { + return this._renderGrid(); +}; + +StockSolutionGrid.prototype.edit = function(stockSolutionId) { + var _this = this; + var stockSolutionWindow = new StockSolutionWindow(); + /** On stock solution SAVED **/ + stockSolutionWindow.onSaved.attach(function(sender, stockSolution) { + _this.onProposalChanged.notify(stockSolution); + }); + stockSolutionWindow.draw(BIOSAXS.proposal.getStockSolutionById(stockSolutionId)); +}; + +StockSolutionGrid.prototype._getStoreFields = function() { + return [ { + name : 'name', + type : 'string' + }, { + name : 'stockSolutionId', + type : 'string' + }, { + name : 'macromolecule', + type : 'string' + }, { + name : 'buffer', + type : 'string' + }, { + name : 'storageTemperature', + type : 'numeric' + }, { + name : 'volume', + type : 'string' + }, { + name : 'concentration', + type : 'string' + }, { + name : 'buffer', + type : 'string' + }, { + name : 'comments', + type : 'string' + } ]; +}; + +//StockSolutionGrid.prototype.refresh = function() { +// this.proposal.onInitialized.attach(function(sender){ +// +// }); +// this.proposal.init(Ext.urlDecode(window.location.href).sessionId); +//}; + +StockSolutionGrid.prototype._renderGrid = function() { + var _this = this; + + /** Store **/ + this.store = Ext.create('Ext.data.Store', { + fields : this._getStoreFields(), //columns, + autoload : true + }); + + var filters = { + ftype : 'filters', + local : true, + filters : this.filters + }; + + var selModel = null; + + if (this.multiselect) { + selModel = Ext.create('Ext.selection.CheckboxModel', { + //multiSelect : false,//this.multiselect, + mode : 'SINGLE', + listeners : { + selectionchange : function(sm, selections) { + _this.selectedStockSolutions = []; + for ( var i = 0; i < selections.length; i++) { + _this.selectedStockSolutions.push(selections[i].raw); } - }, { - "concentration" : "1.75", - "id" : "1.75", - "bufferId" : "422.00", - "bufferAcronym" : "B1", - "rgGuinier" : [], - "frames" : [ { - "total" : "0.5538035065", - "bufferBeforeMeasurementId" : 15051, - "sampleMergeId" : 8180, - "averagedModelId" : null, - "I0" : "146.927428571", - "proposalCode" : "OPD", - "averagedModel" : null, - "bufferAfterMergeId" : 8181, - "framesCount" : "10", - "bufferBeforeMergeId" : 8179, - "averageFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_069_ave.dat", - "bufferAfterMeasurementId" : 15054, - "rgGuinier" : "4.27139", - "subtractedFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_069_sub.dat", - "firstPointUsed" : "33", - "chi2RgFilePath" : null, - "abInitioModelId" : null, - "macromoleculeId" : 649, - "code" : "_4.0_1.75", - "transmission" : "100.0", - "timeStart" : "2013-06-05 15:50:09.395824", - "bufferAcronym" : "B1", - "quality" : "0.765999", - "shapeDeterminationModelId" : null, - "expermientComments" : "[BsxCube] Generated from BsxCube", - "bufferId" : 422, - "isagregated" : "False", - "dmax" : "14.949865", - "bufferBeforeAverageFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_068_ave.dat", - "exposureTemperature" : "4.0", - "subtractionId" : 3379, - "conc" : "1.75", - "rapidShapeDeterminationModel" : null, - "experimentCreationDate" : "Jun 5, 2013 3:30:30 PM", - "lastPointUsed" : "62", - "modelListId" : null, - "framesMerge" : "6", - "rapidShapeDeterminationModelId" : null, - "bufferAfterAverageFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_070_ave.dat", - "nsdFilePath" : null, - "bufferAfterFramesMerged" : "10", - "experimentId" : 700, - "macromoleculeAcronym" : "L9", - "i0stdev" : "0.191914285714", - "sampleMeasurementId" : 15053, - "measurementComments" : "[7]", - "priorityLevelId" : 14, - "bufferBeforeFramesMerged" : "10", - "substractionCreationTime" : "Jun 5, 2013 3:52:31 PM", - "proposalNumber" : "29", - "rgGnom" : "4.28379338749", - "volume" : "356.326", - "shapeDeterminationModel" : null, - "comments" : null, - "groupeField" : "L9 B1" - } ], - "frames_warning" : 1, - "calculation" : { - "rgGuinier" : { - "std" : null, - "sum" : 0, - "avg" : 0, - "validNumber" : 0, - "totalNumber" : 0, - "values" : [] - }, - "i0Guinier" : { - "std" : null, - "sum" : 0, - "avg" : 0, - "validNumber" : 0, - "totalNumber" : 0, - "values" : [] - }, - "quality" : { - "std" : null, - "sum" : 0, - "avg" : 0, - "validNumber" : 0, - "totalNumber" : 0, - "values" : [] - }, - "rgGnom" : { - "std" : null, - "sum" : 0, - "avg" : 0, - "validNumber" : 0, - "totalNumber" : 0, - "values" : [] - }, - "dMax" : { - "std" : null, - "sum" : 0, - "avg" : 0, - "validNumber" : 0, - "totalNumber" : 0, - "values" : [] + } + } + }); + } else { + selModel = { + mode : 'SINGLE' + }; + } + + this.store.sort("stockSolutionId", "desc"); + + this.grid = Ext.create('Ext.grid.Panel', { + style : { + padding : 5 + }, + icon : '/ispyb/images/SampleHolder_24x24_01.png', + title : this.title, + height : this.height, + width : this.width, + minWidth : this.minWidth, + selModel : selModel, + store : this.store, + columns : this._getColumns(), + viewConfig : { + stripeRows : true, + listeners : { + itemdblclick : function(dataview, record, item, e) { + _this.edit(record.raw.stockSolutionId); + }, + cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + var adapter = null; + if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonUnpack') { + _this.grid.setLoading("ISPyB: Unpacking stock solution"); + adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender) { + _this.onProposalChanged.notify(); + _this.grid.setLoading(false); + }); + adapter.onError.attach(function(sender) { + _this.onProposalChanged.notify(); + _this.grid.setLoading(false); + }); + record.raw.boxId = null; + adapter.saveStockSolution(record.raw); + } + + if (grid.getGridColumns()[cellIndex].getId() == _this.id + "box") { + window.location = BUI.getShippingURL(BIOSAXS.proposal.getShipmentByDewarId(record.raw.boxId).shippingId); + } + if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonEdit') { + _this.edit(record.data.stockSolutionId); + } + + if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonRemove') { + _this.grid.setLoading("ISPyB: Removing stock solution"); + adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender) { + _this.onProposalChanged.notify(); + _this.grid.setLoading(false); + }); + adapter.onError.attach(function(sender) { + _this.onProposalChanged.notify(); + _this.grid.setLoading(false); + }); + adapter.removeStockSolution(record.data.stockSolutionId); + } + } + } + } + + }); + + var actions = _this._getTopButtons(); + this.grid.addDocked({ + xtype : 'toolbar', + items : actions + }); + + var i = null; + this.grid.getSelectionModel().on({ + selectionchange : function(sm, selections) { + if (selections.length) { + for ( i = 0; i < actions.length; i++) { + if (actions[i].enable) { + actions[i].enable(); + } + } + } else { + for ( i = 0; i < actions.length; i++) { + if (actions[i].alwaysEnabled == false) { + if (actions[i].disable) { + actions[i].disable(); + } + } + } + } + } + }); + return this.grid; +}; + +StockSolutionGrid.prototype.input = function() { + return { + proposal : DATADOC.getProposal_10() + }; +}; + +StockSolutionGrid.prototype.test = function(targetId) { + var stockSolutionGrid = new StockSolutionGrid({ + height : 300, + width : 900 + }); + BIOSAXS.proposal = new Proposal(stockSolutionGrid.input().proposal); + var panel = stockSolutionGrid.getPanel(); + stockSolutionGrid.refresh(BIOSAXS.proposal.getStockSolutions()); + panel.render(targetId); +}; + + +function SuperpositionGrid(args) { + this.height = null; + this.width = null; + this.id = BUI.id(); + + if (args != null) { + if (args.height != null) { + this.height = args.height; + + if (args.width != null) { + this.width = args.width; + } + } + } + + this.onSelected = new Event(this); +} + +SuperpositionGrid.prototype._prepareData = function(data) { + return data; +}; + +SuperpositionGrid.prototype.refresh = function(subtractions) { + var data = []; + if (subtractions != null){ + if (subtractions.length > 0){ + for (j in subtractions){ + var subtraction = subtractions[j]; + if (subtraction.superposition3VOs != null){ + for (i in subtraction.superposition3VOs){ + data.push(subtraction.superposition3VOs[i]); + } + } + } + } + } + this.store.loadData(data); +}; + +SuperpositionGrid.prototype.getPanel = function() { + var _this = this; + this.store = Ext.create('Ext.data.Store', { + fields : [ 'abinitioModelPdbFilePath', 'aprioriPdbFilePath', 'alignedPdbFilePath' ], + data : [] + }); + + this.selModel = Ext.create('Ext.selection.RowModel', { + allowDeselect : true, + mode : 'MULTI', + listeners : { + selectionchange : function(sm, selections) { + var selected = []; + for (var i = 0; i < selections.length; i++) { + selected.push(selections[i].raw); + } + _this.onSelected.notify(selected); + } + } + }); + + this.store.sort([ { + property : 'name', + direction : 'ASC' + } ]); + + this.panel = Ext.create('Ext.grid.Panel', { + store : this.store, + selModel : this.selModel, + width : this.width, + height : this.height, + margin : 10, + deferredRender : false, + columns : [ { + text : 'Abinitio', + dataIndex : 'abinitioModelPdbFilePath', + hidden : false, + flex : 1, + renderer : function(val){ + return BUI.getFileName(val); + } + }, { + text : 'Apriori', + dataIndex : 'aprioriPdbFilePath', + hidden : false, + flex : 1, + renderer : function(val){ + return BUI.getFileName(val); + } + }, { + text : 'Aligned', + dataIndex : 'alignedPdbFilePath', + hidden : false, + flex : 1, + renderer : function(val){ + return BUI.getFileName(val); + } + } ], + + viewConfig : { + enableTextSelection : true, + preserveScrollOnRefresh : true + }, + listeners : { + cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + + }, + afterrender : function() { + } + } + }); + return this.panel; +}; + +/** + * See ExperimentGrid + * + */ +function TemplateGrid(args) { + + if (args == null) { + args = {}; + } + args.sorters = [ { + property : 'experimentId', + direction : 'DESC' + } ]; + + ExperimentGrid.prototype.constructor.call(this, args); +} + +TemplateGrid.prototype._getFilterTypes = ExperimentGrid.prototype._getFilterTypes; +TemplateGrid.prototype._prettyPrintMacromolecules = ExperimentGrid.prototype._prettyPrintMacromolecules; +TemplateGrid.prototype._getPercentage = ExperimentGrid.prototype._getPercentage; +TemplateGrid.prototype._getPercentageCollected = ExperimentGrid.prototype._getPercentageCollected; +TemplateGrid.prototype.getPercentageMerged = ExperimentGrid.prototype.getPercentageMerged; +TemplateGrid.prototype._prepareData = ExperimentGrid.prototype._prepareData; +TemplateGrid.prototype.getPanel = ExperimentGrid.prototype.getPanel; +TemplateGrid.prototype._renderGrid = ExperimentGrid.prototype._renderGrid; +TemplateGrid.prototype._editExperiment = ExperimentGrid.prototype._editExperiment; +TemplateGrid.prototype._removeExperimentById = ExperimentGrid.prototype._removeExperimentById; + +TemplateGrid.prototype._getTopButtons = function() { + /** Actions buttons * */ + var actions = []; + + actions.push(Ext.create('Ext.Action', { + icon : '../images/add.png', + text : 'Add experiment', + handler : function(widget, event) { + var wizardWidget = new WizardWidget({ + windowMode : true + }); + + wizardWidget.onFinished.attach(function(sender, result) { + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, data) { + var experiment = new Experiment(data); + BIOSAXS.openExperimentByExperiment(experiment); + wizardWidget.window.close(); + }); + wizardWidget.current.setLoading("ISPyB: Creating experiment"); + adapter.createTemplate(result.name, "comments", result.data); + }); + +// wizardWidget.draw(this.targetId, new ExperimentTypeWizardForm()); + wizardWidget.draw(this.targetId, new MeasurementCreatorStepWizardForm(BIOSAXS.proposal.getMacromolecules())); + } + })); + return actions; +}; + +TemplateGrid.prototype.refresh = function(data) { + var filtered = []; + for ( var i = 0; i < data.length; i++) { + if (data[i].experimentType == "TEMPLATE") { + filtered.push(data[i]); + } + } + this.store.loadData(this._prepareData(filtered), false); +}; + +TemplateGrid.prototype._getColumns = function() { + var _this = this; + function actionItemRenderer(value, meta, record, rowIx, ColIx, store) { + if (record.raw.buffer3VOs != null) { + if (record.raw.buffer3VOs.length > 0) { + return 'x-hide-display'; + } + } + + if (record.data.platesCount > 0) { + return 'x-hide-display'; + } + } + + return [ { + xtype : 'rownumberer', + width : 40 + }, { + text : 'experimentId', + dataIndex : 'experimentId', + name : 'experimentId', + type : 'string', + hidden : true + }, { + text : 'Name', + dataIndex : 'name', + name : 'name', + type : 'string', + flex : 1 + }, + + { + text : 'Type', + dataIndex : 'type', + name : 'type', + type : 'string', + hidden : true, + flex : 1, + renderer : function(val) { + return val; + } + }, { + text : 'Macromolecules', + name : 'macromolecules_names', + dataIndex : 'macromolecules_names', + flex : 1, + renderer : function(val) { + return " " + val + ""; + } + }, { + id : _this.id + 'GO', + width : 80, + sortable : false, + renderer : function(value, metaData, record, rowIndex, colIndex, store) { + return BUI.getGreenButton('EDIT'); + } + }, { + id : _this.id + 'REMOVE', + width : 100, + sortable : false, + renderer : function(value, metaData, record, rowIndex, colIndex, store) { + return BUI.getRedButton('REMOVE'); + } + } ]; +}; + +TemplateGrid.prototype.input = function() { + var experiments = DATADOC.getExperimentList_10(); + return { + experiments : experiments, + proposal : new MeasurementGrid().input().proposal + + }; +}; + +TemplateGrid.prototype.test = function(targetId) { + var experimentGrid = new TemplateGrid({ + height : 350, + minHeight : 350, + width : 1000, + gridType : 'Ext.grid.Panel', + title : 'Experiments', + grouping : false, + tbar : true + + }); + BIOSAXS.proposal = new Proposal(experimentGrid.input().proposal); + var panel = experimentGrid.getPanel(experimentGrid.input().experiments); + experimentGrid.refresh(experimentGrid.input().experiments); + panel.render(targetId); +}; + +function VolumeGrid() { + this.id = BUI.id(); +} + +VolumeGrid.prototype.getPanel = function(experiment) { + this.experiment = experiment; + return this.render(); +}; + +VolumeGrid.prototype.getVolumesPanel = function(data, title) { + var _this = this; + var store = Ext.create('Ext.data.Store', { + fields : [ 'name', 'volume', 'macromoleculeId', 'bufferId' ], + data : data + }); + store.sort([ { + property : 'name', + direction : 'ASC' + } ]); + + var grid = Ext.create('Ext.grid.Panel', { + title : title, + height : 400, + maxHeight : 400, + width : 900, + store : store, + margin : '10 0 50 10', + tbar : [ { + text : 'Go to Shipment', + icon : '../images/plane-small.gif', + handler : function() { + window.location = BUI.getCreateShipmentList(); + } + } ], + viewConfig : { + stripeRows : true, + listeners : { + cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonCreate') { + var stockSolutionWindow = new StockSolutionWindow(); + /** On stock solution SAVED **/ + stockSolutionWindow.onSaved.attach(function(sender, stockSolution) { + BIOSAXS.proposal.onInitialized.attach(function(sender, data) { + _this.refresh(_this.experiment); + }); + BIOSAXS.proposal.init(); + }); + var acronym = "ST"; + if (record.raw.macromoleculeId != null) { + acronym = acronym + "_" + BIOSAXS.proposal.getMacromoleculeById(record.raw.macromoleculeId).acronym; + } + if (record.raw.bufferId != null) { + acronym = acronym + "_" + BIOSAXS.proposal.getBufferById(record.raw.bufferId).acronym; } + stockSolutionWindow.draw({ + concentration : record.raw.concentration, + macromoleculeId : record.raw.macromoleculeId, + bufferId : record.raw.bufferId, + name : acronym, + volume : record.raw.volume + }); } - }, { - "concentration" : "0.91", - "id" : "0.91000000000000003", - "bufferId" : "422.00", - "bufferAcronym" : "B1", - "rgGuinier" : [], - "frames" : [ { - "total" : null, - "bufferBeforeMeasurementId" : 15054, - "sampleMergeId" : 8182, - "averagedModelId" : null, - "I0" : "0.0", - "proposalCode" : "OPD", - "averagedModel" : null, - "bufferAfterMergeId" : 8183, - "framesCount" : "10", - "bufferBeforeMergeId" : 8181, - "averageFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_071_ave.dat", - "bufferAfterMeasurementId" : 15057, - "rgGuinier" : null, - "subtractedFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_071_sub.dat", - "firstPointUsed" : "0", - "chi2RgFilePath" : null, - "abInitioModelId" : null, - "macromoleculeId" : 649, - "code" : "_4.0_.91", - "transmission" : "100.0", - "timeStart" : "2013-06-05 15:52:58.133705", - "bufferAcronym" : "B1", - "quality" : "0.0", - "shapeDeterminationModelId" : null, - "expermientComments" : "[BsxCube] Generated from BsxCube", - "bufferId" : 422, - "isagregated" : "False", - "dmax" : null, - "bufferBeforeAverageFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_070_ave.dat", - "exposureTemperature" : "4.0", - "subtractionId" : 3380, - "conc" : "0.91000000000000003", - "rapidShapeDeterminationModel" : null, - "experimentCreationDate" : "Jun 5, 2013 3:30:30 PM", - "lastPointUsed" : "0", - "modelListId" : null, - "framesMerge" : "10", - "rapidShapeDeterminationModelId" : null, - "bufferAfterAverageFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_072_ave.dat", - "nsdFilePath" : null, - "bufferAfterFramesMerged" : "10", - "experimentId" : 700, - "macromoleculeAcronym" : "L9", - "i0stdev" : "0.0", - "sampleMeasurementId" : 15056, - "measurementComments" : "[8]", - "priorityLevelId" : 16, - "bufferBeforeFramesMerged" : "10", - "substractionCreationTime" : "Jun 5, 2013 3:55:35 PM", - "proposalNumber" : "29", - "rgGnom" : null, - "volume" : null, - "shapeDeterminationModel" : null, - "comments" : null, - "groupeField" : "L9 B1" - } ], - "frames_warning" : 1, - "calculation" : { - "rgGuinier" : { - "std" : null, - "sum" : 0, - "avg" : 0, - "validNumber" : 0, - "totalNumber" : 0, - "values" : [] - }, - "i0Guinier" : { - "std" : null, - "sum" : 0, - "avg" : 0, - "validNumber" : 0, - "totalNumber" : 0, - "values" : [] + + if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonStockSolutions') { + var stockSolutionGrid = new StockSolutionGrid({ + btnAddVisible : false, + btnEditVisible : false, + btnRemoveVisible : false, + btnAddExisting : false, + isPackedVisible : true, + multiselect : false + }); + + var window = Ext.create('Ext.window.Window', { + title : 'Stock solutions by specimen', + height : 400, + width : 800, + layout : 'fit', + items : [ stockSolutionGrid.getPanel() ], + buttons : [ { + text : 'Close', + handler : function() { + window.close(); + } + } ] + + }).show(); + stockSolutionGrid.refresh(BIOSAXS.proposal.getStockSolutionsBySpecimen(record.raw.macromoleculeId, record.raw.bufferId)); + } + } + } + }, + columns : [ +// { +// text : '', +// dataIndex : 'macromoleculeId', +// width : 20, +// renderer : function(val, y, sample) { +// if (val != null) { +//// return BUI.getRectangleColorDIV(BIOSAXS.proposal.macromoleculeColors[val], 10, 10); +// return BUI.getRectangleColorDIV(_this.experiment.macromoleculeColors[val], 10, 10); +// } +// } +// }, +// { +// text : '', +// dataIndex : 'bufferId', +// width : 20, +// renderer : function(val, y, sample) { +// if (val != null) { +// return BUI.getRectangleColorDIV(_this.experiment.getSpecimenColorByBufferId(val), 10, 10); +// } +// } +// }, + { + text : 'Specimen', + dataIndex : 'name', + flex : 0.5 + }, + { + text : 'Estimated Volume', + dataIndex : 'volume', + tooltip : 'Estimation of the maximum volume needed for making this experiment', + flex : 0.5, + editor : { + allowBlank : true + }, + renderer : function(val, y, sample) { + return BUI.formatValuesUnits(sample.data.volume, 'µl', { + fontSize : 16, + decimals : 2, + unitsFontSize : this.unitsFontSize + }); + } + }, + { + text : 'Stock Solution', + id : _this.id + 'buttonStockSolutions', + dataIndex : 'name', + flex : 0.5, + tooltip : 'Stock Solutions containing this specimen in this proposal', + renderer : function(value, metaData, record, rowIndex, colIndex, store) { + var macromoleculeId = record.raw.macromoleculeId; + var bufferId = record.raw.bufferId; + var stockSolutions = BIOSAXS.proposal.getStockSolutionsBySpecimen(macromoleculeId, bufferId); + if (stockSolutions.length > 0) { + return "
" + stockSolutions.length + " x
"; + } + + } + }, { + id : _this.id + 'buttonCreate', + text : '', + tooltip : 'Create a new stock solution for shipping', + width : 170, + sortable : false, + renderer : function(value, metaData, record, rowIndex, colIndex, store) { + return BUI.getGreenButton('NEW STOCK SOLUTION', { + width : 160 + }); + } + } ] + }); + return grid; + +}; + +VolumeGrid.prototype._prepareData = function(experiment) { + var keys = {}; + for ( var i = 0; i < experiment.getSamples().length; i++) { + var sample = experiment.getSamples()[i]; + var key = ""; + if (sample.macromoleculeId == null) { + key = experiment.getBufferById(sample.bufferId).acronym; + if (keys[key] == null) { + keys[key] = { + macromoleculeId : sample.macromoleculeId, + name : key, + bufferId : sample.bufferId, + volume : 0 + }; + } + keys[key].volume = Number(sample.volume) + Number(keys[key].volume); + } + + if ((sample.macromolecule3VO != null) || (sample.macromoleculeId != null)) { + macromoleculeId = sample.macromoleculeId; + if (sample.macromoleculeId == null) { + sample.macromoleculeId = sample.macromolecule3VO.macromoleculeId; + } + key = BIOSAXS.proposal.getMacromoleculeById(sample.macromoleculeId).acronym + " + " + experiment.getBufferById(sample.bufferId).acronym; + if (keys[key] == null) { + keys[key] = { + macromoleculeId : sample.macromoleculeId, + name : key, + bufferId : sample.bufferId, + volume : 0 + }; + } + keys[key].volume = Number(sample.volume) + Number(keys[key].volume); + } + } + var data = []; + for (var keyId in keys) { + data.push(keys[keyId]); + } + + return data; +}; + +VolumeGrid.prototype.refresh = function(experiment) { + this.experiment = experiment; + this.macromoleculeGrid.getStore().loadData(this._prepareData(this.experiment), false); +}; + +VolumeGrid.prototype.render = function() { + this.macromoleculeGrid = this.getVolumesPanel(this._prepareData(this.experiment), "Estimation of required Volume"); + + return { + xtype : 'container', + layout : 'vbox', + margin : "0, 0, 0, 5", + items : [ { + xtype : 'container', + layout : 'hbox', + margin : "0, 0, 0, 0", + items : [ this.macromoleculeGrid ] + } ] + }; +}; + +VolumeGrid.prototype.input = function(experiment) { + return { + experiment : DATADOC.getExperiment_10() + }; +}; + +VolumeGrid.prototype.test = function(targetId) { + var volumeGrid = new VolumeGrid(); + BIOSAXS.proposal = new Proposal(new MeasurementGrid().input().proposal); + var panel = volumeGrid.getPanel(new Experiment(new VolumeGrid().input().experiment)); + Ext.create('Ext.panel.Panel', { + height : 500, + width : 1000, + renderTo : targetId, + items : [ panel ] + }); +}; + +/** + * Example of a tab panel to be populated with widgets + * + * @width width in pixels + * @height height in pixels + * + * #myEvent event that this class is supposed to throw + * + **/ +function ExampleTabs(args) { + this.width = 500; + this.height = 500; + + if (args != null){ + if (args.width != null){ + this.width= args.width; + } + if (args.height != null){ + this.height= args.height; + } + } + + /** Events **/ + this.myEvent = new Event(); +} + +/** Populate the widget **/ +ExampleTabs.prototype.refresh = function(macromolecule) { + +}; + +/** It creates a tab panel with the specified with and height **/ +ExampleTabs.prototype.getPanel = function() { + this.panel = Ext.createWidget('tabpanel', { + height : this.height, + width : this.width, + style : { + padding : 2 + }, + items : [ { + tabConfig : { + title : 'Test', + icon : '/ispyb/images/plane-small.gif' + }, + items : [ { + xtype : 'container', + margin : '5 5 5 5', + items : [ ] + } ] + } ] + }); + return this.panel; +}; + + +/** + * Shows an experiments with the specimens, measurements, analysis tabs where + * results are shown and the frames widget + * + * @targetId + */ +function ExperimentTabs(targetId) { + this.height = 900; + this.targetId = targetId; + + this.id = BUI.id(); + var _this = this; + + this.INTERVAL_UPDATE = BUI.getUpdateInterval(); + + this.gridHeight = 1000; + /** data * */ + this.experiment = null; + + + /** Specimen Widget contains a specimenGrid and a sampleChangerWidget than can be displayed with are vertical or horizontal layout **/ + this.specimenWidget = new SpecimenWidget({ + height : 600 + }); + + + /** For Overview * */ + /*this.samplePlateGroupWidget = new SamplePlateGroupWidget({ + showTitle : false, + height : 250, + margin : 5, + border : 0 + + });*/ + + /** For Measurements * */ + /*this.measurementSamplePlateGroupWidget = new SamplePlateGroupWidget({ + showTitle : false, + height : 250, + margin : '5 0 0 0', + border : 0 + });*/ + + this.measurementGridDone = new MeasurementGrid({ + height : 600, + minHeight : 400, + maxHeight : 800, + positionColumnsHidden : true, + showTitle : false, + estimateTime : true, + width : 900, + maxWidth : 1500, + addBtnEnable : false, + markDone : true, + removeBtnEnabled : false + }); + this.measurementGridDone.onSelected.attach(function(sender, measurements) { + var specimens = []; + for ( var i = 0; i < measurements.length; i++) { + specimens.push(_this.experiment.getSampleById(measurements[i].specimenId)); + } + //_this.measurementSamplePlateGroupWidget.selectSpecimens(specimens); + }); + + /** AnalysisGrid * */ + this.analysisGrid = new AnalysisGrid({ + height : Ext.getBody().getViewSize().height * 0.9 - 300, + positionColumnsHidden : true, + sorters : [ { + property : 'priorityLevelId', + direction : 'ASC' + } ] + }); + + + /** Queue * */ + this.queueGrid = new QueueGrid({ + url : BUI.getQueueUrlByExperiment(), + height : Ext.getBody().getViewSize().height * 0.9 - 300, + width : Ext.getBody().getViewSize().width * 0.9 - 300, + isGrouped : true, + tbar : false, + bbar : false, + sorter : [{ + property: 'measurementId', + direction: 'DESC' + }] + }); + +} + +ExperimentTabs.prototype.error = function(error) { + var e = JSON.parse(error); + showError(e); + this.panel.setLoading(false); +}; + +ExperimentTabs.prototype.draw = function(experiment) { + this.renderDataAcquisition(experiment); +}; + +ExperimentTabs.prototype.refreshAnalysisData = function() { + var _this = this; + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, data) { + _this.analysisGrid.refresh(data, {experiment : _this.experiment}); + }); + adapter.getAnalysisInformationByExperimentId(this.experiment.experimentId); + + /** Auto load **/ + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, data) { + _this.queueGrid.refresh(data); + }); + adapter.getCompactAnalysisByExperimentId(this.experiment.experimentId); + this.queueGrid.store.proxy.url = BUI.getQueueUrlByExperiment(this.experiment.experimentId); + this.queueGrid.refresh(); +}; + +ExperimentTabs.prototype.getSpecimenContainerHeight = function(experiment) { + var maxItems = 0; + if (maxItems < experiment.getSamples().length + 1) { + maxItems = experiment.getSamples().length + 1; + } + + var height = (maxItems + 1) * 40 + 40; + if (height > 400) { + height = 400; + } + return height; +}; + +ExperimentTabs.prototype.getExperimentTitle = function() { + var experimentHeaderForm = new ExperimentHeaderForm(); + return experimentHeaderForm.getPanel(this.experiment); +}; + +ExperimentTabs.prototype.renderDataAcquisition = function(experiment) { + var _this = this; + this.experiment = experiment; + + var specimenGrid = new SpecimenGrid({ + height : 400, + maxHeight : 500, + width : 890 + }); + + specimenGrid.onClick.attach(function(sender, args) { + }); + + specimenGrid.onSelected.attach(function(sender, specimens) { + //_this.samplePlateGroupWidget.selectSpecimens(specimens); + }); + var specimenContainer = this.specimenWidget.getPanel(); + this.specimenWidget.refresh(experiment); + /* + var specimenContainer = Ext.create('Ext.container.Container', { + layout : 'hbox', + width : 900, + padding : '10 0 0 2', + items : [ specimenGrid.getPanel() ] + }); + + specimenGrid.refresh(experiment);*/ + + var experimentList = new ExperimentList([ _this.experiment ]); + var measurementContainer = Ext.create('Ext.container.Container', { + layout : 'vbox', + padding : '5px 0px 0px 10px', + items : [] + }); + measurementContainer.insert(0, _this.measurementGridDone.getPanel(this.experiment.getMeasurements(), experimentList)); +// measurementContainer.insert(1, _this.measurementSamplePlateGroupWidget.getPanel(experiment)); + + // this.dataCollectionCurveVisualizer = new DataCollectionCurveVisualizer(); + // this.dataCollectionCurveVisualizer.experiments = [experiment]; + // this.dataCollectionCurveVisualizer.dataCollectionFrameTree.experiments = + // [experiment]; + // this.dataCollectionCurveVisualizer.dataCollections = + // experiment.getDataCollections(); + + this.panel = Ext.createWidget('tabpanel', { + plain : true, + style : { + padding : 2 + }, + items : [ { + tabConfig : { + id : 'genralTabl', + title : "Overview" + }, + items : [ specimenContainer ] + }, { + tabConfig : { + title : 'Measurements' + }, + items : [ measurementContainer] + }, { + tabConfig : { + id : 'SpecimenTab', + title : 'Analysis', + hidden : this.isTemplate() + }, + items : [ { + xtype : 'container', + layout : 'vbox', + padding : '5px 0px 10px 10px', + items : [ _this.analysisGrid.getPanel([]) ] + } ] + }, + { + tabConfig : { + id : 'newAnalysisTab', + title : 'Analysis BETA', + hidden : this.isTemplate() + }, + items : [ { + xtype : 'container', + layout : 'vbox', + padding : '5px 0px 10px 10px', + items : [ _this.queueGrid.getPanel([]) ] + } ] + } + ] + }); + + return this.getPanel(this.panel); +}; + +ExperimentTabs.prototype.isTemplate = function() { + if (this.experiment.json.type == "TEMPLATE") { + return true; + } + return false; +}; + +ExperimentTabs.prototype.update = function() { + var _this = this; + var inter; + if (!_this.isTemplate()) { + function updateExperiments() { + _this.refreshAnalysisData(); + window.clearInterval(inter); + inter = setInterval(function() { + updateExperiments(); + }, _this.INTERVAL_UPDATE); + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, data) { + var experiment = new Experiment(data); + _this.measurementGridDone.refresh(experiment.getMeasurements(), new ExperimentList([ experiment ])); + }); + adapter.getExperimentById(_this.experiment.json.experimentId, "MEDIUM"); + } + inter = setInterval(function() { + updateExperiments(); + }, _this.INTERVAL_UPDATE); + } +}; + +ExperimentTabs.prototype.getPanel = function(panel) { + var _this = this; + if (this.experimentPanel == null) { + this.experimentPanel = Ext.create('Ext.container.Container', { + bodyPadding : 2, + width : Ext.getBody().getViewSize().width * 0.9, + renderTo : this.targetId, + style : { + padding : 2 + }, + items : [ this.getExperimentTitle(), this.panel ], + listeners : { + afterrender : function() { + _this.refreshAnalysisData(); + _this.update(); + } + } + }); + } + + return this.experimentPanel; +}; + +ExperimentTabs.prototype.input = function() { + return { + experiment : DATADOC.getExperiment_10(), + proposal : DATADOC.getProposal_10() + }; +}; + +ExperimentTabs.prototype.test = function(targetId) { + var experimentTabs = new ExperimentTabs(targetId); + BIOSAXS.proposal = new Proposal(experimentTabs.input().proposal); + experimentTabs.draw(new Experiment(experimentTabs.input().experiment)); +}; + + + +/** + * Shows an experiments with the specimens, measurements, analysis tabs where results are shown and the frames widget + * + * @targetId + */ +function HPLCTabs(targetId) { + this.height = 1400; + this.targetId = targetId; + + this.id = BUI.id(); + + this.gridHeight = 600; + this.pointsCount = 1036; + this.plotHeight = 350; + this.plotWidth = 800; + this.plotPanelPadding = 5; + + var _this = this; + + this.analysisGrid = new HPLCAnalysisGrid({ + height : Ext.getBody().getViewSize().height * 0.9 - 300, + positionColumnsHidden : true, + sorters : [ { + property : 'priorityLevelId', + direction : 'ASC' + } ] + }); + + this.frameGrid = new FrameGrid({ + width : 600, + height : 60, + collapsed : false, + tbar : false + }); + + this.peakGrid = new PeakGrid({ + width : 550, + height : 500, + collapsed : false, + tbar : false + }); + + this.peakGrid2 = new PeakGrid({ + width : 102, + height : this.plotHeight, + collapsed : false, + tbar : false, + showExtendedColumns : true + }); + + this.mainPlotPanel = new HPLCGraph({ + title : 'I0', + width : this.plotWidth - 110, + height : this.plotHeight, + bbar : true, + plots : { + "I0" : true, + "Rg" : true + }, + xlabel : "HPLC Frames", + scaled : true, + interactionModel : { + 'dblclick' : function(event, g, context) { + _this._selectFrame(g.lastx_); + var annotations = []; + annotations.push({ + series : g.selPoints_[0].name, + x : g.lastx_, + width : 100, + height : 23, + tickHeight : 4, + shortText : g.lastx_, + text : g.lastx_, + attachAtBottom : true + }); + g.setAnnotations(annotations); + + } + } + }); + + this.intensityPlotPanel = new MergesHPLCGraph({ + title : 'Scattering', + width : this.plotWidth, + height : 500, + showRangeSelector : false, + xParam : 0, + xlabel : "scattering_I", + plots : { + "scattering_I" : true, + "subtracted_I" : true, + "buffer_I" : true + } + }); + +} + +HPLCTabs.prototype._selectFrame = function(frameNumber) { + try{ + this._renderScatteringCurve(frameNumber); + this.frameGrid.refresh([this.mainPlotPanel.getDataByFrameNumber(frameNumber)], this.experiment.experimentId); + } + catch(e){ + console.log(e); + } +}; + +HPLCTabs.prototype.error = function(error) { + var e = JSON.parse(error); + showError(e); + this.panel.setLoading(false); +}; + +HPLCTabs.prototype.loadDataMainPlot = function(data) { + var zeroArray = []; + for ( var i = 0; i < data.I0.length; i++) { + zeroArray.push(0); + } + data = [ { + param : "I0", + data : data.I0, + std : data.I0_Stdev, + color : '#0066CC', + label : "I0" + }, + { + param : "sum_I", + label : "sum_I", + color : "#00FF00", + data : data.sum_I, + std : zeroArray + }, + { + param : "Rg", + label : "Rg", + color : "#21610B", + data : data.Rg, + std : data.Rg_Stdev + }, { + param : "Mass", + data : data.mass, + std : data.mass_Stdev, + color : '#FF9900', + label : "Mass" + }, { + param : "Vc", + data : data.Vc, + std : data.Vc_Stdev, + color : '#990099', + label : "Vc" + }, { + param : "Qr", + data : data.Qr, + std : data.Qr_Stdev, + color : '#FF0066', + label : "Qr" + }, { + param : "quality", + label : "quality", + color : "#FF00FF", + data : data.quality, + std : zeroArray + } ]; + this.data = data; + this.mainPlotPanel.loadData(data); +}; + +HPLCTabs.prototype._loadIntensityPlotByFrameNumber = function(frameNumber) { + var _this = this; + + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, data) { + var array = []; + data = [ { + param : "q", + data : data[frameNumber].q, + std : data[frameNumber].q, + fstd : function(a) { + return parseFloat(a); + }, + color : '#green', + label : "q", + showOnMenu : false + }, + { + param : "scattering_I", + data : data[frameNumber].scattering_I, + fdata : function(a) { + return Math.log(parseFloat(a)); + }, + std : data[frameNumber].scattering_Stdev, + fstd : function(a) { + return Math.log(Math.abs(parseFloat(a))); + }, + color : 'green', + label : "Log(I) Sample" + }, + { + param : "buffer_I", + data : data[frameNumber].buffer_I, + fdata : function(a) { + return Math.log(parseFloat(a)); + }, + fstd : function(a) { + return Math.log(Math.abs(parseFloat(a))); + }, + std : data[frameNumber].subtracted_Stdev, + color : '#0000FF', + label : "Log(I) Avg Buf." + }, + { + param : "subtracted_I", + data : data[frameNumber].subtracted_I, + fdata : function(a) { + var value = (Math.log(parseFloat(a))); + if (isNumber(value)) + return value; + }, + fstd : function(a) { + var value = Math.log(Math.abs(parseFloat(a))); + if (isNumber(value)) + return value; + }, + std : data[frameNumber].subtracted_Stdev, + color : 'red', + label : "Log(I) Sub." + } + ]; + + _this.intensityPlotPanel.xlabel = "Frame " + frameNumber + " (q, nm-1)"; + + if (_this.intensityPlotPanel.hplcData == null) { + /** It creates also top bar **/ + _this.intensityPlotPanel.loadData(data); + } else { + _this.intensityPlotPanel.reloadData(data); + + } + _this.intensityPlotPanel.panel.setLoading(false); + }); + adapter.onError.attach(function(sender, data) { + _this.intensityPlotPanel.panel.setLoading(false); + }); + this.intensityPlotPanel.panel.setLoading("Retrieving Frame " + frameNumber); + adapter.getH5FrameScattering(this.experiment.json.experimentId, frameNumber); +}; + +HPLCTabs.prototype._renderScatteringCurve = function(frameNumber) { + var _this = this; + + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, data) { + _this.intensityPlotPanel.setPeaks(data); + _this._loadIntensityPlotByFrameNumber(frameNumber); + try{ + var peaks = []; + for(key in data){ + peaks.push({ + start : key.split("-")[0], + end : key.split("-")[1], + experimentId : _this.experiment.json.experimentId + }); + } + _this.peakGrid2.refresh(JSON.parse(JSON.stringify(peaks))); + _this.peakGrid.refresh(peaks); +// console.log(peaks); + } + catch(e){ + showError(e); + } + }); + adapter.onError.attach(function(sender, data) { + _this.intensityPlotPanel.setLoading(false); + }); + this.intensityPlotPanel.panel.setLoading("Reading HDF5 File "); + adapter.getH5FramesMerge(this.experiment.json.experimentId); +}; + +HPLCTabs.prototype._postRenderOverviewPanel = function() { + var _this = this; + var adapter = new BiosaxsDataAdapter(); + adapter.onError.attach(function(sender, data) { + data = data.replace(/NaN/g, '0'); + _this.loadDataMainPlot(JSON.parse(data)); + + + }); + adapter.onSuccess.attach(function(sender, data) { + _this.loadDataMainPlot(data); + _this._renderScatteringCurve(0); + }); + adapter.getH5fileParameters(this.experiment.json.experimentId, [ + "I0", "I0_Stdev", "sum_I","Rg", "Rg_Stdev", "Vc", "Vc_Stdev", "Qr", "Qr_Stdev", "mass", "mass_Stdev", "quality" ]); + +}; + +HPLCTabs.prototype._postRenderDetailsPanel = function() { + if (this.data != null) { + this.I0PlotPanel.loadData(this.data); + this.RgPlotPanel.loadData(this.data); + this.MassPlotPanel.loadData(this.data); + this.VcPlotPanel.loadData(this.data); + } + +}; + +HPLCTabs.prototype.draw = function(experiment) { + var _this = this; + this.renderDataAcquisition(experiment); +}; + +HPLCTabs.prototype.getExperimentTitle = function() { + var experimentHeaderForm = new ExperimentHeaderForm(); + return experimentHeaderForm.getPanel(this.experiment); +}; + +HPLCTabs.prototype.renderDataAcquisition = function(experiment) { + var _this = this; + this.experiment = experiment; + if (this.experimentPanel == null) { + this.experimentPanel = Ext.create('Ext.container.Container', { + bodyPadding : 2, + height : this.height, + width : Ext.getBody().getViewSize().width * 0.9, + renderTo : this.targetId, + style : { + padding : 2 + }, + items : [ this.getExperimentTitle(), this.getPanel() ], + listeners : { + afterrender : function() { + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, data) { + _this.analysisGrid.refresh(data); + }); + adapter.getAnalysisInformationByExperimentId(_this.experiment.experimentId); + } + } + }); + } + return this.experimentPanel; +}; + +HPLCTabs.prototype.getPanel = function() { + var _this = this; + this.panel = Ext.createWidget('tabpanel', + { + plain : true, + height : this.height, +// width : 900, + style : { + padding : 2 + }, + items : [ + { + tabConfig : { + title : "Overview" + }, - "quality" : { - "std" : null, - "sum" : 0, - "avg" : 0, - "validNumber" : 0, - "totalNumber" : 0, - "values" : [] + items : [ { + xtype : 'container', + layout : 'hbox', + border : 1, + flex : 1, + items : [ { + xtype : 'container', + layout : 'vbox', + border : 1, + items : [ { + xtype : 'container', + margin : '10px', + border : 1, + items : [ + { + xtype : 'container', + margin : '0px', + layout : 'hbox', + border : 1, + items : [ + this.peakGrid2.getPanel([]), + this.mainPlotPanel.getPanel() + ] + }, + { + xtype : 'container', + layout : 'vbox', + border : 1, + items : [ + { + html: '
Select a frame by double-clicking on the HPLC Frames plot
', + margin: 5, + border : 0 + }, + this.frameGrid.getPanel([]), + this.intensityPlotPanel.getPanel() + ] + } + + ] + }], + listeners : { + afterrender : function() { + _this._postRenderOverviewPanel(); + } + } + } ] + } ] + }, +// { +// tabConfig : { +// title : "Details" +// }, +// items : [ { +// xtype : 'container', +// layout : 'hbox', +// border : 1, +// flex : 1, +// items : [ { +// xtype : 'container', +// layout : 'vbox', +// border : 1, +// items : [ { +// xtype : 'container', +// padding : '1px', +// items : [ +// this.I0PlotPanel.getPanel(), this.RgPlotPanel.getPanel(), this.MassPlotPanel.getPanel(), +// this.VcPlotPanel.getPanel() ] +// } ], +// listeners : { +// afterrender : function() { +// _this._postRenderDetailsPanel(); +// } +// } +// } ] +// } ] +// }, + { + tabConfig : { + title : "Analysis" }, - "rgGnom" : { - "std" : null, - "sum" : 0, - "avg" : 0, - "validNumber" : 0, - "totalNumber" : 0, - "values" : [] + items : [ { + xtype : 'container', + layout : 'vbox', + padding : '5px 0px 10px 10px', + items : [ _this.analysisGrid.getPanel([]) ] + } ] + }, + { + tabConfig : { + title : "File Manager" }, - "dMax" : { - "std" : null, - "sum" : 0, - "avg" : 0, - "validNumber" : 0, - "totalNumber" : 0, - "values" : [] + items : [ { + xtype : 'container', + layout : 'vbox', + padding : '5px 0px 10px 10px', + items : [ + { + html: '
Custom Download
', + margin : '10 0 10 5', + border : 0 + }, + { + html: '
Download has been temporary disabled. Contact your labcontact if you want to extract the frames from the HDF5 file
', + margin : '10 0 10 5', + hidden : _this.experiment.experimentId == 2602, + border : 0 + }, + { + xtype : 'container', + layout : 'hbox', + margin : '0 0 0 20', + flex : 1, + items :[ + { + xtype: 'numberfield', + id: 'field_start', + fieldLabel: 'Frames from', + value: 0, + minValue: 0 + }, + { + xtype: 'numberfield', + id: 'field_end', + fieldLabel: 'to', + labelWidth : 20, + margin : '0 0 0 10', + minValue: 0 + }, + { + xtype: 'button', + /** allowing test account to download frames **/ + disabled : _this.experiment.experimentId != 2602, + text : 'Download', + margin : '0 0 0 10', + handler : function() { + var experimentId = _this.experiment.experimentId; + var start = Ext.getCmp("field_start").getValue(); + var end = Ext.getCmp("field_end").getValue(); + /** Checking if start is bigger than end **/ + if (start > end){ + var aux = end; + end = start; + start = aux; + } + var url = BUI.getZipFrameHPLCUrl(experimentId, start, end); + window.open(url) + } + } + ] + }, + _this.peakGrid.getPanel([]) + + ] + } + ] + } + ] + }); + + return this.panel; +}; + +/** + * Main form tab for macromolecule + * + * @width + * @height + * + * #onClose when user clicks on close button in the MacromoleculeForm + * #onSave when macromole is saved by macromoleculeForm + */ +function MacromoleculeTabs(args) { + this.width = 500; + this.height = 500; + + if (args != null) { + if (args.width != null) { + this.width = args.width; + } + if (args.height != null) { + this.height = args.height; + } + } + + var _this = this; + + /** Widgets **/ + + /** Macromolecule Form **/ + this.macromoleculeForm = new MacromoleculeForm({ + width : this.width - 30, + height : this.height - 50, + }); + + this.macromoleculeForm.onClose.attach(function(sender) { + _this.onClose.notify(); + }); + + this.macromoleculeForm.onSave.attach(function(sender, macromolecule) { + _this.onSave.notify(macromolecule); + }); + + this.assemblyForm = new AssemblyForm({ + width : this.width - 30, + height : this.height - 50, + }); + + this.rigibBodyModelingForm = new RigibBodyModelingForm({ + width : this.width - 30, + height : this.height - 50, + }); + + this.rigibBodyModelingForm.onSave.attach(function(sender, macromolecule) { + _this.onSave.notify(macromolecule); + }); + + + /** Events **/ + this.onClose = new Event(this); + this.onSave = new Event(this); +} + +/** Populate the widget **/ +MacromoleculeTabs.prototype.refresh = function(macromolecule) { + this.macromoleculeForm.refresh(macromolecule); + this.assemblyForm.refresh(macromolecule); + this.rigibBodyModelingForm.refresh(macromolecule); + + if (macromolecule != null){ + if (macromolecule.macromoleculeId == null){ + Ext.getCmp(this.id + "_advancedTab").disable(); + Ext.getCmp(this.id + "_assemblyTab").disable(); + } + else{ + Ext.getCmp(this.id + "_advancedTab").enable(); + Ext.getCmp(this.id + "_assemblyTab").enable(); + } + } + else{ + Ext.getCmp(this.id + "_advancedTab").disable(); + Ext.getCmp(this.id + "_assemblyTab").disable(); + } +}; + +MacromoleculeTabs.prototype.getItems = function() { + return [ { + tabConfig : { + title : 'General' + }, + items : [ { + xtype : 'container', + items : [ this.macromoleculeForm.getPanel() ] + } ] + }, { + id : this.id + "_assemblyTab", + tabConfig : { + title : 'Assembly' + }, + items : [ { + xtype : 'container', + items : [ this.assemblyForm.getPanel() ] + } ] + },{ + id : this.id + "_advancedTab", + tabConfig : { + title : 'Advanced' + }, + items : [ this.rigibBodyModelingForm.getPanel() ] + } ]; +}; + +MacromoleculeTabs.prototype.getPanel = function() { + this.panel = Ext.createWidget('tabpanel', { + height : this.height, + width : this.width, + plain : true, + margin : 5, + border : 0, + items : this.getItems() + }); + return this.panel; +}; + +function ResultTabs() { +} + +ResultTabs.prototype.draw = function(targetId, data, macromoleculeId) { + var panel = this.getPanel(targetId, data, macromoleculeId); + return Ext.create('Ext.container.Container', { + renderTo : targetId, + items : [ BUI.getMacromoleculeHeader(macromoleculeId), panel ] + }); + +}; + +ResultTabs.prototype._splitBySpecimen = function(data) { + var splitted = {}; + + for ( var i = 0; i < data.length; i++) { + var row = data[i]; + if (splitted[row.macromoleculeId + "_" + row.bufferId] == null) { + splitted[row.macromoleculeId + "_" + row.bufferId] = []; + } + splitted[row.macromoleculeId + "_" + row.bufferId].push(row); + } + return splitted; + +}; + +ResultTabs.prototype._getTabTitle = function(key, data) { + var macromoleculeId = key.split("_")[0]; + var bufferId = key.split("_")[1]; + + return BIOSAXS.proposal.getMacromoleculeById(macromoleculeId).acronym + " + " + BIOSAXS.proposal.getBufferById(bufferId).acronym + "(" + data.length + ")"; +}; + +ResultTabs.prototype.getPanel = function(targetId, data, macromoleculeId) { + + var dataFiltered = new AnalysisGrid({ + hideNulls : true, + grouped : true, + sorters : [ { + property : 'quality', + direction : 'DESC' + } ] + })._prepareData(data); + + var items = [ { + tabConfig : { + title : 'Analysis (' + dataFiltered.length + ')' + }, + items : [ { + xtype : 'container', + layout : 'vbox', + padding : 10, + items : [ new AnalysisGrid({ + isScatteringPlotVisible : false + }).getPanel(dataFiltered) ] + } ] + }, { + tabConfig : { + title : 'Concentration Effects' + }, + items : [ new ResultSummaryForm().getPanel(data) ] + } ]; + + var splitted = this._splitBySpecimen(dataFiltered); + for ( var key in splitted) { + items.push({ + tabConfig : { + title : this._getTabTitle(key, splitted[key]) + }, + items : [ new ResultSummaryForm().getPanel(splitted[key]) ] + }); + } + + this.panel = Ext.createWidget('tabpanel', { + style : { + padding : 2 + }, + items : items + }); + return this.panel; + +}; + + +function ShipmentTabs(targetId) { + this.targetId = targetId; + + var _this = this; + this.gridHeight = 500; + /** data **/ + this.shipment = null; + + /** Shipment Form **/ + this.shipmentForm = new ShipmentForm({ + creationMode : false, + showTitle : false + }); + this.shipmentForm.onSaved.attach(function(sender, data) { + _this.refresh(data); + + }); + + + /** Cases grid **/ + this.caseGrid = new CaseGrid({ + height : this.gridHeight + }); + + this.caseGrid.onAddButtonClicked.attach(function(sender, dewar) { + _this.caseGrid.grid.setLoading("ISPyB: Creating a new case"); + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, shipment) { + /** updateing shipment on proposal **/ + for ( var i = 0; i < BIOSAXS.proposal.shippings.length; i++) { + if (BIOSAXS.proposal.shippings[i].shippingId == shipment.shippingId) { + BIOSAXS.proposal.shippings[i] = shipment; + } + } + _this.refresh(shipment); + }); + adapter.onError.attach(function(sender, shipment) { + _this.caseGrid.grid.setLoading(false); + }); + adapter.addCase(_this.shipment.json.shippingId); + }); + + this.caseGrid.onRemoveButtonClicked.attach(function(sender, dewarId) { + _this.panel.setLoading("ISPyB: removing case"); + _this.shipment.onSaved.attach(function(sender, shipment) { + _this.refresh(shipment); + + }); + _this.shipment.removeCase(dewarId); + }); +} + +ShipmentTabs.prototype.refresh = function(shipment) { + + var _this = this; + this.shipment = shipment; + var proposal = new Proposal(); + proposal.onDataRetrieved.attach(function(sender, plates) { + + _this.refreshWithPlates(shipment, plates); + _this.caseGrid.grid.setLoading(false); + }); + proposal.getPlatesByProposal(); +}; + +ShipmentTabs.prototype.refreshWithPlates = function(shipment, plates) { + this.shipment = new Shipment(shipment); + + this.caseGrid.refresh(this.shipment.getDewars(), plates); + this.panel.setLoading(false); +}; + +ShipmentTabs.prototype.error = function(error) { + var e = JSON.parse(error); + showError(e); + this.panel.setLoading(false); +}; + +//ShipmentTabs.prototype.refreshTabTitles = function() { +/*Ext.getCmp("MacromoleculeTab").setText(this.getMacromoleculeTitle()); +Ext.getCmp("BufferTab").setText(this.getBuffersTitle()); +Ext.getCmp("SpecimenTab").setText(this.getSpecimenTitle()); +Ext.getCmp("PlatesTab").setText(this.getPlatesTitle()); +Ext.getCmp("AssembliesTab").setText(this.getShipmentTitle()); +Ext.getCmp("PlateGroupsTab").setText(this.getPlateGroupsTitle());*/ +//}; +ShipmentTabs.prototype.draw = function(shipment) { + var _this = this; + _this.plates = []; + _this.shipment = shipment; + _this.render(shipment); + + // var proposal = new Proposal(); + // proposal.onDataRetrieved.attach(function(sender, plates){ + // _this.plates = plates; + // _this.shipment = shipment; + // _this.render(shipment); + // + // }); + // proposal.getPlatesByProposal(); +}; + +//ShipmentTabs.prototype.getShipmentTitle = function() { +// return 'Shipment'; +//}; + +//ShipmentTabs.prototype.getMacromoleculeTitle = function() { +// return 'Macromolecules (' + this.experiment.getMacromolecules().length + ')'; +//}; + +//ShipmentTabs.prototype.getBuffersTitle = function() { +// return 'Buffers (' + this.experiment.getBuffers().length + ')'; +//}; + +//ShipmentTabs.prototype.getPlateGroupsTitle = function() { +// return 'Plate Groups (' + this.experiment.getPlateGroups().length + ')'; +//}; + +//ShipmentTabs.prototype.getSampleChangerTitle = function() { +// return 'Sample Changer'; +//}; + +//ShipmentTabs.prototype.getSpecimenTitle = function() { +// return 'Specimens(' + this.experiment.getSpecimenCount() + ')'; +//}; + +//ShipmentTabs.prototype.getPlatesTitle = function() { +// return 'Plates(' + this.experiment.getSamplePlates().length + ')'; +//}; + +//ShipmentTabs.prototype.getBuffersTip = function() { +/*if (this.experiment.getBuffers().length == 0){ + return BUI.getWarningHTML("There are no buffers. Click on add to create new ones. Click on edit button or double click to edit them"); + +} +else{ + return BUI.getTipHTML("Click on edit button or double click to edit them. Click on duplicate to create an identical buffer including its additives") +}*/ +//}; + +//ShipmentTabs.prototype.refreshTips = function() { +/*Ext.getCmp("BufferTabTip").update(this.getBuffersTip()); +Ext.getCmp("SpecimenTabTip").update(this.getSpecimensTip());*/ +//}; + +ShipmentTabs.prototype.render = function(shipment) { + return this.getPanel(shipment); +}; + +ShipmentTabs.prototype.getShipmentHeader = function(shipment) { + var _this = this; + function getHTMLSource() { + var name = shipment.json.shippingName; + var status = shipment.json.shippingStatus; + var creationDate = shipment.json.creationDate; + var html = BUI.createFormLabel("Name :", name, 75, 400); + html = html + BUI.createFormLabel("Status :", status, 75, 400); + html = html + BUI.createFormLabel("Date :", creationDate, 75, 400); + return html; + } + + return Ext.create('Ext.container.Container', { + frame : false, + layout : 'hbox', + title : 'General', + padding : 5, + bodyPadding : '5 5 0 0', + width : 890, + margin : '0 0 10 0', + height : 100, + style : { + borderColor : '#BDBDBD', + borderStyle : 'solid', + borderWidth : '1px' + }, + fieldDefaults : { + msgTarget : 'side', + labelWidth : 100 + }, + items : [ { + margin : "0 0 0 0", + width : 475, + border : 0, + html : getHTMLSource() + } + ] + }); +}; + +ShipmentTabs.prototype.getTabPanel = function(shipment) { + this.panel = Ext.createWidget('tabpanel', { + height : 600, + style : { + padding : 2 + }, + items : [ { + tabConfig : { + id : 'Shipment', + title : 'Shipment', + icon : '/ispyb/images/plane-small.gif' + }, + items : [ { + xtype : 'container', + margin : '5 5 5 5', + items : [ this.shipmentForm.getPanel(shipment) ] + } ] + }, { + tabConfig : { + id : 'Cases', + title : 'Cases', + icon : '../images/box-icon-very-small.png' + }, + items : [ { + xtype : 'container', + margin : '5 5 5 5', + items : [ this.caseGrid.getPanel(shipment.getDewars(), this.plates) ] + } ] + } ] + }); + return this.panel; +}; + +ShipmentTabs.prototype.getPanel = function(shipment) { + var _this = this; + this.shipment = shipment; + if (this.plates == null) { + this.plates = []; + } + + if (this.shipPanel == null) { + this.shipPanel = Ext.create('Ext.container.Container', { + bodyPadding : 2, + width : Ext.getBody().getViewSize().width * 0.9, + renderTo : this.targetId, + style : { + padding : 2 + }, + items : [ this.getShipmentHeader(shipment), this.getTabPanel(shipment) ] + }); + } + + return this.shipPanel; +}; + +/** + * Shows an template with the specimens, measurements and the experiment's + * requirement + * + * @targetId + */ +function TemplateTabs(targetId) { + this.height = 600; + this.targetId = targetId; + + this.id = BUI.id(); + var _this = this; + + this.gridHeight = 1000; + /** data * */ + this.experiment = null; + + this.specimenSelected = null; + + /** Specimen Widget contains a specimenGrid and a sampleChangerWidget than can be displayed with are vertical or horizontal layout **/ + this.specimenWidget = new SpecimenWidget({ + height : this.height + }); + + this.samplePlateGroupWidget = new SamplePlateGroupWidget({ + showTitle : false, + height : 250, + margin : 5, + bbar : true + }); + + this.samplePlateGroupWidget.onExperimentChanged.attach(function(sender, experiment) { + _this.refresh(experiment); + }); + + this.samplePlateGroupWidget.onClick.attach(function(sender, args) { + }); + + this.volumePlanificator = new VolumeGrid(); + + /** For Measurements * */ + var storeViscosity = Ext.create('Ext.data.Store', { + fields : [ 'name' ], + data : [ { + "name" : "low" + }, { + "name" : "medium" + }, { + "name" : "high" + } ] + }); + + // Create the combo box, attached to the states data store + var viscosityEditor = Ext.create('Ext.form.ComboBox', { + fieldLabel : '', + store : storeViscosity, + queryMode : 'local', + displayField : 'name', + valueField : 'name' + }); + + this.measurementGrid = new MeasurementGrid({ + maxWidth : 1500, + estimateTime : false, + positionColumnsHidden : true, + isPriorityColumnHidden : true, + isStatusColumnHidden : true, + isTimeColumnHidden : true, + updateRowEnabled : true, + collapsed : true, + removeBtnEnabled : true, + showTitle : false, + collapseBtnEnable : false, + addBtnMultipleEdit : true, + sortingBtnEnable : true, + editor : { + exposureTemperature : { + xtype : 'textfield', + allowBlank : true + }, + comments : { + xtype : 'textfield', + allowBlank : true + }, + volumeToLoad : { + xtype : 'numberfield', + allowBlank : true + }, + transmission : { + xtype : 'numberfield', + allowBlank : true + }, + viscosity : viscosityEditor, + waitTime : { + xtype : 'numberfield', + allowBlank : true + }, + flow : { + xtype : 'checkbox', + allowBlank : true + } + } + }); + + this.measurementGrid.onSelected.attach(function(sender, measurements) { + var specimens = []; + for ( var i = 0; i < measurements.length; i++) { + specimens.push(_this.experiment.getSampleById(measurements[i].specimenId)); + } + }); + + this.measurementGrid.onMeasurementChanged.attach(function(sender, measurement) { + _this.experiment.setMeasurement(measurement); + _this.refresh(_this.experiment); + }); + + this.measurementGrid.onExperimentChanged.attach(function(sender, json) { + _this.refresh(new Experiment(json)); + }); + + this.measurementGrid.onRemoved.attach(function(sender, experiment) { + _this.refreshSpecimen(new Experiment(experiment)); + }); + + this.measurementGrid.onUpdateTime.attach(function(sender, args) { + document.getElementById(_this.id + "_counter").innerHTML = args.hours + 'h, ' + args.minutes + 'min and ' + args.seconds + ' seconds'; + }); + +} + +TemplateTabs.prototype.refreshMeasurement = function(experiment) { + this.experiment = experiment; + this.experiment.onSaved = new Event(this); + this.experiment.onSpecimenSaved = new Event(this); + var experimentList = new ExperimentList([ this.experiment ]); + this.measurementGrid.refresh(experimentList.getMeasurementsNotCollected(), experimentList); +}; + +TemplateTabs.prototype.refreshSpecimen = function(experiment) { + this.experiment = experiment; + this.experiment.onSaved = new Event(this); + this.experiment.onSpecimenSaved = new Event(this); + this.samplePlateGroupWidget.refresh(this.experiment); + this.specimenWidget.refresh(this.experiment); + this.volumePlanificator.refresh(this.experiment); +}; + +TemplateTabs.prototype.refresh = function(experiment) { + // var start = new Date().getTime(); + this.experiment = experiment; + this.experiment.onSaved = new Event(this); + this.experiment.onSpecimenSaved = new Event(this); + + // var experimentList = new ExperimentList([this.experiment]); + this.refreshMeasurement(experiment); + this.refreshSpecimen(experiment); + /** Refreshing grids * */ + this.panel.setLoading(false); + +}; + +TemplateTabs.prototype.error = function(error) { + var e = JSON.parse(error); + showError(e); + this.panel.setLoading(false); + +}; + +TemplateTabs.prototype.draw = function(experiment) { + this.render(experiment); +}; + + +TemplateTabs.prototype.getExperimentTitle = function() { + var _this = this; + var experimentHeaderForm = new ExperimentHeaderForm(); + return experimentHeaderForm.getPanel(this.experiment); +}; + +TemplateTabs.prototype.render = function(experiment) { + var _this = this; + this.experiment = experiment; + + /** + * + * Depending on the sample Changer configuration we want to display the plates vertically or horizontally + * Default is vertical + * + * */ + + var specimenContainer = this.specimenWidget.getPanel(); + this.specimenWidget.refresh(experiment); + + var experimentList = new ExperimentList([ _this.experiment ]); + var measurementContainer = Ext.create('Ext.container.Container', { + layout : { + type : 'vbox' + }, + defaults : { + style : { + padding : '5px 0px 0px 10px' + } + }, + items : [ _this.measurementGrid.getPanel(experimentList.getMeasurementsNotCollected(), experimentList) ] + }); + + this.panel = Ext + .createWidget( + 'tabpanel', + { + plain : true, + items : [ + { + tabConfig : { + title : 'Measurements' + }, + items : [ { + xtype : 'container', + layout : 'vbox', + border : 1, +// height : _this.gridHeight, + margin : "0 0 10 0", + items : [ measurementContainer ] + } + + ] + }, + { + tabConfig : { + id : 'genralTabl', + title : "Specimens" + // width : 900, + // border : 3 + + }, + items : [ specimenContainer ] + }, + { + tabConfig : { + title : "Requirements" + + }, + items : [ + { + html : BUI.getTipHTML("Estimated volume is the maximum volume required. Depending on the order of your measurements you may use less. Click on create stock solutions if you plan to ship these stock solutions"), + margin : "10 10 10 10", + border : 0 + }, this.volumePlanificator.getPanel(experiment) ] + } ] + }); + // ); + return this.getPanel(this.panel); +}; + +TemplateTabs.prototype.isTemplate = function() { + if (this.experiment.json.type == "TEMPLATE") { + return true; + } + return false; +}; + + +TemplateTabs.prototype.getPanel = function(panel) { + var _this = this; + if (this.experimentPanel == null) { + this.experimentPanel = Ext.create('Ext.container.Container', { + bodyPadding : 2, + width : 1000,//Ext.getBody().getViewSize().width * 0.9, + renderTo : this.targetId, + height : 500, +// style : { +// padding : 2 +// }, + items : [ + this.getExperimentTitle(), + this.panel + ], + listeners : { + afterrender : function(thisCmp) { + $("#SchemeReport" + _this.experiment.experimentId).click(function() { + $(this).target = "_blank"; + window.open('viewProjectList.do?reqCode=display&menu=platescheme&experimentId=' + _this.experiment.experimentId); + return false; + }); + + } + } + }); + } + return this.experimentPanel; +}; + +TemplateTabs.prototype.input = function(targetId) { + return new ExperimentTabs().input(); +}; + +TemplateTabs.prototype.test = function(targetId) { + var experimentTabs = new TemplateTabs(targetId); + BIOSAXS.proposal = new Proposal(experimentTabs.input().proposal); + experimentTabs.draw(new Experiment(experimentTabs.input().experiment)); +}; + +var BUI = { + //interval : 60000, + interval : 40000, + rainbow : function(numOfSteps, step) { + // This function generates vibrant, "evenly spaced" colours (i.e. no clustering). This is ideal for creating easily distinguishable vibrant markers in Google Maps and other apps. + // Adam Cole, 2011-Sept-14 + // HSV to RBG adapted from: http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript + var r, g, b; + var h = step / numOfSteps; + var i = ~~(h * 6); + var f = h * 6 - i; + var q = 1 - f; + switch (i % 6) { + case 0: + r = 1, g = f, b = 0; + break; + case 1: + r = q, g = 1, b = 0; + break; + case 2: + r = 0, g = 1, b = f; + break; + case 3: + r = 0, g = q, b = 1; + break; + case 4: + r = f, g = 0, b = 1; + break; + case 5: + r = 1, g = 0, b = q; + break; + } + var c = "#" + ("00" + (~~(r * 255)).toString(16)).slice(-2) + ("00" + (~~(g * 255)).toString(16)).slice(-2) + + ("00" + (~~(b * 255)).toString(16)).slice(-2); + return (c); + }, + getFileNameByPath : function(filePath) { + if (filePath != null){ + var split = filePath.split("/"); + if (split.length > 0){ + return split[split.length - 1]; + } + } + return "Not file"; + }, + getUpdateInterval : function() { + this.interval = this.interval + 2000; + return this.interval; + }, + getRadiationDamageThreshold : function() { + return 0.7; + }, + getQualityThreshold : function() { + return 0.7; + }, + getCreateShipmentURL : function() { + return '/ispyb/user/viewProjectList.do?reqCode=display&menu=create_shipment'; + }, + getCreateShipmentList : function() { + return '/ispyb/user/viewProjectList.do?reqCode=display&menu=list_shipment'; + }, + getShippingURL : function(shippingId) { + return '/ispyb/user/viewProjectList.do?reqCode=display&menu=shipment&shippingId=' + shippingId; + }, + + getMacromoleculeResultsURLByMultipleSearch : function(array) { + return '/ispyb/user/viewProjectList.do?reqCode=display&menu=macromolecule&search=' + + JSON.stringify(array).replace(new RegExp("\"", 'g'), "'"); + }, + getMacromoleculeResultsURL : function(macromoleculeId) { + return '/ispyb/user/viewProjectList.do?reqCode=display&menu=macromolecule¯omoleculeId=' + macromoleculeId; + }, + getMacromoleculeBufferResultsURL : function(macromoleculeId, bufferId) { + return '/ispyb/user/viewProjectList.do?reqCode=display&menu=macromolecule&bufferId=' + bufferId + '¯omoleculeId=' + macromoleculeId; + }, + + getMacromoleculeHeader : function(macromoleculeId) { + + function getHTMLSource(macromoleculeId) { + if (macromoleculeId != null) { + var html = BUI.createFormLabel("Name :", BIOSAXS.proposal.getMacromoleculeById(macromoleculeId).name, 75, 400); + html = html + BUI.createFormLabel("Acronym :", BIOSAXS.proposal.getMacromoleculeById(macromoleculeId).acronym, 75, 400); + if (BIOSAXS.proposal.getMacromoleculeById(macromoleculeId).comments != null) { + html = html + BUI.createFormLabel("Comments :", BIOSAXS.proposal.getMacromoleculeById(macromoleculeId).comments, 75, 400); + } + return html; + } + } + + return Ext.create('Ext.container.Container', { + frame : false, + layout : 'hbox', + title : 'Macromolecule', + bodyPadding : '5', + width : 890, + margin : '0 0 10 0', + height : 100, + style : { + borderColor : '#BDBDBD', + borderStyle : 'solid', + borderWidth : '1px' + }, + fieldDefaults : { + msgTarget : 'side', + labelWidth : 100 + }, + items : [ { + margin : "10 0 0 10", + width : 475, + border : 0, + html : getHTMLSource(macromoleculeId) + }, { + margin : "10 0 0 10", + width : 475, + border : 0, + html : BUI.getZipHTMLByMacromoleculeId(macromoleculeId) + } + ] + }); + }, + + getZipHTMLByMacromoleculeId : function(macromoleculeId) { + if (macromoleculeId != null) { + var fileName = BIOSAXS.proposal.getMacromoleculeById(macromoleculeId).acronym; + return ""; + } + }, + getZipHTMLByExperimentId : function(experimentId, filename) { + if (filename == null){ + filename = "experiment"; + } + return ""; + }, + + getZipURLByAverageId : function(averageId, filename) { + if (filename == null){ + filename = "experiment"; + } + return "/ispyb/user/dataadapter.do?reqCode=getZipFileByAverageListId&f&mergeIdsList=" + averageId + "&fileName=" + filename; + }, + getZipURLBySubtractionId : function(subtractionId, filename) { + if (filename == null){ + filename = "experiment"; + } + return "/ispyb/user/dataadapter.do?reqCode=getZipFileByAverageListId&f&subtractionIdList=" + subtractionId + "&fileName=" + filename; + }, + + + getZipHTMLByFrameRangeId : function(experimentId, start, end) { + var fileName = "experiment"; + return ""; + }, + getZipFrameHPLCUrl : function(experimentId, start, end) { + return "/ispyb/user/dataadapter.do?reqCode=getZipFileH5ByFramesRange&f&experimentId=" + experimentId + "&start=" + Number(start) +"&end="+ Number(end); + }, + + getQueueUrl : function() { + return "/ispyb/user/dataadapter.do?reqCode=getPagingCompactAnalysisByProposalId"; + }, + + getQueueUrlByExperiment: function(experimentId) { + return "/ispyb/user/dataadapter.do?reqCode=getPagingCompactAnalysisByExperimentId&f&experimentId=" + experimentId; + }, + getStandardDeviation : function(values) { + var sum = 0; + var count = 0; + var avg = null; + + var curatedValues = new Array(); + for ( var i = 0; i < values.length; i++) { + var value = values[i]; + if (value != null) { + if (!isNaN(value)) { + count = count + 1; + sum = sum + Number(value); + curatedValues.push(Number(value)); + } + } + } + + if (count > 0) { + avg = sum / count; + } else { + avg = sum; + } + var aux = 0; + for ( var i = 0; i < curatedValues.length; i++) { + var value = curatedValues[i]; + aux = aux + Math.pow(value - avg, 2); + } + /** std **/ + var std = Math.sqrt(aux / count); + return { + std : (std), + sum : (sum), + avg : (avg), + validNumber : count, + totalNumber : values.length, + values : values + }; + }, + + getHTMLTableForFrameAveraged : function(bufferAcronym, macromoleculeAcronym, bbmerges, molmerges, bamerges, totalframes, bufferId,macromoleculeId, macromoleculeColor) { + + function getFrameSpan(framesMerged, total) { + return "(" + framesMerged + " of " + total + ")"; + } + + function getColorFrame(framesMerged, total) { + if (framesMerged / total < 0.5) { + return "#FA5858"; + } + if ((framesMerged / total >= 0.5) && (framesMerged / total < 0.8)) { + return "#FF9900"; + } + return "white"; + } + + function getRow(color, acroynm, framesMerged, totalframes) { + return " " + + BUI.getRectangleColorDIV(color, 10, 10) + + " " + acroynm + "" + + getFrameSpan(framesMerged, totalframes) + ""; + } + + var html = ""; + + /** Buffer Before **/ + if (bufferAcronym != null) { + if (bbmerges != null) { + color = BIOSAXS.proposal.bufferColors[bufferId]; + html = html + getRow(color, bufferAcronym, bbmerges, totalframes); + } + } + + /** Molecule **/ + if (macromoleculeAcronym != null) { + if (molmerges != null) { + if (macromoleculeColor == null){ + color = BIOSAXS.proposal.macromoleculeColors[macromoleculeId]; + } + else{ + color = macromoleculeColor; //BIOSAXS.proposal.macromoleculeColors[macromoleculeId]; + } + html = html + getRow(color, macromoleculeAcronym, molmerges, totalframes); + } + } + + /** Buffer After **/ + if (bufferAcronym != null) { + if (bamerges != null) { + color = BIOSAXS.proposal.bufferColors[bufferId]; + html = html + getRow(color, bufferAcronym, bamerges, totalframes); + } + } + return html + "
"; + }, + isWebGLEnabled : function(return_context) { + if (!!window.WebGLRenderingContext) { + var canvas = document.createElement("canvas"); + names = [ "webgl", "experimental-webgl", "moz-webgl", "webkit-3d" ]; + context = false; + for ( var i = 0; i < 4; i++) { + try { + context = canvas.getContext(names[i]); + if (context && typeof context.getParameter == "function") { + // WebGL is enabled + if (return_context) { + // return WebGL object if the function's argument is present + return { + name : names[i], + gl : context + }; } + // else, return just true + return true; } - } ] - }, - "concentrationLabel" : "7.17 mg/ml ,3.53 mg/ml ,1.75 mg/ml ,0.91 mg/ml " + } catch (e) { + } + } + // WebGL is supported, but disabled + return false; + } + // WebGL not supported28. + return false; + }, + getHTMLTableForPrefixes : function(bufferBeforeaverageFilePath, averageFilePath, bufferAfterAverageFilePath) { + + function getRow(bufferBeforeaverageFilePath) { + file = bufferBeforeaverageFilePath; + try { + file = bufferBeforeaverageFilePath.split("/")[bufferBeforeaverageFilePath.split("/").length - 1]; + } catch (e) { + file = "NA"; + } + return "" + file + ""; + } + var html = ""; + + /** Buffer Before **/ + if (bufferBeforeaverageFilePath != null) { + html = html + getRow(bufferBeforeaverageFilePath); + } + + /** Molecule **/ + if (averageFilePath != null) { + html = html + getRow(averageFilePath); + } + + /** Buffer After **/ + if (bufferAfterAverageFilePath != null) { + html = html + getRow(bufferAfterAverageFilePath); + } + return html + "
"; + }, + + getBaseURL : function() { + return '/ispyb/user/dataadapter.do'; + }, + + getPrintcomponentURL : function(dewarId) { + return '/ispyb/user/viewDewarAction.do?reqCode=generateLabels&dewarId=' + dewarId; + + }, + getPDBVisualizerURL : function(modelId, subtractionId, experimentId) { + return '/ispyb/user/viewProjectList.do?reqCode=display&menu=PDBVisualizer&modelId=' + modelId + '&experimentId=' + experimentId + + '&subtractionId=' + subtractionId; + }, + + getURL : function() { + return this.getBaseURL() + '?reqCode=getImage'; + + }, + getAbinitioImageURL : function() { + return this.getBaseURL() + '?reqCode=getAbinitioImage'; + }, + getNSDImageURL : function(modelListId) { + return BUI.getAbinitioImageURL() + '&type=NSD&modelListId=' + modelListId; + }, + getCHI2ImageURL : function(modelListId) { + return BUI.getAbinitioImageURL() + '&type=CHI2&modelListId=' + modelListId; + }, + getModelFile : function(type, modelId, format) { + return this.getBaseURL() + '?reqCode=getModelFile' + "&type=" + type + "&modelId=" + modelId + "&format=" + format; + }, + getPdbURL : function() { + return this.getBaseURL() + '?reqCode=getPdbFiles'; + }, + getStvArray : function(value, error) { + value = Number(value); + error = Number(error); + return [ value - error, value, value + error ]; + }, + getPointArrayForDygraph : function(x, y, error) { + return [ x, BUI.getStvArray(y, error) ]; + }, + createDIV : function(text, width, className, backgroundColor) { + var nameContainer = document.createElement("div"); + var nameSpan = document.createElement("span"); + if (className != null) { + nameSpan.setAttribute("class", className); + } + if (backgroundColor != null) { + nameSpan.setAttribute("style", "float:left;width:" + width + "px;height:18px;background-color:" + backgroundColor); + } else { + nameSpan.setAttribute("style", "float:left;width:" + width + "px;height:18px;"); + } + nameSpan.appendChild(document.createTextNode(text)); + nameContainer.appendChild(nameSpan); + return nameContainer; + }, + + createFormLabel : function(labelText, text, labelWidth, textWidth, backgroundColor) { + var div = document.createElement("div"); + + div.appendChild(BUI.createDIV(labelText, labelWidth, "bLabel", backgroundColor)); + div.appendChild(BUI.createDIV(text, textWidth, "btext", backgroundColor)); + return div.innerHTML; + }, + + createTextArea : function(labelText, text, labelWidth, rows, cols) { + var div = document.createElement("div"); + div.appendChild(BUI.createDIV(labelText, labelWidth, "bLabel")); + var textArea = document.createElement("textarea"); + textArea.setAttribute("rows", rows); + textArea.setAttribute("cols", cols); + textArea.appendChild(document.createTextNode(text)); + div.appendChild(textArea); + return div.innerHTML; + }, + + showBetaWarning : function() { + alert("ISPyB for Biosaxs version Beta has not this functionality enabled"); + }, + + formatValuesErrorUnitsScientificFormat : function(val, error, unit, args) { + var fontSize = 14; + var decimals = 2; + var errorFontSize = 10; + /** line break **/ + var lineBreak = true; + if (args != null) { + if (args.fontSize != null) { + fontSize = args.fontSize; + } + if (args.decimals != null) { + decimals = args.decimals; + } + if (args.errorFontSize != null) { + errorFontSize = args.errorFontSize; + } + if (args.lineBreak != null) { + lineBreak = args.lineBreak; + } + + } + + if (val == "") { + return ""; + } + if (error == null) { + return "" + Number(val).toFixed(decimals) + ""; + } + var html = "" + Number(val).toFixed(decimals) + " " + unit + ""; + if (lineBreak) { + html = html + "
"; + } + return html + " ± " + Number(Number(error).toFixed(3)).toExponential() + + ""; + }, + + formatValuesErrorUnits : function(val, error, unit, args) { + var fontSize = 16; + var decimals = 2; + var errorFontSize = 10; + /** line break **/ + var lineBreak = true; + if (args != null) { + if (args.fontSize != null) { + fontSize = args.fontSize; + } + if (args.decimals != null) { + decimals = args.decimals; + } + if (args.errorFontSize != null) { + errorFontSize = args.errorFontSize; + } + if (args.lineBreak != null) { + lineBreak = args.lineBreak; + } + + } + + if (val == "") { + return ""; + } + if (error == null) { + return "" + Number(val).toFixed(decimals) + ""; + } + var html = "" + Number(val).toFixed(decimals) + " " + unit + ""; + if (lineBreak) { + html = html + "
"; + } + return html + " ± " + Number(Number(error).toFixed(8)).toExponential() + + ""; + }, + + formatValuesUnits : function(val, unit, args) { + var fontSize = 12; + var decimals = 2; + var unitsFontSize = 10; + if (args != null) { + if (args.fontSize != null) { + fontSize = args.fontSize; + } + if (args.decimals != null) { + decimals = args.decimals; + } + if (args.unitsFontSize != null) { + unitsFontSize = args.unitsFontSize; + } + + } + + if (val === "") { + return ""; + } + if (unit == null) { + return "" + Number(val).toFixed(decimals) + ""; + } + return "" + Number(val).toFixed(decimals) + " " + unit + ""; + }, + + getGreenButton : function(text, args) { + var width = 70; + var height = 20; + if (args != null) { + if (args.width != null) { + width = args.width; + } + if (args.height != null) { + height = args.height; + } + } + + return ''; + }, +// getBlueButton : function(text, args) { +// var width = 70; +// var height = 20; +// if (args != null) { +// if (args.width != null) { +// width = args.width; +// } +// if (args.height != null) { +// height = args.height; +// } +// } +// +// return ''; +// }, + + getBlueButton : function(text, args) { + var width = 70; + var height = 20; + var href = null; + if (args != null) { + if (args.width != null) { + width = args.width; + } + if (args.height != null) { + height = args.height; + } + if (args.href != null) { + href = args.href; + } + } + if (href != null) { + return ''; + } else { + return ''; +// return ''; + } + }, + + getSubmitGreenButton : function(text) { + return ''; + }, + + getRedButton : function(text) { + return ''; + + }, + getRectangleColorDIV : function(color, height, width) { + return '
'; + }, + + openBufferWindow : function(bufferId) { + var window = new BufferWindow(); + window.draw(tabs.experiment.getBufferById(bufferId), tabs.experiment); + }, + + /** Render for safety levels on grids **/ + safetyRenderer : function(val, y, specimen) { + var color = val; + if (val == "YELLOW") { + color = "#E9AB17"; + } + return '' + val + ''; + }, + + getSampleColor : function() { + return '#CB181D'; + }, + + getLightSampleColor : function() { + return '#FCBBA1'; + }, + + getBufferColor : function() { + return '#6A51A3'; + }, + getLightBufferColor : function() { + return '#BCBDDC'; + }, + + formatConcentration : function(val) { + if (val != null) { + return "" + Number(val).toFixed(2) + " mg/ml "; + } + return val; + }, + + formatVolume : function(sample, volume) { + if (Number(sample.data.volumeToLoad) > Number(sample.data.volume)) { + return "" + volume + " �l"; + } + if (Number(sample.data.volumeToLoad) == Number(sample.data.volume)) { + return "" + volume + " �l"; + } + return "" + volume + " �l"; + }, + + getProposal : function() { + return new Proposal(); + }, + + getSampleNameRenderer : function(val, y, record) { + var sample = record.data; + if (record.raw.macromolecule3VO == null) { + return '' + sample.code + ''; + } else { + return '' + sample.code + ''; + } + }, + + getSafetyLevels : function() { + var safetyLevels = new Array(); + safetyLevels.push({ + safetyLevelId : "", + name : "UNKNOWN" + }); + safetyLevels.push({ + safetyLevelId : 1, + name : "GREEN" + }); + safetyLevels.push({ + safetyLevelId : 2, + name : "YELLOW" + }); + safetyLevels.push({ + safetyLevelId : 3, + name : "RED" + }); + return safetyLevels; + }, + + getErrorColor : function() { + return '#F6CED8'; + }, + getWarningColor : function() { + return '#F5DA81'; + }, + getValidColor : function() { + return '#E0F8E0'; + }, + getSamplePlateLetters : function() { + return [ "A", "B", "C", "D", "E", "F", "G", "H" ]; + }, + getSamplePositionHTML : function(sample, experiment) { + var plate = ""; + var row = ""; + var column = ""; + var rows = this.getSamplePlateLetters(); + if (sample.sampleplateposition3VO != null) { + var samplePlate = experiment.getSamplePlateById(sample.sampleplateposition3VO.samplePlateId); + if (samplePlate != null) { + plate = (samplePlate.slotPositionColumn); + row = (sample.sampleplateposition3VO.rowNumber); + column = (sample.sampleplateposition3VO.columnNumber); + // var html = "Plate: " + "" + plate + ""; + // html = html + ", Row: " + "" + rows[row - 1] + ""; + // html = html + ", Column: " + "" + column + ""; + var html = "Plate: " + plate + + "-" + rows[row - 1] + "" + column + ""; + return html; + } + } + return ""; + }, + + getSafetyLabelName : function(safetyLevelId) { + var safetyLevels = BUI.getSafetyLevels(); + for ( var i = 0; i < safetyLevels.length; i++) { + if (safetyLevels[i].safetyLevelId == safetyLevelId) { + return safetyLevels[i].name; + } + } + return "UNKNOWN"; + }, + /** generate random id **/ + id : function() { + var text = ""; + var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + + for ( var i = 0; i < 5; i++) + text += possible.charAt(Math.floor(Math.random() * possible.length)); + + return text; + }, + showWarning : function(message) { + Ext.Msg.show({ + title : 'Warning', + msg : message, + icon : Ext.Msg.WARNING, + animEl : 'elId' + }); + }, + showError : function(message) { + Ext.Msg.show({ + title : 'Warning', + msg : message, + icon : Ext.Msg.ERROR, + animEl : 'elId' + }); + }, + getTipHTML : function(message) { + //return "
Tip
" + // + message + "
"; + return "
Tip
" + + message + "
"; + }, + + getWarningHTML : function(message) { + return "
Warning
" + + message + "
"; + }, + + getErrorHTML : function(message) { + return "
Error
" + + message + "
"; + }, + + getProgessBar : function(percentage, text) { + /** percentage 100% green **/ + var color = "#0a0"; + + color = "#99CC00"; + if (percentage > 100) { + color = "yellow"; + percentage = 100; + } + if (isNaN(percentage)) { + color = "white"; + percentage = 0; + } + + var defaultText = percentage + "%"; + if (text != null) { + defaultText = text; + } + + return "
" + defaultText + "
"; + }, + getFileName : function(filePath){ + if (filePath != null){ + return filePath.split("/")[filePath.split("/").length - 1] + } + return ""; + } + +}; + +Ext.ux.form.RequiredCombo = Ext.extend(Ext.form.ComboBox, { + config : { + cls : 'custom-field-text-required' + }, + initComponent : function() { + Ext.ux.form.RequiredCombo.superclass.initComponent.apply(this, arguments); + }, + checkChange : function() { + if ((this.getValue() == null) || String(this.getValue()).length == 0) { + this.addCls('custom-field-text-required'); + } else { + this.removeCls('custom-field-text-required'); + } + } +}); + +Ext.define('Ext.form.field.RequiredNumber', { + extend : 'Ext.form.field.Number', + alias : 'widget.requirednumberfield', + alternateClassName : [ 'Ext.form.RequiredNumberField', 'Ext.form.RequiredNumber' ], + config : { + cls : 'custom-field-text-required' + }, + + initComponent : function() { + var me = this; + me.callParent(); + me.setMinValue(me.minValue); + me.setMaxValue(me.maxValue); + }, + + onChange : function() { + if ((this.getValue() == null) || String(this.getValue()).length == 0) { + this.addCls('custom-field-text-required'); + } else { + this.removeCls('custom-field-text-required'); + } + this.toggleSpinners(); + this.callParent(arguments); + } +}); + +Ext.define('Ext.form.field.RequiredText', { + extend : 'Ext.form.field.Text', + alias : 'widget.requiredtext', + requires : [ 'Ext.form.field.VTypes', 'Ext.layout.component.field.Text' ], + alternateClassName : [ 'Ext.form.RequiredTextField', 'Ext.form.RequiredText' ], + config : { + cls : 'custom-field-text-required' + }, + initComponent : function() { + var me = this; + if (me.allowOnlyWhitespace === false) { + me.allowBlank = false; } - }; -}; + me.callParent(); + me.addEvents( + /** + * @event autosize + * Fires when the **{@link #autoSize}** function is triggered and the field is resized according to the + * {@link #grow}/{@link #growMin}/{@link #growMax} configs as a result. This event provides a hook for the + * developer to apply additional logic at runtime to resize the field if needed. + * @param {Ext.form.field.Text} this This text field + * @param {Number} width The new field width + */ + 'autosize', -ConcentrationHTMLTableWidget.prototype.test = function(targetId) { - var concentrationHTMLTableWidget = new ConcentrationHTMLTableWidget(); - document.getElementById(targetId).innerHTML = concentrationHTMLTableWidget.getPanel(concentrationHTMLTableWidget.input().data); -}; - -function DataCollectionWidget() { + /** + * @event keydown + * Keydown input field event. This event only fires if **{@link #enableKeyEvents}** is set to true. + * @param {Ext.form.field.Text} this This text field + * @param {Ext.EventObject} e + */ + 'keydown', + /** + * @event keyup + * Keyup input field event. This event only fires if **{@link #enableKeyEvents}** is set to true. + * @param {Ext.form.field.Text} this This text field + * @param {Ext.EventObject} e + */ + 'keyup', + /** + * @event keypress + * Keypress input field event. This event only fires if **{@link #enableKeyEvents}** is set to true. + * @param {Ext.form.field.Text} this This text field + * @param {Ext.EventObject} e + */ + 'keypress'); + me.addStateEvents('change'); + me.setGrowSizePolicy(); + }, + checkChange : function() { + if ((this.getValue() == null) || String(this.getValue()).length == 0) { + this.addCls('custom-field-text-required'); + } else { + this.removeCls('custom-field-text-required'); + } + } +}); -}; +var BIOSAXS_COMBOMANAGER = { -DataCollectionWidget.prototype.refresh = function(subtractionId) { + getComboMacromoleculeByMacromolecules : function(macromolecules, args) { + var labelWidth = 150; + var margin = "0 0 5 0"; + var width = 300; -}; + if (args != null) { + if (args.labelWidth != null) { + labelWidth = args.labelWidth; + } + if (args.margin != null) { + margin = args.margin; + } + if (args.width != null) { + width = args.width; + } + } -DataCollectionWidget.prototype.getMacromolecule = function(data) { - for (var i = 0; i < data.length; i++) { - if (data[i].macromoleculeId != null) { - return BIOSAXS.proposal.getMacromoleculeById(data[i].macromoleculeId); + var store = Ext.create('Ext.data.Store', { + fields : [ 'macromoleculeId', 'acronym' ], + data : macromolecules, + sorters : [ 'acronym' ] + }); + + return Ext.create('Ext.form.ComboBox', { + fieldLabel : 'Macromolecules', + labelWidth : labelWidth, + width : width, + margin : margin, + store : store, + editable: false, + queryMode : 'local', + displayField : 'acronym', + valueField : 'macromoleculeId' + }); + }, + getComboBuffers : function(buffers, args) { + var labelWidth = 150; + var margin = "0 0 5 0"; + var width = 300; + var fieldLabel = 'Buffer'; + + if (args != null) { + if (args.labelWidth != null) { + labelWidth = args.labelWidth; + } + if (args.margin != null) { + margin = args.margin; + } + if (args.width != null) { + width = args.width; + } + if (args.noLabel != null) { + fieldLabel = null; + } } - } - return null; -}; -DataCollectionWidget.prototype.getMacromoleculeContainer = function(data) { - var macromolecule = this.getMacromolecule(data); + var store = Ext.create('Ext.data.Store', { + fields : [ 'bufferId', 'acronym' ], + data : buffers, + sorters : [ 'acronym' ] + }); - var disabled = false; - if (macromolecule == null) { - disabled = true; - } + return Ext.create('Ext.form.ComboBox', { + fieldLabel : fieldLabel, + labelWidth : labelWidth, + width : width, + margin : margin, + editable: false, + store : store, + queryMode : 'local', + displayField : 'acronym', + valueField : 'bufferId' + }); + }, + getComboSessions : function(sessions, args) { + var labelWidth = 150; + var margin = "0 0 5 0"; + var width = 300; - var acronym = (macromolecule == null ? "" : macromolecule.acronym); - var mm = (macromolecule == null ? "" : macromolecule.molecularMass); - var comments = (macromolecule == null ? "" : macromolecule.comments); + if (args != null) { + if (args.labelWidth != null) { + labelWidth = args.labelWidth; + } + if (args.margin != null) { + margin = args.margin; + } + if (args.width != null) { + width = args.width; + } + } - return Ext.create('Ext.Panel', { - width : 500, - height : 200, - disabled : disabled, - title : "Macromolecule", - layout : 'form', - bodyPadding : 5, - defaultType : 'textfield', - tbar : { - defaultButtonUI : 'default', - items : [ { - xtype : 'button', - text : 'Edit', - cls : 'btn-with-border', - handler : function() { - var macromoleculeWindow = new MacromoleculeWindow(); - macromoleculeWindow.draw(macromolecule); - /** TODO: update when save **/ - } - } ] - }, - items : [ { - fieldLabel : 'Acronym', - name : 'first', - readOnly : true, - value : acronym - }, { - fieldLabel : 'Molecular Mass', - name : 'last', - readOnly : true, - value : mm - }, { - xtype : 'textareafield', - fieldLabel : 'Comments', - value : '', - name : 'last', - readOnly : true, - value : comments - } ] - }); -}; + for ( var i = 0; i < sessions.length; i++) { + sessions[i]["startDateFormatted"] = moment(sessions[i].startDate).format("MMM Do YY"); + sessions[i]["sorter"] = moment(sessions[i].startDate).format("YYYYMMDD"); + } -DataCollectionWidget.prototype.getBufferContainer = function(data) { - var _this = this; - var buffer = BIOSAXS.proposal.getBufferById(data[0].bufferId); + var store = Ext.create('Ext.data.Store', { + fields : [ 'sessionId', 'startDateFormatted', 'beamlineName', 'startDate', 'endDate', 'beamlineOperator' ], + data : sessions, + sorters : [ 'sorter' ] + }); - return Ext.create('Ext.Panel', { - width : 500, - height : 200, - title : "Buffer", - layout : 'form', - // collapsed : true, - // collapsible : true, - bodyPadding : 5, - defaultType : 'textfield', - style : { - padding : '0px 0px 0px 2px' - }, - tbar : { - defaultButtonUI : 'default', - items : [ { - xtype : 'button', - text : 'Edit', - cls : 'btn-with-border', - handler : function() { - var bufferWindow = new BufferWindow(); - bufferWindow.draw(BIOSAXS.proposal.getBufferById(data[0].bufferId)); - /** TODO: update when save **/ - } + return Ext.create('Ext.form.ComboBox', { + fieldLabel : 'Sessions', + labelWidth : labelWidth, + width : width, + margin : margin, + store : store, + queryMode : 'local', + // displayField : 'startDate', + valueField : 'sessionId', + // Template for the dropdown menu. + // Note the use of "x-boundlist-item" class, + // this is required to make the items selectable. + tpl : Ext.create('Ext.XTemplate', '', + '
{startDateFormatted} {beamlineName}
', '
'), + // template for the content inside text field + displayTpl : Ext.create('Ext.XTemplate', '', '{startDateFormatted}', '') - } ] - }, - items : [ { - fieldLabel : 'Buffer', - name : 'acronym', - readOnly : true, - value : buffer.acronym - }, { - fieldLabel : 'Composition', - name : 'last', - readOnly : true, - value : buffer.composition + }); + } +}; + + function GenericWindow(args){ + this.title = "title"; + this.width = 700; + this.height = 500; + this.id = BUI.id(); + + this.close = false; + this.draggable = true; + this.modal = true; + + if (args != null){ + if (args.actions != null){ + this.actions = args.actions; + } + if (args.form != null){ + this.form = args.form; + } + if (args.width != null){ + this.width = args.width; + } + if (args.modal != null){ + this.modal = args.modal; + } + + if (args.height != null){ + this.height = args.height; + } + if (args.title != null){ + this.title = args.title; + } + if (args.form != null){ + this.form = args.form; + } + if (args.close != null){ + this.close = args.close; + } + if (args.draggable != null){ + this.draggable = args.draggable; + } + + } + /** Events **/ + this.onSaved = new Event(this); + }; + + + +GenericWindow.prototype.getButtons = function() { + var _this = this; + + if (this.close){ + return [ { + text : 'Close', + handler : function() { + _this.panel.close(); + } + } ]; + } + else{ + return [ { + text : 'Save', + handler : function() { + _this.save(); + + } }, { - xtype : 'textareafield', - fieldLabel : 'Comments', - value : buffer.comments, - name : 'last', - readOnly : true - - } ] - }); + text : 'Cancel', + handler : function() { + _this.panel.close(); + } + } ]; + } }; -DataCollectionWidget.prototype.getSpecimenContainer = function(data) { - return Ext.create('Ext.container.Container', { - layout : { - type : 'hbox' - }, - style : { - margin : '10px 10px 10px 10px' - }, - border : 0, - style : { - borderColor : '#000000', - borderStyle : 'solid', - borderWidth : '1px' - }, - defaults : { - labelWidth : 80, - // implicitly create Container by specifying xtype - xtype : 'datefield', - flex : 1, - - }, - items : [ this.getMacromoleculeContainer(data), this.getBufferContainer(data) ] - }); +GenericWindow.prototype.save = function (){ + alert("Method save of GenerciWindow class is abstract"); }; -DataCollectionWidget.prototype.getSeparator = function() { - return { - html : "
", - border : 0 - } +GenericWindow.prototype._postRender = function(data, experiment){ + }; -DataCollectionWidget.prototype.getFitStructurePanel = function(data) { - var _this = this; - - return Ext.create('Ext.container.Container', { - layout : { - type : 'hbox' - }, - style : { - margin : '10px 10px 10px 10px' - }, - border : 0, - style : { - borderColor : '#000000', - borderStyle : 'solid', - borderWidth : '1px' - }, - defaults : { - labelWidth : 80, - xtype : 'datefield', - flex : 1 - }, - items : [ - ] - }); +GenericWindow.prototype.draw = function (data, experiment){ + this._render(data, experiment); }; -DataCollectionWidget.prototype.getFitStructurePanelWorkflow = function(data) { - var _this = this; - var macromolecule = this.getMacromolecule(data); - - var items = []; +GenericWindow.prototype.refresh = function(data, experiment){ + this.data = data; + this.experiment = experiment; + this.form.refresh(data, experiment); +}; - /** Adding all the pdb files linked to the macromolecule **/ - // if (macromolecule.structure3VOs != null) { - // if (macromolecule.structure3VOs.length > 0) { - // - // items.push(); - // } - // } else { - // items.push({ - // html : "No apriori information added to this macromolecule. Apriori information needed for further analysis", - // margin : "5 5 5 5" - // }); - // } - var fitStructureToExperimentDataGrid = new FitStructureToExperimentDataGrid(); - - fitStructureToExperimentDataGrid.refresh(_this.data[0].subtractionId, _this.getMacromolecule(data)); - return Ext.create('Ext.container.Container', { - style : { - margin : '10px 10px 10px 10px' - }, - border : 0, +GenericWindow.prototype._render = function(data, experiment){ + this.data = data; + var _this = this; + if (this.panel == null){ + this.panel = Ext.create('Ext.Window', { + id : this.id, + title : this.title, + resizable : true, + constrain : true, + border : 1, + modal : this.modal, + frame : false, + draggable : this.draggable, + closable : true, + autoscroll : true, + layout : { type: 'vbox',align: 'stretch'}, + width : this.width, + height : this.form.height, + buttonAlign :'right', + buttons : this.getButtons(), + items : this.form.getPanel(data, experiment), + listeners: { + scope : this, + minimize : function(){ + this.panel.hide(); + }, + destroy : function(){ + delete this.panel; + } + } + }); + this.panel.setLoading(); + } + + this.panel.show(); + this._postRender(); +}; +function CalendarWidget(args) { + this.height = 740; - style : { - borderColor : '#000000', - borderStyle : 'solid', - borderWidth : '1px' - }, - defaults : { - labelWidth : 80, - xtype : 'datefield', - flex : 1 - }, - items : [ fitStructureToExperimentDataGrid.getPanel() ], - listeners : { - afterrender : function() { - } + if (args != null) { + if (args.height != null) { + this.height = args.height; } - }); -}; -/** Static method **/ -DataCollectionWidget.prototype.RUNFitScattering = function(subtractionId, structureId) { - var _this = this; - /** Add to Workflow **/ - var adapter = new BiosaxsDataAdapter(); - var workflow = { - 'workflowTitle' : 'FitExperimentalDatatoStructure', - 'comments' : 'FitExperimentalDatatoStructure run from ISPyB for subtractionId: ' + subtractionId + ' and structureId ' + structureId } - var inputs = [ { - name : 'subtractionId', - value : subtractionId - }, { - name : 'structureId', - value : structureId - } ]; + this.onClick = new Event(); +} - adapter.onSuccess.attach(function(sender, data) { - /** Add to Fit **/ - var adapter2 = new BiosaxsDataAdapter(); - var fit = { - 'workflowId' : data.workflowId, - 'subtractionId' : subtractionId, - 'structureId' : structureId, - 'comments' : 'Sent to workflow engine' - } - adapter2.onSuccess.attach(function(sender, fit) { +CalendarWidget.prototype.loadData = function(data) { + this.events = []; - }); - adapter2.addFitStructureData(fit); + for ( var i = 0; i < data.length; i++) { + var date = moment(data[i].creationDate); + var textColor = 'black'; - }); - adapter.addWorkflow(workflow, inputs); + if (data[i].status == "FINISHED") { + textColor = 'green'; + } + if (data[i].status == "ABORTED") { + textColor = 'red'; + } + this.events.push({ + title : data[i].name, + start : date.format("YYYY-MM-DD HH:mm:ss"), + end : date.format("YYYY-MM-DD HH:mm:ss"), + date : date, + allDay : false, + color : textColor, + className : date.format("YYYY-MM-DD") + }); + } }; -DataCollectionWidget.prototype.getSuperpositionTab = function(data) { +CalendarWidget.prototype.draw = function(targetId) { var _this = this; + $('#' + targetId).fullCalendar({ + eventClick : function(calEvent, jsEvent, view) { + _this.onClick.notify(calEvent.className[0]); - this.superpositionGrid = new SuperpositionGrid(); - - return Ext.create('Ext.container.Container', { - style : { - margin : '10px 10px 10px 10px' - }, - border : 0, - style : { - borderColor : '#000000', - borderStyle : 'solid', - borderWidth : '1px' - }, - items : [ _this.superpositionGrid.getPanel() ], - listeners : { - cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { - - }, - afterrender : function() { - /** Getting Rigid bodies **/ - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, result) { - _this.superpositionGrid.refresh(result); - }); - adapter.getSuperpositionBySubtractionId(data[1].subtractionId); - } - } - }); -}; - -DataCollectionWidget.prototype.getAdvancedTab = function(data) { - var _this = this; - return Ext.create('Ext.container.Container', { - style : { - margin : '10px 10px 10px 10px' }, - border : 0, - style : { - borderColor : '#000000', - borderStyle : 'solid', - borderWidth : '1px' + contentHeight : _this.height, + header : { + left : 'prev,next today', + center : 'title', + right : 'month,basicWeek,basicDay' }, - items : [ _this.getFitStructurePanel(data), _this.getFitStructurePanelWorkflow(data) ] + editable : false, + events : this.events }); -}; - -DataCollectionWidget.prototype.getRigiBodyForm = function(data) { - var _this = this; - _this.rigiBodyGrid = new RigidModelGrid(); - return _this.rigiBodyGrid.getPanel(); }; + +/** + * It shows a table with the guinier and gnom data as well as passed and + * discarded measurements + * + * @height + * @showBufferColumns + */ +function ConcentrationHTMLTableWidget(args) { + this.id = BUI.id(); -DataCollectionWidget.prototype.getRigidBodyModelingTab = function(data) { - var _this = this; - return Ext.create('Ext.container.Container', { - style : { - margin : '10px 10px 10px 10px' - }, - border : 0, - style : { - borderColor : '#000000', - borderStyle : 'solid', - borderWidth : '1px' - }, - items : [ _this.getRigiBodyForm(data) ], - listeners : { - cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + this.showBufferColumns = true; - }, - afterrender : function() { - /** Getting Rigid bodies **/ - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, result) { - _this.rigiBodyGrid.refresh(result); - }); - adapter.getRigidBodyModelingBySubtractionId(data[1].subtractionId); - } + if (args != null) { + if (args.height != null) { + this.height = args.height; } - }); -}; - -DataCollectionWidget.prototype.getTabs = function(data) { - var _this = this; - - this.subtractionCurveVisualizer = new SubtractionCurveVisualizer(); - - this.tabs = Ext.createWidget('tabpanel', { - activeTab : 1, - plain : true, - defaults : { - autoScroll : true, - bodyPadding : 10 - }, - items : [ { - title : 'Solution', - items : [ _this.getSpecimenContainer(data) ] - }, { - title : 'Primary Data Reduction', - active : true, - tabConfig : { - xtype : 'tab', - margins : '0 0 0 20', - }, - items : [ _this.subtractionCurveVisualizer.getPanel() ] - }, { - title : 'Abinitio', - items : [ _this.getAbinitioModellingContainer(data), _this.getSeparator(), _this.getModelViz(data) ] - }, { - title : 'Mixtures', - items : [ _this.getAdvancedTab(data) ] - }, { - title : 'Superpositions', - items : [ _this.getSuperpositionTab(data) ] - }, { - title : 'Rigid Body Modeling', - items : [ _this.getRigidBodyModelingTab(data) ] - } ] - }); - return this.tabs; -}; + if (args.showBufferColumns != null) { + this.showBufferColumns = args.showBufferColumns; + } + } +} -DataCollectionWidget.prototype.getPanel = function(data) { - var _this = this; - _this.data = data; - this.panel = Ext.create('Ext.container.Container', { - width : 1000, - layout : { - type : 'vbox', - align : 'stretch', - padding : 5 - }, - items : [ _this.getTabs(data) +ConcentrationHTMLTableWidget.prototype.refresh = function(parsedData) { + document.getElementById(this.id).innerHTML = this.getPanel(parsedData); +}; - ] - }); +ConcentrationHTMLTableWidget.prototype.getPanel = function(parsedData) { + var html = "
"; + html = html + ""; - this.panel.on("afterrender", function() { - _this.subtractionCurveVisualizer.refresh([ _this.data[1].mergeId ], [ _this.data[1].subtractionId ]); - }); - return this.panel; -}; + /** Title * */ + html = html + ""; + html = html + ""; + if (this.showBufferColumns) { + html = html + ""; + } + html = html + ""; + html = html + ""; + html = html + ""; + html = html + ""; -DataCollectionWidget.prototype.getModelViz = function(data) { - this.modelViz = new ModelVisualizerForm({ - height : 800, - width : 1000 - }); - return this.modelViz.getPanel(); -}; + html = html + ""; + html = html + ""; + html = html + ""; + html = html + ""; + html = html + ""; + html = html + ""; + html = html + ""; + html = html + ""; + html = html + ""; -/** - * PRIMARY DATA PROCESSING - */ -DataCollectionWidget.prototype.getPrimaryDataProcessingContainer = function(data) { - var _this = this; - this.primaryDataProcessingGrid = new PrimaryDataProcessingGrid({ - height : 250, - width : 1000, - title : 'Primary Data Processing' + parsedData.concentration.concentrations.sort(function(a, b) { + return a.concentration - b.concentration; }); + /** Row * */ + for ( var i = 0; i < parsedData.concentration.concentrations.length; i++) { + var row = parsedData[i]; - this.primaryDataProcessingGrid.onSelected.attach(function(sender, selected) { - /** Refresh tabs **/ - var averagesId = []; - var subtractionIds = []; - - var subtractionKeys = {}; - for (var i = 0; i < selected.length; i++) { - if (selected[i].mergeId != null) { - averagesId.push(selected[i].mergeId); - } + var tr = ""; + if (i % 2 == 1) { + tr = ""; + } + var goodMeasurements = (parsedData.concentration.concentrations[i].frames.length - parsedData.concentration.concentrations[i].frames_warning); + if (goodMeasurements == 0) { + tr = ""; + } + html = html + tr; + html = html + ""; + if (this.showBufferColumns) { + html = html + ""; + } + html = html + ""; + html = html + ""; + html = html + ""; + html = html + ""; + html = html + ""; + html = html + ""; + html = html + ""; + html = html + ""; + } + return html + "
ConcentrationBufferMeasurementsGuinierGnom
PassedDiscardedRgI0QualityRgdMax
" + parsedData.concentration.concentrations[i].concentration + "" + parsedData.concentration.concentrations[i].bufferAcronym + "" + goodMeasurements + " of " + parsedData.concentration.concentrations[i].frames.length + "" + parsedData.concentration.concentrations[i].frames_warning + "" + BUI.formatValuesErrorUnits(parsedData.concentration.concentrations[i].calculation.rgGuinier.avg, parsedData.concentration.concentrations[i].calculation.rgGuinier.std, "nm", { + lineBreak : false + }) + "" + BUI.formatValuesErrorUnits(parsedData.concentration.concentrations[i].calculation.i0Guinier.avg, parsedData.concentration.concentrations[i].calculation.i0Guinier.std, " ", { + lineBreak : false + }) + "" + BUI.formatValuesErrorUnits(parsedData.concentration.concentrations[i].calculation.quality.avg, parsedData.concentration.concentrations[i].calculation.quality.std, " ", { + lineBreak : false + }) + "" + BUI.formatValuesErrorUnits(parsedData.concentration.concentrations[i].calculation.rgGnom.avg, parsedData.concentration.concentrations[i].calculation.rgGnom.std, " ", { + lineBreak : false + }) + "" + BUI.formatValuesErrorUnits(parsedData.concentration.concentrations[i].calculation.dMax.avg, parsedData.concentration.concentrations[i].calculation.dMax.std, " ", { + lineBreak : false + }) + "
"; +}; - if (selected[i].subtractionId != null) { - if (selected[i].macromoleculeId != null) { - if (subtractionKeys[selected[i].subtractionId] == null) { - subtractionIds.push(selected[i].subtractionId); +ConcentrationHTMLTableWidget.prototype.input = function() { + return { + data : { + "dataCollectionCount" : 4, + "buffers" : [ { + "bufferId" : "422" + } ], + "concentration" : { + "concentrations" : [ { + "concentration" : "7.17", + "id" : "7.1699999999999999", + "bufferId" : "422.00", + "bufferAcronym" : "B1", + "rgGuinier" : [ "5.12723" ], + "frames" : [ { + "total" : "0.515705406286", + "bufferBeforeMeasurementId" : 15045, + "sampleMergeId" : 8176, + "averagedModelId" : null, + "I0" : "183.457461646", + "proposalCode" : "OPD", + "averagedModel" : null, + "bufferAfterMergeId" : 8177, + "framesCount" : "10", + "bufferBeforeMergeId" : 8175, + "averageFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_065_ave.dat", + "bufferAfterMeasurementId" : 15048, + "rgGuinier" : "5.12723", + "subtractedFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_065_sub.dat", + "firstPointUsed" : "17", + "chi2RgFilePath" : null, + "abInitioModelId" : null, + "macromoleculeId" : 649, + "code" : "_4.0_7.17", + "transmission" : "100.0", + "timeStart" : "2013-06-05 15:43:54.348723", + "bufferAcronym" : "B1", + "quality" : "0.853011", + "shapeDeterminationModelId" : null, + "expermientComments" : "[BsxCube] Generated from BsxCube", + "bufferId" : 422, + "isagregated" : "False", + "dmax" : "25.63615", + "bufferBeforeAverageFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_064_ave.dat", + "exposureTemperature" : "4.0", + "subtractionId" : 3377, + "conc" : "7.1699999999999999", + "rapidShapeDeterminationModel" : null, + "experimentCreationDate" : "Jun 5, 2013 3:30:30 PM", + "lastPointUsed" : "36", + "modelListId" : null, + "framesMerge" : "9", + "rapidShapeDeterminationModelId" : null, + "bufferAfterAverageFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_066_ave.dat", + "nsdFilePath" : null, + "bufferAfterFramesMerged" : "10", + "experimentId" : 700, + "macromoleculeAcronym" : "L9", + "i0stdev" : "0.139364435146", + "sampleMeasurementId" : 15047, + "measurementComments" : "[5]", + "priorityLevelId" : 10, + "bufferBeforeFramesMerged" : "10", + "substractionCreationTime" : "Jun 5, 2013 3:46:31 PM", + "proposalNumber" : "29", + "rgGnom" : "5.40297914939", + "volume" : "475.302", + "shapeDeterminationModel" : null, + "comments" : null, + "groupeField" : "L9 B1" + } ], + "frames_warning" : 0, + "calculation" : { + "rgGuinier" : { + "std" : 0, + "sum" : 5.12723, + "avg" : 5.12723, + "validNumber" : 1, + "totalNumber" : 1, + "values" : [ "5.12723" ] + }, + "i0Guinier" : { + "std" : 0, + "sum" : 183.457461646, + "avg" : 183.457461646, + "validNumber" : 1, + "totalNumber" : 1, + "values" : [ "183.457461646" ] + }, + "quality" : { + "std" : 0, + "sum" : 0.853011, + "avg" : 0.853011, + "validNumber" : 1, + "totalNumber" : 1, + "values" : [ "0.853011" ] + }, + "rgGnom" : { + "std" : 0, + "sum" : 5.40297914939, + "avg" : 5.40297914939, + "validNumber" : 1, + "totalNumber" : 1, + "values" : [ "5.40297914939" ] + }, + "dMax" : { + "std" : 0, + "sum" : 25.63615, + "avg" : 25.63615, + "validNumber" : 1, + "totalNumber" : 1, + "values" : [ "25.63615" ] + } + } + }, { + "concentration" : "3.53", + "id" : "3.5299999999999998", + "bufferId" : "422.00", + "bufferAcronym" : "B1", + "rgGuinier" : [ "4.38278" ], + "frames" : [ { + "total" : "0.497164741078", + "bufferBeforeMeasurementId" : 15048, + "sampleMergeId" : 8178, + "averagedModelId" : null, + "I0" : "160.023229462", + "proposalCode" : "OPD", + "averagedModel" : null, + "bufferAfterMergeId" : 8179, + "framesCount" : "10", + "bufferBeforeMergeId" : 8177, + "averageFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_067_ave.dat", + "bufferAfterMeasurementId" : 15051, + "rgGuinier" : "4.38278", + "subtractedFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_067_sub.dat", + "firstPointUsed" : "36", + "chi2RgFilePath" : null, + "abInitioModelId" : null, + "macromoleculeId" : 649, + "code" : "_4.0_3.53", + "transmission" : "100.0", + "timeStart" : "2013-06-05 15:47:04.630129", + "bufferAcronym" : "B1", + "quality" : "0.742825", + "shapeDeterminationModelId" : null, + "expermientComments" : "[BsxCube] Generated from BsxCube", + "bufferId" : 422, + "isagregated" : "False", + "dmax" : "15.33973", + "bufferBeforeAverageFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_066_ave.dat", + "exposureTemperature" : "4.0", + "subtractionId" : 3378, + "conc" : "3.5299999999999998", + "rapidShapeDeterminationModel" : null, + "experimentCreationDate" : "Jun 5, 2013 3:30:30 PM", + "lastPointUsed" : "61", + "modelListId" : null, + "framesMerge" : "10", + "rapidShapeDeterminationModelId" : null, + "bufferAfterAverageFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_068_ave.dat", + "nsdFilePath" : null, + "bufferAfterFramesMerged" : "10", + "experimentId" : 700, + "macromoleculeAcronym" : "L9", + "i0stdev" : "0.1499898017", + "sampleMeasurementId" : 15050, + "measurementComments" : "[6]", + "priorityLevelId" : 12, + "bufferBeforeFramesMerged" : "10", + "substractionCreationTime" : "Jun 5, 2013 3:49:42 PM", + "proposalNumber" : "29", + "rgGnom" : "4.40615474861", + "volume" : "372.656", + "shapeDeterminationModel" : null, + "comments" : null, + "groupeField" : "L9 B1" + } ], + "frames_warning" : 0, + "calculation" : { + "rgGuinier" : { + "std" : 0, + "sum" : 4.38278, + "avg" : 4.38278, + "validNumber" : 1, + "totalNumber" : 1, + "values" : [ "4.38278" ] + }, + "i0Guinier" : { + "std" : 0, + "sum" : 160.023229462, + "avg" : 160.023229462, + "validNumber" : 1, + "totalNumber" : 1, + "values" : [ "160.023229462" ] + }, + "quality" : { + "std" : 0, + "sum" : 0.742825, + "avg" : 0.742825, + "validNumber" : 1, + "totalNumber" : 1, + "values" : [ "0.742825" ] + }, + "rgGnom" : { + "std" : 0, + "sum" : 4.40615474861, + "avg" : 4.40615474861, + "validNumber" : 1, + "totalNumber" : 1, + "values" : [ "4.40615474861" ] + }, + "dMax" : { + "std" : 0, + "sum" : 15.33973, + "avg" : 15.33973, + "validNumber" : 1, + "totalNumber" : 1, + "values" : [ "15.33973" ] + } + } + }, { + "concentration" : "1.75", + "id" : "1.75", + "bufferId" : "422.00", + "bufferAcronym" : "B1", + "rgGuinier" : [], + "frames" : [ { + "total" : "0.5538035065", + "bufferBeforeMeasurementId" : 15051, + "sampleMergeId" : 8180, + "averagedModelId" : null, + "I0" : "146.927428571", + "proposalCode" : "OPD", + "averagedModel" : null, + "bufferAfterMergeId" : 8181, + "framesCount" : "10", + "bufferBeforeMergeId" : 8179, + "averageFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_069_ave.dat", + "bufferAfterMeasurementId" : 15054, + "rgGuinier" : "4.27139", + "subtractedFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_069_sub.dat", + "firstPointUsed" : "33", + "chi2RgFilePath" : null, + "abInitioModelId" : null, + "macromoleculeId" : 649, + "code" : "_4.0_1.75", + "transmission" : "100.0", + "timeStart" : "2013-06-05 15:50:09.395824", + "bufferAcronym" : "B1", + "quality" : "0.765999", + "shapeDeterminationModelId" : null, + "expermientComments" : "[BsxCube] Generated from BsxCube", + "bufferId" : 422, + "isagregated" : "False", + "dmax" : "14.949865", + "bufferBeforeAverageFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_068_ave.dat", + "exposureTemperature" : "4.0", + "subtractionId" : 3379, + "conc" : "1.75", + "rapidShapeDeterminationModel" : null, + "experimentCreationDate" : "Jun 5, 2013 3:30:30 PM", + "lastPointUsed" : "62", + "modelListId" : null, + "framesMerge" : "6", + "rapidShapeDeterminationModelId" : null, + "bufferAfterAverageFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_070_ave.dat", + "nsdFilePath" : null, + "bufferAfterFramesMerged" : "10", + "experimentId" : 700, + "macromoleculeAcronym" : "L9", + "i0stdev" : "0.191914285714", + "sampleMeasurementId" : 15053, + "measurementComments" : "[7]", + "priorityLevelId" : 14, + "bufferBeforeFramesMerged" : "10", + "substractionCreationTime" : "Jun 5, 2013 3:52:31 PM", + "proposalNumber" : "29", + "rgGnom" : "4.28379338749", + "volume" : "356.326", + "shapeDeterminationModel" : null, + "comments" : null, + "groupeField" : "L9 B1" + } ], + "frames_warning" : 1, + "calculation" : { + "rgGuinier" : { + "std" : null, + "sum" : 0, + "avg" : 0, + "validNumber" : 0, + "totalNumber" : 0, + "values" : [] + }, + "i0Guinier" : { + "std" : null, + "sum" : 0, + "avg" : 0, + "validNumber" : 0, + "totalNumber" : 0, + "values" : [] + }, + "quality" : { + "std" : null, + "sum" : 0, + "avg" : 0, + "validNumber" : 0, + "totalNumber" : 0, + "values" : [] + }, + "rgGnom" : { + "std" : null, + "sum" : 0, + "avg" : 0, + "validNumber" : 0, + "totalNumber" : 0, + "values" : [] + }, + "dMax" : { + "std" : null, + "sum" : 0, + "avg" : 0, + "validNumber" : 0, + "totalNumber" : 0, + "values" : [] + } + } + }, { + "concentration" : "0.91", + "id" : "0.91000000000000003", + "bufferId" : "422.00", + "bufferAcronym" : "B1", + "rgGuinier" : [], + "frames" : [ { + "total" : null, + "bufferBeforeMeasurementId" : 15054, + "sampleMergeId" : 8182, + "averagedModelId" : null, + "I0" : "0.0", + "proposalCode" : "OPD", + "averagedModel" : null, + "bufferAfterMergeId" : 8183, + "framesCount" : "10", + "bufferBeforeMergeId" : 8181, + "averageFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_071_ave.dat", + "bufferAfterMeasurementId" : 15057, + "rgGuinier" : null, + "subtractedFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_071_sub.dat", + "firstPointUsed" : "0", + "chi2RgFilePath" : null, + "abInitioModelId" : null, + "macromoleculeId" : 649, + "code" : "_4.0_.91", + "transmission" : "100.0", + "timeStart" : "2013-06-05 15:52:58.133705", + "bufferAcronym" : "B1", + "quality" : "0.0", + "shapeDeterminationModelId" : null, + "expermientComments" : "[BsxCube] Generated from BsxCube", + "bufferId" : 422, + "isagregated" : "False", + "dmax" : null, + "bufferBeforeAverageFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_070_ave.dat", + "exposureTemperature" : "4.0", + "subtractionId" : 3380, + "conc" : "0.91000000000000003", + "rapidShapeDeterminationModel" : null, + "experimentCreationDate" : "Jun 5, 2013 3:30:30 PM", + "lastPointUsed" : "0", + "modelListId" : null, + "framesMerge" : "10", + "rapidShapeDeterminationModelId" : null, + "bufferAfterAverageFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_072_ave.dat", + "nsdFilePath" : null, + "bufferAfterFramesMerged" : "10", + "experimentId" : 700, + "macromoleculeAcronym" : "L9", + "i0stdev" : "0.0", + "sampleMeasurementId" : 15056, + "measurementComments" : "[8]", + "priorityLevelId" : 16, + "bufferBeforeFramesMerged" : "10", + "substractionCreationTime" : "Jun 5, 2013 3:55:35 PM", + "proposalNumber" : "29", + "rgGnom" : null, + "volume" : null, + "shapeDeterminationModel" : null, + "comments" : null, + "groupeField" : "L9 B1" + } ], + "frames_warning" : 1, + "calculation" : { + "rgGuinier" : { + "std" : null, + "sum" : 0, + "avg" : 0, + "validNumber" : 0, + "totalNumber" : 0, + "values" : [] + }, + "i0Guinier" : { + "std" : null, + "sum" : 0, + "avg" : 0, + "validNumber" : 0, + "totalNumber" : 0, + "values" : [] + }, + "quality" : { + "std" : null, + "sum" : 0, + "avg" : 0, + "validNumber" : 0, + "totalNumber" : 0, + "values" : [] + }, + "rgGnom" : { + "std" : null, + "sum" : 0, + "avg" : 0, + "validNumber" : 0, + "totalNumber" : 0, + "values" : [] + }, + "dMax" : { + "std" : null, + "sum" : 0, + "avg" : 0, + "validNumber" : 0, + "totalNumber" : 0, + "values" : [] + } } - subtractionKeys[selected[i].subtractionId] = true; - } - } + } ] + }, + "concentrationLabel" : "7.17 mg/ml ,3.53 mg/ml ,1.75 mg/ml ,0.91 mg/ml " } - - /** Refreshing Primary Data Reduction **/ - _this.subtractionCurveVisualizer.refresh(averagesId, subtractionIds); - - }); - return this.primaryDataProcessingGrid.getPanel(data); + }; }; -/** - * getAbinitioModellingContainer - */ -DataCollectionWidget.prototype.getAbinitioModellingContainer = function(data) { - var _this = this; - - this.abinitioGrid = new AbinitioGrid(); - this.abinitioGrid.onSelected.attach(function(sender, models) { - _this.modelViz.refresh(models); - - }); - /** It may be abinitio models linked to the buffers **/ - var abinitioIdList = []; - for (var i = 0; i < data.length; i++) { - abinitioIdList.push(data[i].abInitioId); - } - - var uniqueIds = []; - $.each(abinitioIdList, function(i, el) { - if ($.inArray(el, uniqueIds) === -1) - uniqueIds.push(el); - }); - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, data) { - _this.abinitioGrid.refresh(data); - }); - adapter.getAbinitioByIdsList(uniqueIds); - return this.abinitioGrid.getPanel([]); +ConcentrationHTMLTableWidget.prototype.test = function(targetId) { + var concentrationHTMLTableWidget = new ConcentrationHTMLTableWidget(); + document.getElementById(targetId).innerHTML = concentrationHTMLTableWidget.getPanel(concentrationHTMLTableWidget.input().data); }; +function DataCollectionWidget() { + +}; + +DataCollectionWidget.prototype.refresh = function(subtractionId) { + +}; + +DataCollectionWidget.prototype.getMacromolecule = function(data) { + for (var i = 0; i < data.length; i++) { + if (data[i].macromoleculeId != null) { + return BIOSAXS.proposal.getMacromoleculeById(data[i].macromoleculeId); + } + } + return null; +}; + +DataCollectionWidget.prototype.getMacromoleculeContainer = function(data) { + var macromolecule = this.getMacromolecule(data); + + var disabled = false; + if (macromolecule == null) { + disabled = true; + } + + var acronym = (macromolecule == null ? "" : macromolecule.acronym); + var mm = (macromolecule == null ? "" : macromolecule.molecularMass); + var comments = (macromolecule == null ? "" : macromolecule.comments); + + return Ext.create('Ext.Panel', { + width : 500, + height : 200, + disabled : disabled, + title : "Macromolecule", + layout : 'form', + bodyPadding : 5, + defaultType : 'textfield', + tbar : { + defaultButtonUI : 'default', + items : [ { + xtype : 'button', + text : 'Edit', + cls : 'btn-with-border', + handler : function() { + var macromoleculeWindow = new MacromoleculeWindow(); + macromoleculeWindow.draw(macromolecule); + /** TODO: update when save **/ + } + } ] + }, + items : [ { + fieldLabel : 'Acronym', + name : 'first', + readOnly : true, + value : acronym + }, { + fieldLabel : 'Molecular Mass', + name : 'last', + readOnly : true, + value : mm + }, { + xtype : 'textareafield', + fieldLabel : 'Comments', + value : '', + name : 'last', + readOnly : true, + value : comments + } ] + }); +}; + +DataCollectionWidget.prototype.getBufferContainer = function(data) { + var _this = this; + var buffer = BIOSAXS.proposal.getBufferById(data[0].bufferId); + + return Ext.create('Ext.Panel', { + width : 500, + height : 200, + title : "Buffer", + layout : 'form', + // collapsed : true, + // collapsible : true, + bodyPadding : 5, + defaultType : 'textfield', + style : { + padding : '0px 0px 0px 2px' + }, + tbar : { + defaultButtonUI : 'default', + items : [ { + xtype : 'button', + text : 'Edit', + cls : 'btn-with-border', + handler : function() { + var bufferWindow = new BufferWindow(); + bufferWindow.draw(BIOSAXS.proposal.getBufferById(data[0].bufferId)); + /** TODO: update when save **/ + } + + } ] + }, + items : [ { + fieldLabel : 'Buffer', + name : 'acronym', + readOnly : true, + value : buffer.acronym + }, { + fieldLabel : 'Composition', + name : 'last', + readOnly : true, + value : buffer.composition + }, { + xtype : 'textareafield', + fieldLabel : 'Comments', + value : buffer.comments, + name : 'last', + readOnly : true + + } ] + }); +}; + +DataCollectionWidget.prototype.getSpecimenContainer = function(data) { + return Ext.create('Ext.container.Container', { + layout : { + type : 'hbox' + }, + style : { + margin : '10px 10px 10px 10px' + }, + border : 0, + style : { + borderColor : '#000000', + borderStyle : 'solid', + borderWidth : '1px' + }, + defaults : { + labelWidth : 80, + // implicitly create Container by specifying xtype + xtype : 'datefield', + flex : 1, + + }, + items : [ this.getMacromoleculeContainer(data), this.getBufferContainer(data) ] + }); +}; + +DataCollectionWidget.prototype.getSeparator = function() { + return { + html : "
", + border : 0 + } +}; + +DataCollectionWidget.prototype.getFitStructurePanel = function(data) { + var _this = this; + + return Ext.create('Ext.container.Container', { + layout : { + type : 'hbox' + }, + style : { + margin : '10px 10px 10px 10px' + }, + border : 0, + style : { + borderColor : '#000000', + borderStyle : 'solid', + borderWidth : '1px' + }, + defaults : { + labelWidth : 80, + xtype : 'datefield', + flex : 1 + }, + items : [ + + ] + }); +}; + +DataCollectionWidget.prototype.getFitStructurePanelWorkflow = function(data) { + var _this = this; + var macromolecule = this.getMacromolecule(data); + + var items = []; + + /** Adding all the pdb files linked to the macromolecule **/ + // if (macromolecule.structure3VOs != null) { + // if (macromolecule.structure3VOs.length > 0) { + // + // items.push(); + // } + // } else { + // items.push({ + // html : "No apriori information added to this macromolecule. Apriori information needed for further analysis", + // margin : "5 5 5 5" + // }); + // } + var fitStructureToExperimentDataGrid = new FitStructureToExperimentDataGrid(); + + fitStructureToExperimentDataGrid.refresh(_this.data[0].subtractionId, _this.getMacromolecule(data)); + return Ext.create('Ext.container.Container', { + style : { + margin : '10px 10px 10px 10px' + }, + border : 0, + + style : { + borderColor : '#000000', + borderStyle : 'solid', + borderWidth : '1px' + }, + defaults : { + labelWidth : 80, + xtype : 'datefield', + flex : 1 + }, + items : [ fitStructureToExperimentDataGrid.getPanel() ], + listeners : { + afterrender : function() { + } + } + }); +}; +/** Static method **/ +DataCollectionWidget.prototype.RUNFitScattering = function(subtractionId, structureId) { + var _this = this; + /** Add to Workflow **/ + var adapter = new BiosaxsDataAdapter(); + var workflow = { + 'workflowTitle' : 'FitExperimentalDatatoStructure', + 'comments' : 'FitExperimentalDatatoStructure run from ISPyB for subtractionId: ' + subtractionId + ' and structureId ' + structureId + } + + var inputs = [ { + name : 'subtractionId', + value : subtractionId + }, { + name : 'structureId', + value : structureId + } ]; + + adapter.onSuccess.attach(function(sender, data) { + /** Add to Fit **/ + var adapter2 = new BiosaxsDataAdapter(); + var fit = { + 'workflowId' : data.workflowId, + 'subtractionId' : subtractionId, + 'structureId' : structureId, + 'comments' : 'Sent to workflow engine' + } + adapter2.onSuccess.attach(function(sender, fit) { + + }); + adapter2.addFitStructureData(fit); + + }); + adapter.addWorkflow(workflow, inputs); + +}; + +DataCollectionWidget.prototype.getSuperpositionTab = function(data) { + var _this = this; + + this.superpositionGrid = new SuperpositionGrid(); + + return Ext.create('Ext.container.Container', { + style : { + margin : '10px 10px 10px 10px' + }, + border : 0, + style : { + borderColor : '#000000', + borderStyle : 'solid', + borderWidth : '1px' + }, + items : [ _this.superpositionGrid.getPanel() ], + listeners : { + cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + + }, + afterrender : function() { + /** Getting Rigid bodies **/ + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, result) { + _this.superpositionGrid.refresh(result); + }); + adapter.getSuperpositionBySubtractionId(data[1].subtractionId); + } + } + }); +}; + +DataCollectionWidget.prototype.getAdvancedTab = function(data) { + var _this = this; + return Ext.create('Ext.container.Container', { + style : { + margin : '10px 10px 10px 10px' + }, + border : 0, + style : { + borderColor : '#000000', + borderStyle : 'solid', + borderWidth : '1px' + }, + items : [ _this.getFitStructurePanel(data), _this.getFitStructurePanelWorkflow(data) ] + }); +}; + +DataCollectionWidget.prototype.getRigiBodyForm = function(data) { + var _this = this; + _this.rigiBodyGrid = new RigidModelGrid(); + return _this.rigiBodyGrid.getPanel(); + +}; + +DataCollectionWidget.prototype.getRigidBodyModelingTab = function(data) { + var _this = this; + return Ext.create('Ext.container.Container', { + style : { + margin : '10px 10px 10px 10px' + }, + border : 0, + style : { + borderColor : '#000000', + borderStyle : 'solid', + borderWidth : '1px' + }, + items : [ _this.getRigiBodyForm(data) ], + listeners : { + cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + + }, + afterrender : function() { + /** Getting Rigid bodies **/ + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, result) { + _this.rigiBodyGrid.refresh(result); + }); + adapter.getRigidBodyModelingBySubtractionId(data[1].subtractionId); + } + } + }); +}; + +DataCollectionWidget.prototype.getTabs = function(data) { + var _this = this; + + this.subtractionCurveVisualizer = new SubtractionCurveVisualizer(); + + this.tabs = Ext.createWidget('tabpanel', { + activeTab : 1, + plain : true, + defaults : { + autoScroll : true, + bodyPadding : 10 + }, + items : [ { + title : 'Solution', + items : [ _this.getSpecimenContainer(data) ] + }, { + title : 'Primary Data Reduction', + active : true, + tabConfig : { + xtype : 'tab', + margins : '0 0 0 20', + }, + items : [ _this.subtractionCurveVisualizer.getPanel() ] + }, { + title : 'Abinitio', + items : [ _this.getAbinitioModellingContainer(data), _this.getSeparator(), _this.getModelViz(data) ] + }, { + title : 'Mixtures', + items : [ _this.getAdvancedTab(data) ] + }, { + title : 'Superpositions', + items : [ _this.getSuperpositionTab(data) ] + }, { + title : 'Rigid Body Modeling', + items : [ _this.getRigidBodyModelingTab(data) ] + } ] + }); + return this.tabs; +}; + +DataCollectionWidget.prototype.getPanel = function(data) { + var _this = this; + _this.data = data; + this.panel = Ext.create('Ext.container.Container', { + width : 1000, + layout : { + type : 'vbox', + align : 'stretch', + padding : 5 + }, + items : [ _this.getTabs(data) + + ] + }); + + this.panel.on("afterrender", function() { + _this.subtractionCurveVisualizer.refresh([ _this.data[1].mergeId ], [ _this.data[1].subtractionId ]); + }); + return this.panel; +}; + +DataCollectionWidget.prototype.getModelViz = function(data) { + this.modelViz = new ModelVisualizerForm({ + height : 800, + width : 1000 + }); + return this.modelViz.getPanel(); +}; + +/** + * PRIMARY DATA PROCESSING + */ +DataCollectionWidget.prototype.getPrimaryDataProcessingContainer = function(data) { + var _this = this; + this.primaryDataProcessingGrid = new PrimaryDataProcessingGrid({ + height : 250, + width : 1000, + title : 'Primary Data Processing' + }); + + this.primaryDataProcessingGrid.onSelected.attach(function(sender, selected) { + /** Refresh tabs **/ + var averagesId = []; + var subtractionIds = []; + + var subtractionKeys = {}; + for (var i = 0; i < selected.length; i++) { + if (selected[i].mergeId != null) { + averagesId.push(selected[i].mergeId); + } + + if (selected[i].subtractionId != null) { + if (selected[i].macromoleculeId != null) { + if (subtractionKeys[selected[i].subtractionId] == null) { + subtractionIds.push(selected[i].subtractionId); + } + subtractionKeys[selected[i].subtractionId] = true; + } + } + } + + /** Refreshing Primary Data Reduction **/ + _this.subtractionCurveVisualizer.refresh(averagesId, subtractionIds); + + }); + return this.primaryDataProcessingGrid.getPanel(data); +}; +/** + * getAbinitioModellingContainer + */ +DataCollectionWidget.prototype.getAbinitioModellingContainer = function(data) { + var _this = this; + + this.abinitioGrid = new AbinitioGrid(); + this.abinitioGrid.onSelected.attach(function(sender, models) { + _this.modelViz.refresh(models); + + }); + /** It may be abinitio models linked to the buffers **/ + var abinitioIdList = []; + for (var i = 0; i < data.length; i++) { + abinitioIdList.push(data[i].abInitioId); + } + + var uniqueIds = []; + $.each(abinitioIdList, function(i, el) { + if ($.inArray(el, uniqueIds) === -1) + uniqueIds.push(el); + }); + + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, data) { + _this.abinitioGrid.refresh(data); + }); + adapter.getAbinitioByIdsList(uniqueIds); + return this.abinitioGrid.getPanel([]); +}; + /** * Edit the information of a buffer * @@ -29163,258 +29163,258 @@ ShippingWidget.prototype.refresh = function() { }; -/** - * Widget container of Specimen grid and samplePlate widget - * Depending of the sample changer layout it may be displayed vertically or horizontally - * - * @param args - * - * #onExperimentChanged It happens when specimen are modified - */ -function SpecimenWidget(args){ - - this.width = 1000; - this.height = 600; - - if (args != null){ - if (args.width != null){ - this.width = args.width; - } - if (args.height != null){ - this.height = args.height; - } - } - - var _this = this; - - /** Specimen Grid **/ - this.specimenGrid = new SpecimenGrid({ - minHeight : 425, - selectionMode : "SINGLE", - editEnabled : false, - updateRowEnabled : true, - width : 900, - showTitle : false - }); - - - this.specimenGrid.onSpecimenChanged.attach(function(sender, specimen) { - _this.experiment.setSpecimenById(specimen); - _this.refresh(_this.experiment); - }); - - this.specimenGrid.onSelected.attach(function(sender, specimens) { - if (specimens.length > 0) { - _this.specimenSelected = specimens[0]; - } else { - _this.specimenSelected = null; - } - _this.samplePlateGroupWidget.selectSpecimens(specimens); - }); - - - /** Sample plate Widget **/ - this.samplePlateGroupWidget = new SamplePlateGroupWidget({ - showTitle : false, - height : 250, - margin : 5, - bbar : true - }); - - - this.samplePlateGroupWidget.onExperimentChanged.attach(function(sender, json) { - _this.refresh(new Experiment(json)); - }); - - this.samplePlateGroupWidget.onClick.attach(function(sender, args) { - /** Clicking on a plate * */ - var row = args.row; - var column = args.column; - var samplePlateId = args.samplePlate.samplePlateId; - - /** is specimen selected on the grid? * */ - if (_this.specimenSelected != null) { - /** Is position target empty * */ - if (_this.experiment.getSampleByPosition(args.samplePlate.samplePlateId, args.row, args.column).length == 0) { - var specimen = _this.experiment.getSampleById(_this.specimenSelected.specimenId); - if (specimen.sampleplateposition3VO == null) { - specimen.sampleplateposition3VO = {}; - } - - specimen.sampleplateposition3VO = { - columnNumber : column, - rowNumber : row, - samplePlateId : samplePlateId - }; - - _this.samplePlateGroupWidget.panel.setLoading("ISPyB: Saving specimen"); - var adapter = new BiosaxsDataAdapter(); - /** If success * */ - adapter.onSuccess.attach(function(sender, experiment) { - _this.samplePlateGroupWidget.panel.setLoading(false); - }); - - adapter.onError.attach(function(sender, error) { - _this.samplePlateGroupWidget.panel.setLoading(false); - showError(error); - }); - - adapter.saveSpecimen(specimen, _this.experiment); - - _this.samplePlateGroupWidget.refresh(_this.experiment); - _this.specimenGrid.refresh(_this.experiment); - //_this.refresh(_this.experiment); - _this.specimenGrid.deselectAll(); - - } else { - /** - * Can we merge? We can merge when specimen are the - * same. So, same buffer, macromolecule, concentration * - */ - var target = _this.experiment.getSampleByPosition(args.samplePlate.samplePlateId, args.row, args.column)[0]; - var specimen = _this.experiment.getSampleById(_this.specimenSelected.specimenId); - - if ((specimen.bufferId == target.bufferId) && (specimen.concentration == target.concentration)) { - if (((specimen.macromolecule3VO != null) && (target.macromolecule3VO != null) && (specimen.macromolecule3VO.macromoleculeId == target.macromolecule3VO.macromoleculeId)) || - ((specimen.macromolecule3VO == null) && (target.macromolecule3VO == null))) { - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, data) { - _this.refresh(new Experiment(data)); - _this.samplePlateGroupWidget.panel.setLoading(false); - - _this.onExperimentChanged.notify(experiment); - }); - adapter.onError.attach(function(sender, error) { - _this.samplePlateGroupWidget.panel.setLoading(false); - showError(error); - }); - _this.samplePlateGroupWidget.panel.setLoading("ISPyB: Merging specimens"); - adapter.mergeSpecimens(specimen.specimenId, target.specimenId); - } - } else { - alert("Well is not empty. Select another well!"); - } - } - } else { - var specimen = _this.experiment.getSampleByPosition(args.samplePlate.samplePlateId, args.row, args.column)[0]; - if (specimen != null) { - _this.specimenGrid.selectById(specimen.specimenId); - } - } - }); - - /** Events **/ - this.onExperimentChanged = new Event(this); -}; - -/** - * Return vbox or hbox depending on the slot positions of the plates - */ -SpecimenWidget.prototype.getContainerLayoutConfiguration = function(experiment){ - var dimensions = this.samplePlateGroupWidget.getDimensions(experiment.getSamplePlates()); - if (dimensions.maxSlotPositionRow < dimensions.maxSlotPositionColumn){ - return { - layout : "vbox", - specimenGridWidth : this.width - 10, - specimenGridHeight : this.height - 260, - samplePlateGroupWidth : this.width - 10, - samplePlateGroupHeight : 250 - }; - } - return { - layout : "hbox", - samplePlateGroupWidth : this.width*1/3 -10, - samplePlateGroupHeight : this.height - 10, - specimenGridWidth : this.width*2/3, - specimenGridHeight : this.height - 10 - }; - -}; - - -SpecimenWidget.prototype.refresh = function(experiment){ - this.experiment = experiment; - - /** Removing all components **/ - this.panel.removeAll(); - - var layoutConfiguration = this.getContainerLayoutConfiguration(experiment); - - /** Setting new width and height for layout vbox and hbox **/ - this.specimenGrid.width = layoutConfiguration.specimenGridWidth; - this.specimenGrid.height = layoutConfiguration.specimenGridHeight; - - this.samplePlateGroupWidget.width = layoutConfiguration.samplePlateGroupWidth; - this.samplePlateGroupWidget.height = layoutConfiguration.samplePlateGroupHeight; - - if (layoutConfiguration.layout == "hbox"){ - this.specimenGrid.margin = "0 0 0 5"; - this.specimenGrid.width =this.specimenGrid.width - 5; - } - /** Insert container depending on layout [vertical|horizontal] */ - var container = Ext.create('Ext.container.Container', { - layout : layoutConfiguration.layout, - height : this.height, - width : this.width, - padding : '2px', - items : [ ] - }); - if (layoutConfiguration.layout == "vbox"){ - container.insert(this.specimenGrid.getPanel()); - container.insert(this.samplePlateGroupWidget.getPanel()); - } - else{ - container.insert(this.samplePlateGroupWidget.getPanel()); - container.insert(this.specimenGrid.getPanel()); - } - - /** Insert Widget **/ - this.panel.insert(container); - - /** Load data **/ - this.specimenGrid.refresh(experiment); - this.samplePlateGroupWidget.refresh(experiment); - - -}; - -/** It creates a dummy container to be inserted the plates once the method refresh has been called - * This is necessay because we can not know the sample changer layout before hand - * **/ -SpecimenWidget.prototype.getPanel = function(){ - this.panel = Ext.create('Ext.container.Container', { - layout : 'vbox', - height : this.height, - border : 0, - margin : 5, - width : this.width, - items : [] - }); - return this.panel; -}; - - -SpecimenWidget.prototype.input = function() { - return { - experiment : DATADOC.getExperiment_10(), - proposal : DATADOC.getProposal_10() - }; -}; - -SpecimenWidget.prototype.test = function(targetId) { - var specimenWidget = new SpecimenWidget({ - height : 500, - width : 1000 - }); - BIOSAXS.proposal = new Proposal(specimenWidget.input().proposal); - var experiment = new Experiment(specimenWidget.input().experiment); - var panel = specimenWidget.getPanel(); - panel.render(targetId); - specimenWidget.refresh(experiment); - -}; - - +/** + * Widget container of Specimen grid and samplePlate widget + * Depending of the sample changer layout it may be displayed vertically or horizontally + * + * @param args + * + * #onExperimentChanged It happens when specimen are modified + */ +function SpecimenWidget(args){ + + this.width = 1000; + this.height = 600; + + if (args != null){ + if (args.width != null){ + this.width = args.width; + } + if (args.height != null){ + this.height = args.height; + } + } + + var _this = this; + + /** Specimen Grid **/ + this.specimenGrid = new SpecimenGrid({ + minHeight : 425, + selectionMode : "SINGLE", + editEnabled : false, + updateRowEnabled : true, + width : 900, + showTitle : false + }); + + + this.specimenGrid.onSpecimenChanged.attach(function(sender, specimen) { + _this.experiment.setSpecimenById(specimen); + _this.refresh(_this.experiment); + }); + + this.specimenGrid.onSelected.attach(function(sender, specimens) { + if (specimens.length > 0) { + _this.specimenSelected = specimens[0]; + } else { + _this.specimenSelected = null; + } + _this.samplePlateGroupWidget.selectSpecimens(specimens); + }); + + + /** Sample plate Widget **/ + this.samplePlateGroupWidget = new SamplePlateGroupWidget({ + showTitle : false, + height : 250, + margin : 5, + bbar : true + }); + + + this.samplePlateGroupWidget.onExperimentChanged.attach(function(sender, json) { + _this.refresh(new Experiment(json)); + }); + + this.samplePlateGroupWidget.onClick.attach(function(sender, args) { + /** Clicking on a plate * */ + var row = args.row; + var column = args.column; + var samplePlateId = args.samplePlate.samplePlateId; + + /** is specimen selected on the grid? * */ + if (_this.specimenSelected != null) { + /** Is position target empty * */ + if (_this.experiment.getSampleByPosition(args.samplePlate.samplePlateId, args.row, args.column).length == 0) { + var specimen = _this.experiment.getSampleById(_this.specimenSelected.specimenId); + if (specimen.sampleplateposition3VO == null) { + specimen.sampleplateposition3VO = {}; + } + + specimen.sampleplateposition3VO = { + columnNumber : column, + rowNumber : row, + samplePlateId : samplePlateId + }; + + _this.samplePlateGroupWidget.panel.setLoading("ISPyB: Saving specimen"); + var adapter = new BiosaxsDataAdapter(); + /** If success * */ + adapter.onSuccess.attach(function(sender, experiment) { + _this.samplePlateGroupWidget.panel.setLoading(false); + }); + + adapter.onError.attach(function(sender, error) { + _this.samplePlateGroupWidget.panel.setLoading(false); + showError(error); + }); + + adapter.saveSpecimen(specimen, _this.experiment); + + _this.samplePlateGroupWidget.refresh(_this.experiment); + _this.specimenGrid.refresh(_this.experiment); + //_this.refresh(_this.experiment); + _this.specimenGrid.deselectAll(); + + } else { + /** + * Can we merge? We can merge when specimen are the + * same. So, same buffer, macromolecule, concentration * + */ + var target = _this.experiment.getSampleByPosition(args.samplePlate.samplePlateId, args.row, args.column)[0]; + var specimen = _this.experiment.getSampleById(_this.specimenSelected.specimenId); + + if ((specimen.bufferId == target.bufferId) && (specimen.concentration == target.concentration)) { + if (((specimen.macromolecule3VO != null) && (target.macromolecule3VO != null) && (specimen.macromolecule3VO.macromoleculeId == target.macromolecule3VO.macromoleculeId)) || + ((specimen.macromolecule3VO == null) && (target.macromolecule3VO == null))) { + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, data) { + _this.refresh(new Experiment(data)); + _this.samplePlateGroupWidget.panel.setLoading(false); + + _this.onExperimentChanged.notify(experiment); + }); + adapter.onError.attach(function(sender, error) { + _this.samplePlateGroupWidget.panel.setLoading(false); + showError(error); + }); + _this.samplePlateGroupWidget.panel.setLoading("ISPyB: Merging specimens"); + adapter.mergeSpecimens(specimen.specimenId, target.specimenId); + } + } else { + alert("Well is not empty. Select another well!"); + } + } + } else { + var specimen = _this.experiment.getSampleByPosition(args.samplePlate.samplePlateId, args.row, args.column)[0]; + if (specimen != null) { + _this.specimenGrid.selectById(specimen.specimenId); + } + } + }); + + /** Events **/ + this.onExperimentChanged = new Event(this); +}; + +/** + * Return vbox or hbox depending on the slot positions of the plates + */ +SpecimenWidget.prototype.getContainerLayoutConfiguration = function(experiment){ + var dimensions = this.samplePlateGroupWidget.getDimensions(experiment.getSamplePlates()); + if (dimensions.maxSlotPositionRow < dimensions.maxSlotPositionColumn){ + return { + layout : "vbox", + specimenGridWidth : this.width - 10, + specimenGridHeight : this.height - 260, + samplePlateGroupWidth : this.width - 10, + samplePlateGroupHeight : 250 + }; + } + return { + layout : "hbox", + samplePlateGroupWidth : this.width*1/3 -10, + samplePlateGroupHeight : this.height - 10, + specimenGridWidth : this.width*2/3, + specimenGridHeight : this.height - 10 + }; + +}; + + +SpecimenWidget.prototype.refresh = function(experiment){ + this.experiment = experiment; + + /** Removing all components **/ + this.panel.removeAll(); + + var layoutConfiguration = this.getContainerLayoutConfiguration(experiment); + + /** Setting new width and height for layout vbox and hbox **/ + this.specimenGrid.width = layoutConfiguration.specimenGridWidth; + this.specimenGrid.height = layoutConfiguration.specimenGridHeight; + + this.samplePlateGroupWidget.width = layoutConfiguration.samplePlateGroupWidth; + this.samplePlateGroupWidget.height = layoutConfiguration.samplePlateGroupHeight; + + if (layoutConfiguration.layout == "hbox"){ + this.specimenGrid.margin = "0 0 0 5"; + this.specimenGrid.width =this.specimenGrid.width - 5; + } + /** Insert container depending on layout [vertical|horizontal] */ + var container = Ext.create('Ext.container.Container', { + layout : layoutConfiguration.layout, + height : this.height, + width : this.width, + padding : '2px', + items : [ ] + }); + if (layoutConfiguration.layout == "vbox"){ + container.insert(this.specimenGrid.getPanel()); + container.insert(this.samplePlateGroupWidget.getPanel()); + } + else{ + container.insert(this.samplePlateGroupWidget.getPanel()); + container.insert(this.specimenGrid.getPanel()); + } + + /** Insert Widget **/ + this.panel.insert(container); + + /** Load data **/ + this.specimenGrid.refresh(experiment); + this.samplePlateGroupWidget.refresh(experiment); + + +}; + +/** It creates a dummy container to be inserted the plates once the method refresh has been called + * This is necessay because we can not know the sample changer layout before hand + * **/ +SpecimenWidget.prototype.getPanel = function(){ + this.panel = Ext.create('Ext.container.Container', { + layout : 'vbox', + height : this.height, + border : 0, + margin : 5, + width : this.width, + items : [] + }); + return this.panel; +}; + + +SpecimenWidget.prototype.input = function() { + return { + experiment : DATADOC.getExperiment_10(), + proposal : DATADOC.getProposal_10() + }; +}; + +SpecimenWidget.prototype.test = function(targetId) { + var specimenWidget = new SpecimenWidget({ + height : 500, + width : 1000 + }); + BIOSAXS.proposal = new Proposal(specimenWidget.input().proposal); + var experiment = new Experiment(specimenWidget.input().experiment); + var panel = specimenWidget.getPanel(); + panel.render(targetId); + specimenWidget.refresh(experiment); + +}; + + function WarningWidget(args) { this.actions = []; From 27ef65ffe3f154ee26acc484bbd5d136ff88dc0b Mon Sep 17 00:00:00 2001 From: delageniere Date: Mon, 16 Oct 2017 15:13:02 +0200 Subject: [PATCH 12/38] rename parameter reportType to nbRows --- .../src/main/webapp/js/ispyb/min/ispyb-bx.js | 42726 ++++++++-------- .../rest/mx/DataCollectionRestWebService.java | 20 +- 2 files changed, 21371 insertions(+), 21375 deletions(-) diff --git a/ispyb-ui/src/main/webapp/js/ispyb/min/ispyb-bx.js b/ispyb-ui/src/main/webapp/js/ispyb/min/ispyb-bx.js index a98a06520..23e07209a 100644 --- a/ispyb-ui/src/main/webapp/js/ispyb/min/ispyb-bx.js +++ b/ispyb-ui/src/main/webapp/js/ispyb/min/ispyb-bx.js @@ -38,321 +38,321 @@ Event.prototype = { } }; -function GenericGraph(args) { - this.width = 600; - this.height = 400; - - this.targetId = null; - /** Free spaces in the borders * */ - this.top = 10; - this.left = 10; - this.bottom = 50; - this.right = 40; - - /** Ruler * */ - this.rulerHeight = 50; - this.rulerWidth = 50; - this.rulerStroke = 2; - this.rulerVerticalMarksNumber = 5; - - this.rulerMaxValue = null; - this.rulerMinValue = null; - - /** plot options * */ - this.plotPoints = true; - this.pointRadius = 2; - this.fillOpacityPoint = 0.2; - this.strokeOpacityPoint = 0.2; - - /** Cluster titles * */ - this.clusterTitleHeight = 20; - this.interClassesSpace = 2; - this.interClustersSpace = 4; - this.fontSize = 7; - - /** - * If true classes and title will be rendender in the rule otherwise will be - * integer values - */ - this.plotHorizontalByCluster = true; - - if (args != null) { - if (args.targetId != null) { - this.targetId = args.targetId; - } - if (args.plotHorizontalByCluster != null) { - this.plotHorizontalByCluster = args.plotHorizontalByCluster; - } - if (args.height != null) { - this.height = args.height; - } - if (args.width != null) { - this.width = args.width; - } - if (args.rulerMinValue != null) { - this.rulerMinValue = args.rulerMinValue; - } - if (args.rulerMaxValue != null) { - this.rulerMaxValue = args.rulerMaxValue; - } - if (args.rulerHeight != null) { - this.rulerHeight = args.rulerHeight; - } - - } -} - -GenericGraph.prototype.calculate = function(data) { - var result = {}; - - var checked = this.cleanArray(data); - - /** sorting array * */ - checked.sort(function(a, b) { - return a - b; - }); - - var median = this.getMedian(checked); - - result.median = median; - result.Q1 = Number(this.getQ1(checked)); - result.Q2 = Number(this.getQ2(checked)); - result.Q3 = Number(this.getQ3(checked)); - result.population = checked; - - result.IQR = Number(result.Q3 - result.Q1); - result.outlier_below_limit = result.Q1 - (1.5 * result.IQR); - result.outlier_above_limit = result.Q3 + (1.5 * result.IQR); - result.below_outliers = this.getBelowOutliers(result.outlier_below_limit, checked); - result.above_outliers = this.getAboveOutliers(result.outlier_above_limit, checked); - - result.min_whisker = this.minValueWhisker(result.outlier_below_limit, result.Q1, checked); - result.max_whisker = this.maxValueWhisker(result.outlier_above_limit, result.Q3, checked); - - return result; -}; - -GenericGraph.prototype.drawSVGVerticalText = function(x, y, text, properties) { - var transform = [ "transform", "translate(" + x + ", " + y + "), rotate(-90) " ]; - properties.push(transform); - SVG.drawText(0, 0, text, this.svg, properties); -}; - -/** Plot the numbers on the axis * */ -GenericGraph.prototype.plotRuler = function(rulerProperties) { - - SVG.drawRectangle(rulerProperties.vertical.x, rulerProperties.vertical.y, rulerProperties.vertical.width, rulerProperties.vertical.height, - this.svg, [ [ "fill", "black" ] ]); - var distance = rulerProperties.vertical.height / (this.rulerVerticalMarksNumber + 1); - for ( var i = 0; i <= this.rulerVerticalMarksNumber + 1; i++) { - var deltaHeight = distance * i; - var aux = rulerProperties.vertical.height - deltaHeight; - - var text = Number((aux / rulerProperties.vertical.height) * (rulerProperties.maxPoint - rulerProperties.minPoint) + rulerProperties.minPoint) - .toFixed(3); - - SVG.drawLine(rulerProperties.horizontal.x, this.top + deltaHeight, rulerProperties.horizontal.x - rulerProperties.markWidth, - this.top + deltaHeight, this.svg, [ [ "stroke", "black" ] ]); - SVG.drawText(this.left, this.top + distance * i + 5, text, this.svg, [ [ "style", "font-size:xx-small" ] ]); - /** Drawing the mark up to the end * */ - SVG.drawLine(rulerProperties.horizontal.x, this.top + deltaHeight, this.width - this.right, this.top + deltaHeight, this.svg, [ - [ "stroke", rulerProperties.markColor ], [ "stroke-width", "0.3" ] ]); - - if (i == this.rulerVerticalMarksNumber + 1) { - SVG.drawLine(rulerProperties.horizontal.x, this.top + deltaHeight, this.width - this.right, this.top + deltaHeight, this.svg, [ [ - "stroke", rulerProperties.markColor ] ]); - } - } - - /** Drawing horizontal rulers * */ - if (!this.plotHorizontalByCluster) { - var width = rulerProperties.horizontal.width; - var ratio = width / (rulerProperties.horizontal.xValues.range); - for ( i = 0; i < rulerProperties.horizontal.xValues.values.length; i++) { - var coorX = rulerProperties.horizontal.xValues.values[i] - rulerProperties.horizontal.xValues.min; - SVG.drawText(rulerProperties.horizontal.x + coorX * ratio, this.height - (this.bottom + (this.clusterTitleHeight)), - rulerProperties.horizontal.xValues.values[i], this.svg, [ [ "style", "font-size:small" ] ]); - } - } -}; - -GenericGraph.prototype.plotAxes = function(properties) { - /** - * Drawing canvas plot-free spaces SVG.drawRectangle(0, this.top, this.left, - * this.height, this.svg, [["fill", "pink"]]); SVG.drawRectangle(this.width - - * this.right, this.top, plot.width, plot.height, this.svg, [["fill", - * "pink"]]); SVG.drawRectangle(0, 0, this.width, this.top, this.svg, - * [["fill", "red"]]); SVG.drawRectangle(0, this.height - this.bottom, - * this.width, this.bottom, this.svg, [["fill", "red"]]); - */ - - /** Drawing ruler Space * */ - this.plotRuler({ - minPoint : Number(properties.minPoint), - maxPoint : Number(properties.maxPoint), - markColor : "black", - markWidth : 20, - vertical : { - x : this.left + this.rulerWidth - this.rulerStroke, - y : this.top, - width : this.rulerStroke, - height : this.height - (this.top + this.bottom + this.clusterTitleHeight) - this.rulerHeight - }, - horizontal : { - x : this.left + this.rulerWidth - this.rulerStroke, - y : this.height - (this.bottom + this.clusterTitleHeight + this.rulerHeight), - width : properties.width, - height : this.rulerStroke, - xValues : properties.xValues - } - }); - -}; - -/** Remove nulls and NaN elements in the array * */ -GenericGraph.prototype.cleanArray = function(data) { - var checked = []; - - /** checking data are numbers * */ - for ( var i = 0; i < data.length; i++) { - if (data[i] != null) { - if (!isNaN(data[i])) { - checked.push(data[i]); - } - } - } - return checked; -}; - -GenericGraph.prototype.refresh = function(data) { - document.getElementById(this.targetId).innerHTML = ""; - this.draw(this.targetId, data); -}; - -GenericGraph.prototype.getClassColor = function(className) { - for ( var i = 0; i < this.data.clusters.length; i++) { - var cluster = this.data.clusters[i]; - for ( var j = 0; j < cluster.classes.length; j++) { - var classes = cluster.classes[j]; - if (classes.name == className) { - if (classes.color != null) { - return classes.color; - } - } - } - } - return "black"; -}; - -GenericGraph.prototype.getDimensions = function(data) { - var results = {}; - var points = []; - - this.data = data; - var classesNumber = 0; - var xValues = []; - - for ( var i = 0; i < data.clusters.length; i++) { - var cluster = data.clusters[i]; - if (!this.plotHorizontalByCluster) { - xValues.push(data.clusters[i].x); - } - for ( var j = 0; j < cluster.classes.length; j++) { - var classes = cluster.classes[j]; - points = points.concat(classes.values); - classesNumber = classesNumber + 1; - } - } - - var checked = this.cleanArray(points); - - checked.sort(function(a, b) { - return a - b; - }); - - results.minPoint = checked[0]; - if (this.rulerMinValue != null) { - results.minPoint = this.rulerMinValue; - } - results.maxPoint = checked[checked.length - 1]; - if (this.rulerMaxValue != null) { - results.maxPoint = this.rulerMaxValue; - } - - results.classesNumber = classesNumber; - results.clusterNumber = data.clusters.length; - - var totalInterClassesFreeSpace = (classesNumber - data.clusters.length) * this.interClassesSpace; - var totalInterClusterFreeSpace = (data.clusters.length) * this.interClustersSpace; - results.classWidth = (this.width - (this.rulerWidth + this.left + this.right + totalInterClassesFreeSpace + totalInterClusterFreeSpace))/ classesNumber; - results.width = this.width - (this.right + this.left) - this.rulerWidth + this.rulerStroke; - - results.xValues = {}; - - xValues.sort(function(a, b) { - return a - b; - }); - results.xValues.values = xValues; - if (xValues.length > 0) { - results.xValues.min = xValues[0]; - results.xValues.max = xValues[xValues.length - 1]; - results.xValues.range = results.xValues.max - results.xValues.min; - } - return results; - -}; - -GenericGraph.prototype.draw = function(targetId, data) { - this.targetId = targetId; - var properties = (this.getDimensions(data)); - this.svg = SVG.createSVGCanvas(document.getElementById(this.targetId), [ [ "width", this.width ], [ "height", this.height ] ]); - this.plotAxes(properties); -}; - -GenericGraph.prototype.getMedian = function(checked) { - /** Calculating median * */ - if (checked.length % 2 == 1) { - return checked[Math.floor(checked.length / 2)]; - } else { - return (Number(checked[(checked.length / 2) - 1]) + Number(checked[checked.length / 2])) / 2; - } -}; - -GenericGraph.prototype.pointToPixel = function(value, boxProperties) { - var ratio = (value - boxProperties.minPoint) / (boxProperties.maxPoint - boxProperties.minPoint); - var pixelLength = boxProperties.height - boxProperties.y + this.top; - return (-1 * ratio) * (pixelLength) + (boxProperties.y + boxProperties.height); -}; - -GenericGraph.prototype.maxValueWhisker = function(aboveLimit, q3, array) { - var points = []; - for ( var i = 0; i < array.length; i++) { - if ((array[i] > q3) && (array[i]) <= aboveLimit) { - points.push(array[i]); - } - } - if (points.length > 0) { - points.sort(function(a, b) { - return a - b; - }); - return points[points.length - 1]; - } - return null; -}; - -GenericGraph.prototype.input = function() { - return DATADOC.getBoxWhikerData(); -}; - -GenericGraph.prototype.test = function(targetId) { - var plot = new GenericGraph({ - targetId : targetId, - height : 350, - width : 450, - maxBoxWidth : 25 - }); - plot.refresh(this.input()); -}; +function GenericGraph(args) { + this.width = 600; + this.height = 400; + + this.targetId = null; + /** Free spaces in the borders * */ + this.top = 10; + this.left = 10; + this.bottom = 50; + this.right = 40; + + /** Ruler * */ + this.rulerHeight = 50; + this.rulerWidth = 50; + this.rulerStroke = 2; + this.rulerVerticalMarksNumber = 5; + + this.rulerMaxValue = null; + this.rulerMinValue = null; + + /** plot options * */ + this.plotPoints = true; + this.pointRadius = 2; + this.fillOpacityPoint = 0.2; + this.strokeOpacityPoint = 0.2; + + /** Cluster titles * */ + this.clusterTitleHeight = 20; + this.interClassesSpace = 2; + this.interClustersSpace = 4; + this.fontSize = 7; + + /** + * If true classes and title will be rendender in the rule otherwise will be + * integer values + */ + this.plotHorizontalByCluster = true; + + if (args != null) { + if (args.targetId != null) { + this.targetId = args.targetId; + } + if (args.plotHorizontalByCluster != null) { + this.plotHorizontalByCluster = args.plotHorizontalByCluster; + } + if (args.height != null) { + this.height = args.height; + } + if (args.width != null) { + this.width = args.width; + } + if (args.rulerMinValue != null) { + this.rulerMinValue = args.rulerMinValue; + } + if (args.rulerMaxValue != null) { + this.rulerMaxValue = args.rulerMaxValue; + } + if (args.rulerHeight != null) { + this.rulerHeight = args.rulerHeight; + } + + } +} + +GenericGraph.prototype.calculate = function(data) { + var result = {}; + + var checked = this.cleanArray(data); + + /** sorting array * */ + checked.sort(function(a, b) { + return a - b; + }); + + var median = this.getMedian(checked); + + result.median = median; + result.Q1 = Number(this.getQ1(checked)); + result.Q2 = Number(this.getQ2(checked)); + result.Q3 = Number(this.getQ3(checked)); + result.population = checked; + + result.IQR = Number(result.Q3 - result.Q1); + result.outlier_below_limit = result.Q1 - (1.5 * result.IQR); + result.outlier_above_limit = result.Q3 + (1.5 * result.IQR); + result.below_outliers = this.getBelowOutliers(result.outlier_below_limit, checked); + result.above_outliers = this.getAboveOutliers(result.outlier_above_limit, checked); + + result.min_whisker = this.minValueWhisker(result.outlier_below_limit, result.Q1, checked); + result.max_whisker = this.maxValueWhisker(result.outlier_above_limit, result.Q3, checked); + + return result; +}; + +GenericGraph.prototype.drawSVGVerticalText = function(x, y, text, properties) { + var transform = [ "transform", "translate(" + x + ", " + y + "), rotate(-90) " ]; + properties.push(transform); + SVG.drawText(0, 0, text, this.svg, properties); +}; + +/** Plot the numbers on the axis * */ +GenericGraph.prototype.plotRuler = function(rulerProperties) { + + SVG.drawRectangle(rulerProperties.vertical.x, rulerProperties.vertical.y, rulerProperties.vertical.width, rulerProperties.vertical.height, + this.svg, [ [ "fill", "black" ] ]); + var distance = rulerProperties.vertical.height / (this.rulerVerticalMarksNumber + 1); + for ( var i = 0; i <= this.rulerVerticalMarksNumber + 1; i++) { + var deltaHeight = distance * i; + var aux = rulerProperties.vertical.height - deltaHeight; + + var text = Number((aux / rulerProperties.vertical.height) * (rulerProperties.maxPoint - rulerProperties.minPoint) + rulerProperties.minPoint) + .toFixed(3); + + SVG.drawLine(rulerProperties.horizontal.x, this.top + deltaHeight, rulerProperties.horizontal.x - rulerProperties.markWidth, + this.top + deltaHeight, this.svg, [ [ "stroke", "black" ] ]); + SVG.drawText(this.left, this.top + distance * i + 5, text, this.svg, [ [ "style", "font-size:xx-small" ] ]); + /** Drawing the mark up to the end * */ + SVG.drawLine(rulerProperties.horizontal.x, this.top + deltaHeight, this.width - this.right, this.top + deltaHeight, this.svg, [ + [ "stroke", rulerProperties.markColor ], [ "stroke-width", "0.3" ] ]); + + if (i == this.rulerVerticalMarksNumber + 1) { + SVG.drawLine(rulerProperties.horizontal.x, this.top + deltaHeight, this.width - this.right, this.top + deltaHeight, this.svg, [ [ + "stroke", rulerProperties.markColor ] ]); + } + } + + /** Drawing horizontal rulers * */ + if (!this.plotHorizontalByCluster) { + var width = rulerProperties.horizontal.width; + var ratio = width / (rulerProperties.horizontal.xValues.range); + for ( i = 0; i < rulerProperties.horizontal.xValues.values.length; i++) { + var coorX = rulerProperties.horizontal.xValues.values[i] - rulerProperties.horizontal.xValues.min; + SVG.drawText(rulerProperties.horizontal.x + coorX * ratio, this.height - (this.bottom + (this.clusterTitleHeight)), + rulerProperties.horizontal.xValues.values[i], this.svg, [ [ "style", "font-size:small" ] ]); + } + } +}; + +GenericGraph.prototype.plotAxes = function(properties) { + /** + * Drawing canvas plot-free spaces SVG.drawRectangle(0, this.top, this.left, + * this.height, this.svg, [["fill", "pink"]]); SVG.drawRectangle(this.width - + * this.right, this.top, plot.width, plot.height, this.svg, [["fill", + * "pink"]]); SVG.drawRectangle(0, 0, this.width, this.top, this.svg, + * [["fill", "red"]]); SVG.drawRectangle(0, this.height - this.bottom, + * this.width, this.bottom, this.svg, [["fill", "red"]]); + */ + + /** Drawing ruler Space * */ + this.plotRuler({ + minPoint : Number(properties.minPoint), + maxPoint : Number(properties.maxPoint), + markColor : "black", + markWidth : 20, + vertical : { + x : this.left + this.rulerWidth - this.rulerStroke, + y : this.top, + width : this.rulerStroke, + height : this.height - (this.top + this.bottom + this.clusterTitleHeight) - this.rulerHeight + }, + horizontal : { + x : this.left + this.rulerWidth - this.rulerStroke, + y : this.height - (this.bottom + this.clusterTitleHeight + this.rulerHeight), + width : properties.width, + height : this.rulerStroke, + xValues : properties.xValues + } + }); + +}; + +/** Remove nulls and NaN elements in the array * */ +GenericGraph.prototype.cleanArray = function(data) { + var checked = []; + + /** checking data are numbers * */ + for ( var i = 0; i < data.length; i++) { + if (data[i] != null) { + if (!isNaN(data[i])) { + checked.push(data[i]); + } + } + } + return checked; +}; + +GenericGraph.prototype.refresh = function(data) { + document.getElementById(this.targetId).innerHTML = ""; + this.draw(this.targetId, data); +}; + +GenericGraph.prototype.getClassColor = function(className) { + for ( var i = 0; i < this.data.clusters.length; i++) { + var cluster = this.data.clusters[i]; + for ( var j = 0; j < cluster.classes.length; j++) { + var classes = cluster.classes[j]; + if (classes.name == className) { + if (classes.color != null) { + return classes.color; + } + } + } + } + return "black"; +}; + +GenericGraph.prototype.getDimensions = function(data) { + var results = {}; + var points = []; + + this.data = data; + var classesNumber = 0; + var xValues = []; + + for ( var i = 0; i < data.clusters.length; i++) { + var cluster = data.clusters[i]; + if (!this.plotHorizontalByCluster) { + xValues.push(data.clusters[i].x); + } + for ( var j = 0; j < cluster.classes.length; j++) { + var classes = cluster.classes[j]; + points = points.concat(classes.values); + classesNumber = classesNumber + 1; + } + } + + var checked = this.cleanArray(points); + + checked.sort(function(a, b) { + return a - b; + }); + + results.minPoint = checked[0]; + if (this.rulerMinValue != null) { + results.minPoint = this.rulerMinValue; + } + results.maxPoint = checked[checked.length - 1]; + if (this.rulerMaxValue != null) { + results.maxPoint = this.rulerMaxValue; + } + + results.classesNumber = classesNumber; + results.clusterNumber = data.clusters.length; + + var totalInterClassesFreeSpace = (classesNumber - data.clusters.length) * this.interClassesSpace; + var totalInterClusterFreeSpace = (data.clusters.length) * this.interClustersSpace; + results.classWidth = (this.width - (this.rulerWidth + this.left + this.right + totalInterClassesFreeSpace + totalInterClusterFreeSpace))/ classesNumber; + results.width = this.width - (this.right + this.left) - this.rulerWidth + this.rulerStroke; + + results.xValues = {}; + + xValues.sort(function(a, b) { + return a - b; + }); + results.xValues.values = xValues; + if (xValues.length > 0) { + results.xValues.min = xValues[0]; + results.xValues.max = xValues[xValues.length - 1]; + results.xValues.range = results.xValues.max - results.xValues.min; + } + return results; + +}; + +GenericGraph.prototype.draw = function(targetId, data) { + this.targetId = targetId; + var properties = (this.getDimensions(data)); + this.svg = SVG.createSVGCanvas(document.getElementById(this.targetId), [ [ "width", this.width ], [ "height", this.height ] ]); + this.plotAxes(properties); +}; + +GenericGraph.prototype.getMedian = function(checked) { + /** Calculating median * */ + if (checked.length % 2 == 1) { + return checked[Math.floor(checked.length / 2)]; + } else { + return (Number(checked[(checked.length / 2) - 1]) + Number(checked[checked.length / 2])) / 2; + } +}; + +GenericGraph.prototype.pointToPixel = function(value, boxProperties) { + var ratio = (value - boxProperties.minPoint) / (boxProperties.maxPoint - boxProperties.minPoint); + var pixelLength = boxProperties.height - boxProperties.y + this.top; + return (-1 * ratio) * (pixelLength) + (boxProperties.y + boxProperties.height); +}; + +GenericGraph.prototype.maxValueWhisker = function(aboveLimit, q3, array) { + var points = []; + for ( var i = 0; i < array.length; i++) { + if ((array[i] > q3) && (array[i]) <= aboveLimit) { + points.push(array[i]); + } + } + if (points.length > 0) { + points.sort(function(a, b) { + return a - b; + }); + return points[points.length - 1]; + } + return null; +}; + +GenericGraph.prototype.input = function() { + return DATADOC.getBoxWhikerData(); +}; + +GenericGraph.prototype.test = function(targetId) { + var plot = new GenericGraph({ + targetId : targetId, + height : 350, + width : 450, + maxBoxWidth : 25 + }); + plot.refresh(this.input()); +}; /** * Using dygraph it plots a chart. Params: targetId, labelsContainerId, args @@ -2087,338 +2087,338 @@ MacromoleculeConditionGrid.prototype.test = function(targetId) { panel.render(targetId); }; - -function ItemGraphFormatter(id, defaultFormat, selectedFormat, overFormat, draggingFormat){ - this.id = id; - this.args = new Object(); - - this.defaultFormat = new ItemFormat(defaultFormat); - - if(selectedFormat != null){ - this.selected = new ItemFormat(selectedFormat); - } - else{ - this.selected = new ItemFormat(defaultFormat); - } - - if(overFormat != null){ - this.over = new ItemFormat(overFormat); - } - else{ - this.over = new ItemFormat(defaultFormat); - } - - if(draggingFormat != null){ - this.dragging = new ItemFormat(draggingFormat); - } - else{ - this.dragging = new ItemFormat(defaultFormat); - } - - //Events - this.stateChanged = new Event(this); - - - //Attaching events - var _this = this; - this._setEvents(); -}; - -ItemGraphFormatter.prototype.getType = function(){ - return this.args.type; -}; - - -ItemGraphFormatter.prototype.toJSON = function(){ - var json = this.args; - json.defaultFormat = this.getDefault().toJSON(); - json.over = this.getOver().toJSON(); - json.selected = this.getSelected().toJSON(); - json.dragging = this.getDragging().toJSON(); - json.id = this.id; - return json; -}; - -ItemGraphFormatter.prototype.loadFromJSON = function(json){ - this.args = json; - this.defaultFormat = new ItemFormat(json.defaultFormat); - this.over = new ItemFormat(json.over); - this.selected = new ItemFormat(json.selected); - this.dragging = new ItemFormat(json.dragging); - this._setEvents(); -}; - -ItemGraphFormatter.prototype._setEvents = function(){ - //Attaching events - var _this = this; - - this.defaultFormat.changed.attach(function (sender, item){ - _this.over.setSize(_this.defaultFormat.getSize()); - _this.selected.setSize(_this.defaultFormat.getSize()); - _this.dragging.setSize(_this.defaultFormat.getSize()); - _this.stateChanged.notify(_this); - }); - - this.selected.changed.attach(function (sender, item){ - _this.stateChanged.notify(_this); - }); - - this.over.changed.attach(function (sender, item){ - _this.stateChanged.notify(_this); - }); - - this.dragging.changed.attach(function (sender, item){ - _this.stateChanged.notify(_this); - }); -}; - -/** Getters **/ -ItemGraphFormatter.prototype.getId = function(){return this.id;}; -ItemGraphFormatter.prototype.getDefault = function(){return this.defaultFormat;}; -ItemGraphFormatter.prototype.getSelected = function(){return this.selected;}; -ItemGraphFormatter.prototype.getOver = function(){return this.over;}; -ItemGraphFormatter.prototype.getDragging = function(){return this.dragging;}; - -function ItemFormat(args){ - this.defaultFormat = new Object(); - this.args = new Object(); - this.args.title = new Object(); - //Defult properties - this.args.visible = true; - this.args.hidden = false; - this.args.stroke = "#000000"; - this.args.strokeOpacity = 0.8; - this.args["stroke-width"] = 1; - this.args.fill = "#000000"; - this.args["fill-opacity"] = 1; - this.args.size = 1; - this.args.opacity = 1; - this.args.fontSize = "8"; - this.args.fontColor = "#000000"; - - /** For directed edge with arrow **/ - //this.args.arrowSize = 1; - - - if (args != null){ - if (args.visible != null){ - this.args.visible = args.visible; - } - if (args.opacity != null){ - this.args.opacity = args.opacity; - } - if (args.size != null){ - this.args.size = args.size; - } - if (args.hidden != null){ - this.args.hidden = args.hidden; - } - if (args.stroke != null){ - this.args.stroke = this._fixColor(args.stroke); - } - if (args.strokeOpacity != null){ - this.args.strokeOpacity = args.strokeOpacity; - } - if (args["stroke-width"]!=null){ - this.args["stroke-width"] = args["stroke-width"]; - } - if (args["fill-opacity"]!=null){ - this.args["fill-opacity"] = args["fill-opacity"]; - } - if (args.shape!=null){ - this.args.shape = args.shape; - } - if (args.fill!=null){ - this.args.fill = this._fixColor(args.fill); - } - - - if (args.title!=null){ - if (args.title.fontSize!=null){ - this.args.title.fontSize = args.title.fontSize; - } - if (args.title.fill!=null){ - this.args.title.fill = this._fixColor(args.title.fill); - } - } - - /** For directed edge with arrow **/ - /*if (args.arrowSize!=null){ - this.args.arrowSize = args.arrowSize; - }*/ - - } - - this.changed = new Event(); -}; - -ItemFormat.prototype._fixColor = function(color){ - var fixed = color; - if (color.indexOf("green") != -1){ - fixed = '#04B431'; - } - - if (color.indexOf("blue") != -1){ - fixed = '#045FB4'; - } - - if (color.indexOf("red") != -1){ - fixed = '#DF0101'; - } - - if (color.indexOf("black") != -1){ - fixed = '#000000'; - } - - if (color.indexOf("white") != -1){ - fixed = '#FFFFFF'; - } - - if (color.indexOf("#") == -1){ - fixed = "#" + color; - } - return fixed; -}; - -ItemFormat.prototype.toJSON = function(){ - if(this.args.strokeOpacity != null){ - this.args["stroke-opacity"] = this.args.strokeOpacity; - delete this.args.strokeOpacity; - } - -// if(this.args.strokeWidth != null){ -// this.args["stroke-width"] = this.args.strokeWidth; -// delete this.args["stroke-width"]; -// } - - if(this.args.title.fontColor != null){ - this.args.title["font-color"] = this.args.title.fontColor; - } - else{ - this.args.title["font-color"] = this.args.fontColor;//;this.args.title.fontColor; - } - - if(this.args.title.fontSize != null){ - this.args.title["font-size"] = this.args.title.fontSize;//;this.args.title.fontColor; - } - else{ - this.args.title["font-size"] = this.args.fontSize;//;this.args.title.fontColor; - } - //return this.args; - return this.args; -}; - -ItemFormat.prototype.getAttribute = function(name){return this.args[name];}; - -//Getters and Setters -ItemFormat.prototype.setVisible = function(visible){ - if (this.args.visible != visible){ - this.args.visible = visible; - this.changed.notify(this); - } -}; - -ItemFormat.prototype.getVisible = function(){return this.args.visible;}; - -ItemFormat.prototype.setHidden = function(hidden){ - if (this.args.hidden != hidden){ - this.args.hidden = hidden; - this.changed.notify(this); - } -}; - -ItemFormat.prototype.getHidden = function(){return this.args.hidden;}; - - -ItemFormat.prototype.setStroke = function(stroke){ - if (this.args.stroke != stroke){ - this.args.stroke = stroke; - this.changed.notify(this); - } -}; - -ItemFormat.prototype.getStroke = function(){return this.args.stroke;}; - -ItemFormat.prototype.setStrokeOpacity = function(strokeOpacity){ - if (this.args.strokeOpacity != strokeOpacity){ - this.args.strokeOpacity = strokeOpacity; - this.changed.notify(this); - } -}; - -ItemFormat.prototype.getStrokeOpacity = function(){return this.args["stroke-opacity"];}; - -ItemFormat.prototype.setStrokeWidth = function(strokeWidth){ - if (this.args["stroke-width"] != strokeWidth){ - this.args["stroke-width"] = strokeWidth; - this.changed.notify(this); - } -}; - - -ItemFormat.prototype.getFillOpacity = function(){return this.args["fill-opacity"];}; - -ItemFormat.prototype.setfillOpacity = function(strokeWidth){ - if (this.args["fill-opacity"] != strokeWidth){ - this.args["fill-opacity"] = strokeWidth; - this.changed.notify(this); - } -}; - - -ItemFormat.prototype.getStrokeWidth = function(){ - return this.args["stroke-width"]; -}; - -ItemFormat.prototype.setFill = function(fill){ - if (this.args.fill != fill){ - this.args.fill = fill; - this.changed.notify(this); - } -}; - -ItemFormat.prototype.getFill = function(){return this.args.fill;}; - -ItemFormat.prototype.setSize = function(size){ - if (this.args.size != size){ - this.args.size = size; - this.changed.notify(this); - } -}; - -ItemFormat.prototype.getSize = function(){return this.args.size;}; - -ItemFormat.prototype.setOpacity = function(opacity){ - if (this.args.opacity != opacity){ - this.args.opacity = opacity; - this.changed.notify(this); - } -}; - -ItemFormat.prototype.getOpacity = function(){return this.args.opacity;}; - -ItemFormat.prototype.getArrowSize = function(){return this.args.arrowSize;}; - -ItemFormat.prototype.setArrowSize = function(arrowSize){ - if (this.args.arrowSize != arrowSize){ - this.args.arrowSize = arrowSize; - this.changed.notify(this); - } -}; - -ItemFormat.prototype.getFontSize = function(){return this.args.title.fontSize;}; - -ItemFormat.prototype.setFontSize = function(fontSize){ - - if (this.args.title.fontSize != fontSize){ - this.args.title.fontSize = fontSize; - this.changed.notify(this); - } -}; - - - - + +function ItemGraphFormatter(id, defaultFormat, selectedFormat, overFormat, draggingFormat){ + this.id = id; + this.args = new Object(); + + this.defaultFormat = new ItemFormat(defaultFormat); + + if(selectedFormat != null){ + this.selected = new ItemFormat(selectedFormat); + } + else{ + this.selected = new ItemFormat(defaultFormat); + } + + if(overFormat != null){ + this.over = new ItemFormat(overFormat); + } + else{ + this.over = new ItemFormat(defaultFormat); + } + + if(draggingFormat != null){ + this.dragging = new ItemFormat(draggingFormat); + } + else{ + this.dragging = new ItemFormat(defaultFormat); + } + + //Events + this.stateChanged = new Event(this); + + + //Attaching events + var _this = this; + this._setEvents(); +}; + +ItemGraphFormatter.prototype.getType = function(){ + return this.args.type; +}; + + +ItemGraphFormatter.prototype.toJSON = function(){ + var json = this.args; + json.defaultFormat = this.getDefault().toJSON(); + json.over = this.getOver().toJSON(); + json.selected = this.getSelected().toJSON(); + json.dragging = this.getDragging().toJSON(); + json.id = this.id; + return json; +}; + +ItemGraphFormatter.prototype.loadFromJSON = function(json){ + this.args = json; + this.defaultFormat = new ItemFormat(json.defaultFormat); + this.over = new ItemFormat(json.over); + this.selected = new ItemFormat(json.selected); + this.dragging = new ItemFormat(json.dragging); + this._setEvents(); +}; + +ItemGraphFormatter.prototype._setEvents = function(){ + //Attaching events + var _this = this; + + this.defaultFormat.changed.attach(function (sender, item){ + _this.over.setSize(_this.defaultFormat.getSize()); + _this.selected.setSize(_this.defaultFormat.getSize()); + _this.dragging.setSize(_this.defaultFormat.getSize()); + _this.stateChanged.notify(_this); + }); + + this.selected.changed.attach(function (sender, item){ + _this.stateChanged.notify(_this); + }); + + this.over.changed.attach(function (sender, item){ + _this.stateChanged.notify(_this); + }); + + this.dragging.changed.attach(function (sender, item){ + _this.stateChanged.notify(_this); + }); +}; + +/** Getters **/ +ItemGraphFormatter.prototype.getId = function(){return this.id;}; +ItemGraphFormatter.prototype.getDefault = function(){return this.defaultFormat;}; +ItemGraphFormatter.prototype.getSelected = function(){return this.selected;}; +ItemGraphFormatter.prototype.getOver = function(){return this.over;}; +ItemGraphFormatter.prototype.getDragging = function(){return this.dragging;}; + +function ItemFormat(args){ + this.defaultFormat = new Object(); + this.args = new Object(); + this.args.title = new Object(); + //Defult properties + this.args.visible = true; + this.args.hidden = false; + this.args.stroke = "#000000"; + this.args.strokeOpacity = 0.8; + this.args["stroke-width"] = 1; + this.args.fill = "#000000"; + this.args["fill-opacity"] = 1; + this.args.size = 1; + this.args.opacity = 1; + this.args.fontSize = "8"; + this.args.fontColor = "#000000"; + + /** For directed edge with arrow **/ + //this.args.arrowSize = 1; + + + if (args != null){ + if (args.visible != null){ + this.args.visible = args.visible; + } + if (args.opacity != null){ + this.args.opacity = args.opacity; + } + if (args.size != null){ + this.args.size = args.size; + } + if (args.hidden != null){ + this.args.hidden = args.hidden; + } + if (args.stroke != null){ + this.args.stroke = this._fixColor(args.stroke); + } + if (args.strokeOpacity != null){ + this.args.strokeOpacity = args.strokeOpacity; + } + if (args["stroke-width"]!=null){ + this.args["stroke-width"] = args["stroke-width"]; + } + if (args["fill-opacity"]!=null){ + this.args["fill-opacity"] = args["fill-opacity"]; + } + if (args.shape!=null){ + this.args.shape = args.shape; + } + if (args.fill!=null){ + this.args.fill = this._fixColor(args.fill); + } + + + if (args.title!=null){ + if (args.title.fontSize!=null){ + this.args.title.fontSize = args.title.fontSize; + } + if (args.title.fill!=null){ + this.args.title.fill = this._fixColor(args.title.fill); + } + } + + /** For directed edge with arrow **/ + /*if (args.arrowSize!=null){ + this.args.arrowSize = args.arrowSize; + }*/ + + } + + this.changed = new Event(); +}; + +ItemFormat.prototype._fixColor = function(color){ + var fixed = color; + if (color.indexOf("green") != -1){ + fixed = '#04B431'; + } + + if (color.indexOf("blue") != -1){ + fixed = '#045FB4'; + } + + if (color.indexOf("red") != -1){ + fixed = '#DF0101'; + } + + if (color.indexOf("black") != -1){ + fixed = '#000000'; + } + + if (color.indexOf("white") != -1){ + fixed = '#FFFFFF'; + } + + if (color.indexOf("#") == -1){ + fixed = "#" + color; + } + return fixed; +}; + +ItemFormat.prototype.toJSON = function(){ + if(this.args.strokeOpacity != null){ + this.args["stroke-opacity"] = this.args.strokeOpacity; + delete this.args.strokeOpacity; + } + +// if(this.args.strokeWidth != null){ +// this.args["stroke-width"] = this.args.strokeWidth; +// delete this.args["stroke-width"]; +// } + + if(this.args.title.fontColor != null){ + this.args.title["font-color"] = this.args.title.fontColor; + } + else{ + this.args.title["font-color"] = this.args.fontColor;//;this.args.title.fontColor; + } + + if(this.args.title.fontSize != null){ + this.args.title["font-size"] = this.args.title.fontSize;//;this.args.title.fontColor; + } + else{ + this.args.title["font-size"] = this.args.fontSize;//;this.args.title.fontColor; + } + //return this.args; + return this.args; +}; + +ItemFormat.prototype.getAttribute = function(name){return this.args[name];}; + +//Getters and Setters +ItemFormat.prototype.setVisible = function(visible){ + if (this.args.visible != visible){ + this.args.visible = visible; + this.changed.notify(this); + } +}; + +ItemFormat.prototype.getVisible = function(){return this.args.visible;}; + +ItemFormat.prototype.setHidden = function(hidden){ + if (this.args.hidden != hidden){ + this.args.hidden = hidden; + this.changed.notify(this); + } +}; + +ItemFormat.prototype.getHidden = function(){return this.args.hidden;}; + + +ItemFormat.prototype.setStroke = function(stroke){ + if (this.args.stroke != stroke){ + this.args.stroke = stroke; + this.changed.notify(this); + } +}; + +ItemFormat.prototype.getStroke = function(){return this.args.stroke;}; + +ItemFormat.prototype.setStrokeOpacity = function(strokeOpacity){ + if (this.args.strokeOpacity != strokeOpacity){ + this.args.strokeOpacity = strokeOpacity; + this.changed.notify(this); + } +}; + +ItemFormat.prototype.getStrokeOpacity = function(){return this.args["stroke-opacity"];}; + +ItemFormat.prototype.setStrokeWidth = function(strokeWidth){ + if (this.args["stroke-width"] != strokeWidth){ + this.args["stroke-width"] = strokeWidth; + this.changed.notify(this); + } +}; + + +ItemFormat.prototype.getFillOpacity = function(){return this.args["fill-opacity"];}; + +ItemFormat.prototype.setfillOpacity = function(strokeWidth){ + if (this.args["fill-opacity"] != strokeWidth){ + this.args["fill-opacity"] = strokeWidth; + this.changed.notify(this); + } +}; + + +ItemFormat.prototype.getStrokeWidth = function(){ + return this.args["stroke-width"]; +}; + +ItemFormat.prototype.setFill = function(fill){ + if (this.args.fill != fill){ + this.args.fill = fill; + this.changed.notify(this); + } +}; + +ItemFormat.prototype.getFill = function(){return this.args.fill;}; + +ItemFormat.prototype.setSize = function(size){ + if (this.args.size != size){ + this.args.size = size; + this.changed.notify(this); + } +}; + +ItemFormat.prototype.getSize = function(){return this.args.size;}; + +ItemFormat.prototype.setOpacity = function(opacity){ + if (this.args.opacity != opacity){ + this.args.opacity = opacity; + this.changed.notify(this); + } +}; + +ItemFormat.prototype.getOpacity = function(){return this.args.opacity;}; + +ItemFormat.prototype.getArrowSize = function(){return this.args.arrowSize;}; + +ItemFormat.prototype.setArrowSize = function(arrowSize){ + if (this.args.arrowSize != arrowSize){ + this.args.arrowSize = arrowSize; + this.changed.notify(this); + } +}; + +ItemFormat.prototype.getFontSize = function(){return this.args.title.fontSize;}; + +ItemFormat.prototype.setFontSize = function(fontSize){ + + if (this.args.title.fontSize != fontSize){ + this.args.title.fontSize = fontSize; + this.changed.notify(this); + } +}; + + + + /** * This class executes the actions @@ -4952,1101 +4952,2095 @@ function openExperiment(experimentId) { BIOSAXS.openExperiment(experimentId); } -var SITE_CONF = { - SAMPLE_CHANGER_CONFIGURATION : { - "3":{ - "platetype3VO": {"plateTypeId":4,"name":"96 Well plate","rowCount":8,"columnCount":12,"shape":"REC"}, - "name":"96 Well plate", - "slotPositionRow":"1", - "slotPositionColumn":"3", - "storageTemperature":"0", - "sampleplateposition3VOs":[], - "experimentId":0 - }, - "2":{ - "platetype3VO":{"plateTypeId":2,"name":" 4 x ( 8 + 3 ) Block","rowCount":4,"columnCount":11,"shape":"REC"}, - "name":" 4 x ( 8 + 3 ) Block", - "slotPositionRow":"1", - "slotPositionColumn":"2", - "storageTemperature":"0", - "sampleplateposition3VOs":[],"experimentId":0 - }, - "1":{ - "platetype3VO":{"plateTypeId":1,"name":"Deep Well","rowCount":8,"columnCount":12,"shape":"REC"}, - "name":"Deep Well", - "slotPositionRow":"1", - "slotPositionColumn":"1", - "storageTemperature":"0", - "sampleplateposition3VOs":[], - "experimentId":0 - } - } +var SITE_CONF = { + SAMPLE_CHANGER_CONFIGURATION : { + "3":{ + "platetype3VO": {"plateTypeId":4,"name":"96 Well plate","rowCount":8,"columnCount":12,"shape":"REC"}, + "name":"96 Well plate", + "slotPositionRow":"1", + "slotPositionColumn":"3", + "storageTemperature":"0", + "sampleplateposition3VOs":[], + "experimentId":0 + }, + "2":{ + "platetype3VO":{"plateTypeId":2,"name":" 4 x ( 8 + 3 ) Block","rowCount":4,"columnCount":11,"shape":"REC"}, + "name":" 4 x ( 8 + 3 ) Block", + "slotPositionRow":"1", + "slotPositionColumn":"2", + "storageTemperature":"0", + "sampleplateposition3VOs":[],"experimentId":0 + }, + "1":{ + "platetype3VO":{"plateTypeId":1,"name":"Deep Well","rowCount":8,"columnCount":12,"shape":"REC"}, + "name":"Deep Well", + "slotPositionRow":"1", + "slotPositionColumn":"1", + "storageTemperature":"0", + "sampleplateposition3VOs":[], + "experimentId":0 + } + } }; - -var ISPYB_CONF = { - load : function(config) { - for (var key in config) { - ISPYB_CONF[key] = config[key]; - } - }, - SAMPLE_CHANGER_CONFIGURATION : { - "1": { - "platetype3VO": {"plateTypeId": 1, "name": "Deep Well", "rowCount": 8, "columnCount": 12, "shape": "REC"}, - "name": "Deep Well", - "slotPositionRow": "1", - "slotPositionColumn": "1", - "storageTemperature": "0", - "sampleplateposition3VOs": [], - "experimentId": 0 - } - } + +var ISPYB_CONF = { + load : function(config) { + for (var key in config) { + ISPYB_CONF[key] = config[key]; + } + }, + SAMPLE_CHANGER_CONFIGURATION : { + "1": { + "platetype3VO": {"plateTypeId": 1, "name": "Deep Well", "rowCount": 8, "columnCount": 12, "shape": "REC"}, + "name": "Deep Well", + "slotPositionRow": "1", + "slotPositionColumn": "1", + "storageTemperature": "0", + "sampleplateposition3VOs": [], + "experimentId": 0 + } + } }; -var SITE_CONF = { - -}; +var SITE_CONF = { + +}; -/** - * Example form - * - * @witdh - * @height - */ -function AbinitioForm(args) { - this.id = BUI.id(); - this.width = null; - this.height = null; - - if (args != null) { - if (args.width != null) { - this.width = args.width; - } - if (args.height != null) { - this.height = args.height; - } - } - - var _this = this; - /** Widgets **/ - this.abinitioGrid = new AbinitioGrid({ - width : null, - height : 200 - }); - - this.abinitioGrid.onSelected.attach(function(sender, models) { - var modelsIdList = []; - for ( var i in models) { - modelsIdList.push(models[i].modelId); - } - _this._renderPlot(_this.subtractions[0].subtractionId, modelsIdList); - _this._renderPDB(modelsIdList); - }); - - /** Dygraph Widget that plots fir files**/ - this.plotWidget = new PlotWidget({ - width : 745 / 2, - height : 300, - margin : "10 0 5 10" - }); - - /** PDB viewer **/ - this.viewer = new PDBViewer({ - width : 745 / 2, - height : 300 - }); - -} - -AbinitioForm.prototype._renderPlot = function(subtractionId, modelsIdList) { - /** Trying to plot tje subtraction and the models **/ - try { - var colors = [ "#669900", "#0000FF" ]; - this.plotWidget.refresh([], [ ], modelsIdList, [], [], colors); - } catch (e) { - console.log(e); - } -}; - -AbinitioForm.prototype._renderPDB = function(modelsIdList) { - /** Trying to plot the PDB file **/ - try { - var viz = []; - for (var i = 0; i < modelsIdList.length; i++) { - viz.push({ - modelId : modelsIdList[i], - color : "0xFF6600", - opacity : 0.8 - }); - } - this.viewer.refresh(viz); - } catch (e) { - console.log(e); - } -}; - -AbinitioForm.prototype._getItems = function() { - var _this = this; - return [ { - xtype : 'label', - forId : 'myFieldId', - text : 'INLINE HELP: To be updated', - margin : '15 0 20 10', - cls : "inline-help" - }, this.abinitioGrid.getPanel(), { - xtype : 'container', - layout : 'hbox', - items : [ this.plotWidget.getPanel(), this.viewer.getPanel() ] - } ] -}; - -AbinitioForm.prototype._getButtons = function() { - return []; -}; - -AbinitioForm.prototype.getPanel = function() { - var _this = this; - this.panel = Ext.create('Ext.form.Panel', { - width : this.width, - height : this.height, - margin : 10, - border : 1, - defaultType : 'textfield', - items : this._getItems(), - buttons : this._getButtons(), - listeners : { - afterrender : function() { - _this._populate(); - - } - } - }); - return this.panel; -}; - -/** Populates could be call when the DOM is not filled yet **/ -AbinitioForm.prototype._populate = function() { -}; - -/** It populates the form * */ -AbinitioForm.prototype.refresh = function(subtractions) { - this.subtractions = subtractions; - this.abinitioGrid.refresh(subtractions); -}; - -AbinitioForm.prototype.input = function() { - return {}; -}; - -/** It populates the form **/ -AbinitioForm.prototype.test = function(targetId) { - var macromoleculeForm = new AbinitioForm(); - var panel = macromoleculeForm.getPanel(); - panel.render(targetId); -}; +/** + * Example form + * + * @witdh + * @height + */ +function AbinitioForm(args) { + this.id = BUI.id(); + this.width = null; + this.height = null; -/** - * Example form - * - * @witdh - * @height - */ -function DataReductionForm(args) { - this.id = BUI.id(); - this.width = null; - this.height = null; - - if (args != null) { - if (args.width != null) { - this.width = args.width; - } - if (args.height != null) { - this.height = args.height; - } - } - + if (args != null) { + if (args.width != null) { + this.width = args.width; + } + if (args.height != null) { + this.height = args.height; + } + } + + var _this = this; + /** Widgets **/ + this.abinitioGrid = new AbinitioGrid({ + width : null, + height : 200 + }); + + this.abinitioGrid.onSelected.attach(function(sender, models) { + var modelsIdList = []; + for ( var i in models) { + modelsIdList.push(models[i].modelId); + } + _this._renderPlot(_this.subtractions[0].subtractionId, modelsIdList); + _this._renderPDB(modelsIdList); + }); + + /** Dygraph Widget that plots fir files**/ + this.plotWidget = new PlotWidget({ + width : 745 / 2, + height : 300, + margin : "10 0 5 10" + }); + + /** PDB viewer **/ + this.viewer = new PDBViewer({ + width : 745 / 2, + height : 300 + }); + +} + +AbinitioForm.prototype._renderPlot = function(subtractionId, modelsIdList) { + /** Trying to plot tje subtraction and the models **/ + try { + var colors = [ "#669900", "#0000FF" ]; + this.plotWidget.refresh([], [ ], modelsIdList, [], [], colors); + } catch (e) { + console.log(e); + } +}; + +AbinitioForm.prototype._renderPDB = function(modelsIdList) { + /** Trying to plot the PDB file **/ + try { + var viz = []; + for (var i = 0; i < modelsIdList.length; i++) { + viz.push({ + modelId : modelsIdList[i], + color : "0xFF6600", + opacity : 0.8 + }); + } + this.viewer.refresh(viz); + } catch (e) { + console.log(e); + } +}; + +AbinitioForm.prototype._getItems = function() { + var _this = this; + return [ { + xtype : 'label', + forId : 'myFieldId', + text : 'INLINE HELP: To be updated', + margin : '15 0 20 10', + cls : "inline-help" + }, this.abinitioGrid.getPanel(), { + xtype : 'container', + layout : 'hbox', + items : [ this.plotWidget.getPanel(), this.viewer.getPanel() ] + } ] +}; + +AbinitioForm.prototype._getButtons = function() { + return []; +}; + +AbinitioForm.prototype.getPanel = function() { + var _this = this; + this.panel = Ext.create('Ext.form.Panel', { + width : this.width, + height : this.height, + margin : 10, + border : 1, + defaultType : 'textfield', + items : this._getItems(), + buttons : this._getButtons(), + listeners : { + afterrender : function() { + _this._populate(); + + } + } + }); + return this.panel; +}; + +/** Populates could be call when the DOM is not filled yet **/ +AbinitioForm.prototype._populate = function() { +}; + +/** It populates the form * */ +AbinitioForm.prototype.refresh = function(subtractions) { + this.subtractions = subtractions; + this.abinitioGrid.refresh(subtractions); +}; + +AbinitioForm.prototype.input = function() { + return {}; +}; + +/** It populates the form **/ +AbinitioForm.prototype.test = function(targetId) { + var macromoleculeForm = new AbinitioForm(); + var panel = macromoleculeForm.getPanel(); + panel.render(targetId); +}; + +/** + * Example form + * + * @witdh + * @height + */ +function DataReductionForm(args) { + this.id = BUI.id(); + this.width = null; + this.height = null; + + if (args != null) { + if (args.width != null) { + this.width = args.width; + } + if (args.height != null) { + this.height = args.height; + } + } + + var _this = this; + + /** Widgets **/ + this.plotWidget = new PlotWidget({ + width : 650, + height : 490 + }); + + /** Selected frames to be displayed **/ + this.selectedItems = { + frames : [], + averages : [], + subtractions : [] + }; + +} + +DataReductionForm.prototype._parseSelectedItemsToIds = function(selectedArray) { + var ids = []; + if (selectedArray != null) { + for (var i = 0; i < selectedArray.length; i++) { + ids.push(selectedArray[i].id); + } + } + return ids; +}; + +DataReductionForm.prototype.onSelectionChanged = function(columnName, selected) { + if (selected != null) { + if (columnName == "Frames") { + this.selectedItems.frames = selected; + this.selectedItems.subtractions = []; + } + if (columnName == "Averages") { + this.selectedItems.averages = selected; + } + if (columnName == "Subtractions") { + this.selectedItems.frames = []; + this.selectedItems.subtractions = selected; + } + } + this.plotWidget.refresh(this._parseSelectedItemsToIds(this.selectedItems.frames), this._parseSelectedItemsToIds(this.selectedItems.subtractions)); +}; + +DataReductionForm.prototype._getFramesExtPanel = function(store, columnName, height) { + var _this = this; + + var selModel = Ext.create('Ext.selection.RowModel', { + allowDeselect : true, + mode : 'MULTI', + listeners : { + selectionchange : function(sm, selections) { + var selected = []; + for (var i = 0; i < selections.length; i++) { + selected.push(selections[i].raw); + } + _this.onSelectionChanged(columnName, selected); + } + } + }); + + return Ext.create('Ext.grid.Panel', { + store : store, + margin : 10, + height : height, + width : 200, + selModel : selModel, + columns : [ { + text : columnName, + dataIndex : 'fileName', + flex : 1 + } ], + viewConfig : { + } + }); +}; + +DataReductionForm.prototype._getFramesPanel = function() { + var fields = [ 'fileName', 'type', 'id' ]; + + this.framesStore = Ext.create('Ext.data.Store', { + fields : fields, + sorters : 'fileName' + }); + + this.averagesStore = Ext.create('Ext.data.Store', { + fields : fields + }); + + this.subtractionStore = Ext.create('Ext.data.Store', { + fields : fields + }); + + var gridFrames = this._getFramesExtPanel(this.framesStore, "Frames", 375); + var gridAvgs = this._getFramesExtPanel(this.averagesStore, "Averages", 125); + var subtractionAvgs = this._getFramesExtPanel(this.subtractionStore, "Subtractions", 75); + + return { + xtype : 'container', + layout : 'vbox', + items : [ gridFrames, subtractionAvgs ] + }; +}; + +DataReductionForm.prototype._getImageContainer = function(name, help) { + var html = "
" + name + "
" + return { + xtype : 'container', + layout : 'vbox', + items : [ { + html : html, + margin : "5 0 0 0", + height : 95, + width : 100 + }, { + xtype : 'label', + forId : 'myFieldId', + text : help, + margin : '5 0 0 0', + cls : "inline-help" + } ] + } +}; + +DataReductionForm.prototype._getItems = function() { + var _this = this; + return [ { + xtype : 'container', + layout : 'hbox', + items : [ + this._getFramesPanel(), + this.plotWidget.getPanel(), + { + xtype : 'panel', + width : 110, + frame : true, + margin : "10 5 5 5", + border : 0, + layout : 'vbox', + items : [ this._getImageContainer("scattering", "Scattering"), this._getImageContainer("guinier", "Guinier Region"), + this._getImageContainer("kratky", "Kratky Plot"), this._getImageContainer("gnom", "P(r) distribution") ] + } ] + } ] +}; + +DataReductionForm.prototype._getButtons = function() { + return []; +}; + +DataReductionForm.prototype.getPanel = function() { + var _this = this; + this.panel = Ext.create('Ext.form.Panel', { + width : this.width, + height : this.height, + border : 0, + items : this._getItems(), + buttons : this._getButtons(), + listeners : { + afterrender : function() { + _this._populate(); + } + } + }); + return this.panel; +}; + +/** Populates could be call when the DOM is not filled yet **/ +DataReductionForm.prototype._populate = function() { +}; + +/** It populates the form * */ +DataReductionForm.prototype.refresh = function(subtractions) { + if (subtractions != null) { + for (var i = 0; i < subtractions.length; i++) { + /** Loading frame grids **/ + var subtraction = subtractions[i]; + var averages = [ { + fileName : BUI.getFileName(subtraction.bufferAverageFilePath), + type : 'bufferAvg', + id : subtraction.subtractionId + }, { + fileName : BUI.getFileName(subtraction.sampleAverageFilePath), + type : 'sampleAvg', + id : subtraction.subtractionId + } + + ]; + this.averagesStore.loadData(averages, true); + this.subtractionStore.loadData([ { + fileName : BUI.getFileName(subtraction.substractedFilePath), + type : 'SUBTRACTION', + id : subtraction.subtractionId + } ], true); + + var frames = []; + /** Buffers **/ + if (subtraction.bufferOneDimensionalFiles != null) { + if (subtraction.bufferOneDimensionalFiles.frametolist3VOs != null) { + for (var j = 0; j < subtraction.bufferOneDimensionalFiles.frametolist3VOs.length; j++) { + var frametolist3VO = subtraction.bufferOneDimensionalFiles.frametolist3VOs[j]; + if (frametolist3VO.frame3VO != null) { + frames.push({ + fileName : BUI.getFileName(frametolist3VO.frame3VO.filePath), + type : 'BUFFER', + id : frametolist3VO.frame3VO.frameId + }); + } + } + } + } + /** Samples **/ + if (subtraction.sampleOneDimensionalFiles != null) { + if (subtraction.sampleOneDimensionalFiles.frametolist3VOs != null) { + for (var j = 0; j < subtraction.sampleOneDimensionalFiles.frametolist3VOs.length; j++) { + var frametolist3VO = subtraction.sampleOneDimensionalFiles.frametolist3VOs[j]; + if (frametolist3VO.frame3VO != null) { + frames.push({ + fileName : BUI.getFileName(frametolist3VO.frame3VO.filePath), + type : 'SAMPLE', + id : frametolist3VO.frame3VO.frameId + }); + } + } + } + } + + this.framesStore.loadData(frames, true); + + /** Loading images **/ + this._displayImage("scattering", subtraction.subtractionId); + this._displayImage("kratky", subtraction.subtractionId); + this._displayImage("guinier", subtraction.subtractionId); + this._displayImage("gnom", subtraction.subtractionId); + } + } +}; + +DataReductionForm.prototype._displayImage = function(name, subtractionId) { + var url = BUI.getURL() + '&type=' + name + '&subtractionId=' + subtractionId; + var event = "OnClick= window.open('" + url + "')"; + document.getElementById(this.id + "_" + name).innerHTML = ''; +}; + +DataReductionForm.prototype.input = function() { + return {}; +}; + +/** It populates the form **/ +DataReductionForm.prototype.test = function(targetId) { + var macromoleculeForm = new DataReductionForm(); + var panel = macromoleculeForm.getPanel(); + panel.render(targetId); +}; + +function PlotWidget(args) { + this.width = 600; + this.height = 600; + this.id = BUI.id(); + + this.linear = false; + + this.margin = "10 0 0 0"; + /** for each stat the max and minimum value when it is scaled in order to show correctly in the legend **/ + this.ranges = {}; + if (args != null) { + if (args.width != null) { + this.width = args.width; + } + if (args.height != null) { + this.height = args.height; + } + if (args.margin != null) { + this.margin = args.margin; + } + } + +} + +PlotWidget.prototype.getMenu = function() { + var _this = this; + /** Actions buttons **/ + var actions = []; + actions.push("->"); + actions.push({ + text : "Export as Image", + scope : this, + icon : '../images/save.gif', + handler : function(item, pressed) { + var largeImage = document.createElement("img"); + largeImage.style.display = 'block'; + largeImage.style.width = 200 + "px"; + largeImage.style.height = 200 + "px"; + largeImage.setAttribute('src', Dygraph.Export.asCanvas(this.dygraphObject.dygraph).toDataURL()); + window.open(Dygraph.Export.asCanvas(this.dygraphObject.dygraph).toDataURL(), 'Image', ''); + } + }); + + return actions; +}; + +/** Looks for the maximum value and then divide everything but that value **/ +PlotWidget.prototype.scaledData = function(data) { + for (var i = 0; i < data.length; i++) { + var values = this.getMaxAndMinValue(data[i]); + data[i] = this.divideValuesByMax(data[i], values.max); + this.ranges[data[i].label] = values; + } + return data; +}; + +/** Given a stat float[] and a max number it will divide each value by max **/ +PlotWidget.prototype.divideValuesByMax = function(stat, max) { + for (var j = 0; j < stat.data.length; j++) { + if (max != 0) { + stat.data[j] = Number(stat.data[j]) / max; + stat.std[j] = Number(stat.std[j]) / max; + } + } + return stat; +}; + +/** returns max value of a stat **/ +PlotWidget.prototype.getMaxAndMinValue = function(stat) { + var max = 0; + var min = stat.data[0]; + for (var j = 0; j < stat.data.length; j++) { + if (Number(stat.data[j]) > max) { + max = Number(stat.data[j]); + } + if (Number(stat.std[j]) > max) { + max = Number(stat.std[j]); + } + if (Number(stat.data[j]) < min) { + min = Number(stat.data[j]); + } + } + return { + max : Number(max), + min : Number(min) + }; +}; + +PlotWidget.prototype._renderDygraph = function(parsed, colors, labels) { + this.dygraphObject = new StdDevDyGraph(this.id, { + width : this.width - 20, + height : this.height - 40, + xlabel : "", + }); + + this.dygraphObject.draw(parsed, colors, labels); + +}; + +PlotWidget.prototype.getPanel = function() { + this.panel = Ext.create('Ext.panel.Panel', { + width : this.width, + height : this.height, + margin : this.margin, + tbar : this.getMenu(), + items : [ { + html : "", + id : this.id, + width : this.width, + height : this.height, + padding : 10, + margin : "0 0 0 -30", + border : 0 + } ] + }); + + return this.panel; +}; + +PlotWidget.prototype.getPoint = function(y, error) { + var minus = y - error; + var max = y + error; + + if (this.linear) { + return [ Math.abs(minus), y, Math.abs(max) ]; + } + if ((minus != 0) && (max != 0)) { + return [ Math.log(Math.abs(minus)), Math.log(y), Math.log(Math.abs(max)) ]; + } else { + return [ Math.log(y), Math.log(y), Math.log(y) ]; + } + +}; + +PlotWidget.prototype.getDataPlot = function(frames, subtractions, models, fits, rbms) { + var files = []; + var labels = [ "Intensity" ]; + if (frames != null) { + for (var i = 0; i < frames.length; i++) { + files.push(frames[i].data); + labels.push(frames[i].fileName); + } + } + function splitData(data, column, errorColumn, name){ + var result = [] + for (var j = 0; j < data.length; j++) { + console.log(data[j][column]); + result.push([j,data[j][column],data[j][errorColumn]]);//[0, data[i][column],0]]); + } + files.push(result); + labels.push(name); + } + + if (subtractions != null) { + for (var i = 0; i < subtractions.length; i++) { + /** For subtraction **/ + files.push(subtractions[i].subtraction.data); + labels.push(subtractions[i].subtraction.fileName); + /** For sample average **/ +// files.push(subtractions[i].sampleAvg.data); +// labels.push(subtractions[i].sampleAvg.fileName); + /** For buffer average **/ +// files.push(subtractions[i].bufferAvg.data); +// labels.push(subtractions[i].bufferAvg.fileName); + } + } + + if (models != null) { + for (var i = 0; i < models.length; i++) { + for ( var key in models[i]) { + splitData(models[i][key].fir.data, 1, 2, "Intensity"); + splitData(models[i][key].fir.data, 3, 3, "Fit"); + } + } + } + + if (fits != null) { + for (var i = 0; i < fits.length; i++) { + for ( var key in fits[i]) { + + /** adding fit file to be plotted **/ + if (fits[i][key].fit.data[0].length == 3){ + splitData(fits[i][key].fit.data, 1, 1, "Intensity"); + splitData(fits[i][key].fit.data, 2, 2, "Fit"); + } + + /** s, Iexp(s), err, Ifit(s). **/ + if (fits[i][key].fit.data[0].length == 4){ + splitData(fits[i][key].fit.data, 1, 2, "Intensity"); + splitData(fits[i][key].fit.data, 3, 3, "Fit"); + } + + if (fits[i][key].fit.data[0].length == 5){ + /** X Intensity Fit Error Residues **/ + + splitData(fits[i][key].fit.data, 1, 3, "Intensity"); + splitData(fits[i][key].fit.data, 2, 2, "Fit"); + } + } + } + } + + var dataPoints = []; + if (files.length > 0) { + for (var i = 0; i < files[0].length; i++) { + dataPoints.push([ files[0][i][0], this.getPoint(files[0][i][1], files[0][i][2]) ]); + } + if (files.length > 1) { + for (var i = 1; i < files.length; i++) { + for (var j = 0; j < dataPoints.length; j++) { + if (files[i][j] != null){ + dataPoints[j].push(this.getPoint(files[i][j][1], files[i][j][2])); + } + else{ + dataPoints[j].push([0,0,0]); + } + } + } + } + } + + return { + dataPoints : dataPoints, + labels : labels + } +}; + +PlotWidget.prototype.refresh = function(frames, subtractions, models, fits, rbms, colors) { + + var _this = this; + this.panel.setLoading("Reading Files"); + + var dataAdapter = new BiosaxsDataAdapter(); + dataAdapter.onSuccess.attach(function(sender, data) { + _this.panel.setLoading("Rendering"); + var parsed = _this.getDataPlot(data.frames, data.subtractions, data.models, data.fits, rbms); + _this._renderDygraph(parsed.dataPoints, colors, parsed.labels); + _this.panel.setLoading(false); + }); + dataAdapter.onError.attach(function(sender, data) { + _this.panel.setLoading(false); + }); + dataAdapter.getDataPlot(frames, subtractions, models, fits, rbms); +}; + +PlotWidget.prototype.input = function() { + return DATADOC.getHPLCData(); +}; + + +/** + * Fit form + * + * @witdh + * @height + */ +function FitForm(args) { + this.id = BUI.id(); + this.width = null; + this.height = null; + + if (args != null) { + if (args.width != null) { + this.width = args.width; + } + if (args.height != null) { + this.height = args.height; + } + } + + var _this = this; + + /** Widgets **/ + this.fitGrid = new FitStructureToExperimentDataGrid({ + width : null, + height : 200 + }); + + this.fitGrid.onSelected.attach(function(sender, fits) { + var modelsIdList = []; + for ( var i in fits) { + modelsIdList.push(fits[i].fitStructureToExperimentalDataId); + } + _this._renderPlot(_this.subtractions[0].subtractionId, modelsIdList); +// var adapter = new BiosaxsDataAdapter(); +// adapter.onSuccess.attach(function(sender, data) { +// +// }); +// adapter.getScatteringCurveByFrameIdsList([], [], [], ids); + }); + + + /** Dygraph Widget that plots fir files**/ + this.plotWidget = new PlotWidget({ + width : 745, + height : 300, + margin : "10 0 10 10" + }); +} + +FitForm.prototype._renderPlot = function(subtractionId, modelsIdList) { + /** Trying to plot tje subtraction and the models **/ + try { + var colors = [ "#669900", "#0000FF" ]; + + this.plotWidget.refresh([], [ ], [], modelsIdList, [], colors); + } catch (e) { + console.log(e); + } +}; + +FitForm.prototype._getItems = function() { + var _this = this; + return [ { + xtype : 'label', + forId : 'myFieldId', + text : 'INLINE HELP: To be updated', + margin : '15 0 20 10', + cls : "inline-help" + }, this.fitGrid.getPanel(), this.plotWidget.getPanel() ] + +}; + +FitForm.prototype._getButtons = function() { + return []; +}; + +FitForm.prototype.getPanel = function() { + var _this = this; + this.panel = Ext.create('Ext.form.Panel', { + width : this.width, + height : this.height, + margin : 10, + border : 1, + defaultType : 'textfield', + items : this._getItems(), + buttons : this._getButtons(), + listeners : { + afterrender : function() { + _this._populate(); + + } + } + }); + return this.panel; +}; + +/** Populates could be call when the DOM is not filled yet **/ +FitForm.prototype._populate = function() { +}; + +/** It populates the form * */ +FitForm.prototype.refresh = function(subtractions) { + this.subtractions = subtractions; + this.fitGrid.refresh(subtractions); +}; + +FitForm.prototype.input = function() { + return {}; +}; + +/** It populates the form **/ +FitForm.prototype.test = function(targetId) { + var macromoleculeForm = new FitForm(); + var panel = macromoleculeForm.getPanel(); + panel.render(targetId); +}; + +/** + * Example form + * + * @witdh + * @height + */ +function RigidBodyModelingResultForm(args) { + this.id = BUI.id(); + this.width = null; + this.height = null; + + if (args != null) { + if (args.width != null) { + this.width = args.width; + } + if (args.height != null) { + this.height = args.height; + } + } + + var _this = this; + /** Widgets **/ + this.rigidModelGrid = new RigidModelGrid({ + width : null, + height : 200 + }); + + this.rigidModelGrid.onSelected.attach(function(sender, fits){ + var ids = []; + for ( var i in fits) { + ids.push(fits[i].fitStructureToExperimentalDataId); + } + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, data){ +// debugger + + }); + + adapter.getScatteringCurveByFrameIdsList([], [], [], ids); + }); + +} + +RigidBodyModelingResultForm.prototype._getItems = function() { + var _this = this; + return [ { + xtype : 'label', + forId : 'myFieldId', + text : 'INLINE HELP: To be updated', + margin : '15 0 20 10', + cls : "inline-help" + }, this.rigidModelGrid.getPanel() ] + +}; + +RigidBodyModelingResultForm.prototype._getButtons = function() { + return []; +}; + +RigidBodyModelingResultForm.prototype.getPanel = function() { + var _this = this; + this.panel = Ext.create('Ext.form.Panel', { + width : this.width, + height : this.height, + margin : 10, + border : 1, + defaultType : 'textfield', + items : this._getItems(), + buttons : this._getButtons(), + listeners : { + afterrender : function() { + _this._populate(); + + } + } + }); + return this.panel; +}; + +/** Populates could be call when the DOM is not filled yet **/ +RigidBodyModelingResultForm.prototype._populate = function() { +}; + +/** It populates the form * */ +RigidBodyModelingResultForm.prototype.refresh = function(subtractions) { + this.rigidModelGrid.refresh(subtractions); +}; + +RigidBodyModelingResultForm.prototype.input = function() { + return {}; +}; + +/** It populates the form **/ +RigidBodyModelingResultForm.prototype.test = function(targetId) { + var macromoleculeForm = new RigidBodyModelingResultForm(); + var panel = macromoleculeForm.getPanel(); + panel.render(targetId); +}; + +/** + * Example form + * + * @witdh + * @height + */ +function SuperpositionForm(args) { + this.id = BUI.id(); + this.width = null; + this.height = null; + + if (args != null) { + if (args.width != null) { + this.width = args.width; + } + if (args.height != null) { + this.height = args.height; + } + } + + var _this = this; + + /** Widgets **/ + this.superpositionGrid = new SuperpositionGrid({ + width : null, + height : 200 + }); + + this.superpositionGrid.onSelected.attach(function(sender, superpositions) { + var ids = []; + for ( var i in superpositions) { + ids.push(superpositions[i].superpositionId); + } +// _this._renderAbinitio(ids); +// _this.aprioriPDBViewer.refresh(); + _this._renderAligned(ids); + // getAlignedPDBContentBySuperpositionList + }); + + /** PDB viewer **/ +// this.abinitioPDBViewer = new PDBViewer({ +// width : 860 / 2, +// height : 300 / 2, +// margin : 0 +// }); +// +// /** PDB viewer **/ +// this.aprioriPDBViewer = new StructurePDBViewer({ +// width : 860 / 2, +// height : 300 / 2, +// margin : 0 +// }); + + this.alignedPDBViewer = new AlignedSuperpositionPDBViewer({ + width : 860 , + height : 300 + }); + +} + +SuperpositionForm.prototype._renderAligned = function(modelsIdList) { + /** Trying to plot the PDB file **/ + try { + this.alignedPDBViewer.refresh(modelsIdList); + } catch (e) { + console.log(e); + } +}; + +SuperpositionForm.prototype._renderAbinitio = function(modelsIdList) { + /** Trying to plot the PDB file **/ + try { + var viz = []; + for (var i = 0; i < modelsIdList.length; i++) { + viz.push({ + modelId : modelsIdList[i], + color : "0xFF6600", + opacity : 0.8 + }); + } + this.abinitioPDBViewer.refresh(viz); + } catch (e) { + console.log(e); + } +}; + +SuperpositionForm.prototype._getItems = function() { + var _this = this; + return [ { + xtype : 'label', + forId : 'myFieldId', + text : 'INLINE HELP: To be updated', + margin : '15 0 20 10', + cls : "inline-help" + }, this.superpositionGrid.getPanel(), { + xtype : 'container', + layout : 'hbox', + items : [ +// { +// xtype : 'container', +// layout : 'vbox', +// items : [ this.abinitioPDBViewer.getPanel(), this.aprioriPDBViewer.getPanel() +// +// ] +// }, + + this.alignedPDBViewer.getPanel() ] + } ] + +}; + +SuperpositionForm.prototype._getButtons = function() { + return []; +}; + +SuperpositionForm.prototype.getPanel = function() { + var _this = this; + this.panel = Ext.create('Ext.form.Panel', { + width : this.width, + height : this.height, + margin : 10, + border : 1, + defaultType : 'textfield', + items : this._getItems(), + buttons : this._getButtons(), + listeners : { + afterrender : function() { + _this._populate(); + + } + } + }); + return this.panel; +}; + +/** Populates could be call when the DOM is not filled yet **/ +SuperpositionForm.prototype._populate = function() { +}; + +/** It populates the form * */ +SuperpositionForm.prototype.refresh = function(subtractions) { + this.subtractions = subtractions; + this.superpositionGrid.refresh(subtractions); +}; + +SuperpositionForm.prototype.input = function() { + return {}; +}; + +/** It populates the form **/ +SuperpositionForm.prototype.test = function(targetId) { + var macromoleculeForm = new SuperpositionForm(); + var panel = macromoleculeForm.getPanel(); + panel.render(targetId); +}; + +/** + * Example form + * + * @witdh + * @height + */ +function AssemblyForm(args) { + this.id = BUI.id(); + this.width = 700; + this.height = 500; + + if (args != null) { + if (args.width != null) { + this.width = args.width; + } + if (args.height != null) { + this.height = args.height; + } + } + + this.molarityGrid = new MolarityGrid({height : this.height - 50}); +} + +AssemblyForm.prototype._getItems = function() { + return [ { + xtype : 'label', + forId : 'myFieldId', + text : 'List of previously defined macromolecules present in the assembly. This information will be used for additional cross-checks where possible', + margin : '15 0 20 10', + cls : "inline-help" + }, this.molarityGrid.getPanel() ]; +}; + +AssemblyForm.prototype._getButtons = function() { + return []; +}; + +AssemblyForm.prototype.getPanel = function() { + this.panel = Ext.create('Ext.form.Panel', { + width : this.width, + height : this.height, + margin : 10, + border : 0, + defaultType : 'textfield', + items : this._getItems(), + buttons : this._getButtons() + }); + return this.panel; +}; + + +/** It populates the form **/ +AssemblyForm.prototype.refresh = function(macromolecule) { + this.macromolecule = macromolecule; + this.molarityGrid.refresh(macromolecule); +}; + +AssemblyForm.prototype.input = function() { + return {}; +}; + + +AssemblyForm.prototype.test = function(targetId) { + var assemblyForm = new AssemblyForm(); + + var panel = assemblyForm.getPanel(); + panel.render(targetId); +}; +/** + * Edit the information of a buffer + * + * #onSaved + * #onRemoveAdditive + */ +function BufferForm() { var _this = this; - /** Widgets **/ - this.plotWidget = new PlotWidget({ - width : 650, - height : 490 + this.additiveGrid = new AdditiveGrid(); + this.additiveGrid.onRemoveButtonClicked.attach(function(sender, args) { + _this.onRemoveAdditive.notify(args); }); - /** Selected frames to be displayed **/ - this.selectedItems = { - frames : [], - averages : [], - subtractions : [] - }; - + this.onSaved = new Event(this); + this.onRemoveAdditive = new Event(this); } -DataReductionForm.prototype._parseSelectedItemsToIds = function(selectedArray) { - var ids = []; - if (selectedArray != null) { - for (var i = 0; i < selectedArray.length; i++) { - ids.push(selectedArray[i].id); - } - } - return ids; -}; - -DataReductionForm.prototype.onSelectionChanged = function(columnName, selected) { - if (selected != null) { - if (columnName == "Frames") { - this.selectedItems.frames = selected; - this.selectedItems.subtractions = []; - } - if (columnName == "Averages") { - this.selectedItems.averages = selected; - } - if (columnName == "Subtractions") { - this.selectedItems.frames = []; - this.selectedItems.subtractions = selected; - } - } - this.plotWidget.refresh(this._parseSelectedItemsToIds(this.selectedItems.frames), this._parseSelectedItemsToIds(this.selectedItems.subtractions)); -}; - -DataReductionForm.prototype._getFramesExtPanel = function(store, columnName, height) { - var _this = this; - - var selModel = Ext.create('Ext.selection.RowModel', { - allowDeselect : true, - mode : 'MULTI', - listeners : { - selectionchange : function(sm, selections) { - var selected = []; - for (var i = 0; i < selections.length; i++) { - selected.push(selections[i].raw); - } - _this.onSelectionChanged(columnName, selected); - } - } - }); - - return Ext.create('Ext.grid.Panel', { - store : store, - margin : 10, - height : height, - width : 200, - selModel : selModel, - columns : [ { - text : columnName, - dataIndex : 'fileName', - flex : 1 - } ], - viewConfig : { - } - }); +BufferForm.prototype.getBuffer = function() { + this.buffer.name = Ext.getCmp("buffer_name").getValue(); + this.buffer.acronym = Ext.getCmp("buffer_acronym").getValue(); + this.buffer.comments = Ext.getCmp("buffer_comments").getValue(); + this.buffer.ph = Ext.getCmp("buffer_ph").getValue(); + this.buffer.composition = Ext.getCmp("buffer_composition").getValue(); + this.buffer.bufferhasadditive3VOs = this.additiveGrid.getAdditives(); + return this.buffer; }; -DataReductionForm.prototype._getFramesPanel = function() { - var fields = [ 'fileName', 'type', 'id' ]; - - this.framesStore = Ext.create('Ext.data.Store', { - fields : fields, - sorters : 'fileName' - }); - - this.averagesStore = Ext.create('Ext.data.Store', { - fields : fields - }); - - this.subtractionStore = Ext.create('Ext.data.Store', { - fields : fields - }); - - var gridFrames = this._getFramesExtPanel(this.framesStore, "Frames", 375); - var gridAvgs = this._getFramesExtPanel(this.averagesStore, "Averages", 125); - var subtractionAvgs = this._getFramesExtPanel(this.subtractionStore, "Subtractions", 75); - - return { - xtype : 'container', - layout : 'vbox', - items : [ gridFrames, subtractionAvgs ] - }; -}; - -DataReductionForm.prototype._getImageContainer = function(name, help) { - var html = "
" + name + "
" +BufferForm.prototype._getTopPanel = function() { return { xtype : 'container', - layout : 'vbox', + layout : 'hbox', + border : 0, + frame : true, items : [ { - html : html, - margin : "5 0 0 0", - height : 95, - width : 100 + xtype : 'container', + layout : 'hbox', + items : [ { + xtype : 'container', + flex : 1, + border : false, + layout : 'anchor', + defaultType : 'textfield', + items : [ { + xtype : 'requiredtext', + id : 'buffer_name', + fieldLabel : 'Name', + name : 'name', + width : '200px', + value : this.buffer.name + }, { + xtype : 'requiredtext', + id : 'buffer_acronym', + fieldLabel : 'Acronym', + name : 'acronym', + width : '200px', + value : this.buffer.acronym + } ] + } ] }, { - xtype : 'label', - forId : 'myFieldId', - text : help, - margin : '5 0 0 0', - cls : "inline-help" + xtype : 'container', + flex : 1, + layout : 'anchor', + defaultType : 'textfield', + margin : '0 0 0 10', + items : [ { + id : 'buffer_ph', + fieldLabel : 'pH', + name : 'ph', + value : this.buffer.ph, + xtype : 'numberfield', + width : 200, + minValue : 0, + maxValue : 15 + }, { + xtype : 'requiredtext', + id : 'buffer_composition', + fieldLabel : 'Composition', + name : 'composition', + width : 200, + value : this.buffer.composition + } ] } ] - } + }; }; -DataReductionForm.prototype._getItems = function() { - var _this = this; - return [ { +BufferForm.prototype.getPanel = function(buffer) { + this.buffer = buffer; + this.panel = Ext.createWidget({ xtype : 'container', - layout : 'hbox', - items : [ - this._getFramesPanel(), - this.plotWidget.getPanel(), - { - xtype : 'panel', - width : 110, - frame : true, - margin : "10 5 5 5", - border : 0, - layout : 'vbox', - items : [ this._getImageContainer("scattering", "Scattering"), this._getImageContainer("guinier", "Guinier Region"), - this._getImageContainer("kratky", "Kratky Plot"), this._getImageContainer("gnom", "P(r) distribution") ] - } ] - } ] -}; - -DataReductionForm.prototype._getButtons = function() { - return []; -}; + layout : 'vbox', + style : { + padding : '10px' + }, + fieldDefaults : { + labelAlign : 'left', + labelWidth : 50 -DataReductionForm.prototype.getPanel = function() { - var _this = this; - this.panel = Ext.create('Ext.form.Panel', { - width : this.width, - height : this.height, - border : 0, - items : this._getItems(), - buttons : this._getButtons(), - listeners : { - afterrender : function() { - _this._populate(); - } - } + }, + items : [ this._getTopPanel(), { + id : 'buffer_comments', + xtype : 'textareafield', + name : 'comments', + fieldLabel : 'Comments', + width : '100%', + value : buffer.comments + }, this.additiveGrid.getPanel(buffer) ] }); return this.panel; }; -/** Populates could be call when the DOM is not filled yet **/ -DataReductionForm.prototype._populate = function() { -}; - -/** It populates the form * */ -DataReductionForm.prototype.refresh = function(subtractions) { - if (subtractions != null) { - for (var i = 0; i < subtractions.length; i++) { - /** Loading frame grids **/ - var subtraction = subtractions[i]; - var averages = [ { - fileName : BUI.getFileName(subtraction.bufferAverageFilePath), - type : 'bufferAvg', - id : subtraction.subtractionId - }, { - fileName : BUI.getFileName(subtraction.sampleAverageFilePath), - type : 'sampleAvg', - id : subtraction.subtractionId - } - - ]; - this.averagesStore.loadData(averages, true); - this.subtractionStore.loadData([ { - fileName : BUI.getFileName(subtraction.substractedFilePath), - type : 'SUBTRACTION', - id : subtraction.subtractionId - } ], true); - - var frames = []; - /** Buffers **/ - if (subtraction.bufferOneDimensionalFiles != null) { - if (subtraction.bufferOneDimensionalFiles.frametolist3VOs != null) { - for (var j = 0; j < subtraction.bufferOneDimensionalFiles.frametolist3VOs.length; j++) { - var frametolist3VO = subtraction.bufferOneDimensionalFiles.frametolist3VOs[j]; - if (frametolist3VO.frame3VO != null) { - frames.push({ - fileName : BUI.getFileName(frametolist3VO.frame3VO.filePath), - type : 'BUFFER', - id : frametolist3VO.frame3VO.frameId - }); - } - } - } - } - /** Samples **/ - if (subtraction.sampleOneDimensionalFiles != null) { - if (subtraction.sampleOneDimensionalFiles.frametolist3VOs != null) { - for (var j = 0; j < subtraction.sampleOneDimensionalFiles.frametolist3VOs.length; j++) { - var frametolist3VO = subtraction.sampleOneDimensionalFiles.frametolist3VOs[j]; - if (frametolist3VO.frame3VO != null) { - frames.push({ - fileName : BUI.getFileName(frametolist3VO.frame3VO.filePath), - type : 'SAMPLE', - id : frametolist3VO.frame3VO.frameId - }); - } - } - } - } - - this.framesStore.loadData(frames, true); - - /** Loading images **/ - this._displayImage("scattering", subtraction.subtractionId); - this._displayImage("kratky", subtraction.subtractionId); - this._displayImage("guinier", subtraction.subtractionId); - this._displayImage("gnom", subtraction.subtractionId); +BufferForm.prototype.input = function() { + return { + buffer : { + "bufferId" : 422, + "proposalId" : 10, + "safetyLevelId" : null, + "name" : "B1", + "acronym" : "B1", + "ph" : null, + "composition" : null, + "bufferhasadditive3VOs" : [], + "comments" : null } - } -}; - -DataReductionForm.prototype._displayImage = function(name, subtractionId) { - var url = BUI.getURL() + '&type=' + name + '&subtractionId=' + subtractionId; - var event = "OnClick= window.open('" + url + "')"; - document.getElementById(this.id + "_" + name).innerHTML = ''; -}; - -DataReductionForm.prototype.input = function() { - return {}; + }; }; -/** It populates the form **/ -DataReductionForm.prototype.test = function(targetId) { - var macromoleculeForm = new DataReductionForm(); - var panel = macromoleculeForm.getPanel(); +BufferForm.prototype.test = function(targetId) { + var bufferForm = new BufferForm(); + var panel = bufferForm.getPanel(bufferForm.input().buffer); panel.render(targetId); }; - -function PlotWidget(args) { - this.width = 600; - this.height = 600; - this.id = BUI.id(); - - this.linear = false; - - this.margin = "10 0 0 0"; - /** for each stat the max and minimum value when it is scaled in order to show correctly in the legend **/ - this.ranges = {}; + +/** + * @showTitle + * + * #onSaved + * #onAddPlates + * #onRemovePlates + **/ +function CaseForm(args) { + this.width = 700; + this.showTitle = true; if (args != null) { - if (args.width != null) { - this.width = args.width; - } - if (args.height != null) { - this.height = args.height; - } - if (args.margin != null) { - this.margin = args.margin; + if (args.showTitle != null) { + this.showTitle = args.showTitle; } } -} - -PlotWidget.prototype.getMenu = function() { var _this = this; - /** Actions buttons **/ - var actions = []; - actions.push("->"); - actions.push({ - text : "Export as Image", - scope : this, - icon : '../images/save.gif', - handler : function(item, pressed) { - var largeImage = document.createElement("img"); - largeImage.style.display = 'block'; - largeImage.style.width = 200 + "px"; - largeImage.style.height = 200 + "px"; - largeImage.setAttribute('src', Dygraph.Export.asCanvas(this.dygraphObject.dygraph).toDataURL()); - window.open(Dygraph.Export.asCanvas(this.dygraphObject.dygraph).toDataURL(), 'Image', ''); + this.stockSolutionGrid = new StockSolutionGrid({ + width : this.width - 10, + minHeight : 300, + height : 300, + tbar : true, + showTitle : true, + isPackedVisible : false, + btnAddExisting : true, + btnRemoveVisible : false, + btnUnpackVisible : true + }); + + /** When selecting existing stock solutions **/ + this.stockSolutionGrid.onStockSolutionSelected.attach(function(sender, stockSolutions) { + if (stockSolutions != null) { + for ( var i = 0; i < stockSolutions.length; i++) { + _this.saveStockSolution(stockSolutions[i]); + } } }); - return actions; -}; + /** it can be because it has been added a new one or removed **/ + this.stockSolutionGrid.onProposalChanged.attach(function(sender, stockSolution) { + if (stockSolution != null) { + _this.saveStockSolution(stockSolution); + } else { + BIOSAXS.proposal.onInitialized.attach(function() { + _this.stockSolutionGrid.refresh(BIOSAXS.proposal.getStockSolutionsByDewarId(_this.dewar.dewarId)); -/** Looks for the maximum value and then divide everything but that value **/ -PlotWidget.prototype.scaledData = function(data) { - for (var i = 0; i < data.length; i++) { - var values = this.getMaxAndMinValue(data[i]); - data[i] = this.divideValuesByMax(data[i], values.max); - this.ranges[data[i].label] = values; - } - return data; -}; + _this.stockSolutionGrid.grid.setLoading(false); + }); + BIOSAXS.proposal.init(); -/** Given a stat float[] and a max number it will divide each value by max **/ -PlotWidget.prototype.divideValuesByMax = function(stat, max) { - for (var j = 0; j < stat.data.length; j++) { - if (max != 0) { - stat.data[j] = Number(stat.data[j]) / max; - stat.std[j] = Number(stat.std[j]) / max; } - } - return stat; -}; -/** returns max value of a stat **/ -PlotWidget.prototype.getMaxAndMinValue = function(stat) { - var max = 0; - var min = stat.data[0]; - for (var j = 0; j < stat.data.length; j++) { - if (Number(stat.data[j]) > max) { - max = Number(stat.data[j]); - } - if (Number(stat.std[j]) > max) { - max = Number(stat.std[j]); - } - if (Number(stat.data[j]) < min) { - min = Number(stat.data[j]); - } - } - return { - max : Number(max), - min : Number(min) - }; -}; - -PlotWidget.prototype._renderDygraph = function(parsed, colors, labels) { - this.dygraphObject = new StdDevDyGraph(this.id, { - width : this.width - 20, - height : this.height - 40, - xlabel : "", }); - this.dygraphObject.draw(parsed, colors, labels); + this.onSaved = new Event(this); + this.onAddPlates = new Event(this); + this.onRemovePlates = new Event(this); +} +CaseForm.prototype.saveStockSolution = function(stockSolution) { + var _this = this; + var adapter = new BiosaxsDataAdapter(); + this.stockSolutionGrid.grid.setLoading("ISPyB: setting case to Stock solution"); + adapter.onSuccess.attach(function(sender, stockSolution) { + BIOSAXS.proposal.onInitialized.attach(function() { + _this.stockSolutionGrid.refresh(BIOSAXS.proposal.getStockSolutionsByDewarId(_this.dewar.dewarId)); + _this.stockSolutionGrid.grid.setLoading(false); + }); + BIOSAXS.proposal.init(); + }); + adapter.onError.attach(function(sender, data) { + _this.stockSolutionGrid.grid.setLoading(false); + BUI.showError(data); + }); + stockSolution.boxId = _this.dewar.dewarId; + adapter.saveStockSolution(stockSolution); }; -PlotWidget.prototype.getPanel = function() { - this.panel = Ext.create('Ext.panel.Panel', { - width : this.width, - height : this.height, - margin : this.margin, - tbar : this.getMenu(), - items : [ { - html : "", - id : this.id, - width : this.width, - height : this.height, - padding : 10, - margin : "0 0 0 -30", - border : 0 - } ] +CaseForm.prototype.fillStores = function() { + var _this = this; + this.panel.setLoading("Loading Labcontacts from database"); + + var proposal = BUI.getProposal(); + proposal.onDataRetrieved.attach(function(sender, data) { + _this.labContactForSendingStore.loadData(data, false); + _this.labContactForReturnStore.loadData(data, false); + _this.panel.setLoading(false); }); + proposal.getLabContactsByProposalId(); - return this.panel; }; -PlotWidget.prototype.getPoint = function(y, error) { - var minus = y - error; - var max = y + error; - - if (this.linear) { - return [ Math.abs(minus), y, Math.abs(max) ]; - } - if ((minus != 0) && (max != 0)) { - return [ Math.log(Math.abs(minus)), Math.log(y), Math.log(Math.abs(max)) ]; - } else { - return [ Math.log(y), Math.log(y), Math.log(y) ]; - } - +CaseForm.prototype.refresh = function(dewar) { + this.setDewar(dewar); + this.stockSolutionGrid.refresh(BIOSAXS.proposal.getStockSolutionsByDewarId(dewar.dewarId)); }; -PlotWidget.prototype.getDataPlot = function(frames, subtractions, models, fits, rbms) { - var files = []; - var labels = [ "Intensity" ]; - if (frames != null) { - for (var i = 0; i < frames.length; i++) { - files.push(frames[i].data); - labels.push(frames[i].fileName); - } - } - function splitData(data, column, errorColumn, name){ - var result = [] - for (var j = 0; j < data.length; j++) { - console.log(data[j][column]); - result.push([j,data[j][column],data[j][errorColumn]]);//[0, data[i][column],0]]); - } - files.push(result); - labels.push(name); - } - - if (subtractions != null) { - for (var i = 0; i < subtractions.length; i++) { - /** For subtraction **/ - files.push(subtractions[i].subtraction.data); - labels.push(subtractions[i].subtraction.fileName); - /** For sample average **/ -// files.push(subtractions[i].sampleAvg.data); -// labels.push(subtractions[i].sampleAvg.fileName); - /** For buffer average **/ -// files.push(subtractions[i].bufferAvg.data); -// labels.push(subtractions[i].bufferAvg.fileName); - } - } - - if (models != null) { - for (var i = 0; i < models.length; i++) { - for ( var key in models[i]) { - splitData(models[i][key].fir.data, 1, 2, "Intensity"); - splitData(models[i][key].fir.data, 3, 3, "Fit"); - } - } - } - - if (fits != null) { - for (var i = 0; i < fits.length; i++) { - for ( var key in fits[i]) { - - /** adding fit file to be plotted **/ - if (fits[i][key].fit.data[0].length == 3){ - splitData(fits[i][key].fit.data, 1, 1, "Intensity"); - splitData(fits[i][key].fit.data, 2, 2, "Fit"); - } - - /** s, Iexp(s), err, Ifit(s). **/ - if (fits[i][key].fit.data[0].length == 4){ - splitData(fits[i][key].fit.data, 1, 2, "Intensity"); - splitData(fits[i][key].fit.data, 3, 3, "Fit"); - } - - if (fits[i][key].fit.data[0].length == 5){ - /** X Intensity Fit Error Residues **/ - - splitData(fits[i][key].fit.data, 1, 3, "Intensity"); - splitData(fits[i][key].fit.data, 2, 2, "Fit"); - } - } - } - } - - var dataPoints = []; - if (files.length > 0) { - for (var i = 0; i < files[0].length; i++) { - dataPoints.push([ files[0][i][0], this.getPoint(files[0][i][1], files[0][i][2]) ]); - } - if (files.length > 1) { - for (var i = 1; i < files.length; i++) { - for (var j = 0; j < dataPoints.length; j++) { - if (files[i][j] != null){ - dataPoints[j].push(this.getPoint(files[i][j][1], files[i][j][2])); - } - else{ - dataPoints[j].push([0,0,0]); - } - } - } - } - } +CaseForm.prototype.getDewar = function() { + this.dewar.code = Ext.getCmp("dewar_code").getValue(); + this.dewar.comments = Ext.getCmp("dewar_comments").getValue(); + this.dewar.trackingNumberFromSynchrotron = Ext.getCmp("dewar_trackingNumberFromSynchrotron").getValue(); + this.dewar.trackingNumberToSynchrotron = Ext.getCmp("dewar_trackingNumberToSynchrotron").getValue(); + this.dewar.transportValue = Ext.getCmp("dewar_transportValue").getValue(); + this.dewar.storageLocation = Ext.getCmp("dewar_storageLocation").getValue(); + this.dewar.firstExperimentId = this.macromoleculeCombo.getValue(); + return this.dewar; +}; - return { - dataPoints : dataPoints, - labels : labels +CaseForm.prototype.setDewar = function(dewar) { + this.dewar = dewar; + Ext.getCmp("dewar_code").setValue(this.dewar.code); + Ext.getCmp("dewar_dewarStatus").setText(new String(this.dewar.dewarStatus).toUpperCase()); + Ext.getCmp("dewar_comments").setValue(this.dewar.comments); + Ext.getCmp("dewar_trackingNumberFromSynchrotron").setValue(this.dewar.trackingNumberFromSynchrotron); + Ext.getCmp("dewar_trackingNumberToSynchrotron").setValue(this.dewar.trackingNumberToSynchrotron); + Ext.getCmp("dewar_transportValue").setValue(this.dewar.transportValue); + Ext.getCmp("dewar_storageLocation").setValue(this.dewar.storageLocation); + if (dewar.sessionVO != null) { + this.macromoleculeCombo.setValue(dewar.sessionVO.sessionId); } }; -PlotWidget.prototype.refresh = function(frames, subtractions, models, fits, rbms, colors) { - - var _this = this; - this.panel.setLoading("Reading Files"); - - var dataAdapter = new BiosaxsDataAdapter(); - dataAdapter.onSuccess.attach(function(sender, data) { - _this.panel.setLoading("Rendering"); - var parsed = _this.getDataPlot(data.frames, data.subtractions, data.models, data.fits, rbms); - _this._renderDygraph(parsed.dataPoints, colors, parsed.labels); - _this.panel.setLoading(false); - }); - dataAdapter.onError.attach(function(sender, data) { - _this.panel.setLoading(false); +CaseForm.prototype.getSessionCombo = function() { + this.macromoleculeCombo = BIOSAXS_COMBOMANAGER.getComboSessions(BIOSAXS.proposal.getSessions(), { + labelWidth : 100, + margin : '5 0 00 0', + width : 250 }); - dataAdapter.getDataPlot(frames, subtractions, models, fits, rbms); -}; - -PlotWidget.prototype.input = function() { - return DATADOC.getHPLCData(); + return this.macromoleculeCombo; }; - -/** - * Fit form - * - * @witdh - * @height - */ -function FitForm(args) { - this.id = BUI.id(); - this.width = null; - this.height = null; - - if (args != null) { - if (args.width != null) { - this.width = args.width; - } - if (args.height != null) { - this.height = args.height; - } - } +CaseForm.prototype.getInformationPanel = function() { + if (this.panel == null) { + this.informationPanel = Ext.create('Ext.form.Panel', { + width : this.width - 10, + border : 0, + items : [ { + xtype : 'container', + margin : "2 2 2 2", + collapsible : false, + defaultType : 'textfield', + layout : 'anchor', + items : [ { + xtype : 'container', + layout : 'hbox', + items : [ { + xtype : 'requiredtext', + fieldLabel : 'Code', + allowBlank : false, + name : 'code', + id : 'dewar_code', + anchor : '50%' + }, { + xtype : 'label', + margin : '0 0 0 20', + readOnly : true, + id : 'dewar_dewarStatus', + anchor : '50%' + } ] + }, this.getSessionCombo(), { + margin : '5 0 0 0', + xtype : 'textareafield', + name : 'comments', + id : 'dewar_comments', + width : this.width - 50, + fieldLabel : 'Comments' + } ] + }, { + xtype : 'fieldset', + title : 'Courier Accounts Details for Return', + collapsible : false, + layout : 'anchor', + defaults : { + anchor : '100%' + }, + items : [ { + xtype : 'container', + layout : 'hbox', + items : [ { + xtype : 'textfield', + labelWidth : 200, + width : 280, + fieldLabel : 'Track Number From Synchrotron', + id : 'dewar_trackingNumberFromSynchrotron' + }, { + xtype : 'numberfield', + width : 190, + labelWidth : 110, + margin : '0 0 0 30', + fieldLabel : 'Transport Value', + id : 'dewar_transportValue' + } - var _this = this; - - /** Widgets **/ - this.fitGrid = new FitStructureToExperimentDataGrid({ - width : null, - height : 200 - }); + ] + }, { + xtype : 'container', + layout : 'hbox', + margin : '10 0 0 0', + items : [ - this.fitGrid.onSelected.attach(function(sender, fits) { - var modelsIdList = []; - for ( var i in fits) { - modelsIdList.push(fits[i].fitStructureToExperimentalDataId); - } - _this._renderPlot(_this.subtractions[0].subtractionId, modelsIdList); -// var adapter = new BiosaxsDataAdapter(); -// adapter.onSuccess.attach(function(sender, data) { -// -// }); -// adapter.getScatteringCurveByFrameIdsList([], [], [], ids); - }); - - - /** Dygraph Widget that plots fir files**/ - this.plotWidget = new PlotWidget({ - width : 745, - height : 300, - margin : "10 0 10 10" - }); -} + { + xtype : 'textfield', + labelWidth : 200, + width : 280, + fieldLabel : 'Track Number To Synchrotron', + id : 'dewar_trackingNumberToSynchrotron' + }, { + xtype : 'textfield', + margin : '0 0 0 30', + width : 190, + labelWidth : 110, + fieldLabel : 'Storage Location', + id : 'dewar_storageLocation' + } -FitForm.prototype._renderPlot = function(subtractionId, modelsIdList) { - /** Trying to plot tje subtraction and the models **/ - try { - var colors = [ "#669900", "#0000FF" ]; - - this.plotWidget.refresh([], [ ], [], modelsIdList, [], colors); - } catch (e) { - console.log(e); + ] + } ] + } ] + }); } -}; - -FitForm.prototype._getItems = function() { - var _this = this; - return [ { - xtype : 'label', - forId : 'myFieldId', - text : 'INLINE HELP: To be updated', - margin : '15 0 20 10', - cls : "inline-help" - }, this.fitGrid.getPanel(), this.plotWidget.getPanel() ] - -}; -FitForm.prototype._getButtons = function() { - return []; + return this.informationPanel; }; -FitForm.prototype.getPanel = function() { - var _this = this; - this.panel = Ext.create('Ext.form.Panel', { +CaseForm.prototype.getPanel = function(dewar) { + this.dewar = dewar; + this.panel = Ext.createWidget({ + xtype : 'container', + layout : 'vbox', width : this.width, - height : this.height, - margin : 10, - border : 1, - defaultType : 'textfield', - items : this._getItems(), - buttons : this._getButtons(), - listeners : { - afterrender : function() { - _this._populate(); + border : 0, + items : [ { + items : { + xtype : "container", + layout : "vbox", + margin : "5 5 5 5", + items : [ this.getInformationPanel(dewar), this.stockSolutionGrid.getPanel() ] } - } + } ] }); + + this.refresh(dewar); return this.panel; -}; -/** Populates could be call when the DOM is not filled yet **/ -FitForm.prototype._populate = function() { }; -/** It populates the form * */ -FitForm.prototype.refresh = function(subtractions) { - this.subtractions = subtractions; - this.fitGrid.refresh(subtractions); -}; +CaseForm.prototype.input = function() { + return { + dewar : { + "dewarId" : 305861, + "code" : "ESRF-TEST", + "comments" : "comments", + "storageLocation" : "FRIDGE", + "dewarStatus" : "opened", + "timeStamp" : null, + "isStorageDewar" : null, + "barCode" : "ESRF305861", + "customsValue" : null, + "transportValue" : null, + "trackingNumberToSynchrotron" : "3333", + "trackingNumberFromSynchrotron" : "224466", + "type" : "Dewar", + "sessionVO" : { + "sessionId" : 31697, + "expSessionPk" : null, + "projectCode" : null, + "startDate" : "2012 07 21", + "endDate" : "2012 07 23", + "beamlineName" : "BM29", + "scheduled" : 1, + "nbShifts" : 2, + "comments" : null, + "beamlineOperator" : "PERNOT P", + "usedFlag" : null, + "sessionTitle" : null, + "structureDeterminations" : null, + "dewarTransport" : null, + "databackupFrance" : null, + "databackupEurope" : null, + "visit_number" : null, + "operatorSiteNumber" : "14061", + "timeStamp" : "2012 04 25" + } + }, + proposal : { + "assemblies" : [], + "sessions" : [ { + "sessionId" : 31697, + "expSessionPk" : null, + "projectCode" : null, + "startDate" : "2012 07 21", + "endDate" : "2012 07 23", + "beamlineName" : "BM29", + "scheduled" : 1, + "nbShifts" : 2, + "comments" : null, + "beamlineOperator" : "PERNOT P", + "usedFlag" : null, + "sessionTitle" : null, + "structureDeterminations" : null, + "dewarTransport" : null, + "databackupFrance" : null, + "databackupEurope" : null, + "visit_number" : null, + "operatorSiteNumber" : "14061", + "timeStamp" : "2012 04 25" + } ], + "labcontacts" : [ { + "labContactId" : 787, + "personVO" : { + "personId" : 304252, + "personUUID" : null, + "familyName" : "KIM", + "givenName" : "Henry", + "title" : null, + "emailAddress" : "henry-sung.kim@ibs.fr", + "phoneNumber" : "", + "login" : "", + "passwd" : "", + "faxNumber" : "" + }, + "cardName" : "KIM-Institut de Bio", + "defaultCourrierCompany" : "22", + "courierAccount" : "", + "billingReference" : "", + "dewarAvgCustomsValue" : 0, + "dewarAvgTransportValue" : 0 + } ], + "buffers" : [ { + "bufferId" : 811, + "proposalId" : 3124, + "safetyLevelId" : null, + "name" : "EDTA", + "acronym" : "EDTA", + "ph" : null, + "composition" : "", + "bufferhasadditive3VOs" : [], + "comments" : "" + }, { + "bufferId" : 810, + "proposalId" : 3124, + "safetyLevelId" : null, + "name" : "HEPES", + "acronym" : "HEPES", + "ph" : null, + "composition" : "", + "bufferhasadditive3VOs" : [], + "comments" : "" + } ], + "shippings" : [ { + "shippingId" : 304107, + "shippingName" : "TEST", + "deliveryAgentAgentName" : null, + "deliveryAgentShippingDate" : null, + "deliveryAgentDeliveryDate" : null, + "deliveryAgentAgentCode" : null, + "deliveryAgentFlightCode" : null, + "shippingStatus" : "opened", + "timeStamp" : "2013 09 25", + "laboratoryId" : null, + "isStorageShipping" : null, + "creationDate" : "2013 09 25", + "comments" : "test", + "sendingLabContactVO" : { + "labContactId" : 787, + "personVO" : { + "personId" : 304252, + "personUUID" : null, + "familyName" : "KIM", + "givenName" : "Henry", + "title" : null, + "emailAddress" : "henry-sung.kim@ibs.fr", + "phoneNumber" : "", + "login" : "", + "passwd" : "", + "faxNumber" : "" + }, + "cardName" : "KIM-Institut de Bio", + "defaultCourrierCompany" : "22", + "courierAccount" : "", + "billingReference" : "", + "dewarAvgCustomsValue" : 0, + "dewarAvgTransportValue" : 0 + }, + "returnLabContactVO" : { + "labContactId" : 787, + "personVO" : { + "personId" : 304252, + "personUUID" : null, + "familyName" : "KIM", + "givenName" : "Henry", + "title" : null, + "emailAddress" : "henry-sung.kim@ibs.fr", + "phoneNumber" : "", + "login" : "", + "passwd" : "", + "faxNumber" : "" + }, + "cardName" : "KIM-Institut de Bio", + "defaultCourrierCompany" : "22", + "courierAccount" : "", + "billingReference" : "", + "dewarAvgCustomsValue" : 0, + "dewarAvgTransportValue" : 0 + }, + "returnCourier" : null, + "dateOfShippingToUser" : null, + "shippingType" : "DewarTracking", + "dewarVOs" : [ { + "dewarId" : 305861, + "code" : "ESRF-TEST", + "comments" : "comments", + "storageLocation" : "FRIDGE", + "dewarStatus" : "opened", + "timeStamp" : null, + "isStorageDewar" : null, + "barCode" : "ESRF305861", + "customsValue" : null, + "transportValue" : null, + "trackingNumberToSynchrotron" : "3333", + "trackingNumberFromSynchrotron" : "224466", + "type" : "Dewar", + "sessionVO" : { + "sessionId" : 31697, + "expSessionPk" : null, + "projectCode" : null, + "startDate" : "2012 07 21", + "endDate" : "2012 07 23", + "beamlineName" : "BM29", + "scheduled" : 1, + "nbShifts" : 2, + "comments" : null, + "beamlineOperator" : "PERNOT P", + "usedFlag" : null, + "sessionTitle" : null, + "structureDeterminations" : null, + "dewarTransport" : null, + "databackupFrance" : null, + "databackupEurope" : null, + "visit_number" : null, + "operatorSiteNumber" : "14061", + "timeStamp" : "2012 04 25" + } + } ] + } ], + "macromolecules" : [ { + "macromoleculeId" : 5933, + "safetylevelId" : null, + "proposalId" : 3124, + "name" : "A", + "acronym" : "A", + "molecularMass" : "", + "extintionCoefficient" : "", + "sequence" : null, + "creationDate" : null, + "comments" : "", + "macromoleculeregion3VOs" : [], + "stoichiometry3VOsForHostMacromoleculeId" : [], + "structure3VOs" : [] + } ], + "stockSolutions" : [ { + "stockSolutionId" : 6, + "proposalId" : 3124, + "macromoleculeId" : 5933, + "bufferId" : 811, + "instructionSet3VO" : null, + "boxId" : 305861, + "storageTemperature" : "20", + "volume" : "300", + "concentration" : "1.2", + "comments" : "Buffer EDTA with A", + "name" : "A_EDTA_1.2", + "samples" : [] + } ] + } -FitForm.prototype.input = function() { - return {}; + }; }; -/** It populates the form **/ -FitForm.prototype.test = function(targetId) { - var macromoleculeForm = new FitForm(); - var panel = macromoleculeForm.getPanel(); - panel.render(targetId); -}; - -/** - * Example form - * - * @witdh - * @height - */ -function RigidBodyModelingResultForm(args) { +CaseForm.prototype.test = function(targetId) { + var caseForm = new CaseForm(); + BIOSAXS.proposal = new Proposal(caseForm.input().proposal); + var panel = caseForm.getPanel(caseForm.input().dewar); + panel.render(targetId); +}; + +/** + * Example form + * + * @witdh + * @height + */ +function ExampleForm(args) { + this.id = BUI.id(); + this.width = 700; + this.height = 500; + + if (args != null) { + if (args.width != null) { + this.width = args.width; + } + if (args.height != null) { + this.height = args.height; + } + } +} + +ExampleForm.prototype._getItems = function() { + return [{ + fieldLabel : 'First Name', + name : 'first', + allowBlank : false + }, { + fieldLabel : 'Last Name', + name : 'last', + allowBlank : false + } ]; +}; + +ExampleForm.prototype._getItems = function() { + return [ ]; +}; + +ExampleForm.prototype._getButtons = function() { + return [ ]; +}; + +ExampleForm.prototype.getPanel = function() { + this.panel = Ext.create('Ext.form.Panel', { + width : this.width, + height : this.height, + margin : 10, + border : 1, + defaultType : 'textfield', + items : this._getItems(), + buttons : this._getButtons() + }); + return this.panel; +}; + +/** It populates the form **/ +ExampleForm.prototype.refresh = function(macromolecule) { +}; + +function ExperimentForm(args) { this.id = BUI.id(); - this.width = null; - this.height = null; if (args != null) { - if (args.width != null) { - this.width = args.width; - } - if (args.height != null) { - this.height = args.height; - } } - var _this = this; - /** Widgets **/ - this.rigidModelGrid = new RigidModelGrid({ - width : null, - height : 200 + this.onSaved = new Event(this); +} + +ExperimentForm.prototype._getItems = function(experiment) { + this.experiment = experiment; + var typeCombo = Ext.create('Ext.form.ComboBox', { + id : this.id + 'type', + fieldLabel : 'Type', + store : [ "STATIC", "CALIBRATION", "HPLC" ], + queryMode : 'local', + labelWidth : 120, + displayField : 'name', + valueField : 'name', + value : experiment.json.type, + disabled : (experiment.json.type == 'TEMPLATE') }); - this.rigidModelGrid.onSelected.attach(function(sender, fits){ - var ids = []; - for ( var i in fits) { - ids.push(fits[i].fitStructureToExperimentalDataId); + var items = []; + + + if (experiment.json.type == "HPLC" ){ + var typeMacromolecule = BIOSAXS_COMBOMANAGER.getComboMacromoleculeByMacromolecules(BIOSAXS.proposal.getMacromolecules(), {labelWidth : 120, width: "120px"}); + if (experiment.getHPLCMacromolecule() != null){ + typeMacromolecule.setValue(experiment.getHPLCMacromolecule().macromoleculeId); + items.push(typeMacromolecule); } - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, data){ -// debugger - - }); - - adapter.getScatteringCurveByFrameIdsList([], [], [], ids); + } + + + items.push(typeCombo, { + id : this.id + 'name', + xtype : 'textfield', + fieldLabel : 'Name', + labelWidth : 120, + width : '100%', + value : experiment.json.name + }, { + id : this.id + 'comments', + xtype : 'textareafield', + name : 'comments', + fieldLabel : 'Comments', + labelWidth : 120, + height : 120, + width : '100%', + value : experiment.json.comments }); - -} - -RigidBodyModelingResultForm.prototype._getItems = function() { - var _this = this; - return [ { - xtype : 'label', - forId : 'myFieldId', - text : 'INLINE HELP: To be updated', - margin : '15 0 20 10', - cls : "inline-help" - }, this.rigidModelGrid.getPanel() ] - -}; - -RigidBodyModelingResultForm.prototype._getButtons = function() { - return []; + return items; }; - -RigidBodyModelingResultForm.prototype.getPanel = function() { +ExperimentForm.prototype.getPanel = function(experiment) { var _this = this; - this.panel = Ext.create('Ext.form.Panel', { - width : this.width, - height : this.height, - margin : 10, - border : 1, - defaultType : 'textfield', - items : this._getItems(), - buttons : this._getButtons(), - listeners : { - afterrender : function() { - _this._populate(); - - } - } + + this.panel = Ext.createWidget({ + xtype : 'container', + layout : 'vbox', + border : 0, + style : { + padding : '10px' + }, + fieldDefaults : { + labelAlign : 'left', + labelWidth : 50 + }, + items : this._getItems(experiment) }); return this.panel; }; -/** Populates could be call when the DOM is not filled yet **/ -RigidBodyModelingResultForm.prototype._populate = function() { -}; - -/** It populates the form * */ -RigidBodyModelingResultForm.prototype.refresh = function(subtractions) { - this.rigidModelGrid.refresh(subtractions); -}; - -RigidBodyModelingResultForm.prototype.input = function() { - return {}; +ExperimentForm.prototype.input = function() { + return new ExperimentHeaderForm().input(); }; -/** It populates the form **/ -RigidBodyModelingResultForm.prototype.test = function(targetId) { - var macromoleculeForm = new RigidBodyModelingResultForm(); - var panel = macromoleculeForm.getPanel(); +ExperimentForm.prototype.test = function(targetId) { + var experimentForm = new ExperimentForm(); + var panel = experimentForm.getPanel(new Experiment(experimentForm.input().experiment)); panel.render(targetId); }; /** - * Example form + * Shows the header for the experiments changing the color and parameters depending on experiment type * - * @witdh - * @height */ -function SuperpositionForm(args) { +function ExperimentHeaderForm(args) { this.id = BUI.id(); - this.width = null; - this.height = null; + this.backgroundColor = '#FFFFFF'; +} - if (args != null) { - if (args.width != null) { - this.width = args.width; - } - if (args.height != null) { - this.height = args.height; +ExperimentHeaderForm.prototype.getHTMLSource = function(experiment) { + var html = BUI.createFormLabel("Name :", experiment.json.name, 75, 400, this.backgroundColor); + if (experiment.json.type == "HPLC") { + if (experiment.getHPLCMacromolecule() != null){ + html = html + BUI.createFormLabel("Molecule :", experiment.getHPLCMacromolecule().acronym, 75, 400, this.backgroundColor); } } + else{ + html = html + BUI.createFormLabel("Type :", experiment.json.type, 75, 400, this.backgroundColor); + } + + html = html + BUI.createFormLabel("Date :", experiment.json.creationDate, 75, 400, this.backgroundColor); + return html; +}; - var _this = this; - - /** Widgets **/ - this.superpositionGrid = new SuperpositionGrid({ - width : null, - height : 200 - }); - - this.superpositionGrid.onSelected.attach(function(sender, superpositions) { - var ids = []; - for ( var i in superpositions) { - ids.push(superpositions[i].superpositionId); - } -// _this._renderAbinitio(ids); -// _this.aprioriPDBViewer.refresh(); - _this._renderAligned(ids); - // getAlignedPDBContentBySuperpositionList - }); - - /** PDB viewer **/ -// this.abinitioPDBViewer = new PDBViewer({ -// width : 860 / 2, -// height : 300 / 2, -// margin : 0 -// }); -// -// /** PDB viewer **/ -// this.aprioriPDBViewer = new StructurePDBViewer({ -// width : 860 / 2, -// height : 300 / 2, -// margin : 0 -// }); +ExperimentHeaderForm.prototype.getHTMLDownload = function(experiment) { + var bgcolor = "background-color:" + this.backgroundColor + ";"; + var html = "
"; + if ((experiment.json.type == "CALIBATION") || (experiment.json.type == "STATIC")) { + html = html + " Download Source File
"; + html = html + + ""; + } + if (experiment.json.type == "TEMPLATE") { + html = html + + " Download Source File
"; + } - this.alignedPDBViewer = new AlignedSuperpositionPDBViewer({ - width : 860 , - height : 300 - }); + if (experiment.json.type == "HPLC") { + html = html + " Download h5 File
"; + } -} + return html; +}; -SuperpositionForm.prototype._renderAligned = function(modelsIdList) { - /** Trying to plot the PDB file **/ - try { - this.alignedPDBViewer.refresh(modelsIdList); - } catch (e) { - console.log(e); - } +ExperimentHeaderForm.prototype.getTopPanel = function(experiment) { + return { + xtype : 'container', + layout : 'hbox', + items : [ { + margin : "0 0 0 0", + width : 475, + border : 0, + html : this.getHTMLSource(experiment) + }, { + margin : "0 0 0 0", + width : 475, + border : 0, + html : this.getHTMLDownload(experiment) + } ] + }; }; -SuperpositionForm.prototype._renderAbinitio = function(modelsIdList) { - /** Trying to plot the PDB file **/ - try { - var viz = []; - for (var i = 0; i < modelsIdList.length; i++) { - viz.push({ - modelId : modelsIdList[i], - color : "0xFF6600", - opacity : 0.8 +ExperimentHeaderForm.prototype.getButton = function(experiment) { + var _this = this; + return Ext.create('Ext.Button', { + text : 'EDIT', + minWidth : '100', + margin : '10 0 0 30', + handler : function() { + var experimentWindow = new ExperimentWindow(); + experimentWindow.onSaved.attach(function(sender, data) { + _this.experiment.json.name = data.name; + _this.experiment.json.type = data.type; + _this.experiment.json.comments = data.comments; + _this.panel.remove(_this.panel.items.items[0]); + _this.panel.remove(_this.panel.items.items[0]); + _this.panel.insert(_this.getTopPanel(_this.experiment)); + _this.panel.insert(_this.getBottomPanel(_this.experiment)); }); + experimentWindow.show(experiment); } - this.abinitioPDBViewer.refresh(viz); - } catch (e) { - console.log(e); - } + }); }; -SuperpositionForm.prototype._getItems = function() { - var _this = this; - return [ { - xtype : 'label', - forId : 'myFieldId', - text : 'INLINE HELP: To be updated', - margin : '15 0 20 10', - cls : "inline-help" - }, this.superpositionGrid.getPanel(), { +ExperimentHeaderForm.prototype.getBottomPanel = function(experiment) { + return { xtype : 'container', layout : 'hbox', - items : [ -// { -// xtype : 'container', -// layout : 'vbox', -// items : [ this.abinitioPDBViewer.getPanel(), this.aprioriPDBViewer.getPanel() -// -// ] -// }, - - this.alignedPDBViewer.getPanel() ] - } ] - -}; - -SuperpositionForm.prototype._getButtons = function() { - return []; + margin : '10 0 0 0', + items : [ this.getComments(experiment), this.getButton(experiment) ] + }; }; -SuperpositionForm.prototype.getPanel = function() { - var _this = this; - this.panel = Ext.create('Ext.form.Panel', { - width : this.width, - height : this.height, - margin : 10, - border : 1, - defaultType : 'textfield', - items : this._getItems(), - buttons : this._getButtons(), - listeners : { - afterrender : function() { - _this._populate(); - - } - } - }); - return this.panel; +ExperimentHeaderForm.prototype.getComments = function(experiment) { + return { + xtype : 'textareafield', + labelStyle : 'font-weight: bold;', + name : 'comments', + fieldLabel : 'Comments', + labelWidth : 70, + height : 40, + minWidth : '450', + readOnly : true, + value : experiment.json.comments + }; }; -/** Populates could be call when the DOM is not filled yet **/ -SuperpositionForm.prototype._populate = function() { -}; +ExperimentHeaderForm.prototype.getPanel = function(experiment) { + this.experiment = experiment; + + if (experiment.json.type == 'CALIBRATION') { + this.backgroundColor = '#EFFBFB'; + } + if (experiment.json.type == 'TEMPLATE') { + this.backgroundColor = '#E0F8E6'; + } -/** It populates the form * */ -SuperpositionForm.prototype.refresh = function(subtractions) { - this.subtractions = subtractions; - this.superpositionGrid.refresh(subtractions); + this.panel = Ext.create('Ext.container.Container', { + frame : false, + layout : 'vbox', + padding : 5, + bodyPadding : '5 5 0 0', + width : 890, + margin : '0 0 10 0', + height : 120, + style : { + borderColor : '#99bce8', + borderStyle : 'solid', + borderWidth : '1px', + 'background-color' : this.backgroundColor + }, + fieldDefaults : { + msgTarget : 'side', + labelWidth : 100 + }, + items : [ this.getTopPanel(experiment), this.getBottomPanel(experiment) ] + }); + return this.panel; }; -SuperpositionForm.prototype.input = function() { - return {}; +ExperimentHeaderForm.prototype.input = function() { + return { + experiment : DATADOC.getExperiment_10() + }; }; -/** It populates the form **/ -SuperpositionForm.prototype.test = function(targetId) { - var macromoleculeForm = new SuperpositionForm(); - var panel = macromoleculeForm.getPanel(); +ExperimentHeaderForm.prototype.test = function(targetId) { + var experimentHeaderForm = new ExperimentHeaderForm(); + var panel = experimentHeaderForm.getPanel(new Experiment(experimentHeaderForm.input().experiment)); panel.render(targetId); + }; /** - * Example form + * Macromolecule form with the general parameters of a macromolecule * * @witdh * @height + * + * #onSave button save has been clicked + * #onClose button close has been clicked */ -function AssemblyForm(args) { +function MacromoleculeForm(args) { this.id = BUI.id(); this.width = 700; this.height = 500; @@ -6059,15944 +7053,9793 @@ function AssemblyForm(args) { this.height = args.height; } } - - this.molarityGrid = new MolarityGrid({height : this.height - 50}); + + /** Events **/ + this.onSave = new Event(this); + this.onClose = new Event(this); } -AssemblyForm.prototype._getItems = function() { - return [ { - xtype : 'label', - forId : 'myFieldId', - text : 'List of previously defined macromolecules present in the assembly. This information will be used for additional cross-checks where possible', - margin : '15 0 20 10', - cls : "inline-help" - }, this.molarityGrid.getPanel() ]; -}; - -AssemblyForm.prototype._getButtons = function() { - return []; +/** Type : is the Ext type then requiredtext or textfield * */ +MacromoleculeForm.prototype._getFieldTextWithHelp = function(type, fieldLabel, fieldName, help) { + return Ext.create('Ext.container.Container', { + margin : "10 0 0 10", + items : [ { + xtype : type, + fieldLabel : fieldLabel, + name : fieldName, + id : this.id + fieldName + }, { + xtype : 'label', + forId : 'myFieldId', + text : help, + margin : "5 0 0 105", + cls : "inline-help" + } ] + }); }; -AssemblyForm.prototype.getPanel = function() { - this.panel = Ext.create('Ext.form.Panel', { - width : this.width, - height : this.height, - margin : 10, - border : 0, - defaultType : 'textfield', - items : this._getItems(), - buttons : this._getButtons() +MacromoleculeForm.prototype._getNumericWithHelp = function(type, fieldLabel, fieldName, help) { + return Ext.create('Ext.container.Container', { + margin : "0 0 0 10", + items : [ { + xtype : type, + fieldLabel : fieldLabel, + name : fieldName, + id : this.id + fieldName, + decimalPrecision : 6, + width : 220 + }, { + xtype : 'label', + forId : 'myFieldId', + text : help, + margin : "5 0 0 10", + cls : "inline-help" + } ] }); - return this.panel; }; - -/** It populates the form **/ -AssemblyForm.prototype.refresh = function(macromolecule) { - this.macromolecule = macromolecule; - this.molarityGrid.refresh(macromolecule); +MacromoleculeForm.prototype._getButtons = function() { + var _this = this; + return [ { + text : 'Save', + handler : function() { + _this._save(); + } + },{ + text : 'Close', + handler : function() { + _this.onClose.notify(); + + } + } ]; }; -AssemblyForm.prototype.input = function() { - return {}; +/** It persits the macromolecule in the database **/ +MacromoleculeForm.prototype._persist = function(macromoleculeId, acronym, name, molecularMass, extintionCoefficient, comments, refractiveIndex, solventViscosity) { + + /** Checking not duplicated acronym **/ + if (((macromoleculeId == null) && (BIOSAXS.proposal.getMacromoleculeByAcronym(acronym) != null))== true){ + BUI.showError("Duplicated acronym"); + return; + } + + + if (macromoleculeId == null){ + /** new macromolecule **/ + this.macromolecule = {}; + this.macromolecule.macromoleculeId = null; + } + else{ + this.macromolecule.macromoleculeId = macromoleculeId; + } + + this.macromolecule["acronym"] = acronym; + this.macromolecule["name"] = name; + this.macromolecule["molecularMass"] = molecularMass; + this.macromolecule["extintionCoefficient"] = extintionCoefficient; + this.macromolecule["comments"] = comments; + this.macromolecule["symmetry"] = Ext.getCmp(this.id + 'comboSym').getValue(); + this.macromolecule["refractiveIndex"] = refractiveIndex; + this.macromolecule["solventViscosity"] = solventViscosity; + + var _this = this; + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, proposal) { + /** Updating the proposal global object **/ + BIOSAXS.proposal.setItems(proposal); + _this.panel.setLoading(false); + + var saved = BIOSAXS.proposal.getMacromoleculeByAcronym(acronym); + /** Refreshing to check that everything is ok **/ + _this.refresh(saved); + _this.onSave.notify(saved); + }); + + this.panel.setLoading("Saving Macromolecule") + adapter.saveMacromolecule(this.macromolecule); }; - -AssemblyForm.prototype.test = function(targetId) { - var assemblyForm = new AssemblyForm(); +/** Save the macromolecule in the DB **/ +MacromoleculeForm.prototype._save = function() { - var panel = assemblyForm.getPanel(); - panel.render(targetId); -}; -/** - * Edit the information of a buffer - * - * #onSaved - * #onRemoveAdditive - */ -function BufferForm() { var _this = this; + + var acronym = this._getField("acronym"); + var name = this._getField("name"); + var molecularMass = this._getField("molecularMass"); + var extintionCoefficient = this._getField("extintionCoefficient"); + var comments = this._getField("comments"); + + var refractiveIndex = this._getField("refractiveIndex"); + var solventViscosity = this._getField("solventViscosity"); + + /** Checking required fields **/ + if (name == "") { + BUI.showError("Name field is mandatory"); + return; + } + if (acronym == "") { + BUI.showError("Acroynm field is mandatory"); + return; + } - this.additiveGrid = new AdditiveGrid(); - this.additiveGrid.onRemoveButtonClicked.attach(function(sender, args) { - _this.onRemoveAdditive.notify(args); - }); + if (this.macromolecule != null){ + /** Checking if it is a new macromolecule **/ + if (this.macromolecule.macromoleculeId == null){ + /** Check if the acronym exists already **/ + this._persist(null, acronym, name, molecularMass, extintionCoefficient, comments, refractiveIndex, solventViscosity); + } + else{ + /** It is an update **/ + this._persist(this.macromolecule.macromoleculeId, acronym, name, molecularMass, extintionCoefficient, comments, refractiveIndex, solventViscosity); + } + } + else{ + /** It is a new macromolecule **/ + this._persist(null, acronym, name, molecularMass, extintionCoefficient, comments, refractiveIndex, solventViscosity); + } +}; - this.onSaved = new Event(this); - this.onRemoveAdditive = new Event(this); -} -BufferForm.prototype.getBuffer = function() { - this.buffer.name = Ext.getCmp("buffer_name").getValue(); - this.buffer.acronym = Ext.getCmp("buffer_acronym").getValue(); - this.buffer.comments = Ext.getCmp("buffer_comments").getValue(); - this.buffer.ph = Ext.getCmp("buffer_ph").getValue(); - this.buffer.composition = Ext.getCmp("buffer_composition").getValue(); - this.buffer.bufferhasadditive3VOs = this.additiveGrid.getAdditives(); - return this.buffer; -}; -BufferForm.prototype._getTopPanel = function() { - return { - xtype : 'container', - layout : 'hbox', - border : 0, - frame : true, - items : [ { - xtype : 'container', - layout : 'hbox', - items : [ { +MacromoleculeForm.prototype._getItems = function() { + var _this = this; + /** Symmetry combo box **/ + var symmetry = Ext.create('Ext.data.Store', { + fields : [ 's' ], + data : this._getSymmetries() + }); + + this.symmetryComboBox = Ext.create('Ext.form.ComboBox', { + fieldLabel : 'Symmetry', + store : symmetry, + id : this.id + 'comboSym', + queryMode : 'local', + displayField : 's', + valueField : 's', + value : "P1", + margin : "0 0 0 30", + width : 220 + }); + + return [this._getFieldTextWithHelp("requiredtext", "Name", "name", "Long name. i.e: Bovine serum albumin"), + this._getFieldTextWithHelp("requiredtext", "Acronym", "acronym", "Acronym will be used in the files and analisys. i.e: BSA"), + this._getFieldTextWithHelp("textfield", "Mol. Mass (Da)", "molecularMass", "Atomic mass estimation measured in Da"), + { xtype : 'container', - flex : 1, - border : false, - layout : 'anchor', - defaultType : 'textfield', - items : [ { - xtype : 'requiredtext', - id : 'buffer_name', - fieldLabel : 'Name', - name : 'name', - width : '200px', - value : this.buffer.name - }, { - xtype : 'requiredtext', - id : 'buffer_acronym', - fieldLabel : 'Acronym', - name : 'acronym', - width : '200px', - value : this.buffer.acronym - } ] - } ] - }, { - xtype : 'container', - flex : 1, - layout : 'anchor', - defaultType : 'textfield', - margin : '0 0 0 10', - items : [ { - id : 'buffer_ph', - fieldLabel : 'pH', - name : 'ph', - value : this.buffer.ph, - xtype : 'numberfield', - width : 200, - minValue : 0, - maxValue : 15 - }, { - xtype : 'requiredtext', - id : 'buffer_composition', - fieldLabel : 'Composition', - name : 'composition', - width : 200, - value : this.buffer.composition - } ] - } ] - }; + layout : 'hbox', + margin : "10 0 0 0", + items :[ + this._getNumericWithHelp("numberfield", "Extinction coef.", "extintionCoefficient", ""), + this.symmetryComboBox + ] + }, + { + xtype : 'container', + layout : 'hbox', + margin : "5 0 0 0", + items :[ + this._getNumericWithHelp("numberfield", "Refractive Index", "refractiveIndex", "How radiation propagates through the medium"), + this._getNumericWithHelp("numberfield", "Solvent Viscosity", "solventViscosity", "") + ] + }, + { + id : this.id + "comments", + xtype : 'textareafield', + name : 'comments', + margin : '35 0 0 10', + fieldLabel : 'Comments', + width : this.width - 100 + } ]; }; -BufferForm.prototype.getPanel = function(buffer) { - this.buffer = buffer; - this.panel = Ext.createWidget({ - xtype : 'container', - layout : 'vbox', - style : { - padding : '10px' - }, - fieldDefaults : { - labelAlign : 'left', - labelWidth : 50 +MacromoleculeForm.prototype._getSymmetries = function() { + return [ { + "s" : "P1" + }, { + "s" : "P2" + }, { + "s" : "P3" + }, { + "s" : "P4" + }, { + "s" : "P5" + }, { + "s" : "P6" + }, { + "s" : "P32" + }, { + "s" : "P42" + }, { + "s" : "P52" + }, { + "s" : "P62" + }, { + "s" : "P222" + } ] +}; - }, - items : [ this._getTopPanel(), { - id : 'buffer_comments', - xtype : 'textareafield', - name : 'comments', - fieldLabel : 'Comments', - width : '100%', - value : buffer.comments - }, this.additiveGrid.getPanel(buffer) ] +MacromoleculeForm.prototype.getPanel = function() { + this.panel = Ext.create('Ext.form.Panel', { + width : this.width, + height : this.height, + margin : 10, + border : 1, + defaultType : 'textfield', + items : this._getItems(), + buttons : this._getButtons() }); return this.panel; }; -BufferForm.prototype.input = function() { - return { - buffer : { - "bufferId" : 422, - "proposalId" : 10, - "safetyLevelId" : null, - "name" : "B1", - "acronym" : "B1", - "ph" : null, - "composition" : null, - "bufferhasadditive3VOs" : [], - "comments" : null - } - }; + +/** Populates each text field by field name and value **/ +MacromoleculeForm.prototype._populateField = function(fieldName, value) { + if (value != null){ + Ext.getCmp(this.id + fieldName).setValue(value); + } }; -BufferForm.prototype.test = function(targetId) { - var bufferForm = new BufferForm(); - var panel = bufferForm.getPanel(bufferForm.input().buffer); - panel.render(targetId); +/** Gets the value of a textfield **/ +MacromoleculeForm.prototype._getField = function(fieldName) { + return Ext.getCmp(this.id + fieldName).getValue(); }; - -/** - * @showTitle - * - * #onSaved - * #onAddPlates - * #onRemovePlates - **/ -function CaseForm(args) { - this.width = 700; - this.showTitle = true; - if (args != null) { - if (args.showTitle != null) { - this.showTitle = args.showTitle; - } - } - var _this = this; - this.stockSolutionGrid = new StockSolutionGrid({ - width : this.width - 10, - minHeight : 300, - height : 300, - tbar : true, - showTitle : true, - isPackedVisible : false, - btnAddExisting : true, - btnRemoveVisible : false, - btnUnpackVisible : true - }); - /** When selecting existing stock solutions **/ - this.stockSolutionGrid.onStockSolutionSelected.attach(function(sender, stockSolutions) { - if (stockSolutions != null) { - for ( var i = 0; i < stockSolutions.length; i++) { - _this.saveStockSolution(stockSolutions[i]); - } +/** It populates the form **/ +MacromoleculeForm.prototype.refresh = function(macromolecule) { + this.macromolecule = macromolecule; + if (macromolecule != null){ + this._populateField("name", macromolecule.name); + this._populateField("acronym", macromolecule.acronym); + this._populateField("extintionCoefficient", macromolecule.extintionCoefficient); + this._populateField("molecularMass", macromolecule.molecularMass); + this._populateField("comments", macromolecule.comments); + this._populateField("refractiveIndex", macromolecule.refractiveIndex); + this._populateField("solventViscosity", macromolecule.solventViscosity); + if (macromolecule.symmetry != null){ + Ext.getCmp(this.id + 'comboSym').setValue(macromolecule.symmetry); } - }); + } +}; - /** it can be because it has been added a new one or removed **/ - this.stockSolutionGrid.onProposalChanged.attach(function(sender, stockSolution) { - if (stockSolution != null) { - _this.saveStockSolution(stockSolution); - } else { - BIOSAXS.proposal.onInitialized.attach(function() { - _this.stockSolutionGrid.refresh(BIOSAXS.proposal.getStockSolutionsByDewarId(_this.dewar.dewarId)); - _this.stockSolutionGrid.grid.setLoading(false); - }); - BIOSAXS.proposal.init(); +MacromoleculeForm.prototype.input = function() { + return {}; +}; - } +/** It populates the form **/ +MacromoleculeForm.prototype.test = function(targetId) { + var macromoleculeForm = new MacromoleculeForm(); + macromoleculeForm.onClose.attach(function(sender){ + alert("Click on close"); }); + var panel = macromoleculeForm.getPanel(); + panel.render(targetId); +}; - this.onSaved = new Event(this); - this.onAddPlates = new Event(this); - this.onRemovePlates = new Event(this); -} -CaseForm.prototype.saveStockSolution = function(stockSolution) { - var _this = this; - var adapter = new BiosaxsDataAdapter(); - this.stockSolutionGrid.grid.setLoading("ISPyB: setting case to Stock solution"); - adapter.onSuccess.attach(function(sender, stockSolution) { - BIOSAXS.proposal.onInitialized.attach(function() { - _this.stockSolutionGrid.refresh(BIOSAXS.proposal.getStockSolutionsByDewarId(_this.dewar.dewarId)); - _this.stockSolutionGrid.grid.setLoading(false); - }); - BIOSAXS.proposal.init(); - }); - adapter.onError.attach(function(sender, data) { - _this.stockSolutionGrid.grid.setLoading(false); - BUI.showError(data); - }); - stockSolution.boxId = _this.dewar.dewarId; - adapter.saveStockSolution(stockSolution); -}; - -CaseForm.prototype.fillStores = function() { - var _this = this; - this.panel.setLoading("Loading Labcontacts from database"); + - var proposal = BUI.getProposal(); - proposal.onDataRetrieved.attach(function(sender, data) { - _this.labContactForSendingStore.loadData(data, false); - _this.labContactForReturnStore.loadData(data, false); - _this.panel.setLoading(false); - }); - proposal.getLabContactsByProposalId(); +function ModelVisualizerForm(args){ + this.id =BUI.id(); + this.width = 600; + this.height = 400; + if (args!= null){ + if (args.width != null){ + this.width = args.width; + } + if (args.height != null){ + this.height = args.height; + } + } }; -CaseForm.prototype.refresh = function(dewar) { - this.setDewar(dewar); - this.stockSolutionGrid.refresh(BIOSAXS.proposal.getStockSolutionsByDewarId(dewar.dewarId)); +ModelVisualizerForm.prototype._getFirHTML = function(modelId, width, height, type, desc) { + var html = ""; + html = html + ''; + html = html + ''; + html = html + ''; + html = html + '
dammin.' + type + '
' + desc + '
'; + return html; }; -CaseForm.prototype.getDewar = function() { - this.dewar.code = Ext.getCmp("dewar_code").getValue(); - this.dewar.comments = Ext.getCmp("dewar_comments").getValue(); - this.dewar.trackingNumberFromSynchrotron = Ext.getCmp("dewar_trackingNumberFromSynchrotron").getValue(); - this.dewar.trackingNumberToSynchrotron = Ext.getCmp("dewar_trackingNumberToSynchrotron").getValue(); - this.dewar.transportValue = Ext.getCmp("dewar_transportValue").getValue(); - this.dewar.storageLocation = Ext.getCmp("dewar_storageLocation").getValue(); - this.dewar.firstExperimentId = this.macromoleculeCombo.getValue(); - return this.dewar; +ModelVisualizerForm.prototype.getItems = function(modelPanel){ + _this = this; + var height = _this.height/2 - 60; + var width = _this.width/2 - 10; + + return Ext.create('Ext.container.Container', { + layout: { + type: 'vbox', // Arrange child items vertically + }, + items: [ + modelPanel, + { + xtype : 'container', + layout: { + type: 'hbox', // Arrange child items vertically + }, + items : [{ + html : this._getFirHTML("id", width, height, "fir", "Fit of the simulated scattering curve versus a smoothed experimental data (spline interpolation)"), + height :height, + width : width, + padding: 2 + }, + { + html : this._getFirHTML("id", width, height, "fit", "Fit of the simulated scattering curve versus the experimental data."), + height : height, + width : width, + padding: 2 + }] + } + + ] + }); }; -CaseForm.prototype.setDewar = function(dewar) { - this.dewar = dewar; - Ext.getCmp("dewar_code").setValue(this.dewar.code); - Ext.getCmp("dewar_dewarStatus").setText(new String(this.dewar.dewarStatus).toUpperCase()); - Ext.getCmp("dewar_comments").setValue(this.dewar.comments); - Ext.getCmp("dewar_trackingNumberFromSynchrotron").setValue(this.dewar.trackingNumberFromSynchrotron); - Ext.getCmp("dewar_trackingNumberToSynchrotron").setValue(this.dewar.trackingNumberToSynchrotron); - Ext.getCmp("dewar_transportValue").setValue(this.dewar.transportValue); - Ext.getCmp("dewar_storageLocation").setValue(this.dewar.storageLocation); - if (dewar.sessionVO != null) { - this.macromoleculeCombo.setValue(dewar.sessionVO.sessionId); +ModelVisualizerForm.prototype.refresh = function(models){ + var input = []; +// var colors = ["008000", "F0A804", "0000FF", "800080", "C0C0C0"]; + for (var i = 0; i < models.length; i++) { + console.log(BUI.rainbow(models.length, i).replace("#", "0x")); + input.push({ + color: BUI.rainbow(models.length, i).replace("#", "0x"), + modelId: models[i].modelId, + opacity: 0.8, + radius: 3, + title: BUI.getFileNameByPath(models[i].pdbFile), + type: "SHAPEDETERMINATIONMODEL" + + }); } + + this.panel.removeAll(); + this.panel.add( + _this.getItems( + new PDBViewer({ + width : this.width - 10, + height : (this.height/2) - 10, + title : "" + }).draw(input) + ) + ); + + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, data) { + var splitted = data.toString().split("\n"); + var array = []; + for ( var i = 0; i < splitted.length; i++) { + var line = splitted[i].trim(); + var line_splited = line.split(/\s*[\s,]\s*/); + if (line_splited.length == 4) { + array.push([ Number(line_splited[0]), BUI.getStvArray(line_splited[1], line_splited[2]), BUI.getStvArray(line_splited[3], 0) ]); + } + } + + var id = (_this.id + "firid"); + var dygraphWidget = new StdDevDyGraph(id, { + width : (_this.width/2) - 10, + height :(_this.height/2) -110, + xlabel : 'q(nm-1)' + }); + dygraphWidget.draw(array, [ "#FF0000", "#0000FF", "#FF00FF" ], [ 's', 'I(exp)', 'I(sim)' ]); + }); + + adapter.getModelFile("FIR", models[0].modelId); + + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, data) { + var splitted = data.toString().split("\n"); + var array = []; + for ( var i = 0; i < splitted.length; i++) { + var line = splitted[i].trim(); + var line_splited = line.split(/\s*[\s,]\s*/); + if (line_splited.length == 3) { + array.push([ Number(line_splited[0]), BUI.getStvArray(line_splited[1], 0), BUI.getStvArray(line_splited[2], 0) ]); + } + } + + var id = (_this.id + "fitid"); + + var dygraphWidget = new StdDevDyGraph(id, { + width : (_this.width/2) - 10, + height : (_this.height/2) - 110, + xlabel : 'q(nm-1)' + }); + dygraphWidget.draw(array, [ "#FF0000", "#0000FF" ], [ "s", "I(exp)", "I(sim)" ]); + }); + adapter.getModelFile("FIT", models[0].modelId); }; -CaseForm.prototype.getSessionCombo = function() { - this.macromoleculeCombo = BIOSAXS_COMBOMANAGER.getComboSessions(BIOSAXS.proposal.getSessions(), { - labelWidth : 100, - margin : '5 0 00 0', - width : 250 +ModelVisualizerForm.prototype.getPanel = function(modelList){ + _this = this; + this.modelList = modelList; + this.panel = Ext.create('Ext.Panel', { + title: 'Results', + width: this.width, + height: this.height, + layout: { + type: 'vbox', // Arrange child items vertically +// align: 'stretch' // Each takes up full width + }, + items: [ + + ], + listeners : { + afterrender : function(grid, eOpts) { +// alert(_this.modelList) + } + } }); - return this.macromoleculeCombo; + + return this.panel; + }; -CaseForm.prototype.getInformationPanel = function() { - if (this.panel == null) { - this.informationPanel = Ext.create('Ext.form.Panel', { - width : this.width - 10, - border : 0, - items : [ { - xtype : 'container', - margin : "2 2 2 2", - collapsible : false, - defaultType : 'textfield', - layout : 'anchor', - items : [ { - xtype : 'container', - layout : 'hbox', - items : [ { - xtype : 'requiredtext', - fieldLabel : 'Code', - allowBlank : false, - name : 'code', - id : 'dewar_code', - anchor : '50%' - }, { - xtype : 'label', - margin : '0 0 0 20', - readOnly : true, - id : 'dewar_dewarStatus', - anchor : '50%' - } ] - }, this.getSessionCombo(), { - margin : '5 0 0 0', - xtype : 'textareafield', - name : 'comments', - id : 'dewar_comments', - width : this.width - 50, - fieldLabel : 'Comments' - } ] - }, { - xtype : 'fieldset', - title : 'Courier Accounts Details for Return', - collapsible : false, - layout : 'anchor', - defaults : { - anchor : '100%' - }, - items : [ { - xtype : 'container', - layout : 'hbox', - items : [ { - xtype : 'textfield', - labelWidth : 200, - width : 280, - fieldLabel : 'Track Number From Synchrotron', - id : 'dewar_trackingNumberFromSynchrotron' - }, { - xtype : 'numberfield', - width : 190, - labelWidth : 110, - margin : '0 0 0 30', - fieldLabel : 'Transport Value', - id : 'dewar_transportValue' - } - - ] - }, { - xtype : 'container', - layout : 'hbox', - margin : '10 0 0 0', - items : [ - - { - xtype : 'textfield', - labelWidth : 200, - width : 280, - fieldLabel : 'Track Number To Synchrotron', - id : 'dewar_trackingNumberToSynchrotron' - }, { - xtype : 'textfield', - margin : '0 0 0 30', - width : 190, - labelWidth : 110, - fieldLabel : 'Storage Location', - id : 'dewar_storageLocation' - } - - ] - } ] - } ] - }); - } - - return this.informationPanel; -}; - -CaseForm.prototype.getPanel = function(dewar) { - this.dewar = dewar; - this.panel = Ext.createWidget({ - xtype : 'container', - layout : 'vbox', - width : this.width, - border : 0, - items : [ { - items : { - xtype : "container", - layout : "vbox", - margin : "5 5 5 5", - items : [ this.getInformationPanel(dewar), this.stockSolutionGrid.getPanel() ] - - } - } ] - }); - - this.refresh(dewar); - return this.panel; - -}; - -CaseForm.prototype.input = function() { - return { - dewar : { - "dewarId" : 305861, - "code" : "ESRF-TEST", - "comments" : "comments", - "storageLocation" : "FRIDGE", - "dewarStatus" : "opened", - "timeStamp" : null, - "isStorageDewar" : null, - "barCode" : "ESRF305861", - "customsValue" : null, - "transportValue" : null, - "trackingNumberToSynchrotron" : "3333", - "trackingNumberFromSynchrotron" : "224466", - "type" : "Dewar", - "sessionVO" : { - "sessionId" : 31697, - "expSessionPk" : null, - "projectCode" : null, - "startDate" : "2012 07 21", - "endDate" : "2012 07 23", - "beamlineName" : "BM29", - "scheduled" : 1, - "nbShifts" : 2, - "comments" : null, - "beamlineOperator" : "PERNOT P", - "usedFlag" : null, - "sessionTitle" : null, - "structureDeterminations" : null, - "dewarTransport" : null, - "databackupFrance" : null, - "databackupEurope" : null, - "visit_number" : null, - "operatorSiteNumber" : "14061", - "timeStamp" : "2012 04 25" - } - }, - proposal : { - "assemblies" : [], - "sessions" : [ { - "sessionId" : 31697, - "expSessionPk" : null, - "projectCode" : null, - "startDate" : "2012 07 21", - "endDate" : "2012 07 23", - "beamlineName" : "BM29", - "scheduled" : 1, - "nbShifts" : 2, - "comments" : null, - "beamlineOperator" : "PERNOT P", - "usedFlag" : null, - "sessionTitle" : null, - "structureDeterminations" : null, - "dewarTransport" : null, - "databackupFrance" : null, - "databackupEurope" : null, - "visit_number" : null, - "operatorSiteNumber" : "14061", - "timeStamp" : "2012 04 25" - } ], - "labcontacts" : [ { - "labContactId" : 787, - "personVO" : { - "personId" : 304252, - "personUUID" : null, - "familyName" : "KIM", - "givenName" : "Henry", - "title" : null, - "emailAddress" : "henry-sung.kim@ibs.fr", - "phoneNumber" : "", - "login" : "", - "passwd" : "", - "faxNumber" : "" - }, - "cardName" : "KIM-Institut de Bio", - "defaultCourrierCompany" : "22", - "courierAccount" : "", - "billingReference" : "", - "dewarAvgCustomsValue" : 0, - "dewarAvgTransportValue" : 0 - } ], - "buffers" : [ { - "bufferId" : 811, - "proposalId" : 3124, - "safetyLevelId" : null, - "name" : "EDTA", - "acronym" : "EDTA", - "ph" : null, - "composition" : "", - "bufferhasadditive3VOs" : [], - "comments" : "" - }, { - "bufferId" : 810, - "proposalId" : 3124, - "safetyLevelId" : null, - "name" : "HEPES", - "acronym" : "HEPES", - "ph" : null, - "composition" : "", - "bufferhasadditive3VOs" : [], - "comments" : "" - } ], - "shippings" : [ { - "shippingId" : 304107, - "shippingName" : "TEST", - "deliveryAgentAgentName" : null, - "deliveryAgentShippingDate" : null, - "deliveryAgentDeliveryDate" : null, - "deliveryAgentAgentCode" : null, - "deliveryAgentFlightCode" : null, - "shippingStatus" : "opened", - "timeStamp" : "2013 09 25", - "laboratoryId" : null, - "isStorageShipping" : null, - "creationDate" : "2013 09 25", - "comments" : "test", - "sendingLabContactVO" : { - "labContactId" : 787, - "personVO" : { - "personId" : 304252, - "personUUID" : null, - "familyName" : "KIM", - "givenName" : "Henry", - "title" : null, - "emailAddress" : "henry-sung.kim@ibs.fr", - "phoneNumber" : "", - "login" : "", - "passwd" : "", - "faxNumber" : "" - }, - "cardName" : "KIM-Institut de Bio", - "defaultCourrierCompany" : "22", - "courierAccount" : "", - "billingReference" : "", - "dewarAvgCustomsValue" : 0, - "dewarAvgTransportValue" : 0 - }, - "returnLabContactVO" : { - "labContactId" : 787, - "personVO" : { - "personId" : 304252, - "personUUID" : null, - "familyName" : "KIM", - "givenName" : "Henry", - "title" : null, - "emailAddress" : "henry-sung.kim@ibs.fr", - "phoneNumber" : "", - "login" : "", - "passwd" : "", - "faxNumber" : "" - }, - "cardName" : "KIM-Institut de Bio", - "defaultCourrierCompany" : "22", - "courierAccount" : "", - "billingReference" : "", - "dewarAvgCustomsValue" : 0, - "dewarAvgTransportValue" : 0 - }, - "returnCourier" : null, - "dateOfShippingToUser" : null, - "shippingType" : "DewarTracking", - "dewarVOs" : [ { - "dewarId" : 305861, - "code" : "ESRF-TEST", - "comments" : "comments", - "storageLocation" : "FRIDGE", - "dewarStatus" : "opened", - "timeStamp" : null, - "isStorageDewar" : null, - "barCode" : "ESRF305861", - "customsValue" : null, - "transportValue" : null, - "trackingNumberToSynchrotron" : "3333", - "trackingNumberFromSynchrotron" : "224466", - "type" : "Dewar", - "sessionVO" : { - "sessionId" : 31697, - "expSessionPk" : null, - "projectCode" : null, - "startDate" : "2012 07 21", - "endDate" : "2012 07 23", - "beamlineName" : "BM29", - "scheduled" : 1, - "nbShifts" : 2, - "comments" : null, - "beamlineOperator" : "PERNOT P", - "usedFlag" : null, - "sessionTitle" : null, - "structureDeterminations" : null, - "dewarTransport" : null, - "databackupFrance" : null, - "databackupEurope" : null, - "visit_number" : null, - "operatorSiteNumber" : "14061", - "timeStamp" : "2012 04 25" - } - } ] - } ], - "macromolecules" : [ { - "macromoleculeId" : 5933, - "safetylevelId" : null, - "proposalId" : 3124, - "name" : "A", - "acronym" : "A", - "molecularMass" : "", - "extintionCoefficient" : "", - "sequence" : null, - "creationDate" : null, - "comments" : "", - "macromoleculeregion3VOs" : [], - "stoichiometry3VOsForHostMacromoleculeId" : [], - "structure3VOs" : [] - } ], - "stockSolutions" : [ { - "stockSolutionId" : 6, - "proposalId" : 3124, - "macromoleculeId" : 5933, - "bufferId" : 811, - "instructionSet3VO" : null, - "boxId" : 305861, - "storageTemperature" : "20", - "volume" : "300", - "concentration" : "1.2", - "comments" : "Buffer EDTA with A", - "name" : "A_EDTA_1.2", - "samples" : [] - } ] - } - - }; -}; - -CaseForm.prototype.test = function(targetId) { - var caseForm = new CaseForm(); - BIOSAXS.proposal = new Proposal(caseForm.input().proposal); - var panel = caseForm.getPanel(caseForm.input().dewar); - panel.render(targetId); -}; - -/** - * Example form - * - * @witdh - * @height - */ -function ExampleForm(args) { - this.id = BUI.id(); - this.width = 700; - this.height = 500; - - if (args != null) { - if (args.width != null) { - this.width = args.width; - } - if (args.height != null) { - this.height = args.height; - } - } -} - -ExampleForm.prototype._getItems = function() { - return [{ - fieldLabel : 'First Name', - name : 'first', - allowBlank : false - }, { - fieldLabel : 'Last Name', - name : 'last', - allowBlank : false - } ]; -}; - -ExampleForm.prototype._getItems = function() { - return [ ]; -}; - -ExampleForm.prototype._getButtons = function() { - return [ ]; -}; - -ExampleForm.prototype.getPanel = function() { - this.panel = Ext.create('Ext.form.Panel', { - width : this.width, - height : this.height, - margin : 10, - border : 1, - defaultType : 'textfield', - items : this._getItems(), - buttons : this._getButtons() - }); - return this.panel; -}; - -/** It populates the form **/ -ExampleForm.prototype.refresh = function(macromolecule) { -}; - -function ExperimentForm(args) { - this.id = BUI.id(); - - if (args != null) { - } - - this.onSaved = new Event(this); -} - -ExperimentForm.prototype._getItems = function(experiment) { - this.experiment = experiment; - var typeCombo = Ext.create('Ext.form.ComboBox', { - id : this.id + 'type', - fieldLabel : 'Type', - store : [ "STATIC", "CALIBRATION", "HPLC" ], - queryMode : 'local', - labelWidth : 120, - displayField : 'name', - valueField : 'name', - value : experiment.json.type, - disabled : (experiment.json.type == 'TEMPLATE') - }); - - var items = []; - - - if (experiment.json.type == "HPLC" ){ - var typeMacromolecule = BIOSAXS_COMBOMANAGER.getComboMacromoleculeByMacromolecules(BIOSAXS.proposal.getMacromolecules(), {labelWidth : 120, width: "120px"}); - if (experiment.getHPLCMacromolecule() != null){ - typeMacromolecule.setValue(experiment.getHPLCMacromolecule().macromoleculeId); - items.push(typeMacromolecule); - } - } - - - items.push(typeCombo, { - id : this.id + 'name', - xtype : 'textfield', - fieldLabel : 'Name', - labelWidth : 120, - width : '100%', - value : experiment.json.name - }, { - id : this.id + 'comments', - xtype : 'textareafield', - name : 'comments', - fieldLabel : 'Comments', - labelWidth : 120, - height : 120, - width : '100%', - value : experiment.json.comments - }); - return items; -}; -ExperimentForm.prototype.getPanel = function(experiment) { - var _this = this; - - this.panel = Ext.createWidget({ - xtype : 'container', - layout : 'vbox', - border : 0, - style : { - padding : '10px' - }, - fieldDefaults : { - labelAlign : 'left', - labelWidth : 50 - }, - items : this._getItems(experiment) - }); - return this.panel; -}; - -ExperimentForm.prototype.input = function() { - return new ExperimentHeaderForm().input(); -}; - -ExperimentForm.prototype.test = function(targetId) { - var experimentForm = new ExperimentForm(); - var panel = experimentForm.getPanel(new Experiment(experimentForm.input().experiment)); - panel.render(targetId); -}; - -/** - * Shows the header for the experiments changing the color and parameters depending on experiment type - * - */ -function ExperimentHeaderForm(args) { - this.id = BUI.id(); - this.backgroundColor = '#FFFFFF'; -} - -ExperimentHeaderForm.prototype.getHTMLSource = function(experiment) { - var html = BUI.createFormLabel("Name :", experiment.json.name, 75, 400, this.backgroundColor); - if (experiment.json.type == "HPLC") { - if (experiment.getHPLCMacromolecule() != null){ - html = html + BUI.createFormLabel("Molecule :", experiment.getHPLCMacromolecule().acronym, 75, 400, this.backgroundColor); - } - } - else{ - html = html + BUI.createFormLabel("Type :", experiment.json.type, 75, 400, this.backgroundColor); - } - - html = html + BUI.createFormLabel("Date :", experiment.json.creationDate, 75, 400, this.backgroundColor); - return html; -}; - -ExperimentHeaderForm.prototype.getHTMLDownload = function(experiment) { - var bgcolor = "background-color:" + this.backgroundColor + ";"; - var html = "
"; - if ((experiment.json.type == "CALIBATION") || (experiment.json.type == "STATIC")) { - html = html + " Download Source File
"; - html = html - + ""; - } - if (experiment.json.type == "TEMPLATE") { - html = html - + " Download Source File"; - } - - if (experiment.json.type == "HPLC") { - html = html + " Download h5 File"; - } - - return html; -}; - -ExperimentHeaderForm.prototype.getTopPanel = function(experiment) { - return { - xtype : 'container', - layout : 'hbox', - items : [ { - margin : "0 0 0 0", - width : 475, - border : 0, - html : this.getHTMLSource(experiment) - }, { - margin : "0 0 0 0", - width : 475, - border : 0, - html : this.getHTMLDownload(experiment) - } ] - }; -}; - -ExperimentHeaderForm.prototype.getButton = function(experiment) { - var _this = this; - return Ext.create('Ext.Button', { - text : 'EDIT', - minWidth : '100', - margin : '10 0 0 30', - handler : function() { - var experimentWindow = new ExperimentWindow(); - experimentWindow.onSaved.attach(function(sender, data) { - _this.experiment.json.name = data.name; - _this.experiment.json.type = data.type; - _this.experiment.json.comments = data.comments; - _this.panel.remove(_this.panel.items.items[0]); - _this.panel.remove(_this.panel.items.items[0]); - _this.panel.insert(_this.getTopPanel(_this.experiment)); - _this.panel.insert(_this.getBottomPanel(_this.experiment)); - }); - experimentWindow.show(experiment); - } - }); -}; - -ExperimentHeaderForm.prototype.getBottomPanel = function(experiment) { - return { - xtype : 'container', - layout : 'hbox', - margin : '10 0 0 0', - items : [ this.getComments(experiment), this.getButton(experiment) ] - }; -}; - -ExperimentHeaderForm.prototype.getComments = function(experiment) { - return { - xtype : 'textareafield', - labelStyle : 'font-weight: bold;', - name : 'comments', - fieldLabel : 'Comments', - labelWidth : 70, - height : 40, - minWidth : '450', - readOnly : true, - value : experiment.json.comments - }; -}; - -ExperimentHeaderForm.prototype.getPanel = function(experiment) { - this.experiment = experiment; - - if (experiment.json.type == 'CALIBRATION') { - this.backgroundColor = '#EFFBFB'; - } - if (experiment.json.type == 'TEMPLATE') { - this.backgroundColor = '#E0F8E6'; - } - - this.panel = Ext.create('Ext.container.Container', { - frame : false, - layout : 'vbox', - padding : 5, - bodyPadding : '5 5 0 0', - width : 890, - margin : '0 0 10 0', - height : 120, - style : { - borderColor : '#99bce8', - borderStyle : 'solid', - borderWidth : '1px', - 'background-color' : this.backgroundColor - }, - fieldDefaults : { - msgTarget : 'side', - labelWidth : 100 - }, - items : [ this.getTopPanel(experiment), this.getBottomPanel(experiment) ] - }); - return this.panel; -}; - -ExperimentHeaderForm.prototype.input = function() { - return { - experiment : DATADOC.getExperiment_10() - }; -}; - -ExperimentHeaderForm.prototype.test = function(targetId) { - var experimentHeaderForm = new ExperimentHeaderForm(); - var panel = experimentHeaderForm.getPanel(new Experiment(experimentHeaderForm.input().experiment)); - panel.render(targetId); - -}; - -/** - * Macromolecule form with the general parameters of a macromolecule - * - * @witdh - * @height - * - * #onSave button save has been clicked - * #onClose button close has been clicked - */ -function MacromoleculeForm(args) { - this.id = BUI.id(); - this.width = 700; - this.height = 500; - - if (args != null) { - if (args.width != null) { - this.width = args.width; - } - if (args.height != null) { - this.height = args.height; - } - } - - /** Events **/ - this.onSave = new Event(this); - this.onClose = new Event(this); -} - -/** Type : is the Ext type then requiredtext or textfield * */ -MacromoleculeForm.prototype._getFieldTextWithHelp = function(type, fieldLabel, fieldName, help) { - return Ext.create('Ext.container.Container', { - margin : "10 0 0 10", - items : [ { - xtype : type, - fieldLabel : fieldLabel, - name : fieldName, - id : this.id + fieldName - }, { - xtype : 'label', - forId : 'myFieldId', - text : help, - margin : "5 0 0 105", - cls : "inline-help" - } ] - }); -}; - -MacromoleculeForm.prototype._getNumericWithHelp = function(type, fieldLabel, fieldName, help) { - return Ext.create('Ext.container.Container', { - margin : "0 0 0 10", - items : [ { - xtype : type, - fieldLabel : fieldLabel, - name : fieldName, - id : this.id + fieldName, - decimalPrecision : 6, - width : 220 - }, { - xtype : 'label', - forId : 'myFieldId', - text : help, - margin : "5 0 0 10", - cls : "inline-help" - } ] - }); -}; - -MacromoleculeForm.prototype._getButtons = function() { - var _this = this; - return [ { - text : 'Save', - handler : function() { - _this._save(); - } - },{ - text : 'Close', - handler : function() { - _this.onClose.notify(); - - } - } ]; -}; - -/** It persits the macromolecule in the database **/ -MacromoleculeForm.prototype._persist = function(macromoleculeId, acronym, name, molecularMass, extintionCoefficient, comments, refractiveIndex, solventViscosity) { - - /** Checking not duplicated acronym **/ - if (((macromoleculeId == null) && (BIOSAXS.proposal.getMacromoleculeByAcronym(acronym) != null))== true){ - BUI.showError("Duplicated acronym"); - return; - } - - - if (macromoleculeId == null){ - /** new macromolecule **/ - this.macromolecule = {}; - this.macromolecule.macromoleculeId = null; - } - else{ - this.macromolecule.macromoleculeId = macromoleculeId; - } - - this.macromolecule["acronym"] = acronym; - this.macromolecule["name"] = name; - this.macromolecule["molecularMass"] = molecularMass; - this.macromolecule["extintionCoefficient"] = extintionCoefficient; - this.macromolecule["comments"] = comments; - this.macromolecule["symmetry"] = Ext.getCmp(this.id + 'comboSym').getValue(); - this.macromolecule["refractiveIndex"] = refractiveIndex; - this.macromolecule["solventViscosity"] = solventViscosity; - - var _this = this; - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, proposal) { - /** Updating the proposal global object **/ - BIOSAXS.proposal.setItems(proposal); - _this.panel.setLoading(false); - - var saved = BIOSAXS.proposal.getMacromoleculeByAcronym(acronym); - /** Refreshing to check that everything is ok **/ - _this.refresh(saved); - _this.onSave.notify(saved); - }); - - this.panel.setLoading("Saving Macromolecule") - adapter.saveMacromolecule(this.macromolecule); -}; - -/** Save the macromolecule in the DB **/ -MacromoleculeForm.prototype._save = function() { - - var _this = this; - - var acronym = this._getField("acronym"); - var name = this._getField("name"); - var molecularMass = this._getField("molecularMass"); - var extintionCoefficient = this._getField("extintionCoefficient"); - var comments = this._getField("comments"); - - var refractiveIndex = this._getField("refractiveIndex"); - var solventViscosity = this._getField("solventViscosity"); - - /** Checking required fields **/ - if (name == "") { - BUI.showError("Name field is mandatory"); - return; - } - if (acronym == "") { - BUI.showError("Acroynm field is mandatory"); - return; - } - - if (this.macromolecule != null){ - /** Checking if it is a new macromolecule **/ - if (this.macromolecule.macromoleculeId == null){ - /** Check if the acronym exists already **/ - this._persist(null, acronym, name, molecularMass, extintionCoefficient, comments, refractiveIndex, solventViscosity); - } - else{ - /** It is an update **/ - this._persist(this.macromolecule.macromoleculeId, acronym, name, molecularMass, extintionCoefficient, comments, refractiveIndex, solventViscosity); - } - } - else{ - /** It is a new macromolecule **/ - this._persist(null, acronym, name, molecularMass, extintionCoefficient, comments, refractiveIndex, solventViscosity); - } -}; - - - -MacromoleculeForm.prototype._getItems = function() { - var _this = this; - /** Symmetry combo box **/ - var symmetry = Ext.create('Ext.data.Store', { - fields : [ 's' ], - data : this._getSymmetries() - }); - - this.symmetryComboBox = Ext.create('Ext.form.ComboBox', { - fieldLabel : 'Symmetry', - store : symmetry, - id : this.id + 'comboSym', - queryMode : 'local', - displayField : 's', - valueField : 's', - value : "P1", - margin : "0 0 0 30", - width : 220 - }); - - return [this._getFieldTextWithHelp("requiredtext", "Name", "name", "Long name. i.e: Bovine serum albumin"), - this._getFieldTextWithHelp("requiredtext", "Acronym", "acronym", "Acronym will be used in the files and analisys. i.e: BSA"), - this._getFieldTextWithHelp("textfield", "Mol. Mass (Da)", "molecularMass", "Atomic mass estimation measured in Da"), - { - xtype : 'container', - layout : 'hbox', - margin : "10 0 0 0", - items :[ - this._getNumericWithHelp("numberfield", "Extinction coef.", "extintionCoefficient", ""), - this.symmetryComboBox - ] - }, - { - xtype : 'container', - layout : 'hbox', - margin : "5 0 0 0", - items :[ - this._getNumericWithHelp("numberfield", "Refractive Index", "refractiveIndex", "How radiation propagates through the medium"), - this._getNumericWithHelp("numberfield", "Solvent Viscosity", "solventViscosity", "") - ] - }, - { - id : this.id + "comments", - xtype : 'textareafield', - name : 'comments', - margin : '35 0 0 10', - fieldLabel : 'Comments', - width : this.width - 100 - } ]; -}; - -MacromoleculeForm.prototype._getSymmetries = function() { - return [ { - "s" : "P1" - }, { - "s" : "P2" - }, { - "s" : "P3" - }, { - "s" : "P4" - }, { - "s" : "P5" - }, { - "s" : "P6" - }, { - "s" : "P32" - }, { - "s" : "P42" - }, { - "s" : "P52" - }, { - "s" : "P62" - }, { - "s" : "P222" - } ] -}; - -MacromoleculeForm.prototype.getPanel = function() { - this.panel = Ext.create('Ext.form.Panel', { - width : this.width, - height : this.height, - margin : 10, - border : 1, - defaultType : 'textfield', - items : this._getItems(), - buttons : this._getButtons() - }); - return this.panel; -}; - - -/** Populates each text field by field name and value **/ -MacromoleculeForm.prototype._populateField = function(fieldName, value) { - if (value != null){ - Ext.getCmp(this.id + fieldName).setValue(value); - } -}; - -/** Gets the value of a textfield **/ -MacromoleculeForm.prototype._getField = function(fieldName) { - return Ext.getCmp(this.id + fieldName).getValue(); -}; - - -/** It populates the form **/ -MacromoleculeForm.prototype.refresh = function(macromolecule) { - this.macromolecule = macromolecule; - if (macromolecule != null){ - this._populateField("name", macromolecule.name); - this._populateField("acronym", macromolecule.acronym); - this._populateField("extintionCoefficient", macromolecule.extintionCoefficient); - this._populateField("molecularMass", macromolecule.molecularMass); - this._populateField("comments", macromolecule.comments); - this._populateField("refractiveIndex", macromolecule.refractiveIndex); - this._populateField("solventViscosity", macromolecule.solventViscosity); - if (macromolecule.symmetry != null){ - Ext.getCmp(this.id + 'comboSym').setValue(macromolecule.symmetry); - } - } -}; - - -MacromoleculeForm.prototype.input = function() { - return {}; -}; - - -/** It populates the form **/ -MacromoleculeForm.prototype.test = function(targetId) { - var macromoleculeForm = new MacromoleculeForm(); - macromoleculeForm.onClose.attach(function(sender){ - alert("Click on close"); - }); - var panel = macromoleculeForm.getPanel(); - panel.render(targetId); -}; - - - - - -function ModelVisualizerForm(args){ - this.id =BUI.id(); - this.width = 600; - this.height = 400; - if (args!= null){ - if (args.width != null){ - this.width = args.width; - } - if (args.height != null){ - this.height = args.height; - } - } -}; - -ModelVisualizerForm.prototype._getFirHTML = function(modelId, width, height, type, desc) { - var html = ""; - html = html + ''; - html = html + ''; - html = html + ''; - html = html + '
dammin.' + type + '
' + desc + '
'; - return html; -}; - -ModelVisualizerForm.prototype.getItems = function(modelPanel){ - _this = this; - var height = _this.height/2 - 60; - var width = _this.width/2 - 10; - - return Ext.create('Ext.container.Container', { - layout: { - type: 'vbox', // Arrange child items vertically - }, - items: [ - modelPanel, - { - xtype : 'container', - layout: { - type: 'hbox', // Arrange child items vertically - }, - items : [{ - html : this._getFirHTML("id", width, height, "fir", "Fit of the simulated scattering curve versus a smoothed experimental data (spline interpolation)"), - height :height, - width : width, - padding: 2 - }, - { - html : this._getFirHTML("id", width, height, "fit", "Fit of the simulated scattering curve versus the experimental data."), - height : height, - width : width, - padding: 2 - }] - } - - ] - }); -}; - -ModelVisualizerForm.prototype.refresh = function(models){ - var input = []; -// var colors = ["008000", "F0A804", "0000FF", "800080", "C0C0C0"]; - for (var i = 0; i < models.length; i++) { - console.log(BUI.rainbow(models.length, i).replace("#", "0x")); - input.push({ - color: BUI.rainbow(models.length, i).replace("#", "0x"), - modelId: models[i].modelId, - opacity: 0.8, - radius: 3, - title: BUI.getFileNameByPath(models[i].pdbFile), - type: "SHAPEDETERMINATIONMODEL" - - }); - } - - this.panel.removeAll(); - this.panel.add( - _this.getItems( - new PDBViewer({ - width : this.width - 10, - height : (this.height/2) - 10, - title : "" - }).draw(input) - ) - ); - - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, data) { - var splitted = data.toString().split("\n"); - var array = []; - for ( var i = 0; i < splitted.length; i++) { - var line = splitted[i].trim(); - var line_splited = line.split(/\s*[\s,]\s*/); - if (line_splited.length == 4) { - array.push([ Number(line_splited[0]), BUI.getStvArray(line_splited[1], line_splited[2]), BUI.getStvArray(line_splited[3], 0) ]); - } - } - - var id = (_this.id + "firid"); - var dygraphWidget = new StdDevDyGraph(id, { - width : (_this.width/2) - 10, - height :(_this.height/2) -110, - xlabel : 'q(nm-1)' - }); - dygraphWidget.draw(array, [ "#FF0000", "#0000FF", "#FF00FF" ], [ 's', 'I(exp)', 'I(sim)' ]); - }); - - adapter.getModelFile("FIR", models[0].modelId); - - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, data) { - var splitted = data.toString().split("\n"); - var array = []; - for ( var i = 0; i < splitted.length; i++) { - var line = splitted[i].trim(); - var line_splited = line.split(/\s*[\s,]\s*/); - if (line_splited.length == 3) { - array.push([ Number(line_splited[0]), BUI.getStvArray(line_splited[1], 0), BUI.getStvArray(line_splited[2], 0) ]); - } - } - - var id = (_this.id + "fitid"); - - var dygraphWidget = new StdDevDyGraph(id, { - width : (_this.width/2) - 10, - height : (_this.height/2) - 110, - xlabel : 'q(nm-1)' - }); - dygraphWidget.draw(array, [ "#FF0000", "#0000FF" ], [ "s", "I(exp)", "I(sim)" ]); - }); - adapter.getModelFile("FIT", models[0].modelId); -}; - -ModelVisualizerForm.prototype.getPanel = function(modelList){ - _this = this; - this.modelList = modelList; - this.panel = Ext.create('Ext.Panel', { - title: 'Results', - width: this.width, - height: this.height, - layout: { - type: 'vbox', // Arrange child items vertically -// align: 'stretch' // Each takes up full width - }, - items: [ - - ], - listeners : { - afterrender : function(grid, eOpts) { -// alert(_this.modelList) - } - } - }); - - return this.panel; - -}; - - -/** - * Example form - * - * @witdh - * @height - */ -function MolarityForm(args) { - this.id = BUI.id(); - this.width = 700; - this.height = 500; - - if (args != null) { - if (args.width != null) { - this.width = args.width; - } - if (args.height != null) { - this.height = args.height; - } - } - - this.onSave = new Event(this); - this.onClose = new Event(this); -} - - -MolarityForm.prototype._getNumericWithHelp = function(type, fieldLabel, fieldName, help) { - return Ext.create('Ext.container.Container', { - margin : "10 0 0 10", - items : [ { - xtype : type, - fieldLabel : fieldLabel, - name : fieldName, - id : this.id + fieldName, - decimalPrecision : 6 - }, { - xtype : 'label', - forId : 'myFieldId', - text : help, - margin : "5 0 0 105", - cls : "inline-help" - } ] - }); -}; - - -MolarityForm.prototype._getItems = function() { - this.macromoleculeCombo = BIOSAXS_COMBOMANAGER.getComboMacromoleculeByMacromolecules(this.getMacromuleculesCandidates(this.macromolecule), { - width : 250, - labelWidth : 100, - margin : 10 - }); - - return [ { - xtype : 'container', - flex : 1, - margin : '10 0 0 10', - border : 0, - layout : 'anchor', - defaultType : 'requiredtext', - items : [ this.macromoleculeCombo, - this._getNumericWithHelp("textfield", "Ratio", "ratio", "Number in assymmetric units") - ] - } ]; -}; - -MolarityForm.prototype._persist = function() { - var _this = this; - var macromoleculeId = this.macromoleculeCombo.getValue(); - var ratio = Ext.getCmp(this.id + "ratio").getValue(); - var comments = "Not used yet"; - var dataAdapter = new BiosaxsDataAdapter(); - this.panel.setLoading("Saving"); - dataAdapter.onSuccess.attach(function(sender, args) { - _this.onSave.notify(); - }); - dataAdapter.saveStoichiometry(this.macromolecule.macromoleculeId, macromoleculeId, ratio, comments); -}; - -MolarityForm.prototype._getButtons = function() { - var _this = this; - - function onClose() { - _this.onClose.notify(); - } - - return [ { - text : 'Save', - handler : function() { - _this._persist(); - } - }, { - text : 'Cancel', - handler : function() { - onClose(); - } - } ]; -}; - -MolarityForm.prototype.getPanel = function() { - this.panel = Ext.create('Ext.form.Panel', { -// width : null, - height : this.height, - margin : 2, - border : 1, - defaultType : 'requiredtext', - items : this._getItems(), - buttons : this._getButtons() - }); - return this.panel; -}; - -/** macromolecules contains all macromolecules except this one **/ -MolarityForm.prototype.getMacromuleculesCandidates = function(macromolecule) { - var macromolecules = []; - if ( BIOSAXS.proposal.macromolecules){ - for (var i = 0; i < BIOSAXS.proposal.macromolecules.length; i++) { - var m = BIOSAXS.proposal.macromolecules[i]; - if (this.macromolecule != null){ - if (m.macromoleculeId != this.macromolecule.macromoleculeId) { - macromolecules.push(m); - } - } - } - } - return macromolecules; -}; - - -/** It populates the form **/ -MolarityForm.prototype.refresh = function(macromolecule) { - this.macromolecule = macromolecule; - -}; - - -MolarityForm.prototype.input = function() { - return {}; -}; - - -/** It populates the form **/ -MolarityForm.prototype.test = function(targetId) { - var macromoleculeForm = new MolarityForm(); - var panel = macromoleculeForm.getPanel(); - panel.render(targetId); -}; - -///** -// * -// * @witdh -// * @height -// */ -//function MacromoleculeForm(args) { -// this.id = BUI.id(); -// this.width = 700; -// this.height = 500; -// -// if (args != null) { -// if (args.width != null) { -// this.width = args.width; -// } -// if (args.height != null) { -// this.height = args.height; -// } -// } -// -// this.onClose = new Event(this); -//} -// -//MacromoleculeForm.prototype.getMacromolecule = function() { -// this.macromolecule.name = Ext.getCmp(this.panel.getItemId()).getValues().name; -// this.macromolecule.acronym = Ext.getCmp(this.panel.getItemId()).getValues().acronym; -// this.macromolecule.comments = Ext.getCmp(this.panel.getItemId()).getValues().comments; -// this.macromolecule.extintionCoefficient = Ext.getCmp(this.panel.getItemId()).getValues().extintionCoefficient; -// this.macromolecule.molecularMass = Ext.getCmp(this.panel.getItemId()).getValues().molecularMass; -// return this.macromolecule; -//}; -// -//MacromoleculeForm.prototype.setMacromolecule = function(macromolecule) { -// this.pdbStore.loadData(macromolecule.structure3VOs); -// -//}; -// -//MacromoleculeForm.prototype.getForm = function(macromolecule) { -// this.panel = Ext.createWidget('form', { -// frame : false, -// border : 0, -// padding : 15, -// width : 550, -// height : 350, -// items : [ { -// xtype : 'container', -// layout : 'hbox', -// items : [ { -// xtype : 'container', -// flex : 1, -// border : false, -// layout : 'anchor', -// defaultType : 'requiredtext', -// items : [ { -// fieldLabel : 'Name', -// name : 'name', -// anchor : '95%', -// tooltip : "Name of the macromolecule", -// value : macromolecule.name -// }, { -// fieldLabel : 'Acronym', -// name : 'acronym', -// anchor : '95%', -// value : macromolecule.acronym -// } ] -// }, { -// xtype : 'container', -// flex : 1, -// layout : 'anchor', -// defaultType : 'textfield', -// items : [ { -// xtype : 'numberfield', -// fieldLabel : 'Mol. Mass (Da)', -// name : 'molecularMass', -// value : macromolecule.molecularMass, -// decimalPrecision : 6 -// }, { -// xtype : 'numberfield', -// fieldLabel : 'Extinction coef.', -// name : 'extintionCoefficient', -// value : macromolecule.extintionCoefficient, -// decimalPrecision : 6 -// } ] -// } ] -// }, { -// xtype : 'textareafield', -// name : 'comments', -// fieldLabel : 'Comments', -// value : macromolecule.comments, -// width : this.width - 10 -// }, -// -// { -// html : BUI.getWarningHTML("The macromolecule is unsaved. Save the macromolecule to get access to other tabs"), -// margin : "150 10 10 10", -// id : this.id + "unsavedWarning", -// hidden : !(!macromolecule.macromoleculeId) -// } -// -// ] -// }); -// return this.panel; -//}; -// -//MacromoleculeForm.prototype.save = function() { -// var _this = this; -// -// var adapter = new BiosaxsDataAdapter(); -// adapter.onSuccess.attach(function(sender, proposal) { -// BIOSAXS.proposal.setItems(proposal); -// _this.panel.setLoading(false); -// -// Ext.getCmp(_this.id + "assembly").enable() -// Ext.getCmp(_this.id + "advanced").enable(); -// Ext.getCmp(_this.id + "unsavedWarning").hide(); -// }); -// -// if (this.getMacromolecule().name == "") { -// BUI.showError("Name field is mandatory"); -// return; -// } -// if (this.getMacromolecule().acronym == "") { -// BUI.showError("Acroynm field is mandatory"); -// return; -// } -// -// /** Check if acronym is unique * */ -// if (this.getMacromolecule().macromoleculeId == null) { -// if (BIOSAXS.proposal.getMacromoleculeByAcronym(this.getMacromolecule().acronym) == null) { -// this.panel.setLoading("ISPyB: Saving Macromolecule") -// adapter.saveMacromolecule(this.getMacromolecule()); -// } else { -// alert("There is already an existing macromolecule with the same acronym"); -// -// } -// } else { -// this.panel.setLoading("ISPyB: Saving Macromolecule") -// adapter.saveMacromolecule(this.getMacromolecule()); -// } -// -//}; -// -//MacromoleculeForm.prototype.getPanel = function(macromolecule) { -// var _this = this; -// this.macromolecule = macromolecule; -// return Ext.createWidget('tabpanel', { -// height : this.height, -// margin : 5, -// plain : true, -// style : { -// padding : 5 -// }, -// items : [ { -// tabConfig : { -// title : "General", -// disabled : false -// }, -// items : [ this.getForm(macromolecule) ], -// bbar : [ "->", { -// text : 'Save', -// cls : 'btn-with-border', -// style : { -// -// border : 1 -// }, -// handler : function() { -// _this.save(); -// } -// }, { -// text : 'Close', -// cls : 'btn-with-border', -// handler : function() { -// _this.onClose.notify(); -// } -// } ] -// }, { -// tabConfig : { -// id : this.id + "assembly", -// title : "Assembly", -// tooltip : 'Description of subunits present in the macromolecule', -// // hidden : (!macromolecule.macromoleculeId), -// disabled : (!macromolecule.macromoleculeId) -// }, -// items : [ this.getMolarityGrid(macromolecule) ] -// }, { -// tabConfig : { -// id : this.id + "advanced", -// title : "Advanced Modeling", -// // hidden : (!macromolecule.macromoleculeId), -// tooltip : 'Definition of the description contacts and symetries', -// disabled : (!macromolecule.macromoleculeId) -// }, -// items : [ this.getRigidBodyForm(macromolecule) ] -// } ] -// }); -//}; -// -//MacromoleculeForm.prototype.getRigidBodyForm = function(macromolecule) { -// var _this = this; -// -// // [P1, P2, P3, P4 ,P5 ,P6 ,32, P42, P52, P62] + P222 -// var symmetry = Ext.create('Ext.data.Store', { -// fields : [ 's' ], -// data : [ { -// "s" : "P1" -// }, { -// "s" : "P2" -// }, { -// "s" : "P3" -// }, { -// "s" : "P4" -// }, { -// "s" : "P5" -// }, { -// "s" : "P6" -// }, { -// "s" : "P32" -// }, { -// "s" : "P42" -// }, { -// "s" : "P52" -// }, { -// "s" : "P62" -// }, { -// "s" : "P222" -// } ] -// }); -// -// if (macromolecule.symmetry == null) { -// macromolecule.symmetry = "P1"; -// } -// var comboBox = Ext.create('Ext.form.ComboBox', { -// fieldLabel : 'Symmetry', -// store : symmetry, -// id : 'comboSym', -// queryMode : 'local', -// displayField : 's', -// valueField : 's', -// value : macromolecule.symmetry, -// margin : "10 0 0 0", -// listeners : { -// change : function(combo, newValue, oldValue, eOpts) { -// macromolecule.symmetry = newValue; -// } -// } -// }); -// -// this.rigidBodyPanel = Ext.createWidget('form', { -// frame : false, -// border : 0, -// padding : 10, -// width : 550, -// height : 400, -// items : [ { -// xtype : 'container', -// layout : 'hbox', -// items : [ { -// xtype : 'container', -// border : false, -// layout : 'hbox', -// items : [ { -// xtype : 'label', -// forId : 'myFieldId', -// text : 'Contact Desc:', -// width : 105, -// margin : '0 0 0 0' -// }, { -// xtype : 'textfield', -// hideLabel : true, -// id : "contactsDescriptionFilePath", -// margin : '0 0 0 0', -// width : 300, -// value : macromolecule.contactsDescriptionFilePath -// }, { -// text : 'Upload', -// xtype : 'button', -// margin : "0 0 0 20", -// width : 100, -// handler : function() { -// _this._openUploadDialog(macromolecule.macromoleculeId, "CONTACTS", "Upload Contact Description File"); -// } -// } ] -// } ] -// }, -// -// comboBox, { -// xtype : 'checkbox', -// margin : '10 0 0 5', -// boxLabel : "I want rigid body modeling run on this stuff", -// checked : true, -// width : 300 -// }, _this.getPDBGrid(macromolecule) ] -// }); -// return this.rigidBodyPanel; -//}; -// -//MacromoleculeForm.prototype.update = function() { -// var _this = this; -// BIOSAXS.proposal.onInitialized.attach(function() { -// if (BIOSAXS.proposal != null) { -// var macromolecules = BIOSAXS.proposal.macromolecules; -// for (var i = 0; i < macromolecules.length; i++) { -// if (macromolecules[i].macromoleculeId == _this.macromolecule.macromoleculeId) { -// _this.macromolecule = macromolecules[i]; -// _this.setMacromolecule(_this.macromolecule); -// _this.molarityStore.loadData(_this.parseMolarityData(_this.macromolecule)); -// _this.pdbGrid.setLoading(false); -// Ext.getCmp("contactsDescriptionFilePath").setValue(_this.macromolecule.contactsDescriptionFilePath) -// _this.molarityGrid.setLoading(false); -// -// } -// } -// } -// }); -// this.molarityGrid.setLoading("Updating"); -// this.pdbGrid.setLoading("Updating"); -// BIOSAXS.proposal.init(); -//}; -// -///******************************************************************************* -// * MOLARITY GRID -// ******************************************************************************/ -// -//MacromoleculeForm.prototype.parseMolarityData = function(macromolecule) { -// var data = []; -// if (macromolecule.stoichiometry != null) { -// for (var i = 0; i < macromolecule.stoichiometry.length; i++) { -// data.push({ -// ratio : macromolecule.stoichiometry[i].ratio, -// acronym : macromolecule.stoichiometry[i].macromolecule3VO.acronym, -// comments : macromolecule.stoichiometry[i].macromolecule3VO.comments, -// stoichiometryId : macromolecule.stoichiometry[i].stoichiometryId, -// name : macromolecule.stoichiometry[i].macromolecule3VO.name -// }); -// } -// } -// return data; -//}; -// -//MacromoleculeForm.prototype.getMolarityGrid = function(macromolecule) { -// var _this = this; -// -// this.molarityStore = Ext.create('Ext.data.Store', { -// fields : [ 'acronym', 'ratio', 'comments', 'stoichiometryId', 'name' ], -// data : this.parseMolarityData(macromolecule), -// sorters : { -// property : 'ratio', -// direction : 'DESC' -// } -// }); -// -// this.molarityGrid = Ext.create('Ext.grid.Panel', { -// store : this.molarityStore, -// height : 350, -// padding : 5, -// columns : [ -// -// { -// text : 'Subunit', -// columns : [ { -// text : "Acronym", -// width : 100, -// hidden : false, -// dataIndex : 'acronym', -// sortable : true -// }, { -// text : "Name", -// width : 100, -// hidden : false, -// dataIndex : 'name', -// sortable : true -// }, { -// text : "Comments", -// width : 100, -// dataIndex : 'comments', -// sortable : true -// } ] -// }, { -// text : "Number
in assymmetric units", -// width : 150, -// dataIndex : 'ratio', -// tooltip : 'Number of times this subunit is present in the macromolecule', -// sortable : true -// }, { -// id : this.id + 'MOLARITY_REMOVE', -// flex : 0.1, -// sortable : false, -// renderer : function(value, metaData, record, rowIndex, colIndex, store) { -// return BUI.getRedButton('REMOVE'); -// } -// } ], -// listeners : { -// cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { -// if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'MOLARITY_REMOVE') { -// var dataAdapter = new BiosaxsDataAdapter(); -// dataAdapter.onSuccess.attach(function() { -// _this.molarityGrid.setLoading(false); -// _this.update(); -// }); -// dataAdapter.removeStoichiometry(record.data.stoichiometryId); -// _this.molarityGrid.setLoading("Removing Structure"); -// } -// } -// }, -// buttons : [ { -// text : 'Add molarity', -// handler : function() { -// -// function onClose() { -// w.destroy(); -// _this.update(); -// } -// var w = Ext.create('Ext.window.Window', { -// title : 'Molarity', -// height : 300, -// width : 500, -// modal : true, -// buttons : [ { -// text : 'Save', -// handler : function() { -// var macromoleculeId = (_this.macromoleculeCombo.getValue()); -// var ratio = Ext.getCmp(_this.id + "ratio").getValue(); -// var comments = ""; -// var dataAdapter = new BiosaxsDataAdapter(); -// dataAdapter.onSuccess.attach(function(sender, args) { -// _this.update(); -// w.destroy(); -// }); -// dataAdapter.saveStoichiometry(_this.macromolecule.macromoleculeId, macromoleculeId, ratio, comments); -// } -// }, { -// text : 'Cancel', -// handler : function() { -// onClose(); -// } -// } ], -// items : [ _this.getMolarityForm(macromolecule) ], -// listeners : { -// onEsc : function() { -// onClose(); -// }, -// close : function() { -// onClose(); -// } -// } -// }).show(); -// } -// } ] -// }); -// return this.molarityGrid; -//}; -// -///******************************************************************************* -// * PDB GRID -// ******************************************************************************/ -// -//MacromoleculeForm.prototype._openUploadDialog = function(macromoleculeId, type, title) { -// var _this = this; -// function onClose() { -// w.destroy(); -// _this.update(); -// } -// -// var w = Ext.create('Ext.window.Window', { -// title : title, -// height : 200, -// width : 400, -// modal : true, -// buttons : [ { -// text : 'Close', -// handler : function() { -// onClose(); -// } -// } ], -// layout : 'fit', -// items : { -// html : "" -// }, -// listeners : { -// onEsc : function() { -// onClose(); -// }, -// close : function() { -// onClose(); -// } -// } -// }).show(); -//}; -// -//MacromoleculeForm.prototype._getPlugins = function() { -// var _this = this; -// var plugins = []; -// // if (this.updateRowEnabled) { -// plugins.push(Ext.create('Ext.grid.plugin.RowEditing', { -// clicksToEdit : 1, -// listeners : { -// validateedit : function(grid, e) { -// /** Comments are always updatable* */ -// e.record.raw.symmetry = e.newValues.symmetry; -// e.record.raw.multiplicity = e.newValues.multiplicity; -// -// var adapter = new BiosaxsDataAdapter(); -// adapter.onSuccess.attach(function(sender, measurement) { -// // _this.grid.setLoading(false); -// }); -// adapter.onError.attach(function() { -// alert("Error"); -// // _this.grid.setLoading(false); -// }); -// -// // _this.grid.setLoading(); -// adapter.saveStructure(e.record.raw); -// } -// } -// })); -// // } -// return plugins; -//}; -// -//MacromoleculeForm.prototype.getPDBGrid = function(macromolecule) { -// var _this = this; -// -// var data = macromolecule.structure3VOs; -// -// // /** Getting PDB from subunits **/ -// // if (macromolecule != null){ -// // if (macromolecule.stoichiometry != null){ -// // for (var i =0; i < macromolecule.stoichiometry.length; i++){ -// // var stoichiometry = macromolecule.stoichiometry[i]; -// // if (stoichiometry.macromolecule3VO != null){ -// // if (stoichiometry.macromolecule3VO.structure3VOs != null){ -// // for (var j = 0; j < stoichiometry.macromolecule3VO.structure3VOs.length; -// // j++) { -// // var structure = stoichiometry.macromolecule3VO.structure3VOs[j]; -// // structure["isSubunit"] = stoichiometry.macromolecule3VO.acronym; -// // data.push(structure) -// // } -// // } -// // } -// // } -// // } -// // } -// -// this.pdbStore = Ext.create('Ext.data.Store', { -// fields : [ 'filePath', 'structureId', 'structureType', 'symmetry', 'structureId', 'name', 'multiplicity' ], -// data : macromolecule.structure3VOs, -// groupField : 'structureType', -// sorters : { -// property : 'structureId', -// direction : 'DESC' -// } -// }); -// -// var groupingFeature = Ext.create('Ext.grid.feature.Grouping', { -// groupHeaderTpl : Ext.create('Ext.XTemplate', -// "
{name:this.formatName}
", { -// formatName : function(name) { -// return name; -// } -// }), -// hideGroupedHeader : true, -// startCollapsed : false -// }); -// -// this.pdbGrid = Ext.create('Ext.grid.Panel', { -// margin : "15 0 0 5", -// height : 250, -// store : this.pdbStore, -// plugins : _this._getPlugins(), -// buttons : [ { -// // text : 'Add PDB file', -// text : 'Add Modeling Option', -// handler : function() { -// _this._openUploadDialog(macromolecule.macromoleculeId, "PDB", 'Upload PDB File'); -// } -// } -// -// ], -// columns : [ -// { -// text : "structureId", -// flex : 0.2, -// hidden : true, -// dataIndex : 'structureId', -// sortable : true -// }, -// { -// text : "File", -// flex : 0.5, -// dataIndex : 'filePath', -// sortable : true, -// hidden : true -// }, -// { -// text : "PDB", -// flex : 0.4, -// dataIndex : 'name', -// sortable : true -// }, -// { -// text : "Symmetry", -// flex : 0.4, -// dataIndex : 'symmetry', -// sortable : true, -// editor : { -// xtype : 'combobox', -// typeAhead : true, -// triggerAction : 'all', -// selectOnTab : true, -// store : [ [ "P1", "P1" ], [ "P2", "P2" ], [ "P3", "P3" ], [ "P4", "P4" ], [ "P5", "P5" ], [ "P6", "P6" ], [ "P32", "P32" ], [ "P42", "P42" ], -// [ "P52", "P52" ], [ "P62", "P62" ], [ "P222", "P222" ] ], -// } -// }, { -// text : "Multiplicity", -// flex : 0.4, -// dataIndex : 'multiplicity', -// sortable : true, -// editor : { -// xtype : 'textfield' -// } -// -// }, { -// text : "Subunit", -// flex : 0.2, -// dataIndex : 'isSubunit', -// sortable : true, -// hidden : true -// }, { -// text : "Type", -// flex : 0.2, -// dataIndex : 'structureType', -// sortable : true, -// hidden : true -// }, -// -// { -// id : this.id + 'REMOVE', -// flex : 0.2, -// hidden : true, -// sortable : false, -// renderer : function(value, metaData, record, rowIndex, colIndex, store) { -// return BUI.getRedButton('REMOVE'); -// } -// }, ], -// -// listeners : { -// itemdblclick : function(dataview, record, item, e) { -// _this._editExperiment(record.raw.experimentId); -// }, -// cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { -// -// if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'REMOVE') { -// var dataAdapter = new BiosaxsDataAdapter(); -// dataAdapter.onSuccess.attach(function() { -// _this.pdbGrid.setLoading(false); -// _this.update(); -// }); -// dataAdapter.removeStructure(record.data.structureId); -// _this.pdbGrid.setLoading("Removing PDB file"); -// } -// -// } -// }, -// viewConfig : { -// getRowClass : function(record, rowIdx, params, store) { -// if (record.raw.isSubunit != null) { -// return "blue-row"; -// } -// } -// } -// }); -// -// return this.pdbGrid; -//}; -// -//MacromoleculeForm.prototype.getMolarityForm = function(macromolecule) { -// var _this = this; -// var data = []; -// for (var i = 0; i < BIOSAXS.proposal.macromolecules.length; i++) { -// var m = BIOSAXS.proposal.macromolecules[i]; -// if (m.macromoleculeId != macromolecule.macromoleculeId) { -// data.push(m); -// } -// -// } -// this.macromoleculeCombo = BIOSAXS_COMBOMANAGER.getComboMacromoleculeByMacromolecules(data, { -// width : 250, -// labelWidth : 100, -// margin : 10 -// }); -// -// return Ext.createWidget('form', { -// -// frame : false, -// border : 0, -// // padding : 15, -// width : 550, -// height : 350, -// items : [ { -// xtype : 'container', -// flex : 1, -// border : false, -// layout : 'anchor', -// defaultType : 'requiredtext', -// items : [ this.macromoleculeCombo, { -// xtype : 'numberfield', -// name : 'Ratio', -// id : _this.id + "ratio", -// fieldLabel : 'Number in assymmetric units', -// value : 1, -// decimalPrecision : 6, -// margin : 10 -// }, { -// xtype : 'textareafield', -// name : 'comments', -// fieldLabel : 'Comments', -// margin : 10, -// width : 400, -// value : "" -// -// } ] -// } ] -// }); -// -//}; -// -///******************************************************************************* -// * JAVASCRIPT DOC -// ******************************************************************************/ -//MacromoleculeForm.prototype.input = function() { -// return { -// macromolecule : DATADOC.getMacromolecule_10() -// }; -//}; -// -//MacromoleculeForm.prototype.test = function(targetId) { -// var macromoleculeForm = new MacromoleculeForm(); -// var panel = macromoleculeForm.getPanel(macromoleculeForm.input().macromolecule); -// panel.render(targetId); -//}; - -function ResultSummaryForm() { - this.radiationDamageThreshold = BUI.getRadiationDamageThreshold(); - this.qualityThreshold = BUI.getQualityThreshold(); - - /** Data clusters with bufers **/ - this.clusterByBuffers = false; - - /** Visualization are groupeb by concenttrations. Ex: 4.5mg/ml and 4.7mg/ml will be clusterized together **/ - this.collapseConcentrations = true; - - this.summaryHeight = 350; - this.id = BUI.id(); - - var _this = this; - - this.qualityControlResultsWidget = new QualityControlResultsWidget({ - qualityThreshold : this.radiationDamageThreshold, - radiationDamageThreshold : this.qualityThreshold - - }); - this.qualityControlResultsWidget.onQualityChanged.attach(function(sender, value) { - _this.qualityThreshold = value; - _this.thresholdsChanged(); - }); - - this.qualityControlResultsWidget.onRadiationDatamageChanged.attach(function(sender, value) { - _this.radiationDamageThreshold = value; - _this.thresholdsChanged(); - - }); - this.concentrationHTMLTableWidget = new ConcentrationHTMLTableWidget({ - height : 250, - showBufferColumns : this.clusterByBuffers - }); - - this.plot = new BoxWhiskerGraph({ - targetId : _this.id + '_boxPlot', - height : 350, - width : 450, - maxBoxWidth : 25 - }); - - this.rangePlot = new RangeWhiskerGraph({ - targetId : _this.id + '_rangePlot', - height : 350, - width : 450, - maxBoxWidth : 25 - }); -} - -ResultSummaryForm.prototype.thresholdsChanged = function() { - var parsedData = this.prepareData(this.rawData); - - var filtered = JSON.parse(JSON.stringify(parsedData)); - filtered.concentration.concentrations = []; - for ( var conc in parsedData.concentration.concentrations) { - if (parsedData.concentration.concentrations[conc].calculation.rgGnom.validNumber > 0) { - filtered.concentration.concentrations.push(parsedData.concentration.concentrations[conc]); - } - } - - this.plotWhisker(filtered); - this.plotRange(filtered); - - this.concentrationHTMLTableWidget.refresh(parsedData); -}; - -/** Given a frame object (object returned by analyzeFrames()) and depending of the threshold indicates if a datacollection has radiation damage **/ -ResultSummaryForm.prototype.hasRadiationDamage = function(frameObject) { - var bb = frameObject.bufferBeforeFramesMerged / frameObject.framesCount; - var ba = frameObject.bufferAfterFramesMerged / frameObject.framesCount; - var mol = frameObject.framesMerge / frameObject.framesCount; - return !((bb >= this.radiationDamageThreshold) && (ba >= this.radiationDamageThreshold) && (mol >= this.radiationDamageThreshold)); -}; - -/** Return (frameObject) an object with the information about the frames for a data collection**/ -ResultSummaryForm.prototype.analyzeFrames = function(dataCollectionRow) { - return { - bufferAfterFramesMerged : dataCollectionRow.bufferAfterFramesMerged, - bufferBeforeFramesMerged : dataCollectionRow.bufferBeforeFramesMerged, - framesCount : dataCollectionRow.framesCount, - framesMerge : dataCollectionRow.framesMerge - }; -}; - -ResultSummaryForm.prototype.detectDataCollectionWarnings = function(dataCollectionRow) { - var frameObject = this.analyzeFrames(dataCollectionRow); - var warnings = { - count : 0, - type : [] - }; - - if (this.hasRadiationDamage(frameObject)) { - warnings.count = warnings.count + 1; - warnings.type.push("RADIATION DAMAGE"); - } - - if (Number(dataCollectionRow.quality) < this.qualityThreshold) { - warnings.count = 1;//warnings.count + 1; - warnings.type.push("Quality <" + this.qualityThreshold); - } - return warnings; -}; - -/** Return array composed by {concentration} objects **/ -ResultSummaryForm.prototype.getConcentrations = function(data) { - var concentrationsId = {}; - - for ( var i = 0; i < data.length; i++) { - var warning = this.detectDataCollectionWarnings(data[i]); - var id = data[i].conc;// + "_" + data[i].bufferId; - if (this.clusterByBuffers) { - id = data[i].conc + "_" + data[i].bufferId; - } - - if (concentrationsId[id] == null) { - concentrationsId[id] = { - id : id, - concentration : Number(data[i].conc).toFixed(2), - bufferId : data[i].bufferId, - bufferAcronym : data[i].bufferAcronym, - rgGuinier : [], - i0Guinier : [], - rgGnom : [], - dMax : [], - quality : [], - frames : [], - frames_warning : warning.count - }; - } else { - concentrationsId[id].frames_warning = concentrationsId[id].frames_warning + warning.count; - } - - concentrationsId[id].frames.push(data[i]); - - if (warning.count == 0) { - concentrationsId[id].rgGuinier.push(data[i].rgGuinier); - concentrationsId[id].i0Guinier.push(data[i].I0); - concentrationsId[id].quality.push(data[i].quality); - concentrationsId[id].rgGnom.push(data[i].rgGnom); - concentrationsId[id].dMax.push(data[i].dmax); - } - - } - var concentrations = []; - for ( var item in concentrationsId) { - if (concentrationsId.hasOwnProperty(item)) { - concentrations.push({ - concentration : concentrationsId[item].concentration, - id : item, - bufferId : Number(concentrationsId[item].bufferId).toFixed(2), - bufferAcronym : concentrationsId[item].bufferAcronym, - rgGuinier : concentrationsId[item].rgGuinier, - /** Frames **/ - frames : concentrationsId[item].frames, - frames_warning : concentrationsId[item].frames_warning, - /** Calculation **/ - calculation : { - rgGuinier : BUI.getStandardDeviation(concentrationsId[item].rgGuinier), - i0Guinier : BUI.getStandardDeviation(concentrationsId[item].i0Guinier), - quality : BUI.getStandardDeviation(concentrationsId[item].quality), - rgGnom : BUI.getStandardDeviation(concentrationsId[item].rgGnom), - dMax : BUI.getStandardDeviation(concentrationsId[item].dMax) - - } - }); - } - } - - return { - concentrations : concentrations - }; -}; - -ResultSummaryForm.prototype.prepareData = function(data) { - /** Return array composed by {acronym, bufferId} objects **/ - function getBuffers(data) { - var buffersId = {}; - for ( var i = 0; i < data.length; i++) { - buffersId[data[i].bufferId] = data[i].acronym; - } - var buffers = []; - for ( var id in buffersId) { - if (buffersId.hasOwnProperty(id)) { - buffers.push({ - acronym : buffersId[id], - bufferId : id - }); - } - } - return buffers; - } - - /** Get a string with all the concentrations **/ - function getConcentrationString(parseConcentrations) { - var concentrations = []; - for ( var i = 0; i < parseConcentrations.concentrations.length; i++) { - concentrations.push(parseConcentrations.concentrations[i].concentration + " mg/ml "); - } - return concentrations.toString(); - } - - var parseConcentrations = this.getConcentrations(data); - - return { - dataCollectionCount : data.length, - buffers : getBuffers(data), - concentration : parseConcentrations, - concentrationLabel : getConcentrationString(parseConcentrations) - }; -}; - -ResultSummaryForm.prototype.getDataForWhisker = function(data) { - var clusters = []; - - var concentrations = {}; - var i = 0; - var conc = 0; - if (this.collapseConcentrations) { - for (i = 0; i < data.concentration.concentrations.length; i++) { - conc = data.concentration.concentrations[i]; - var concentration = Number(conc.concentration).toFixed(0); - if (concentrations[concentration] == null) { - concentrations[concentration] = {}; - concentrations[concentration].concentration = concentration; - concentrations[concentration].calculation = {}; - concentrations[concentration].calculation.rgGuinier = {}; - concentrations[concentration].calculation.rgGuinier.values = []; - concentrations[concentration].calculation.rgGuinier.values = conc.calculation.rgGuinier.values; - concentrations[concentration].calculation.rgGnom = {}; - concentrations[concentration].calculation.rgGnom.values = []; - concentrations[concentration].calculation.rgGnom.values = conc.calculation.rgGnom.values; - } else { - concentrations[concentration].calculation.rgGuinier.values = concentrations[concentration].calculation.rgGuinier.values - .concat(conc.calculation.rgGuinier.values); - concentrations[concentration].calculation.rgGnom.values = concentrations[concentration].calculation.rgGnom.values - .concat(conc.calculation.rgGnom.values); - } - } - - /** From object to array **/ - var array = []; - for ( var key in concentrations) { - if (concentrations.hasOwnProperty(key)) { - array.push(concentrations[key]); - } - } - data.concentration.concentrations = array; - } - - for (i = 0; i < data.concentration.concentrations.length; i++) { - conc = data.concentration.concentrations[i]; - clusters.push({ - name : conc.concentration + "mg/ml", - concentration : Number(conc.concentration), - x : Number(conc.concentration), - classes : [] - }); - clusters[clusters.length - 1].classes.push({ - name : "Guinier", - color : '#9A2EFE', - values : conc.calculation.rgGuinier.values - - }); - clusters[clusters.length - 1].classes.push({ - name : "P(r)", - color : '#2E64FE', - values : conc.calculation.rgGnom.values - - }); - } - return { - clusters : clusters.sort(function(a, b) { - return a.concentration - b.concentration; - }) - }; -}; - -ResultSummaryForm.prototype.getDataForWhiskerQuality = function(data) { - var clusters = []; - - for ( var i = 0; i < data.concentration.concentrations.length; i++) { - var conc = data.concentration.concentrations[i]; - clusters.push({ - name : conc.concentration + "mg/ml", - classes : [] - }); - clusters[clusters.length - 1].classes.push({ - name : "Quality", - values : conc.calculation.quality.values - - }); - } - - return { - clusters : clusters - }; -}; - -ResultSummaryForm.prototype.plotQualityWhisker = function(parsedData) { - this.qualityPlot.refresh(this.getDataForWhiskerQuality(parsedData)); -}; - -ResultSummaryForm.prototype.plotWhisker = function(parsedData) { - this.plot.refresh(this.getDataForWhisker(parsedData)); -}; - -ResultSummaryForm.prototype.plotRange = function(parsedData) { - this.rangePlot.refresh(this.getDataForWhisker(parsedData)); -}; - -ResultSummaryForm.prototype.getPanel = function(data) { - var _this = this; - _this.rawData = data; - var parsedData = this.prepareData(data); - - this.panel = Ext - .createWidget( - 'form', - { - bodyPadding : 20, - frame : false, - border : 0, - width : this.width, - height : this.summaryHeight + 1000, - items : [ { - xtype : 'container', - layout : 'hbox', - items : [ { - xtype : 'container', - flex : 1, - border : false, - layout : 'anchor', - defaultType : 'textfield', - items : [ - _this.qualityControlResultsWidget.getPanel(), - { - xtype : 'fieldset', - width : 950, - margin : "20, 0 0 0", - height : this.summaryHeight + 500, - items : [ - { - html : "
Concentration Analysis
", - border : 0, - width : 900 - - }, - { - html : this.concentrationHTMLTableWidget.getPanel(parsedData), - border : 0, - width : 900, - margin : "10, 0 0 10" - - }, - { - xtype : 'container', - layout : 'vbox', - border : 5, - items : [ - { - html : "
Rg based on Guinier and Gnom
", - border : 8, - width : 900, - margin : "20 0 0 10" - - }, { - xtype : 'container', - layout : 'hbox', - items : [ { - xtype : 'container', - layout : 'vbox', - items : [ { - html : "
", - border : 0, - width : this.plot.width, - padding : "10 0 0 20", - listeners : { - afterrender : function() { - _this.plotWhisker(parsedData); - - } - } - }, { - html : "
Concentration (mg/ml)
", - width : 450, - border : 0, - margin : "-50 0 0 0" - - } ] - }, { - xtype : 'container', - layout : 'vbox', - items : [ { - html : "
", - border : 0, - width : this.rangePlot.width, - padding : "10 0 0 10", - listeners : { - afterrender : function() { - _this.plotRange(parsedData); - } - } - }, { - html : "
Concentration (mg/ml)
", - width : 450, - border : 0, - margin : "-50 0 0 0" - - } ] - } ] - } ] - } ] - } ] - } - - ] - } ] - }); - return this.panel; - -}; - -ResultSummaryForm.prototype.input = function() { - return { - data : DATADOC.getData_3367() - }; -}; - -ResultSummaryForm.prototype.test = function(targetId) { - var resultSummaryForm = new ResultSummaryForm(); - var panel = resultSummaryForm.getPanel(resultSummaryForm.input().data); - panel.render(targetId); - -}; - -/** - * Example form - * - * @witdh - * @height - */ -function RigibBodyModelingForm(args) { - this.id = BUI.id(); - this.width = 700; - this.height = 500; - - if (args != null) { - if (args.width != null) { - this.width = args.width; - } - if (args.height != null) { - this.height = args.height; - } - } - - var _this = this; - this.rigidBodyGrid = new AprioriRigidBodyGrid(); - - this.rigidBodyGrid.onUploadFile.attach(function(sender, type, title){ - _this._openUploadDialog(_this.macromolecule.macromoleculeId, type, title); - }); - - this.rigidBodyGrid.onRemove.attach(function(sender, type, title){ - _this._update(); - }); - - this.onSave = new Event(this); - -} - -RigibBodyModelingForm.prototype._getItems = function() { - var _this = this; - - - return [ { - xtype : 'label', - forId : 'myFieldId', - text : 'Information for model fit, mixture analysis and rigid body modeling', - margin : '15 0 20 10', - cls : "inline-help" - }, - this.rigidBodyGrid.getPanel(), - { - xtype : 'label', - forId : 'myFieldId', - text : 'Distance restraints may be imposed on the model using contacts conditions file (OPTIONAL)', - margin : '25 0 5 10', - cls : "inline-help" - }, - { - xtype : 'container', - layout : 'hbox', - items : [ { - xtype : 'container', - border : false, - layout : 'hbox', - items : [ { - xtype : 'label', - forId : 'myFieldId', - text : 'Contact Description File: (Optional)', - width : 150, - margin : '10 0 0 10' - }, { - id : this.id + "contactsDescriptionFilePath", - xtype : 'textfield', - hideLabel : true, - margin : '10 0 0 0', - width : 200, - disabled: true - }, { - text : 'Upload', - xtype : 'button', - margin : "10 0 0 10", - width : 80, - handler : function() { - - _this._openUploadDialog(_this.macromolecule.macromoleculeId, "CONTACTS", "Upload Contact Description File"); - } - }, { - text : 'Remove', - id : _this.id + "_remove", - xtype : 'button', - margin : "10 0 0 10", - width : 80, - handler : function() { - _this.macromolecule.contactsDescriptionFilePath = null; - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, proposal) { - /** Updating the proposal global object **/ - BIOSAXS.proposal.setItems(proposal); - _this.panel.setLoading(false); - - var saved = BIOSAXS.proposal.getMacromoleculeByAcronym(_this.macromolecule.acronym); - /** Refreshing to check that everything is ok **/ - _this.refresh(saved); - _this.onSave.notify(saved); - }); - - _this.panel.setLoading("Saving Macromolecule") - adapter.saveMacromolecule(_this.macromolecule); - } - } ] - } ] - }, { - xtype : 'panel', - html : "Go to SASREF manual for further information", - margin : "10 0 0 160", - border : 0 - - }, - { - xtype : 'checkbox', - margin : '10 0 0 5', - boxLabel : "I want rigid body modeling run on this stuff", - checked : true, - width : 300 - } ] - -}; - -/** Because update is a jsp page we don't know if the user has uploaded a file or not then we need to refresh **/ -RigibBodyModelingForm.prototype._update = function(macromoleculeId, type, title) { - var _this = this; - BIOSAXS.proposal.onInitialized.attach(function() { - if (BIOSAXS.proposal != null) { - _this.refresh(BIOSAXS.proposal.getMacromoleculeById(_this.macromolecule.macromoleculeId)); - _this.panel.setLoading(false); - } - }); - this.panel.setLoading(); - BIOSAXS.proposal.init(); -}; - -RigibBodyModelingForm.prototype._openUploadDialog = function(macromoleculeId, type, title) { - var _this = this; - function onClose() { - w.destroy(); - _this._update(); - - } - - var w = Ext.create('Ext.window.Window', { - title : title, - height : 200, - width : 400, - modal : true, - buttons : [ { - text : 'Close', - handler : function() { - onClose(); - } - } ], - layout : 'fit', - items : { - html : "" - }, - listeners : { - onEsc : function() { - onClose(); - }, - close : function() { - onClose(); - } - } - }).show(); -}; - -RigibBodyModelingForm.prototype._getButtons = function() { - return []; -}; - -RigibBodyModelingForm.prototype.getPanel = function() { - var _this = this; - this.panel = Ext.create('Ext.form.Panel', { - width : this.width, - height : this.height, - margin : 10, - border : 1, - defaultType : 'textfield', - items : this._getItems(), - buttons : this._getButtons(), - listeners : { - afterrender : function(){ - _this._populate(); - - } - } - }); - return this.panel; -}; - -/** Populates could be call when the DOM is not filled yet **/ -RigibBodyModelingForm.prototype._populate = function() { - if (this.macromolecule != null){ - if (Ext.getCmp(this.id + "contactsDescriptionFilePath") != null){ - if (this.macromolecule.contactsDescriptionFilePath != null){ - Ext.getCmp(this.id + "contactsDescriptionFilePath").setValue(this.macromolecule.contactsDescriptionFilePath); - Ext.getCmp(this.id + "_remove").enable(); - } - else{ - Ext.getCmp(this.id + "_remove").disable(); - Ext.getCmp(this.id + "contactsDescriptionFilePath").setValue(""); - - } - } - } -}; - -/** It populates the form * */ -RigibBodyModelingForm.prototype.refresh = function(macromolecule) { - this.macromolecule = macromolecule; - this.rigidBodyGrid.refresh(macromolecule); - this._populate(); -}; - -RigibBodyModelingForm.prototype.input = function() { - return {}; -}; - - -/** It populates the form **/ -RigibBodyModelingForm.prototype.test = function(targetId) { - var macromoleculeForm = new RigibBodyModelingForm(); - var panel = macromoleculeForm.getPanel(); - panel.render(targetId); -}; - - -/** - * Same form as MX part - * - * @creationMode if true a create button appears instead of save - * @showTitle true or false - */ -function ShipmentForm(args) { - this.id = BUI.id(); - - this.creationMode = false; - this.showTitle = true; - if (args != null) { - if (args.creationMode != null) { - this.creationMode = args.creationMode; - } - if (args.showTitle != null) { - this.showTitle = args.showTitle; - } - } - - this.onSaved = new Event(this); -} - -ShipmentForm.prototype.fillStores = function() { - this.panel.setLoading("Loading Labcontacts from database"); - this.labContactForSendingStore.loadData(BIOSAXS.proposal.getLabcontacts(), false); - this.labContactForReturnStore.loadData(BIOSAXS.proposal.getLabcontacts(), false); - this.panel.setLoading(false); - if (this.shipment != null) { - this.setShipment(this.shipment); - } -}; - -ShipmentForm.prototype.draw = function(targetId) { - this.getPanel().render(targetId); -}; - -ShipmentForm.prototype.setShipment = function(shipment) { - this.shipment = shipment; - var _this = this; - Ext.getCmp(_this.id + "shippingName").setValue(shipment.json.shippingName); - Ext.getCmp(_this.id + "shippingStatus").setValue(shipment.json.shippingStatus); - Ext.getCmp(_this.id + "comments").setValue(shipment.json.comments); - if (shipment.json.sendingLabContactVO != null) { - this.labContactsSendingCombo.setValue(shipment.json.sendingLabContactVO.labContactId); - } - if (shipment.json.returnLabContactVO != null) { - this.labContactsReturnCombo.setValue(shipment.json.returnLabContactVO.labContactId); - } - -}; - -ShipmentForm.prototype._saveShipment = function() { - var _this = this; - var shippingId = null; - if (this.shipment != null) { - shippingId = this.shipment.json.shippingId; - } - var json = { - shippingId : shippingId, - name : Ext.getCmp(_this.id + "shippingName").getValue(), - status : Ext.getCmp(_this.id + "shippingStatus").getValue(), - sendingLabContactId : Ext.getCmp(_this.id + "shipmentform_sendingLabContactId").getValue(), - returnLabContactId : Ext.getCmp(_this.id + "returnLabContactId").getValue(), - returnCourier : Ext.getCmp(_this.id + "returnCourier").getValue(), - courierAccount : Ext.getCmp(_this.id + "courierAccount").getValue(), - BillingReference : Ext.getCmp(_this.id + "BillingReference").getValue(), - dewarAvgCustomsValue : Ext.getCmp(_this.id + "dewarAvgCustomsValue").getValue(), - dewarAvgTransportValue : Ext.getCmp(_this.id + "dewarAvgTransportValue").getValue(), - comments : Ext.getCmp(_this.id + "comments").getValue() - }; - - var dataAdapter = new BiosaxsDataAdapter(); - dataAdapter.onSuccess.attach(function(sender, shipment) { - window.location = BUI.getShippingURL(shipment.shippingId); - }); - - dataAdapter.onError.attach(function(sender, error) { - _this.onError.notify(error); - }); - - /** Cheking params **/ - if (json.name == "") { - BUI.showError("Name field is mandatory"); - return; - } - - if (json.sendingLabContactId == null) { - BUI.showError("Lab contact for sending field is mandatory"); - return; - } - - if (json.returnLabContactId == null) { - BUI.showError("Lab contact for return field is mandatory"); - return; - } - - dataAdapter.createShipment(json.shippingId, json.name, json.status, json.comments, json.sendingLabContactId, json.returnLabContactId, - json.returnCourier, json.courierAccount, json.BillingReference, json.dewarAvgCustomsValue, json.dewarAvgTransportValue); - -}; - -ShipmentForm.prototype.getPanel = function(shipment) { - var _this = this; - this.shipment = shipment; - var required = '*'; - var buttons = []; - - if (_this.creationMode) { - buttons.push({ - text : 'Create', - scope : this, - handler : function() { - _this._saveShipment(); - } - }); - } else { - buttons.push({ - text : 'Save', - scope : this, - handler : function() { - _this._saveShipment(); - } - }); - - } - - this.labContactForSendingStore = Ext.create('Ext.data.Store', { - fields : [ 'cardName', 'labContactId' ] - }); - - this.labContactForReturnStore = Ext.create('Ext.data.Store', { - fields : [ 'cardName', 'labContactId' ] - }); - - // Create the combo box, attached to the states data store - this.labContactsSendingCombo = Ext.create('Ext.form.ComboBox', { - id : _this.id + "shipmentform_sendingLabContactId", - fieldLabel : 'Lab contact for sending', - afterLabelTextTpl : required, - store : this.labContactForSendingStore, - queryMode : 'local', - labelWidth : 200, - displayField : 'cardName', - valueField : 'labContactId' - }); - - this.labContactsReturnCombo = Ext.create('Ext.form.ComboBox', { - id : _this.id + "returnLabContactId", - fieldLabel : 'If No, Lab-Contact for Return', - afterLabelTextTpl : required, - store : this.labContactForReturnStore, - queryMode : 'local', - labelWidth : 200, - displayField : 'cardName', - valueField : 'labContactId', - listeners : { - change : function(x, newValue) { - for ( var i = 0; i < x.getStore().data.items.length; i++) { - if (x.getStore().data.items[i].raw.labContactId == newValue) { - Ext.getCmp(_this.id + "returnCourier").setValue(x.getStore().data.items[i].raw.defaultCourrierCompany); - Ext.getCmp(_this.id + "courierAccount").setValue(x.getStore().data.items[i].raw.courierAccount); - Ext.getCmp(_this.id + "BillingReference").setValue(x.getStore().data.items[i].raw.billingReference); - Ext.getCmp(_this.id + "dewarAvgCustomsValue").setValue(x.getStore().data.items[i].raw.dewarAvgCustomsValue); - Ext.getCmp(_this.id + "dewarAvgTransportValue").setValue(x.getStore().data.items[i].raw.dewarAvgTransportValue); - } - } - } - } - }); - - if (this.panel == null) { - this.panel = Ext.create('Ext.form.Panel', { - bodyPadding : 5, - width : 600, - border : 1, - items : [ { - xtype : 'fieldset', - title : 'Details', - collapsible : false, - defaultType : 'textfield', - layout : 'anchor', - defaults : { - anchor : '100%' - }, - items : [ { - xtype : 'requiredtext', - fieldLabel : 'Shipment Label', - allowBlank : false, - name : 'shippingName', - id : _this.id + 'shippingName', - value : '', - anchor : '50%' - }, { - - xtype : 'textareafield', - name : 'comments', - id : _this.id + 'comments', - fieldLabel : 'Comments', - value : '' - }, { - fieldLabel : 'Status', - readOnly : true, - id : _this.id + 'shippingStatus', - value : 'Opened', - anchor : '50%' - } ] - }, { - xtype : 'fieldset', - title : 'Lab-Contacts', - collapsible : false, - defaultType : 'textfield', - layout : 'anchor', - defaults : { - anchor : '100%' - }, - items : [ this.labContactsSendingCombo, this.labContactsReturnCombo ] - }, { - border : 0, - html : BUI.getWarningHTML("These informations are relevant for all shipments") - }, { - xtype : 'fieldset', - title : 'Courier accounts details for return', - collapsible : false, - layout : 'anchor', - defaults : { - anchor : '100%' - }, - items : [ { - xtype : 'textfield', - labelWidth : 400, - fieldLabel : 'Courier company for return (if ESRF sends a dewar back)', - id : _this.id + 'returnCourier', - value : '' - }, { - xtype : 'textfield', - labelWidth : 400, - fieldLabel : 'Courier account', - id : _this.id + 'courierAccount', - value : '' - }, { - xtype : 'textfield', - labelWidth : 400, - fieldLabel : 'Billing reference', - id : _this.id + 'BillingReference', - value : '' - }, { - xtype : 'numberfield', - labelWidth : 400, - fieldLabel : 'Average Customs value of a dewar (Euro)', - id : _this.id + 'dewarAvgCustomsValue', - value : '' - }, { - xtype : 'numberfield', - labelWidth : 400, - fieldLabel : 'Average Transport value of a dewar (Euro)', - id : _this.id + 'dewarAvgTransportValue', - value : '' - } ] - } ], - buttons : buttons - }); - } - this.fillStores(); - if (this.showTitle) { - this.panel.setTitle('Create a new Shipment'); - } - return this.panel; -}; - -ShipmentForm.prototype.input = function() { - return { - proposal : DATADOC.getProposal_10() - - }; -}; - -ShipmentForm.prototype.test = function(targetId) { - var shipmentForm = new ShipmentForm({ - creationMode : true - - }); - BIOSAXS.proposal = new Proposal(shipmentForm.input().proposal); - shipmentForm.getPanel().render(targetId); -}; - -/** - * #onSaved - */ -function StockSolutionForm(args) { - this.id = BUI.id(); - this.actions = []; - - if (args != null) { - if (args.actions != null) { - this.actions = args.actions; - } - } - this.onSaved = new Event(this); -} - -StockSolutionForm.prototype.getStockSolution = function() { - if (this.stockSolution != null) { - this.stockSolution.concentration = Ext.getCmp(this.id + "stockSolution_concentration").getValue(); - this.stockSolution.storageTemperature = Ext.getCmp(this.id + "stockSolution_storageTemperature").getValue(); - this.stockSolution.volume = Ext.getCmp(this.id + "stockSolution_volume").getValue(); - this.stockSolution.comments = Ext.getCmp(this.id + "stockSolution_comments").getValue(); - this.stockSolution.name = Ext.getCmp(this.id + "stockSolution_name").getValue(); - this.stockSolution.bufferId = this.bufferCombo.getValue(); - - if (this.macromoleculeCombo.getValue() != null) { - this.stockSolution.macromoleculeId = this.macromoleculeCombo.getValue(); - } else { - this.stockSolution.macromolecule3VO = null; - } - - } else { - return { - concentration : Ext.getCmp(this.id + "stockSolution_concentration").getValue(), - storageTemperature : Ext.getCmp(this.id + "stockSolution_storageTemperature").getValue(), - volume : Ext.getCmp(this.id + "stockSolution_volume").getValue(), - comments : Ext.getCmp(this.id + "stockSolution_comments").getValue(), - name : Ext.getCmp(this.id + "stockSolution_name").getValue(), - bufferId : this.bufferCombo.getValue(), - macromoleculeId : this.macromoleculeCombo.getValue() - }; - } - return this.stockSolution; -}; - -StockSolutionForm.prototype.setStockSolution = function(stockSolution) { - if (stockSolution != null) { - if (stockSolution.macromoleculeId != null) { - this.macromoleculeCombo.setValue(stockSolution.macromoleculeId); - } - this.bufferCombo.setValue(stockSolution.bufferId); - Ext.getCmp(this.id + "stockSolution_concentration").setValue(this.stockSolution.concentration); - Ext.getCmp(this.id + "stockSolution_storageTemperature").setValue(this.stockSolution.storageTemperature); - Ext.getCmp(this.id + "stockSolution_volume").setValue(this.stockSolution.volume); - Ext.getCmp(this.id + "stockSolution_name").setValue(this.stockSolution.name); - Ext.getCmp(this.id + "stockSolution_comments").setValue(this.stockSolution.comments); - } -}; - -StockSolutionForm.prototype.getBufferCombo = function() { - this.bufferCombo = BIOSAXS_COMBOMANAGER.getComboBuffers(BIOSAXS.proposal.getBuffers(), { - labelWidth : 120, - margin : '0 0 10 0', - width : 220 - - }); - return this.bufferCombo; -}; - -StockSolutionForm.prototype.getMacromoleculeCombo = function() { - this.macromoleculeCombo = BIOSAXS_COMBOMANAGER.getComboMacromoleculeByMacromolecules(BIOSAXS.proposal.getMacromolecules(), { - labelWidth : 120, - margin : '0 0 10 0', - width : 220 - - }); - return this.macromoleculeCombo; -}; - -StockSolutionForm.prototype.refresh = function() { -}; - -StockSolutionForm.prototype._getTopPanel = function() { - return { - xtype : 'container', - layout : 'hbox', - border : 0, - items : [ { - xtype : 'container', - layout : 'hbox', - items : [ { - xtype : 'container', - flex : 1, - border : false, - layout : 'anchor', - defaultType : 'textfield', - items : [ - - this.getMacromoleculeCombo(), { - xtype : 'requiredtext', - id : this.id + 'stockSolution_name', - fieldLabel : 'Acronym', - labelWidth : 120, - width : 250 - }, { - xtype : 'requiredtext', - id : this.id + 'stockSolution_concentration', - fieldLabel : 'Conc. (mg/ml)', - labelWidth : 120, - width : 250 - }, - - { - id : this.id + 'stockSolution_storageTemperature', - fieldLabel : 'Storage Temp.(C)', - labelWidth : 120, - width : 250 - }, { - xtype : 'requiredtext', - id : this.id + 'stockSolution_volume', - fieldLabel : 'Volume in Well (µl)', - labelWidth : 120, - width : 250 - } ] - } ] - }, { - xtype : 'container', - flex : 1, - layout : 'anchor', - defaultType : 'textfield', - margin : '0 0 0 10', - items : [ this.getBufferCombo() ] - } ] - }; - -}; - -StockSolutionForm.prototype.getPanel = function(stockSolution) { - this.stockSolution = stockSolution; - this.panel = Ext.createWidget({ - xtype : 'container', - layout : 'vbox', - border : 0, - style : { - padding : '10px' - }, - fieldDefaults : { - labelAlign : 'left', - labelWidth : 50 - }, - items : [ this._getTopPanel(stockSolution), { - id : this.id + 'stockSolution_comments', - xtype : 'textareafield', - name : 'comments', - fieldLabel : 'Comments', - labelWidth : 120, - width : '100%' - } ] - }); - - this.setStockSolution(stockSolution); - return this.panel; -}; - -StockSolutionForm.prototype.input = function() { - return { - stock : { - "stockSolutionId" : 6, - "proposalId" : 3124, - "macromoleculeId" : 5933, - "bufferId" : 811, - "instructionSet3VO" : null, - "boxId" : 305861, - "storageTemperature" : "20", - "volume" : "300", - "concentration" : "1.2", - "comments" : "Buffer EDTA with A", - "name" : "A_EDTA_1.2", - "samples" : [], - "buffer" : "EDTA", - "macromolecule" : "A" - }, - proposal : new MeasurementGrid().input().proposal - }; -}; - -StockSolutionForm.prototype.test = function(targetId) { - var stockSolutionForm = new StockSolutionForm(); - BIOSAXS.proposal = new Proposal(stockSolutionForm.input().proposal); - var panel = stockSolutionForm.getPanel(new Shipment(stockSolutionForm.input().stock)); - panel.render(targetId); -}; - - -BoxWhiskerGraph.prototype.cleanArray = GenericGraph.prototype.cleanArray; -BoxWhiskerGraph.prototype.plotAxes = GenericGraph.prototype.plotAxes; -BoxWhiskerGraph.prototype.plotRuler = GenericGraph.prototype.plotRuler; -BoxWhiskerGraph.prototype.drawSVGVerticalText = GenericGraph.prototype.drawSVGVerticalText; -BoxWhiskerGraph.prototype.getClassColor = GenericGraph.prototype.getClassColor; -BoxWhiskerGraph.prototype.getDimensions = GenericGraph.prototype.getDimensions; -BoxWhiskerGraph.prototype.calculate = GenericGraph.prototype.calculate; -BoxWhiskerGraph.prototype.getMedian = GenericGraph.prototype.getMedian; -BoxWhiskerGraph.prototype.pointToPixel = GenericGraph.prototype.pointToPixel; -BoxWhiskerGraph.prototype.maxValueWhisker = GenericGraph.prototype.maxValueWhisker; -BoxWhiskerGraph.prototype.refresh = GenericGraph.prototype.refresh; - -/** - * Subclass of GenericGraph - * - * @maxBoxWidth - */ -function BoxWhiskerGraph(args){ - this.maxBoxWidth = 25; - - if (args == null){ - args = new Object(); - } - args["plotHorizontalByCluster"] = true; - - GenericGraph.call(this, args); - - if (args.maxBoxWidth != null){ - this.maxBoxWidth = args.maxBoxWidth; - } -}; - - - -/** -There are several different methods for calculating quartiles.[1] This calculator uses a method described by Moore and McCabe to find quartile values. -The same method also used by The TI-83 to calculate quartile values. -With this method, the first quartile is the median of the numbers below the median, the third quartile is the median of the numbers above the median. - -http://www.miniwebtool.com/quartile-calculator/ -http://www.alcula.com/calculators/statistics/box-plot/ -**/ -BoxWhiskerGraph.prototype.getQ1 = function(array){ - array = array.slice(0, array.length/2); - return this.getMedian(array); -}; - -BoxWhiskerGraph.prototype.getQ3 = function(array){ - array = array.slice((array.length + 1)/2); - return this.getMedian(array); - -}; - -BoxWhiskerGraph.prototype.getQ2 = function(array){ - return this.getMedian(array); -}; - -BoxWhiskerGraph.prototype.getBelowOutliers = function(belowLimit, array){ - var points = new Array(); - for (var i = 0; i < array.length; i++){ - if (array[i] <= belowLimit){ - points.push(array[i]); - } - } - return points; -}; - -BoxWhiskerGraph.prototype.getAboveOutliers = function(aboveLimit, array){ - var points = new Array(); - for (var i = 0; i < array.length; i++){ - if (array[i] >= aboveLimit){ - points.push(array[i]); - } - } - return points; -}; - - -BoxWhiskerGraph.prototype.minValueWhisker = function(belowLimit, q1, array){ - var points = new Array(); - - for (var i = 0; i < array.length; i++){ - if ((array[i] < q1) && (array[i]) > belowLimit){ - points.push(array[i]); - } - } - - if (points.length > 0){ - points.sort(function(a, b){return a - b;}); - return points[0]; - } - return null; -}; - -BoxWhiskerGraph.prototype.isNumber = function(value){ - if (value=="") return false; - - var d = parseInt(value); - if (!isNaN(d)) return true; else return false; - -}; - -BoxWhiskerGraph.prototype.plotBoxWhisker = function(boxProperties){ - if (this.maxBoxWidth != null){ - if (boxProperties.width > this.maxBoxWidth){ - boxProperties.x = boxProperties.x + (boxProperties.width/2) - (this.maxBoxWidth/2); - boxProperties.width = this.maxBoxWidth; - } - - } - - if (this.plotPoints){ - for(var i = 0; i < boxProperties.values.length; i++){ - var value = boxProperties.values[i]; - var toPixel = this.pointToPixel(value, boxProperties); - SVG.drawCircle(boxProperties.x + (boxProperties.width/2) + 2, toPixel, this.pointRadius, this.svg, [["fill", "green"], ["fill-opacity", this.fillOpacityPoint],['stroke-opacity', this.strokeOpacityPoint], ["stroke", "black"]]); - } - } - - - var boxColor = this.getClassColor(boxProperties.name); - var result = this.calculate(boxProperties.values); - /** Q1 **/ - - if (this.isNumber(result.Q1)){ - SVG.drawLine(boxProperties.x, this.pointToPixel(result.Q1, boxProperties), boxProperties.x + boxProperties.width, this.pointToPixel(result.Q1, boxProperties), this.svg, [["stroke", boxColor]]); - } - /** Q2 **/ - if (this.isNumber(result.Q2)){ - SVG.drawLine(boxProperties.x, this.pointToPixel(result.Q2, boxProperties), boxProperties.x + boxProperties.width, this.pointToPixel(result.Q2, boxProperties), this.svg, [["stroke", "blue"], ["stroke-width", "2"]]); - } - - /** Q3 **/ - if (this.isNumber(result.Q3)){ - SVG.drawLine(boxProperties.x, this.pointToPixel(result.Q3, boxProperties), boxProperties.x + boxProperties.width, this.pointToPixel(result.Q3, boxProperties), this.svg, [["stroke", boxColor]]); - } - - /** Concenting Q1 and Q3 **/ - if (this.isNumber(result.Q1)&&this.isNumber(result.Q3)){ - SVG.drawLine(boxProperties.x, this.pointToPixel(result.Q1, boxProperties), boxProperties.x, this.pointToPixel(result.Q3, boxProperties), this.svg, [["stroke", boxColor]]); - SVG.drawLine(boxProperties.x + boxProperties.width, this.pointToPixel(result.Q1, boxProperties), boxProperties.x + boxProperties.width, this.pointToPixel(result.Q3, boxProperties), this.svg, [["stroke", boxColor]]); - } - - /** min-whisker **/ - if (result["min-whisker"] != null){ - if (this.isNumber(result.Q1)){ - SVG.drawLine(boxProperties.x + (boxProperties.width/2) + 2, this.pointToPixel(result.Q1, boxProperties), boxProperties.x + (boxProperties.width/2) + 2, this.pointToPixel(result["min-whisker"], boxProperties), this.svg, [["stroke", boxColor]]); - } - SVG.drawLine(boxProperties.x,this.pointToPixel(result["min-whisker"], boxProperties), boxProperties.x + boxProperties.width, this.pointToPixel(result["min-whisker"], boxProperties) , this.svg, [["stroke", boxColor]]); - - } - - /** max-whisker **/ - if (result["max-whisker"] != null){ - if (this.isNumber(result.Q3)){ - SVG.drawLine(boxProperties.x + (boxProperties.width/2) + 2, this.pointToPixel(result.Q3, boxProperties), boxProperties.x + (boxProperties.width/2) + 2, this.pointToPixel(result["max-whisker"], boxProperties), this.svg, [["stroke", boxColor]]); - } - SVG.drawLine(boxProperties.x , this.pointToPixel(result["max-whisker"], boxProperties), boxProperties.x + boxProperties.width, this.pointToPixel(result["max-whisker"], boxProperties), this.svg, [["stroke", boxColor]]); - - } - - /** outliners **/ - if (result["above-outliers"] != null){ - for (var point in result["above-outliers"]){ - SVG.drawCircle(boxProperties.x + (boxProperties.width/2) + 2, this.pointToPixel(result["above-outliers"][point], boxProperties), this.pointRadius, this.svg, [["fill", "red"], ["fill-opacity", "1"]]); - //SVG.drawText(boxProperties.x + (boxProperties.width/2) - 2, this.pointToPixel(result["above-outliers"][point], boxProperties) + 2, "x",this.svg, [["fill", "black"], ["font-size", "x-small"]]); - } - } - if (result["below-outliers"] != null){ - for (var point in result["below-outliers"]){ - SVG.drawCircle(boxProperties.x + (boxProperties.width/2) + 2, this.pointToPixel(result["below-outliers"][point], boxProperties), this.pointRadius, this.svg, [["fill", "red"], ["fill-opacity", "1"]]); - //SVG.drawText(boxProperties.x + (boxProperties.width/2) - 2, this.pointToPixel(result["below-outliers"][point], boxProperties) + 2, "x",this.svg, [["fill", "black"], ["font-size", "x-small"]]); - } - } -}; - - - -BoxWhiskerGraph.prototype.plotClusterTitles = function(properties){ - /** Cluster Titles **/ - var posX = this.left + this.rulerWidth; - var data = this.data; - for(var i = 0; i < data.clusters.length; i++ ){ - /** title for the clusters **/ - posX = posX + this.interClustersSpace; - - /** Drawing title of classes **/ - var cluster = data.clusters[i]; - var posXClasses = posX; - for(var j = 0; j < cluster.classes.length; j++){ - //SVG.drawRectangle(posXClasses, this.height - (this.bottom + this.clusterTitleHeight + this.rulerHeight), properties.classWidth, this.rulerHeight, this.svg, [["fill", "green"]]); -// SVG.drawText(posXClasses + 1/6*properties.classWidth, this.height - (this.bottom + this.clusterTitleHeight + (this.rulerHeight*1/4)), cluster.classes[j].name, this.svg, [["style", "font-size:xx-small"]]); - var color = cluster.classes[j].color; - this.drawSVGVerticalText(posXClasses + 1/2*(properties.classWidth), this.height - (this.bottom - this.clusterTitleHeight + (this.rulerHeight)), cluster.classes[j].name , [["style", "font-size:x-small;"], ["fill", color]]); - - // SVG.drawRectangle(posXClasses, this.height - (this.bottom + this.clusterTitleHeight), properties.classWidth, 2, this.svg, [["fill", "black"]]); - posXClasses = posXClasses + properties.classWidth + this.interClassesSpace; - } - - var clusterTitleSpace = (data.clusters[i].classes.length * properties.classWidth) + ((data.clusters[i].classes.length - 1) * this.interClassesSpace); - //SVG.drawRectangle(posX, this.height - (this.bottom + this.clusterTitleHeight), clusterTitleSpace, this.clusterTitleHeight, this.svg, [["fill", "pink"]]); - SVG.drawRectangle(posX, this.height - (this.clusterTitleHeight+this.bottom), clusterTitleSpace, 1, this.svg, []); - -// var transform = ["translate", "(" + posX + 1/3*clusterTitleSpace +", " + this.height - (this.bottom + (this.clusterTitleHeight*1/4)) +")"], ["transform", "rotate(180)"]; -// var transform = ["transform", "translate(" + (posX + 1/3*clusterTitleSpace) +", " + (this.height - (this.bottom + (this.clusterTitleHeight*1/4))) + "), rotate(-90) "]; -// this.drawSVGVerticalText(posX + 1/3*clusterTitleSpace, this.height - (this.bottom + (this.clusterTitleHeight*1/4)),data.clusters[i].name, [["style", "font-size:x-small"]]); - SVG.drawText(posX , this.height - (this.bottom + (this.clusterTitleHeight*1/4)),data.clusters[i].name ,this.svg, [["style", "font-size:x-small"]]); - posX = posX + clusterTitleSpace; - } -}; - -BoxWhiskerGraph.prototype.plotWhisters = function(data, properties){ - var colors = ["yellow", "orange", "green"]; - var posX = this.left + this.rulerWidth; - for(var i = 0; i < data.clusters.length; i++ ){ - var cluster = data.clusters[i]; - /** inter cluster space **/ - //SVG.drawRectangle(posX, this.top, this.interClustersSpace, this.height - this.top - this.bottom - this.clusterTitleHeight - this.rulerHeight, this.svg, [["fill", "black"]]); - posX = posX + this.interClustersSpace; - - for (var j = 0; j < cluster.classes.length; j ++){ - - //SVG.drawRectangle(posX, this.top, properties.classWidth, this.height - this.top - this.bottom - this.clusterTitleHeight - this.rulerHeight, this.svg, [["fill", colors[j]]]); - this.plotBoxWhisker( - { - name : cluster.classes[j].name, - values : cluster.classes[j].values, - minPoint : properties.minPoint, - maxPoint : properties.maxPoint, - x : posX, - y : this.top, - width : properties.classWidth, - height : this.height - this.top - this.bottom - this.clusterTitleHeight - this.rulerHeight - - - } - ); - posX = posX + properties.classWidth; - //SVG.drawRectangle(posX, this.top, this.interClassesSpace, this.height - this.top - this.bottom - this.clusterTitleHeight - this.rulerHeight, this.svg, [["fill", "black"]]); - if (j < cluster.classes.length - 1){ - posX = posX + this.interClassesSpace; - } - } - } -}; - -BoxWhiskerGraph.prototype.draw = function(targetId, data){ - this.targetId = targetId; - var properties = (this.getDimensions(data)); - this.svg = SVG.createSVGCanvas(document.getElementById(this.targetId), [["width", this.width], ["height", this.height]]); - this.plotAxes(properties); - this.plotClusterTitles(properties); - this.plotWhisters(data,properties); -}; - -BoxWhiskerGraph.prototype.input = function(){ - return DATADOC.getBoxWhikerData(); -}; - -BoxWhiskerGraph.prototype.test = function(targetId){ - var plot = new BoxWhiskerGraph( - { - targetId : targetId, - height : 350, - width : 450, - maxBoxWidth : 25 - } - ); - plot.refresh(this.input()); -}; - - - - - - - -function DataSet(){ - this.json = null; - -}; - - -DataSet.prototype.loadFromJSON = function(json){ - if(json != null) { - if(this.validate(json)) { - this.json = json; - } - } -}; - - -DataSet.prototype.toJSON = function(json){ - return this.json; -}; - - -/** Abstract method to be override on childs classes **/ -DataSet.prototype.validate = function(json){ - if (true){ - return true; - } - /*else{ - throw "Data validation failed"; - }*/ -}; - - - - -Edge.prototype.getName = GraphItem.prototype.getName; -Edge.prototype.setName = GraphItem.prototype.setName; -Edge.prototype.getId = GraphItem.prototype.getId; - -function Edge(id, name, nodeSource, nodeTarget, args){ - GraphItem.prototype.constructor.call(this, id, name, args); - - this.sourceNode = nodeSource; - this.targetNode = nodeTarget; - -}; - -Edge.prototype.toJSON = function(){ - return {"index": this.id,"sourceIndex":this.sourceNode.getId(),"targetIndex":this.targetNode.getId(),"args":this.args}; -}; - -Edge.prototype.getNodeSource = function(){ - return this.sourceNode; -}; - -Edge.prototype.getNodeTarget = function(){ - return this.targetNode; -}; - -Edge.prototype.remove = function(){ - //Remove edge object in the nodes - this.getNodeSource().removeEdge(this); - this.getNodeTarget().removeEdge(this); - - this.deleted.notify(this); -}; - - -EdgeGraphFormatter.prototype.setProperties = ItemGraphFormatter.prototype.setProperties; -EdgeGraphFormatter.prototype.getDefault = ItemGraphFormatter.prototype.getDefault; -EdgeGraphFormatter.prototype.getSelected = ItemGraphFormatter.prototype.getSelected; -EdgeGraphFormatter.prototype.getOver = ItemGraphFormatter.prototype.getOver; -EdgeGraphFormatter.prototype.getDragging = ItemGraphFormatter.prototype.getDragging; -EdgeGraphFormatter.prototype.getId = ItemGraphFormatter.prototype.getId; -EdgeGraphFormatter.prototype.toJSON = ItemGraphFormatter.prototype.toJSON; -EdgeGraphFormatter.prototype.loadFromJSON = ItemGraphFormatter.prototype.loadFromJSON; -EdgeGraphFormatter.prototype._setEvents = ItemGraphFormatter.prototype._setEvents; - -function EdgeGraphFormatter(id, defaultFormat, selectedFormat, overFormat, dragginFormat){ - ItemGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, dragginFormat); - - this.getDefault().args.type = "EdgeGraphFormatter"; -}; - - - - -LineEdgeGraphFormatter.prototype.setProperties = EdgeGraphFormatter.prototype.setProperties; -LineEdgeGraphFormatter.prototype.getDefault = EdgeGraphFormatter.prototype.getDefault; -LineEdgeGraphFormatter.prototype.getSelected = EdgeGraphFormatter.prototype.getSelected; -LineEdgeGraphFormatter.prototype.getOver = EdgeGraphFormatter.prototype.getOver; -LineEdgeGraphFormatter.prototype.getDragging = EdgeGraphFormatter.prototype.getDragging; -LineEdgeGraphFormatter.prototype.getId = EdgeGraphFormatter.prototype.getId; -LineEdgeGraphFormatter.prototype.toJSON = EdgeGraphFormatter.prototype.toJSON; -LineEdgeGraphFormatter.prototype.loadFromJSON = EdgeGraphFormatter.prototype.loadFromJSON; -LineEdgeGraphFormatter.prototype._setEvents = EdgeGraphFormatter.prototype._setEvents; - -function LineEdgeGraphFormatter(id, defaultFormat, selectedFormat, overFormat, dragginFormat){ - EdgeGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, dragginFormat); - this.args.type = "LineEdgeGraphFormatter"; -}; - -/** DIRECTED **/ -DirectedLineEdgeGraphFormatter.prototype.setProperties = EdgeGraphFormatter.prototype.setProperties; -DirectedLineEdgeGraphFormatter.prototype.getDefault = EdgeGraphFormatter.prototype.getDefault; -DirectedLineEdgeGraphFormatter.prototype.getSelected = EdgeGraphFormatter.prototype.getSelected; -DirectedLineEdgeGraphFormatter.prototype.getOver = EdgeGraphFormatter.prototype.getOver; -DirectedLineEdgeGraphFormatter.prototype.getDragging = EdgeGraphFormatter.prototype.getDragging; -DirectedLineEdgeGraphFormatter.prototype.getId = EdgeGraphFormatter.prototype.getId; -DirectedLineEdgeGraphFormatter.prototype.toJSON = EdgeGraphFormatter.prototype.toJSON; -DirectedLineEdgeGraphFormatter.prototype.loadFromJSON = EdgeGraphFormatter.prototype.loadFromJSON; -DirectedLineEdgeGraphFormatter.prototype._setEvents = EdgeGraphFormatter.prototype._setEvents; - -function DirectedLineEdgeGraphFormatter(id, defaultFormat, selectedFormat, overFormat, dragginFormat){ - EdgeGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, dragginFormat); - this.args.type = "DirectedLineEdgeGraphFormatter"; - this.args.arrowSize = "3"; -}; -DirectedLineEdgeGraphFormatter.prototype.getArrowSize = function(){ - return this.args.arrowSize; -}; - -OdirectedLineEdgeGraphFormatter.prototype.setProperties = EdgeGraphFormatter.prototype.setProperties; -OdirectedLineEdgeGraphFormatter.prototype.getDefault = EdgeGraphFormatter.prototype.getDefault; -OdirectedLineEdgeGraphFormatter.prototype.getSelected = EdgeGraphFormatter.prototype.getSelected; -OdirectedLineEdgeGraphFormatter.prototype.getOver = EdgeGraphFormatter.prototype.getOver; -OdirectedLineEdgeGraphFormatter.prototype.getDragging = EdgeGraphFormatter.prototype.getDragging; -OdirectedLineEdgeGraphFormatter.prototype.getId = EdgeGraphFormatter.prototype.getId; -OdirectedLineEdgeGraphFormatter.prototype.toJSON = EdgeGraphFormatter.prototype.toJSON; -OdirectedLineEdgeGraphFormatter.prototype.loadFromJSON = EdgeGraphFormatter.prototype.loadFromJSON; -OdirectedLineEdgeGraphFormatter.prototype._setEvents = EdgeGraphFormatter.prototype._setEvents; - -function OdirectedLineEdgeGraphFormatter(id, defaultFormat, selectedFormat, overFormat, dragginFormat){ - EdgeGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, dragginFormat); - this.args.type = "DirectedLineEdgeGraphFormatter"; - this.args.arrowSize = "3"; -}; - -OdirectedLineEdgeGraphFormatter.prototype.getArrowSize = function(){ - return this.args.arrowSize; -}; - - -/** CUT (inhibition) **/ -CutDirectedLineEdgeGraphFormatter.prototype.setProperties = EdgeGraphFormatter.prototype.setProperties; -CutDirectedLineEdgeGraphFormatter.prototype.getDefault = EdgeGraphFormatter.prototype.getDefault; -CutDirectedLineEdgeGraphFormatter.prototype.getSelected = EdgeGraphFormatter.prototype.getSelected; -CutDirectedLineEdgeGraphFormatter.prototype.getOver = EdgeGraphFormatter.prototype.getOver; -CutDirectedLineEdgeGraphFormatter.prototype.getDragging = EdgeGraphFormatter.prototype.getDragging; -CutDirectedLineEdgeGraphFormatter.prototype.getId = EdgeGraphFormatter.prototype.getId; -CutDirectedLineEdgeGraphFormatter.prototype.toJSON = EdgeGraphFormatter.prototype.toJSON; -CutDirectedLineEdgeGraphFormatter.prototype.loadFromJSON = EdgeGraphFormatter.prototype.loadFromJSON; -CutDirectedLineEdgeGraphFormatter.prototype._setEvents = EdgeGraphFormatter.prototype._setEvents; - -function CutDirectedLineEdgeGraphFormatter(id, defaultFormat, selectedFormat, overFormat, dragginFormat){ - EdgeGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, dragginFormat); - this.args.type = "CutDirectedLineEdgeGraphFormatter"; -}; - - - - -BezierEdgeGraphFormatter.prototype.setProperties = EdgeGraphFormatter.prototype.setProperties; -BezierEdgeGraphFormatter.prototype.setProperties = EdgeGraphFormatter.prototype.setProperties; -BezierEdgeGraphFormatter.prototype.getDefault = EdgeGraphFormatter.prototype.getDefault; -BezierEdgeGraphFormatter.prototype.getSelected = EdgeGraphFormatter.prototype.getSelected; -BezierEdgeGraphFormatter.prototype.getOver = EdgeGraphFormatter.prototype.getOver; -BezierEdgeGraphFormatter.prototype.getDragging = EdgeGraphFormatter.prototype.getDragging; -BezierEdgeGraphFormatter.prototype.getId = EdgeGraphFormatter.prototype.getId; -BezierEdgeGraphFormatter.prototype.toJSON = EdgeGraphFormatter.prototype.toJSON; -BezierEdgeGraphFormatter.prototype.loadFromJSON = EdgeGraphFormatter.prototype.loadFromJSON; -BezierEdgeGraphFormatter.prototype._setEvents = EdgeGraphFormatter.prototype._setEvents; - -function BezierEdgeGraphFormatter(id, defaultFormat, selectedFormat, overFormat, dragginFormat){ - EdgeGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, dragginFormat); - this.args.type = "BezierEdgeGraphFormatter"; -}; - - - -DotDirectedLineEdgeGraphFormatter.prototype.setProperties = EdgeGraphFormatter.prototype.setProperties; -DotDirectedLineEdgeGraphFormatter.prototype.getDefault = EdgeGraphFormatter.prototype.getDefault; -DotDirectedLineEdgeGraphFormatter.prototype.getSelected = EdgeGraphFormatter.prototype.getSelected; -DotDirectedLineEdgeGraphFormatter.prototype.getOver = EdgeGraphFormatter.prototype.getOver; -DotDirectedLineEdgeGraphFormatter.prototype.getDragging = EdgeGraphFormatter.prototype.getDragging; -DotDirectedLineEdgeGraphFormatter.prototype.getId = EdgeGraphFormatter.prototype.getId; -DotDirectedLineEdgeGraphFormatter.prototype.toJSON = EdgeGraphFormatter.prototype.toJSON; -DotDirectedLineEdgeGraphFormatter.prototype.loadFromJSON = EdgeGraphFormatter.prototype.loadFromJSON; -DotDirectedLineEdgeGraphFormatter.prototype._setEvents = EdgeGraphFormatter.prototype._setEvents; - -function DotDirectedLineEdgeGraphFormatter(id, defaultFormat, selectedFormat, overFormat, dragginFormat){ - EdgeGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, dragginFormat); - this.args.type = "DotDirectedLineEdgeGraphFormatter"; -}; - - -OdotDirectedLineEdgeGraphFormatter.prototype.setProperties = EdgeGraphFormatter.prototype.setProperties; -OdotDirectedLineEdgeGraphFormatter.prototype.getDefault = EdgeGraphFormatter.prototype.getDefault; -OdotDirectedLineEdgeGraphFormatter.prototype.getSelected = EdgeGraphFormatter.prototype.getSelected; -OdotDirectedLineEdgeGraphFormatter.prototype.getOver = EdgeGraphFormatter.prototype.getOver; -OdotDirectedLineEdgeGraphFormatter.prototype.getDragging = EdgeGraphFormatter.prototype.getDragging; -OdotDirectedLineEdgeGraphFormatter.prototype.getId = EdgeGraphFormatter.prototype.getId; -OdotDirectedLineEdgeGraphFormatter.prototype.toJSON = EdgeGraphFormatter.prototype.toJSON; -OdotDirectedLineEdgeGraphFormatter.prototype.loadFromJSON = EdgeGraphFormatter.prototype.loadFromJSON; -OdotDirectedLineEdgeGraphFormatter.prototype._setEvents = EdgeGraphFormatter.prototype._setEvents; - -function OdotDirectedLineEdgeGraphFormatter(id, defaultFormat, selectedFormat, overFormat, dragginFormat){ - EdgeGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, dragginFormat); - this.args.type = "OdotDirectedLineEdgeGraphFormatter"; -}; - - - -function GraphDataset(){ - DataSet.prototype.constructor.call(this); - this.edges = new Object(); - this.vertices = new Object(); - this.verticesIndex = new Object(); - - //Events - this.newVertex = new Event(this); - this.vertexNameChanged = new Event(this); - this.vertexDeleted = new Event(this); - - this.newEdge = new Event(this); - this.edgeNameChanged = new Event(this); - this.edgeDeleted = new Event(this); - - this.json = new Object(); - this.json.vertices = new Array(); - this.json.edges = new Array(); - this.json.relations = new Array(); -}; - -GraphDataset.prototype.loadFromJSON = DataSet.prototype.loadFromJSON; -GraphDataset.prototype.toJSON = DataSet.prototype.toJSON; -GraphDataset.prototype.validate = DataSet.prototype.validate; - -/** Devuelve el numero de edges incidentes sobre el nodo con mas edges **/ -GraphDataset.prototype.getMaxClass = function(){ - var maxClassNode = 0; - for ( var node in this.vertices) { - if (this.vertices[node].getEdgesCount() > maxClassNode){ - maxClassNode = this.vertices[node].getEdgesCount(); - } - } - return maxClassNode; -}; - -/** Devuelve el numero de edges incidentes sobre el nodo con mas edges **/ -GraphDataset.prototype.getMinClass = function(){ - var minClassNode = Math.min(); - for ( var node in this.vertices) { - if (this.vertices[node].getEdgesCount() < minClassNode){ - minClassNode = this.vertices[node].getEdgesCount(); - } - } - return minClassNode; -}; - -GraphDataset.prototype.getVertexByName = function(nodeName){ - var results = new Array(); - - for (var vertexId in this.verticesIndex[nodeName]){ - var vertexByid = this.getVertexById(this.verticesIndex[nodeName][vertexId]); - results.push(vertexByid); - //* añadido nuevo porque fallaba el anterior codigo - return vertexByid - } - - if (results <= 1){ - return this.getVertexById(this.verticesIndex[nodeName]); - } - else{ - return results; - } -}; - -GraphDataset.prototype.getVertexById = function(id){ - return this.vertices[id]; -}; - -GraphDataset.prototype.toSIF = function(){ - var sifDataAdapter = new SifFileDataAdapter(); - return sifDataAdapter.toSIF(this); -}; - -GraphDataset.prototype.toSIFID = function(){ - var sifDataAdapter = new SifFileDataAdapter(); - return sifDataAdapter.toSIFID(this); -}; - -GraphDataset.prototype.toDOT = function(){ - var dotFileDataAdapter = new DotFileDataAdapter(); - return dotFileDataAdapter.toDOT(this); -}; - -GraphDataset.prototype.toDOTID = function(){ - var dotFileDataAdapter = new DotFileDataAdapter(); - return dotFileDataAdapter.toDOTID(this); -}; - -GraphDataset.prototype._addNode = function(nodeName, args){ - return new Vertex(this._getVerticesCount()-1, nodeName, args); -}; - -GraphDataset.prototype.addNode = function(nodeName, args){ - this.json.vertices.push(nodeName); - this._addVerticesIndex(nodeName, this._getVerticesCount() - 1); - var vertex = this._addNode(nodeName, args); - this.vertices[this._getVerticesCount()-1] = vertex; - this._setNodeEvents(vertex); - this.newVertex.notify(vertex); -}; - -GraphDataset.prototype._addVerticesIndex = function(nodeName, id){ - if (this.verticesIndex[nodeName] == null){ - this.verticesIndex[nodeName] = new Array(); - } - this.verticesIndex[nodeName].push(id); -}; - -GraphDataset.prototype.addEdge = function(edgeName, nodeSourceId, nodeTargetId, args){ - this.json.edges.push(edgeName); - var nodeSource = this.getVertexById(nodeSourceId); - var nodeTarget = this.getVertexById(nodeTargetId); - var index = this.getEdgesCount() - 1; - this.edges[index] = new Edge(index, edgeName, nodeSource, nodeTarget, args); - this.json.relations.push({"index": index, "sourceIndex": nodeSourceId, "targetIndex": nodeTargetId, "args": args }); - - nodeSource.addEdge(this.edges[index]); - nodeTarget.addEdge(this.edges[index]); - this._setEdgeEvents(this.edges[index]); - this.newEdge.notify(this.edges[index]); -}; - -GraphDataset.prototype.getVertices = function(){ - return this.vertices; -}; - -GraphDataset.prototype.getEdges = function(){ - return this.edges; -}; - -GraphDataset.prototype.getEdgeById = function(edgeId){ - return this.edges[edgeId]; -}; - -GraphDataset.prototype._getVerticesCount = function(){ - return this.json.vertices.length; -}; - - -GraphDataset.prototype.getVerticesCount = function(){ - var count = 0; - for ( var vertex in this.getVertices()) { - count ++; - } - return count; -}; - - -GraphDataset.prototype.getEdgesCount = function(){ - return this.json.edges.length; -}; - -GraphDataset.prototype.init = function(){ - this.edges = new Object(); - this.vertices = new Object(); -}; - -GraphDataset.prototype._setNodeEvents = function(node){ - var _this = this; - //NODE EVENTS - node.deleted.attach(function (sender, node){ - _this._removeNode(node); - }); - - node.nameChanged.attach(function (sender, args){ - var item = args.item; - var newName = item.name; - var indexes = _this.verticesIndex[args.previousName]; - for(var i = 0; i < indexes.length; i++){ - if(indexes[i] == item.id) - indexes.splice(i,1); - } - if(indexes.length == 0){ - delete _this.verticesIndex[args.previousName]; - } - _this._addVerticesIndex(newName, item.id); - _this.json.vertices[item.id] = newName; - _this.vertexNameChanged.notify(args); - }); -}; - -GraphDataset.prototype._setEdgeEvents = function(edge){ - var _this = this; - //EDGE EVENTS - edge.nameChanged.attach(function (sender, edge){ - _this.edgeNameChanged.notify(edge); - - }); - - edge.deleted.attach(function (sender, edge){ - _this._removeEdge(edge); - }); -}; - -GraphDataset.prototype._connectVerticesByName = function(nodeNameSource, nodeNameTarget){ - var source = this.getVertexByName(nodeNameSource); - var target = this.getVertexByName(nodeNameTarget); - - if ((source != null)&&(target!=null)){ - this.addEdge(source.getName() +"_" + target.getName(), source.getId(), target.getId(), {}); - } - else{ - if (source == null){ - console.log("No encontrado: " + nodeNameSource) - } - if (target == null){ - console.log("No encontrado: " + nodeNameTarget) - } - } -}; - -GraphDataset.prototype.loadFromJSON = function(json){ - var json = json; - this.init(); - this.json = new Object(); - this.json.vertices = new Array(); - this.json.edges = new Array(); - this.json.relations = new Array(); - - for ( var i = 0; i < json.nodes.length; i++) { - if (json.nodes[i] != null){ - var name = json.nodes[i]; - this.addNode(name); - } - else{ - this.json.vertices.push(null); - } - } - - for ( var i = 0; i < json.edges.length; i++) { - if (json.edges[i] != null){ - if (json.relations[i] != null){ - var name = json.edges[i]; - this.addEdge(name, json.relations[i].sourceIndex, json.relations[i].targetIndex, json.relations[i].args); - } - } - else{ - this.json.edges.push(null); - this.json.relations.push(null); - } - } -}; - -GraphDataset.prototype.prettyPrint = function(){ - for ( var node in this.vertices) { - console.log(this.vertices[node].getName() ); - for ( var j = 0; j < this.vertices[node].getEdgesIn().length; j++) { - console.log(" --> " + this.vertices[node].getEdgesIn()[j].getNodeTarget().getName() ); - } - } -}; - -GraphDataset.prototype._removeEdge = function(edge){ - this.json.edges[edge.getId()] = null; - this.json.relations[edge.getId()] = null; - - delete this.edges[edge.getId()]; - this.edgeDeleted.notify(edge); - - -}; - -GraphDataset.prototype._removeNode = function(node){ - this.json.vertices[node.getId()] = null; - delete this.vertices[node.getId()]; - this.vertexDeleted.notify(node); -}; - -GraphDataset.prototype.toJSON = function(){ - var json = new Object(); - var nodes = new Array(); - json.nodes = this.json.vertices; //nodes; - json.edges = this.json.edges; //edges; - json.relations = this.json.relations; - return json; -}; - -GraphDataset.prototype.clone = function(){ - var dsDataset = new GraphDataset(); - dsDataset.loadFromJSON(this.toJSON()); - return dsDataset; -}; -//GraphDataset.prototype.test = function(){ -// this.loadFromJSON(this.toJSON()); -//}; - -function labels(){ - var names = new Array(); - - var dataset = interactomeViewer.graphEditorWidget.dataset; - var layout = interactomeViewer.graphEditorWidget.layout; - - for ( var vertexId in interactomeViewer.graphEditorWidget.dataset.getVertices()) { - names.push(interactomeViewer.graphEditorWidget.dataset.getVertexById(vertexId).getName()); - } - - var sorted = (names.sort()); - console.log(sorted) - var distance = 0.01; - var altura = 0.6; - for ( var i = 0; i < names.length; i++) { - var id =dataset.getVertexByName(names[i]).getId(); - - layout.getNodeById(id).setCoordenates(distance, altura); - - - distance = parseFloat(distance) + parseFloat(0.03); - - altura = parseFloat(altura) + parseFloat(0.02); - - if (parseFloat(altura) == 0.9800000000000003){ - - altura = 0.6; - distance = distance - 0.51; - } - - } - - -}; - -function GraphItem(id, name, args){ - this.id = id; - this.name = name; - this.type = "NONE"; - - this.args = new Object(); - - - if (args!=null){ - this.args = args; - if (args.type !=null){ - this.type = args.type; - } - } - - //Events - this.nameChanged = new Event(this); - this.deleted = new Event(this); -} - -GraphItem.prototype.getName = function(){ - return this.name; -}; - -GraphItem.prototype.getId = function(){ - return this.id; -}; - -GraphItem.prototype.setName = function(name){ - var oldName = this.getName(); - this.name = name; - this.nameChanged.notify({"item": this, "previousName" : oldName}); -}; - - - - - -function LayoutDataset(){ - this.dataset = null; - this.vertices = new Object(); - this.changed = new Event(this); - - - this.args = new Object(); - - //RANDOM, CIRCLE - this.args.type = "CIRCLE"; -}; - -LayoutDataset.prototype.loadFromJSON = function(dataset, json){ - var _this = this; - this.vertices = new Object(); - this.dataset = dataset; //new GraphDataset(); - for ( var vertex in json) { - this.vertices[vertex] = new NodeLayout(vertex, json[vertex].x, json[vertex].y); - this.vertices[vertex].changed.attach(function (sender, item){ - _this.changed.notify(item); - }); - } - this._attachDatasetEvents(); -}; - - -LayoutDataset.prototype.toJSON = function(){ - var serialize = new Object(); - for ( var vertex in this.vertices) { - serialize[vertex] = new Object(); - serialize[vertex].x = this.vertices[vertex].x; - serialize[vertex].y = this.vertices[vertex].y; - } - serialize.dataset = new Object(); - serialize.dataset =this.dataset.toJSON(); - return serialize; -}; - -LayoutDataset.prototype.dataBind = function(graphDataset){ - this.dataset = graphDataset; - this._attachDatasetEvents(); - this._calculateLayout(); -}; - -LayoutDataset.prototype._removeVertex = function(vertexId){ - delete this.vertices[vertexId]; -}; - -LayoutDataset.prototype._attachDatasetEvents = function(){ - var _this = this; - - this.dataset.vertexDeleted.attach(function (sender, item){ - _this._removeVertex(item.getId()); - }); - - this.dataset.newVertex.attach(function (sender, item){ - _this.vertices[item.getId()] = new NodeLayout(item.getId(), 0.5, 0.5); - _this.vertices[item.getId()].changed.attach(function (sender, item){ - _this.changed.notify(item); - }); - }); -}; - -LayoutDataset.prototype.getType = function(){ - return this.args.type; -}; - -LayoutDataset.prototype._calculateLayoutVertices = function(type, count){ - - if (type == "CIRCLE"){ - var radius = 0.4; - var centerX = 0.5; - var centerY = 0.5; - var verticesCoordinates = new Array(); - for(var i = 0; i < count; i++){ - x = centerX + radius * Math.sin(i * 2 * Math.PI/count); - y = centerY + radius * Math.cos(i * 2 * Math.PI/count); - verticesCoordinates.push({'x':x,'y':y}); - } - return verticesCoordinates; - } -}; - - -LayoutDataset.prototype._calculateLayout = function(){ - var _this = this; - if (this.getType() == "RANDOM"){ - for ( var vertex in this.dataset.getVertices()) { - if (this.vertices[vertex] == null){ - this.vertices[vertex] = new NodeLayout(vertex, 0, 0); - } - this.vertices[vertex].setCoordinates(Math.random(), Math.random()); - this.vertices[vertex].changed.attach(function (sender, item){ - _this.changed.notify(item); - }); - } - } - - if ( this.getType() == "CIRCLE"){ - - var count = this.dataset._getVerticesCount(); - var verticesCoordinates = this._calculateLayoutVertices(this.getType(), count); - - var aux = 0; - for ( var vertex in this.dataset.getVertices()) { - if (this.vertices[vertex] == null){ - this.vertices[vertex] = new NodeLayout(vertex, 0, 0); - } - this.vertices[vertex].setCoordinates(verticesCoordinates[aux].x, verticesCoordinates[aux].y);//{"x":, "y":}; - aux++; - this.vertices[vertex].changed.attach(function (sender, item){ - _this.changed.notify(item); - }); - } - } - - - if (this.getType() == "SQUARE"){ - - var count = this.dataset._getVerticesCount(); - var xMin = 0.1; - var xMax = 0.9; - var yMin = 0.1; - var yMax = 0.9; - - var rows = Math.sqrt(count); - var step = (xMax - xMin) / rows; - - var verticesCoordinates = new Array(); - for(var i = 0; i < rows; i ++){ - for ( var j = 0; j < rows; j++) { - x = i * step + xMin; - y = j * step + yMin; - verticesCoordinates.push({'x':x,'y':y}); - } - } - - var aux = 0; - for ( var vertex in this.dataset.getVertices()) { - if (this.vertices[vertex] == null){ - this.vertices[vertex] = new NodeLayout(vertex, 0, 0); - } - this.vertices[vertex].setCoordinates(verticesCoordinates[aux].x, verticesCoordinates[aux].y);//{"x":, "y":}; - aux++; - this.vertices[vertex].changed.attach(function (sender, item){ - _this.changed.notify(item); - }); - } - } - -}; - -LayoutDataset.prototype.getNodeById = function(id){ - return this.vertices[id]; -}; - -LayoutDataset.prototype.getVerticesByArea = function(x1, y1, x2, y2){ - var vertices = new Array(); - for ( var vertex in this.dataset.getVertices()) { - if ((this.vertices[vertex].x >= x1)&&(this.vertices[vertex].x <= x2)){ - if ((this.vertices[vertex].y >= y1)&&(this.vertices[vertex].y <= y2)){ - vertices.push(this.vertices[vertex]); - } - } - } - return vertices; -}; - - - - -LayoutDataset.prototype.getLayout = function(type){ - - if (type == "CIRCLE"){ - this.args.type = "CIRCLE"; - this._calculateLayout(); - return; - } - - if (type == "SQUARE"){ - this.args.type = "SQUARE"; - this._calculateLayout(); - return; - } - - if (type == "RANDOM"){ - this.args.type = "RANDOM"; - this._calculateLayout(); - return; - } - - - var dotText = this.dataset.toDOTID(); - var url = "http://bioinfo.cipf.es/utils/ws/rest/network/layout/"+type+".coords"; - var _this = this; - - $.ajax({ - async: true, - type: "POST", - url: url, - dataType: "text", - data: { - dot :dotText - }, - cache: false, - success: function(data){ - var response = JSON.parse(data); - for ( var vertexId in response) { - _this.vertices[vertexId].setCoordinates(response[vertexId].x, response[vertexId].y); - } - } - }); - -// $.ajax({ -// async: true, -// type: "POST", -// url: url, -// dataType: "script", -// data: { -// dot :dotText -// }, -// cache: false, -// success: function(data){ -// var response = JSON.parse(data); -// for ( var vertexId in response) { -// _this.vertices[vertexId].setCoordinates(response[vertexId].x, response[vertexId].y); -// } -// } -// }); - -}; - -function NodeLayout(id, x, y, args){ - this.id = id; - this.x = x; - this.y = y; - this.changed = new Event(this); -}; - -NodeLayout.prototype.getId = function(id){ - return this.id; -}; - -NodeLayout.prototype.setCoordinates = function(x, y){ - this.x = x; - this.y = y; - this.changed.notify(this); -}; - - - -function NetworkDataSetFormatter(vertexFormatProperties, defaultEdgeProperties, args){ - DataSet.prototype.constructor.call(this); - - this.args = new Object(); - - this.vertices = new Object(); - this.edges = new Object(); - this.dataset = null; - this.maxClass = 0; - this.minClass = 0; - - /** Coordenates with default Setting */ - this.args.width = 1100; - this.args.height = 500; - - /** Optional parameters */ - this.args.backgroundColor = "#FFFFFF"; - this.args.backgroundImage = null; - this.args.backgroundImageHeight = null; - this.args.backgroundImageWidth = null; - this.args.backgroundImageX = null; - this.args.backgroundImageY = null; - - - this.args.balanceNodes = false; - this.args.nodesMaxSize = 3; - this.args.nodesMinSize = 0.5; - - - /** Zoom **/ - this.args.zoomScale = 1; - this.args.zoomScaleStepFactor = 0.4; - - if (args != null){ - if (args.backgroundImage != null){ - this.args.backgroundImage = args.backgroundImage; - } - - if (args.width != null){ - this.args.width = args.width; - } - - if (args.height != null){ - this.args.height = args.height; - this.args.svgHeight = args.height; - } - - if (args.svgHeight != null){ - this.args.svgHeight = args.svgHeight; - } - - if (args.backgroundColor != null){ - this.args.backgroundColor = args.backgroundColor; - } - - if(args.balanceNodes != null){ - this.args.balanceNodes = args.balanceNodes; - } - - if(args.nodesMaxSize != null){ - this.args.nodesMaxSize = args.nodesMaxSize; - } - if(args.nodesMinSize != null){ - this.args.nodesMinSize = args.nodesMinSize; - } - } - - this.args.defaultEdgeProperties = {"fill":"#000000", "radius":"1", "stroke":"#000000", "size":1, "title":{"fontSize":10, "fontColor":"#000000"}}; - this.args.vertexFormatProperties = { "fill":"#000000", "stroke":"#000000", "stroke-opacity":"#000000"}; - - if (vertexFormatProperties!= null){ - this.args.vertexFormatProperties = vertexFormatProperties; - } - if (defaultEdgeProperties!= null){ - this.args.defaultEdgeProperties = defaultEdgeProperties; - } - - /** Events **/ - this.changed = new Event(this); - this.edgeChanged = new Event(this); - this.resized = new Event(this); - this.backgroundImageChanged= new Event(this); - this.backgroundColorChanged= new Event(this); -}; - -NetworkDataSetFormatter.prototype.loadFromJSON = function(dataset, json){ - this.args = new Object(); - this.vertices = new Object(); - this.args = json; - this._setDataset(dataset); - var _this = this; - - for ( var vertex in json.vertices) { - this.addVertex(vertex, json.vertices[vertex]); - } - - for ( var edgeId in json.edges) { - this.addEdge(edgeId, json.edges[edgeId]); - } -}; - - -NetworkDataSetFormatter.prototype.toJSON = function(){ - - -// this.args.vertices = new Object(); -// this.args.edges = new Object(); -// -// for ( var vertex in this.vertices) { -// this.args.vertices[vertex] = this.getVertexById(vertex).toJSON(); -// } -// for ( var edge in this.edges) { -// this.args.edges[edge] = this.getEdgeById(edge).toJSON(); -// } -// -// return (this.args); - - - var serialize = new Object(); - serialize = JSON.parse(JSON.stringify(this.args)); - serialize.vertices = new Object(); - serialize.edges = new Object(); - - for ( var vertex in this.vertices) { - serialize.vertices[vertex] = this.getVertexById(vertex).toJSON(); - } - for ( var edge in this.edges) { - serialize.edges[edge] = this.getEdgeById(edge).toJSON(); - } - - return (serialize); -}; - - - -NetworkDataSetFormatter.prototype._getNodeSize = function(nodeId){ - if (this.isVerticesBalanced()){ - var total = this.maxClass - this.minClass; - if (total == 0){ total = 1;} - var nodeConnection = this.dataset.getVertexById(nodeId).getEdges().length; - return ((nodeConnection*this.args.nodesMaxSize)/total) + this.args.nodesMinSize; - } - return this.getVertexById(nodeId).getDefault().getSize(); -}; - -NetworkDataSetFormatter.prototype._recalculateSize = function(){ - if (this.isVerticesBalanced()){ - this.maxClass = this.dataset.getMaxClass(); - this.minClass = this.dataset.getMinClass(); - for ( var vertexIdAll in this.vertices) { - var size = this._getNodeSize(vertexIdAll); - this.vertices[vertexIdAll].getDefault().setSize(size); - this.vertices[vertexIdAll].getSelected().setSize(size); - this.vertices[vertexIdAll].getOver().setSize(size); - } - } -}; - - -NetworkDataSetFormatter.prototype.addVertex = function(vertexId, json){ - - - if (json == null){ - this.vertices[vertexId] = new CircleVertexGraphFormatter(vertexId, this.args.vertexFormatProperties.defaultFormat, this.args.vertexFormatProperties.selected, this.args.vertexFormatProperties.over, this.args.vertexFormatProperties.dragging); - } - else{ - - /** Cargo los attributos desde el json **/ - if (json.type == null){ - this.vertices[vertexId] = new CircleVertexGraphFormatter(vertexId, this.args.vertexFormatProperties.defaultFormat, this.args.vertexFormatProperties.selected, this.args.vertexFormatProperties.over, this.args.vertexFormatProperties.dragging); - } - - if ((json.type == "SquareVertexGraphFormatter")||(json.type == "SquareVertexNetworkFormatter")){ - this.vertices[vertexId] = new SquareVertexGraphFormatter(vertexId); - this.vertices[vertexId].loadFromJSON(json); - } - - if ((json.type == "CircleVertexGraphFormatter")||(json.type == "CircleVertexNetworkFormatter")){ - this.vertices[vertexId] = new CircleVertexGraphFormatter(vertexId); - this.vertices[vertexId].loadFromJSON(json); - } - - if ((json.type == "EllipseVertexGraphFormatter")||(json.type == "EllipseVertexNetworkFormatter")){ - this.vertices[vertexId] = new EllipseVertexGraphFormatter(vertexId); - this.vertices[vertexId].loadFromJSON(json); - } - - if ((json.type == "RectangleVertexGraphFormatter")||(json.type == "RectangleVertexNetworkFormatter")){ - this.vertices[vertexId] = new RectangleVertexGraphFormatter(vertexId); - this.vertices[vertexId].loadFromJSON(json); - } - - if ((json.type == "RoundedVertexGraphFormatter")||(json.type == "RoundedVertexNetworkFormatter")){ - this.vertices[vertexId] = new RoundedVertexGraphFormatter(vertexId); - this.vertices[vertexId].loadFromJSON(json); - } - - } - - - var _this = this; - this.vertices[vertexId].stateChanged.attach(function (sender, item){ - _this.changed.notify(item); - }); - - var size = this._getNodeSize(vertexId); - this.vertices[vertexId].defaultFormat.args.size = size; - this.vertices[vertexId].selected.args.size = size; - this.vertices[vertexId].over.args.size = size; - -}; - -NetworkDataSetFormatter.prototype.addEdge = function(edgeId, json){ - - /** Es un edge nuevo que le doy los atributos por defecto **/ - if (json == null){ - if (this.dataset.getEdgeById(edgeId).getNodeSource().getId() == this.dataset.getEdgeById(edgeId).getNodeTarget().getId()){ - this.edges[edgeId] = new BezierEdgeGraphFormatter(edgeId, this.args.defaultEdgeProperties.defaultFormat, this.args.defaultEdgeProperties.selected, this.args.defaultEdgeProperties.over, this.args.defaultEdgeProperties.dragging); - }else{ - this.edges[edgeId] = new DirectedLineEdgeGraphFormatter(edgeId, this.args.defaultEdgeProperties.defaultFormat, this.args.defaultEdgeProperties.selected, this.args.defaultEdgeProperties.over, this.args.defaultEdgeProperties.dragging); - } - } - else{ - /** Cargo los attributos desde el json **/ - if (json.type == null){ - this.edges[edgeId] = new LineEdgeGraphFormatter(edgeId, this.args.defaultEdgeProperties.defaultFormat, this.args.defaultEdgeProperties.selected, this.args.defaultEdgeProperties.over, this.args.defaultEdgeProperties.dragging); - } - - if ((json.type == "LineEdgeGraphFormatter")||(json.type == "LineEdgeNetworkFormatter")){ - this.edges[edgeId] = new LineEdgeGraphFormatter(edgeId); - this.edges[edgeId].loadFromJSON(json); - } - if ((json.type == "DirectedLineEdgeGraphFormatter")||(json.type == "DirectedLineEdgeNetworkFormatter")){ - this.edges[edgeId] = new DirectedLineEdgeGraphFormatter(edgeId); - this.edges[edgeId].loadFromJSON(json); - } - - if ((json.type == "BezierEdgeGraphFormatter")||(json.type == "BezierEdgeNetworkFormatter")){ - this.edges[edgeId] = new BezierEdgeGraphFormatter(edgeId); - this.edges[edgeId].loadFromJSON(json); - } - - - if ((json.type == "CutDirectedLineEdgeGraphFormatter")||(json.type == "CutDirectedLineEdgeNetworkFormatter")){ - this.edges[edgeId] = new CutDirectedLineEdgeGraphFormatter(edgeId); - this.edges[edgeId].loadFromJSON(json); - } - - if ((json.type == "DotDirectedLineEdgeGraphFormatter")||(json.type == "DotDirectedLineEdgeNetworkFormatter")){ - this.edges[edgeId] = new DotDirectedLineEdgeGraphFormatter(edgeId); - this.edges[edgeId].loadFromJSON(json); - } - if ((json.type == "OdotDirectedLineEdgeGraphFormatter")||(json.type == "OdotDirectedLineEdgeNetworkFormatter")){ - this.edges[edgeId] = new OdotDirectedLineEdgeGraphFormatter(edgeId); - this.edges[edgeId].loadFromJSON(json); - } - - if ((json.type == "OdirectedLineEdgeGraphFormatter")||(json.type == "OdirectedLineEdgeNetworkFormatter")){ - this.edges[edgeId] = new OdirectedLineEdgeGraphFormatter(edgeId); - this.edges[edgeId].loadFromJSON(json); - } - } - - var _this = this; - this.edges[edgeId].stateChanged.attach(function (sender, item){ - _this.edgeChanged.notify(item); - }); - - this._recalculateSize(); -}; - -NetworkDataSetFormatter.prototype._setDataset = function(dataset){ - this.dataset = dataset; - this.maxClass = dataset.getMaxClass(); - this.minClass = dataset.getMinClass(); - this._attachDatasetEvents(); -}; - -NetworkDataSetFormatter.prototype.changeEdgeType = function(edgeId, type){ - if ((type == "LineEdgeGraphFormatter")||(type == "LineEdgeNetworkFormatter")){ - this.edges[edgeId] = new LineEdgeGraphFormatter(edgeId, this.args.defaultEdgeProperties.defaultFormat, this.args.defaultEdgeProperties.selected, this.args.defaultEdgeProperties.over, this.args.defaultEdgeProperties.dragging); - - } - if ((type == "DirectedLineEdgeGraphFormatter")||(type == "DirectedLineEdgeNetworkFormatter")){ - this.edges[edgeId] = new DirectedLineEdgeGraphFormatter(edgeId, this.args.defaultEdgeProperties.defaultFormat, this.args.defaultEdgeProperties.selected, this.args.defaultEdgeProperties.over, this.args.defaultEdgeProperties.dragging); - } - - if ((type == "CutDirectedLineEdgeGraphFormatter")||(type == "CutDirectedLineEdgeNetworkFormatter")){ - this.edges[edgeId] = new CutDirectedLineEdgeGraphFormatter(edgeId, this.args.defaultEdgeProperties.defaultFormat, this.args.defaultEdgeProperties.selected, this.args.defaultEdgeProperties.over, this.args.defaultEdgeProperties.dragging); - } - - if ((type == "DotDirectedLineEdgeGraphFormatter")||(type == "DotDirectedLineEdgeNetworkFormatter")){ - this.edges[edgeId] = new DotDirectedLineEdgeGraphFormatter(edgeId, this.args.defaultEdgeProperties.defaultFormat, this.args.defaultEdgeProperties.selected, this.args.defaultEdgeProperties.over, this.args.defaultEdgeProperties.dragging); - } - - if ((type == "OdotDirectedLineEdgeGraphFormatter")||(type == "OdotDirectedLineEdgeNetworkFormatter")){ - this.edges[edgeId] = new OdotDirectedLineEdgeGraphFormatter(edgeId, this.args.defaultEdgeProperties.defaultFormat, this.args.defaultEdgeProperties.selected, this.args.defaultEdgeProperties.over, this.args.defaultEdgeProperties.dragging); - } - - if ((type == "OdirectedLineEdgeGraphFormatter")||(type == "OdirectedLineEdgeNetworkFormatter")){ - this.edges[edgeId] = new OdirectedLineEdgeGraphFormatter(edgeId, this.args.defaultEdgeProperties.defaultFormat, this.args.defaultEdgeProperties.selected, this.args.defaultEdgeProperties.over, this.args.defaultEdgeProperties.dragging); - } - - - - var _this = this; - this.edges[edgeId].stateChanged.attach(function (sender, item){ - _this.edgeChanged.notify(item); - }); - _this.edgeChanged.notify(this.edges[edgeId]); -}; -/* -classe = "SquareNetworkNodeFormatter"; -} -if (value == "circle"){ - classe = "CircleNetworkNodeFormatter"; - **/ -NetworkDataSetFormatter.prototype.changeNodeType = function(vertexId, type){ - var defaultFormat = JSON.parse(JSON.stringify(this.vertices[vertexId].getDefault())); - var selectedFormat = JSON.parse(JSON.stringify(this.vertices[vertexId].getSelected())); - var defaultFormat = JSON.parse(JSON.stringify(this.vertices[vertexId].getDefault())); - var defaultFormat = JSON.parse(JSON.stringify(this.vertices[vertexId].getDefault())); - - if ((type == "SquareVertexGraphFormatter")||(type == "SquareVertexNetworkFormatter")){ - this.vertices[vertexId] = new SquareVertexGraphFormatter(vertexId,defaultFormat, this.args.vertexFormatProperties.selected, this.args.vertexFormatProperties.over, this.args.vertexFormatProperties.dragging); - } - - if ((type == "CircleVertexGraphFormatter")||(type == "CircleVertexNetworkFormatter")){ - this.vertices[vertexId] = new CircleVertexGraphFormatter(vertexId, defaultFormat, this.args.vertexFormatProperties.selected, this.args.vertexFormatProperties.over, this.args.vertexFormatProperties.dragging); - } - - if ((type == "EllipseVertexGraphFormatter")||(type == "EllipseVertexNetworkFormatter")){ - this.vertices[vertexId] = new EllipseVertexGraphFormatter(vertexId, defaultFormat, this.args.vertexFormatProperties.selected, this.args.vertexFormatProperties.over, this.args.vertexFormatProperties.dragging); - } - - if ((type == "RectangleVertexGraphFormatter")||(type == "RectangleVertexNetworkFormatter")){ - this.vertices[vertexId] = new RectangleVertexGraphFormatter(vertexId, defaultFormat, this.args.vertexFormatProperties.selected, this.args.vertexFormatProperties.over, this.args.vertexFormatProperties.dragging); - } - - if ((type == "RoundedVertexGraphFormatter")||(type == "RoundedVertexNetworkFormatter")){ - this.vertices[vertexId] = new RoundedVertexGraphFormatter(vertexId, defaultFormat, this.args.vertexFormatProperties.selected, this.args.vertexFormatProperties.over, this.args.vertexFormatProperties.dragging); - } - - if ((type == "OctagonVertexGraphFormatter")||(type == "OctagonVertexNetworkhFormatter")){ - this.vertices[vertexId] = new OctagonVertexGraphFormatter(vertexId, defaultFormat, this.args.vertexFormatProperties.selected, this.args.vertexFormatProperties.over, this.args.vertexFormatProperties.dragging); - } - - - var _this = this; - this.vertices[vertexId].stateChanged.attach(function (sender, item){ - _this.changed.notify(item); - }); - _this.changed.notify(this.vertices[vertexId]); -}; - - - -NetworkDataSetFormatter.prototype.dataBind = function(networkDataSet){ - var _this = this; - this._setDataset(networkDataSet); - - for ( var vertex in this.dataset.getVertices()) { - this.addVertex(vertex); - } - - for ( var edge in this.dataset.getEdges()) { - this.addEdge(edge); - } -}; - -NetworkDataSetFormatter.prototype._removeEdge = function(edgeId){ - delete this.edges[edgeId]; -}; - -NetworkDataSetFormatter.prototype._removeVertex = function(vertexId){ - delete this.vertices[vertexId]; -}; - -NetworkDataSetFormatter.prototype._attachDatasetEvents = function(id){ - var _this = this; - this.dataset.vertexDeleted.attach(function (sender, item){ - _this._removeVertex(item.getId()); - }); - - this.dataset.edgeDeleted.attach(function (sender, item){ - _this._removeEdge(item.getId()); - }); - - this.dataset.newVertex.attach(function (sender, item){ - _this.addVertex(item.getId()); - }); - - this.dataset.newEdge.attach(function (sender, item){ - _this.addEdge(item.getId()); - }); -}; - - -NetworkDataSetFormatter.prototype.getVertexById = function(id){ - return this.vertices[id]; -}; - -NetworkDataSetFormatter.prototype.getEdgeById = function(id){ - return this.edges[id]; -}; - -NetworkDataSetFormatter.prototype.makeLabelsBigger = function(){ -for ( var vertexId in this.vertices) { - var fontSize = this.vertices[vertexId].getDefault().getFontSize() + 2; - this.vertices[vertexId].getDefault().setFontSize(fontSize); - } -}; - -NetworkDataSetFormatter.prototype.makeLabelsSmaller = function(){ - for ( var vertexId in this.vertices) { - var fontSize = this.vertices[vertexId].getDefault().getFontSize() - 2; - this.vertices[vertexId].getDefault().setFontSize(fontSize); - } -}; - - -NetworkDataSetFormatter.prototype.resize = function(width, height){ - this.args.width = width; - this.args.height = height; - this.resized.notify(); -}; - -/** ZOOM GETTERS **/ -NetworkDataSetFormatter.prototype.getZoomScaleStepFactor = function(){return this.args.zoomScaleStepFactor;}; -NetworkDataSetFormatter.prototype.setZoomScaleStepFactor = function(scaleFactor){this.args.zoomScaleStepFactor = scaleFactor;}; -NetworkDataSetFormatter.prototype.getZoomScale = function(){return this.args.zoomScale;}; -NetworkDataSetFormatter.prototype.setZoomScale = function(scale){this.args.zoomScale = scale;}; - -NetworkDataSetFormatter.prototype.getNodesMaxSize = function(){return this.args.nodesMaxSize;}; -NetworkDataSetFormatter.prototype.getNodesMinSize = function(){return this.args.nodesMinSize;}; - -/** SIZE SETTERS AND GETTERS **/ -NetworkDataSetFormatter.prototype.isVerticesBalanced = function(){return this.args.balanceNodes;}; -NetworkDataSetFormatter.prototype.getWidth = function(){return this.args.width;}; -NetworkDataSetFormatter.prototype.getHeight = function(){return this.args.height;}; - -/** OPTIONAL PARAMETERS GETTERS AND SETTERS **/ -NetworkDataSetFormatter.prototype.getBackgroundImage = function(){return this.args.backgroundImage;}; -NetworkDataSetFormatter.prototype.setBackgroundImage = function(value){this.args.backgroundImage = value; this.backgroundImageChanged.notify(this);}; - - -NetworkDataSetFormatter.prototype.getBackgroundImageWidth = function(){return this.args.backgroundImageWidth;}; -NetworkDataSetFormatter.prototype.setBackgroundImageWidth = function(value){this.args.backgroundImageWidth = value; this.backgroundImageChanged.notify(this);}; - -NetworkDataSetFormatter.prototype.getBackgroundImageHeight = function(){return this.args.backgroundImageHeight;}; -NetworkDataSetFormatter.prototype.setBackgroundImageHeight = function(value){this.args.backgroundImageHeight = value; this.backgroundImageChanged.notify(this);}; - -NetworkDataSetFormatter.prototype.getBackgroundImageX = function(){return this.args.backgroundImageX;}; -NetworkDataSetFormatter.prototype.setBackgroundImageX = function(value){this.args.backgroundImageX = value; this.backgroundImageChanged.notify(this);}; - -NetworkDataSetFormatter.prototype.getBackgroundImageY = function(){return this.args.backgroundImageX;}; -NetworkDataSetFormatter.prototype.setBackgroundImageY = function(value){this.args.backgroundImageY = value; this.backgroundImageChanged.notify(this);}; - - - -NetworkDataSetFormatter.prototype.getBackgroundColor = function(){return this.args.backgroundColor;}; -NetworkDataSetFormatter.prototype.setBackgroundColor = function(value){this.args.backgroundColor = value; this.backgroundColorChanged.notify(this);}; - -NetworkDataSetFormatter.prototype.getWidth = function(){return this.args.width;}; -NetworkDataSetFormatter.prototype.setWidth = function(value){this.args.width = value;}; - -NetworkDataSetFormatter.prototype.getHeight = function(){return this.args.height;}; -NetworkDataSetFormatter.prototype.setHeight = function(value){this.args.height = value;}; - - - - -Vertex.prototype.getName = GraphItem.prototype.getName; -Vertex.prototype.setName = GraphItem.prototype.setName; -Vertex.prototype.getId = GraphItem.prototype.getId; - -function Vertex(id, name, args){ - GraphItem.prototype.constructor.call(this, id, name, args); - this.edgesIn= new Array(); - this.edgesOut= new Array(); -}; - -Vertex.prototype.getEdges = function(){ - return this.edgesIn.concat(this.edgesOut); -}; - -Vertex.prototype.getEdgesCount = function(){ - return this.getEdges().length; -}; - -Vertex.prototype.getEdgesIn = function(){ - return this.edgesIn; -}; - -Vertex.prototype.getEdgesOut = function(){ - return this.edgesOut; -}; - -Vertex.prototype.addEdge = function(edge){ - if (edge.getNodeSource().getId() == this.id){ - this.edgesIn.push(edge); - } - else{ - this.edgesOut.push(edge); - } -}; - -Vertex.prototype.removeEdge = function(edge){ - for ( var i = 0; i < this.getEdgesIn().length; i++) { - var edgeIn = this.edgesIn[i]; - if (edgeIn.getId() == edge.getId()){ - this.edgesIn.splice(i, 1); - } - } - for ( var i = 0; i < this.getEdgesOut().length; i++) { - var edgeIn = this.edgesOut[i]; - if (edgeIn.getId() == edge.getId()){ - this.edgesOut.splice(i, 1); - } - } -}; - -Vertex.prototype.remove = function(){ - var edges = this.getEdges(); - for ( var i = 0; i < edges.length; i++) { - var edge = edges[i]; - edge.remove(); - } - this.deleted.notify(this); -}; - - - - - -VertexGraphFormatter.prototype.getDefault = ItemGraphFormatter.prototype.getDefault; -VertexGraphFormatter.prototype.getSelected = ItemGraphFormatter.prototype.getSelected; -VertexGraphFormatter.prototype.getOver = ItemGraphFormatter.prototype.getOver; -VertexGraphFormatter.prototype.getDragging = ItemGraphFormatter.prototype.getDragging; -VertexGraphFormatter.prototype.getId = ItemGraphFormatter.prototype.getId; -VertexGraphFormatter.prototype.toJSON = ItemGraphFormatter.prototype.toJSON; -VertexGraphFormatter.prototype.loadFromJSON = ItemGraphFormatter.prototype.loadFromJSON; -VertexGraphFormatter.prototype._setEvents = ItemGraphFormatter.prototype._setEvents; - - -function VertexGraphFormatter(defaultFormat, selectedFormat, overFormat, draggingFormat){ - ItemGraphFormatter.prototype.constructor.call(this, defaultFormat, selectedFormat, overFormat, draggingFormat); -}; - - -CircleVertexGraphFormatter.prototype.getDefault = VertexGraphFormatter.prototype.getDefault; -CircleVertexGraphFormatter.prototype.getSelected = VertexGraphFormatter.prototype.getSelected; -CircleVertexGraphFormatter.prototype.getOver = VertexGraphFormatter.prototype.getOver; -CircleVertexGraphFormatter.prototype.getDragging = VertexGraphFormatter.prototype.getDragging; -CircleVertexGraphFormatter.prototype.getId = VertexGraphFormatter.prototype.getId; -CircleVertexGraphFormatter.prototype.toJSON = VertexGraphFormatter.prototype.toJSON; -CircleVertexGraphFormatter.prototype.loadFromJSON = VertexGraphFormatter.prototype.loadFromJSON; -CircleVertexGraphFormatter.prototype._setEvents = VertexGraphFormatter.prototype._setEvents; - -function CircleVertexGraphFormatter(id, defaultFormat, selectedFormat, overFormat, draggingFormat){ - VertexGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, draggingFormat); - this.args.type = "CircleVertexGraphFormatter"; - if (defaultFormat != null){ - if (defaultFormat.radius != null){ - this.defaultFormat.args.radius = defaultFormat.radius; - } - } - - if (selectedFormat != null){ - if (selectedFormat.radius != null){ - this.selected.args.radius = selectedFormat.radius; - } - } - - if (overFormat != null){ - if (overFormat.radius != null){ - this.over.args.radius = overFormat.radius; - } - } - - if (draggingFormat != null){ - if (draggingFormat.radius != null){ - this.dragging.args.draggingFormat = draggingFormat.radius; - } - } -}; - - - -SquareVertexGraphFormatter.prototype.getDefault = VertexGraphFormatter.prototype.getDefault; -SquareVertexGraphFormatter.prototype.getSelected = VertexGraphFormatter.prototype.getSelected; -SquareVertexGraphFormatter.prototype.getOver = VertexGraphFormatter.prototype.getOver; -SquareVertexGraphFormatter.prototype.getDragging = VertexGraphFormatter.prototype.getDragging; -SquareVertexGraphFormatter.prototype.getId = VertexGraphFormatter.prototype.getId; -SquareVertexGraphFormatter.prototype.toJSON = VertexGraphFormatter.prototype.toJSON; -SquareVertexGraphFormatter.prototype.loadFromJSON = VertexGraphFormatter.prototype.loadFromJSON; -SquareVertexGraphFormatter.prototype._setEvents = VertexGraphFormatter.prototype._setEvents; - -function SquareVertexGraphFormatter(id, defaultFormat, selectedFormat, overFormat, draggingFormat){ - VertexGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, draggingFormat); - this.args.type = "SquareVertexGraphFormatter"; - if (defaultFormat != null){ - if (defaultFormat.radius != null){ - this.defaultFormat.args.radius = defaultFormat.radius; - } - } - - if (selectedFormat != null){ - if (selectedFormat.radius != null){ - this.selected.args.radius = selectedFormat.radius; - } - } - - if (overFormat != null){ - if (overFormat.radius != null){ - this.over.args.radius = overFormat.radius; - } - } - - if (draggingFormat != null){ - if (draggingFormat.radius != null){ - this.dragging.args.draggingFormat = draggingFormat.radius; - } - } -}; - - -EllipseVertexGraphFormatter.prototype.getDefault = VertexGraphFormatter.prototype.getDefault; -EllipseVertexGraphFormatter.prototype.getSelected = VertexGraphFormatter.prototype.getSelected; -EllipseVertexGraphFormatter.prototype.getOver = VertexGraphFormatter.prototype.getOver; -EllipseVertexGraphFormatter.prototype.getDragging = VertexGraphFormatter.prototype.getDragging; -EllipseVertexGraphFormatter.prototype.getId = VertexGraphFormatter.prototype.getId; -EllipseVertexGraphFormatter.prototype.toJSON = VertexGraphFormatter.prototype.toJSON; -EllipseVertexGraphFormatter.prototype.loadFromJSON = VertexGraphFormatter.prototype.loadFromJSON; -EllipseVertexGraphFormatter.prototype._setEvents = VertexGraphFormatter.prototype._setEvents; - -function EllipseVertexGraphFormatter(id, defaultFormat, selectedFormat, overFormat, draggingFormat){ - VertexGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, draggingFormat); - this.args.type = "EllipseVertexGraphFormatter"; - if (defaultFormat != null){ - if (defaultFormat.radius != null){ - this.defaultFormat.args.radius = defaultFormat.radius; - } - } - - if (selectedFormat != null){ - if (selectedFormat.radius != null){ - this.selected.args.radius = selectedFormat.radius; - } - } - - if (overFormat != null){ - if (overFormat.radius != null){ - this.over.args.radius = overFormat.radius; - } - } - - if (draggingFormat != null){ - if (draggingFormat.radius != null){ - this.dragging.args.draggingFormat = draggingFormat.radius; - } - } -}; - - - -RectangleVertexGraphFormatter.prototype.getDefault = ItemGraphFormatter.prototype.getDefault; -RectangleVertexGraphFormatter.prototype.getSelected = ItemGraphFormatter.prototype.getSelected; -RectangleVertexGraphFormatter.prototype.getOver = ItemGraphFormatter.prototype.getOver; -RectangleVertexGraphFormatter.prototype.getDragging = ItemGraphFormatter.prototype.getDragging; -RectangleVertexGraphFormatter.prototype.getId = ItemGraphFormatter.prototype.getId; -RectangleVertexGraphFormatter.prototype.toJSON = ItemGraphFormatter.prototype.toJSON; -RectangleVertexGraphFormatter.prototype.loadFromJSON = ItemGraphFormatter.prototype.loadFromJSON; -RectangleVertexGraphFormatter.prototype._setEvents = ItemGraphFormatter.prototype._setEvents; - -function RectangleVertexGraphFormatter(id, defaultFormat, selectedFormat, overFormat, draggingFormat){ - VertexGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, draggingFormat); - this.args.type = "RectangleVertexGraphFormatter"; - if (defaultFormat != null){ - if (defaultFormat.radius != null){ - this.defaultFormat.args.radius = defaultFormat.radius; - } - } - - if (selectedFormat != null){ - if (selectedFormat.radius != null){ - this.selected.args.radius = selectedFormat.radius; - } - } - - if (overFormat != null){ - if (overFormat.radius != null){ - this.over.args.radius = overFormat.radius; - } - } - - if (draggingFormat != null){ - if (draggingFormat.radius != null){ - this.dragging.args.draggingFormat = draggingFormat.radius; - } - } -}; - - - -RoundedVertexGraphFormatter.prototype.getDefault = ItemGraphFormatter.prototype.getDefault; -RoundedVertexGraphFormatter.prototype.getSelected = ItemGraphFormatter.prototype.getSelected; -RoundedVertexGraphFormatter.prototype.getOver = ItemGraphFormatter.prototype.getOver; -RoundedVertexGraphFormatter.prototype.getDragging = ItemGraphFormatter.prototype.getDragging; -RoundedVertexGraphFormatter.prototype.getId = ItemGraphFormatter.prototype.getId; -RoundedVertexGraphFormatter.prototype.toJSON = ItemGraphFormatter.prototype.toJSON; -RoundedVertexGraphFormatter.prototype.loadFromJSON = ItemGraphFormatter.prototype.loadFromJSON; -RoundedVertexGraphFormatter.prototype._setEvents = ItemGraphFormatter.prototype._setEvents; - -function RoundedVertexGraphFormatter(id, defaultFormat, selectedFormat, overFormat, draggingFormat){ - VertexGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, draggingFormat); - this.args.type = "RoundedVertexGraphFormatter"; - if (defaultFormat != null){ - if (defaultFormat.radius != null){ - this.defaultFormat.args.radius = defaultFormat.radius; - } - } - - if (selectedFormat != null){ - if (selectedFormat.radius != null){ - this.selected.args.radius = selectedFormat.radius; - } - } - - if (overFormat != null){ - if (overFormat.radius != null){ - this.over.args.radius = overFormat.radius; - } - } - - if (draggingFormat != null){ - if (draggingFormat.radius != null){ - this.dragging.args.draggingFormat = draggingFormat.radius; - } - } -}; - - - -OctagonVertexGraphFormatter.prototype.getDefault = ItemGraphFormatter.prototype.getDefault; -OctagonVertexGraphFormatter.prototype.getSelected = ItemGraphFormatter.prototype.getSelected; -OctagonVertexGraphFormatter.prototype.getOver = ItemGraphFormatter.prototype.getOver; -OctagonVertexGraphFormatter.prototype.getDragging = ItemGraphFormatter.prototype.getDragging; -OctagonVertexGraphFormatter.prototype.getId = ItemGraphFormatter.prototype.getId; -OctagonVertexGraphFormatter.prototype.toJSON = ItemGraphFormatter.prototype.toJSON; -OctagonVertexGraphFormatter.prototype.loadFromJSON = ItemGraphFormatter.prototype.loadFromJSON; -OctagonVertexGraphFormatter.prototype._setEvents = ItemGraphFormatter.prototype._setEvents; - -function OctagonVertexGraphFormatter(id, defaultFormat, selectedFormat, overFormat, draggingFormat){ - VertexGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, draggingFormat); - this.args.type = "OctagonVertexGraphFormatter"; - if (defaultFormat != null){ - if (defaultFormat.radius != null){ - this.defaultFormat.args.radius = defaultFormat.radius; - } - } - - if (selectedFormat != null){ - if (selectedFormat.radius != null){ - this.selected.args.radius = selectedFormat.radius; - } - } - - if (overFormat != null){ - if (overFormat.radius != null){ - this.over.args.radius = overFormat.radius; - } - } - - if (draggingFormat != null){ - if (draggingFormat.radius != null){ - this.dragging.args.draggingFormat = draggingFormat.radius; - } - } -}; - - - -var Colors = new function() -{ - this.hashColor = []; - this.getColorByScoreArrayValue = function (arrayScore) - { - var array = new Array(); - - for (var i = 0; i< arrayScore.length; i++) - { - - var color = this.getColorByScoreValue(arrayScore[i]) - array.push( color); - - } - return array; - }; - - this.getHexStringByScoreArrayValue = function (arrayScore) - { - var arrayColor = this.getColorByScoreArrayValue(arrayScore); - var arrayHex = new Array(); - for (var i = 0; i< arrayColor.length; i++) - { - arrayHex.push( arrayColor[i].HexString()); - } - return arrayHex; - }; - - this.getColorByScoreValue = function (score) - { - - var truncate = score.toString().substr(0,4); - if (this.hashColor[truncate]!=null) - { - return this.hashColor[truncate]; - } - - - if(isNaN(score)) { - return Colors.ColorFromRGB(0,0,0); - } - var value; - - var from, to; - if(score < 0.5) { - from = Colors.ColorFromRGB(0,0,255); - to = Colors.ColorFromRGB(255,255,255); - value = (score * 2); - } else { - from = Colors.ColorFromRGB(255,255,255); - to = Colors.ColorFromRGB(255,0,0); - value = (score * 2) - 1; - } - - var x = value; - var y = 1.0 - value; - var color = Colors.ColorFromRGB(y * from.Red() + x * to.Red(), y * from.Green() + x * to.Green(), y * from.Blue() + x * to.Blue()); - - this.hashColor[truncate] = color; - - return color; - }; - - this.ColorFromHSV = function(hue, sat, val) - { - var color = new Color(); - color.SetHSV(hue,sat,val); - return color; - }; - - this.ColorFromRGB = function(r, g, b) - { - var color = new Color(); - color.SetRGB(r,g,b); - return color; - }; - - this.ColorFromHex = function(hexStr) - { - var color = new Color(); - color.SetHexString(hexStr); - return color; - }; - - function Color() { - //Stored as values between 0 and 1 - var red = 0; - var green = 0; - var blue = 0; - - //Stored as values between 0 and 360 - var hue = 0; - - //Strored as values between 0 and 1 - var saturation = 0; - var value = 0; - - this.SetRGB = function(r, g, b) - { - red = r/255.0; - green = g/255.0; - blue = b/255.0; - calculateHSV(); - }; - - this.Red = function() { return Math.round(red*255); }; - - this.Green = function() { return Math.round(green*255); }; - - this.Blue = function() { return Math.round(blue*255); }; - - this.SetHSV = function(h, s, v) - { - hue = h; - saturation = s; - value = v; - calculateRGB(); - }; - - this.Hue = function() - { return hue; }; - - this.Saturation = function() - { return saturation; }; - - this.Value = function() - { return value; }; - - this.SetHexString = function(hexString) - { - if(hexString == null || typeof(hexString) != "string") - { - this.SetRGB(0,0,0); - return; - } - - if (hexString.substr(0, 1) == '#') - hexString = hexString.substr(1); - - if(hexString.length != 6) - { - this.SetRGB(0,0,0); - return; - } - - var r = parseInt(hexString.substr(0, 2), 16); - var g = parseInt(hexString.substr(2, 2), 16); - var b = parseInt(hexString.substr(4, 2), 16); - if (isNaN(r) || isNaN(g) || isNaN(b)) - { - this.SetRGB(0,0,0); - return; - } - - this.SetRGB(r,g,b); - }; - - this.HexString = function() - { - - var rStr = this.Red().toString(16); - if (rStr.length == 1) - rStr = '0' + rStr; - var gStr = this.Green().toString(16); - if (gStr.length == 1) - gStr = '0' + gStr; - var bStr = this.Blue().toString(16); - if (bStr.length == 1) - bStr = '0' + bStr; - return ('#' + rStr + gStr + bStr).toUpperCase(); - }; - - this.Complement = function() - { - var newHue = (hue >= 180) ? hue - 180 : hue + 180; - var newVal = (value * (saturation - 1) + 1); - var newSat = (value*saturation) / newVal; - var newColor = new Color(); - newColor.SetHSV(newHue, newSat, newVal); - return newColor; - } ; - - function calculateHSV() - { - var max = Math.max(Math.max(red, green), blue); - var min = Math.min(Math.min(red, green), blue); - - value = max; - - saturation = 0; - if(max != 0) - saturation = 1 - min/max; - - hue = 0; - if(min == max) - return; - - var delta = (max - min); - if (red == max) - hue = (green - blue) / delta; - else if (green == max) - hue = 2 + ((blue - red) / delta); - else - hue = 4 + ((red - green) / delta); - hue = hue * 60; - if(hue < 0) - hue += 360; - } - - function calculateRGB() - { - red = value; - green = value; - blue = value; - - if(value == 0 || saturation == 0) - return; - - var tHue = (hue / 60); - var i = Math.floor(tHue); - var f = tHue - i; - var p = value * (1 - saturation); - var q = value * (1 - saturation * f); - var t = value * (1 - saturation * (1 - f)); - switch(i) - { - case 0: - red = value; green = t; blue = p; - break; - case 1: - red = q; green = value; blue = p; - break; - case 2: - red = p; green = value; blue = t; - break; - case 3: - red = p; green = q; blue = value; - break; - case 4: - red = t; green = p; blue = value; - break; - default: - red = value; green = p; blue = q; - break; - } - } - } -} -(); -/* - * Clase gestiona la insercción, eliminación de los elementos en el DOM - * - * Vital hacerla SIEMPRE compatible hacia atrás - * - * Last update: 28-10-2010 - * - */ - - -var DOM = {}; - -DOM.createNewElement = function(type, nodeParent, attributes) -{ - - var node = document.createElement(type); - for (var i=0; i0) - { - parent.removeChild(parent.childNodes[0]); - - } -}; - -DOM.select = function(targetID) -{ - return document.getElementById(targetID); -// return $("#"+targetID); -}; -var Geometry = -{ - - /** From tow points obtains the angles formed with the cartesian side **/ - getAngleBetweenTwoPoints : function(x1, y1, x2, y2){ - //var m = (y1 - y2)/ (x1 - x2); - return Math.atan2(y2-y1, x2-x1); - }, - - getAdjacentSideOfRectangleRight : function(angle, hypotenuse){ - return Math.abs(Math.cos(angle)*hypotenuse); - }, - - getOppositeSideOfRectangleRight : function(angle, hypotenuse){ - return Math.abs(Math.sin(angle)*hypotenuse); - }, - - toDegree: function(radian){ - return radian*180/Math.PI; - - } - - -}; - -var SVG = -{ - svgns : 'http://www.w3.org/2000/svg', - xlinkns : "http://www.w3.org/1999/xlink", - - createSVGCanvas: function(parentNode, attributes) - { - - attributes.push( ['xmlns', SVG.svgns], ['xmlns:xlink', 'http://www.w3.org/1999/xlink']); - var svg = document.createElementNS("http://www.w3.org/2000/svg", "svg"); - this._setProperties(svg, attributes); - parentNode.appendChild( svg); - return svg; - - }, - - createRectangle : function (x, y, width, height, attributes){ - var rect = document.createElementNS(this.svgns, "rect"); - rect.setAttribute("x",x); - rect.setAttribute("y",y); - rect.setAttribute("width",width); - rect.setAttribute("height",height); - SVG._setProperties(rect, attributes); - return rect; - }, - - drawImage64 : function (x, y, width, height, base64, svgNode, attributes) { - var node = SVG.createImage64(x, y, width, height, base64, attributes); - svgNode.appendChild(node); - return node; - }, - - createImage64 : function (x, y, width, height, base64, attributes) { - var img = document.createElementNS(this.svgns, "image"); - img.setAttribute("x",x); - img.setAttribute("y",y); - img.setAttribute("width",width); - img.setAttribute("height",height); - img.setAttribute("xlink:href",base64); - SVG._setProperties(img, attributes); - return img; - }, - - createLine: function (x1, y1, x2, y2, attributes){ - var line = document.createElementNS(this.svgns,"line"); - line.setAttribute("x1",x1); - line.setAttribute("y1",y1); - line.setAttribute("x2", x2); - line.setAttribute("y2", y2); - SVG._setProperties(line, attributes); - return line; - }, - - createClip: function (id, nodeToClip, attributes){ - var clip = document.createElementNS(this.svgns,"clipPath"); - clip.setAttribute("id",id); - clip.appendChild(nodeToClip); - return clip; - }, - - drawClip : function (id, nodeToClip, svgNode) { - var node = SVG.createClip(id, nodeToClip); - svgNode.appendChild(node); - return node; - }, - - drawRectangle : function (cx, cy, width, height, svgNode, attributes) { - try{ - var node = SVG.createRectangle(cx, cy, width, height, attributes); - svgNode.appendChild(node); - } - catch(e){ - - console.log("-------------------- "); - console.log("Error on drawRectangle " + e); - console.log(attributes); - console.log("-------------------- "); - } - return node; - }, - - createEllipse : function (x, y, rx, ry, attributes){ - var rect = document.createElementNS(this.svgns, "ellipse"); - rect.setAttribute("cx",x); - rect.setAttribute("cy",y); - rect.setAttribute("rx",rx); - rect.setAttribute("ry",ry); - SVG._setProperties(rect, attributes); - return rect; - }, - - drawEllipse : function (cx, cy, rx, ry, svgNode, attributes) { - var node = SVG.createEllipse(cx, cy, rx, ry, attributes); - svgNode.appendChild(node); - return node; - }, - - drawImage : function (x, y, canvasSVG, attributes) { - var image = document.createElementNS(this.svgns, "image"); - image.setAttribute("x",x); - image.setAttribute("y",y); - canvasSVG.appendChild(image); - SVG._setProperties(image, attributes); - }, - - drawLine : function (x1, y1, x2, y2, nodeSVG, attributes) { - try{ - var line = SVG.createLine(x1, y1, x2, y2, attributes); - nodeSVG.appendChild(line); - }catch(e){ - } - return line; - }, - - - drawPath: function (d, nodeSVG, attributes) { - var path = SVG.createPath(d, attributes); - nodeSVG.appendChild(path); - return path; - }, - - createPoligon : function (points, attributes){ - var poligon = document.createElementNS(this.svgns, "polygon"); - poligon.setAttribute("points",points); - SVG._setProperties(poligon, attributes); - return poligon; - }, - - drawPoligon : function (points, canvasSVG, attributes){ - var poligon = SVG.createPoligon(points, attributes); - canvasSVG.appendChild(poligon); - return poligon; - }, - // - - createPath : function (d, attributes){ - var path = document.createElementNS(this.svgns, "path"); - path.setAttribute("d",d); - SVG._setProperties(path, attributes); - return path; - }, - - drawCircle : function (x, y, radio, canvasSVG, attributes) { - - var newText = document.createElementNS(this.svgns,"circle"); - newText.setAttribute("cx",x); - newText.setAttribute("cy",y); - newText.setAttribute("r",radio); - - canvasSVG.appendChild(newText); - attributes["cursor"] = "pointer"; - this._setProperties(newText, attributes); - return newText; - }, - - - _setProperties: function(node, attributes) - { - if (attributes instanceof Array){ - for (var i=0; i< attributes.length; i++) - { - node.setAttribute(attributes[i][0], attributes[i][1]); - } - } - else{ - for ( var key in attributes){ - node.setAttribute(key, attributes[key]); - } - } - }, - -/* drawPath: function(pointsArray, canvasSVG, attributes){ - var path = document.createElementNS(this.svgns,"polyline"); - path.setAttribute ('id', id); - - var d= pointsArray[0].x+ " "+ pointsArray[0].y; - for (var i=1; i< pointsArray.length; i++) - { - d=d+" "+pointsArray[i].x+" "+pointsArray[i].y; - } - path.setAttribute ('points', d); - canvasSVG.appendChild(path); - },*/ - - createText : function (x, y, text, attributes) { - var node = document.createElementNS(this.svgns,"text"); - node.setAttributeNS(null , "x",x); - node.setAttributeNS(null, "y",y); - - var textNode = document.createTextNode(text); - node.appendChild(textNode); - - this._setProperties(node, attributes); - return node; - }, - - drawText : function (x, y, text, canvasSVG, attributes) { - var text = SVG.createText(x, y, text, attributes); - canvasSVG.appendChild(text); - return text; - }, - - - - drawGroup: function(svgNode, attributes) - { - var group = SVG.createGroup(attributes); - svgNode.appendChild(group); - return group; - }, - - createGroup: function(attributes){ - var group = document.createElementNS(this.svgns,"g"); - this._setProperties(group, attributes); - return group; - } - -}; - - - -var CanvasToSVG = { - - convert: function(sourceCanvas, targetSVG, x, y, id, attributes) { - - var img = this._convert(sourceCanvas, targetSVG, x, y, id); - - for (var i=0; i< attributes.length; i++) - { - img.setAttribute(attributes[i][0], attributes[i][1]); - } - }, - - _convert: function(sourceCanvas, targetSVG, x, y, id) { - var svgNS = "http://www.w3.org/2000/svg"; - var xlinkNS = "http://www.w3.org/1999/xlink"; - // get base64 encoded png from Canvas - var image = sourceCanvas.toDataURL(); - - // must be careful with the namespaces - var svgimg = document.createElementNS(svgNS, "image"); - - svgimg.setAttribute('id', id); - - //svgimg.setAttribute('class', class); - //svgimg.setAttribute('xlink:href', image); - svgimg.setAttributeNS(xlinkNS, 'xlink:href', image); - - - - - svgimg.setAttribute('x', x ? x : 0); - svgimg.setAttribute('y', y ? y : 0); - svgimg.setAttribute('width', sourceCanvas.width); - svgimg.setAttribute('height', sourceCanvas.height); - //svgimg.setAttribute('cursor', 'pointer'); - svgimg.imageData = image; - - targetSVG.appendChild(svgimg); - return svgimg; - }, - - importSVG: function(sourceSVG, targetCanvas) { - svg_xml = sourceSVG;//(new XMLSerializer()).serializeToString(sourceSVG); - var ctx = targetCanvas.getContext('2d'); - - var img = new Image(); - img.src = "data:image/svg+xml;base64," + btoa(svg_xml); -// img.onload = function() { - ctx.drawImage(img, 0, 0); -// }; - } - -}; -/* -Graph.prototype.importSVG = function(sourceSVG, targetCanvas) { - sourceSVG = this._svg; - targetCanvas = document.createElementNS('canvas'); - // https://developer.mozilla.org/en/XMLSerializer - svg_xml = (new XMLSerializer()).serializeToString(sourceSVG); - var ctx = targetCanvas.getContext('2d'); - // this is just a JavaScript (HTML) image - var img = new Image(); - // http://en.wikipedia.org/wiki/SVG#Native_support - // https://developer.mozilla.org/en/DOM/window.btoa - img.src = "data:image/svg+xml;base64," + btoa(svg_xml); - img.onload = function() { - // after this, Canvas’ origin-clean is DIRTY - ctx.drawImage(img, 0, 0); - } -}; -*/ - -/* - -Normalizacion de datos para dibujar colores -Issues: - No sé como debería llamarse esta libreria - No sé si ya existe una funciçon en javascript que lo haga - - -*/ - - -var Normalizer = new function() -{ - this.normalizeArray = function (arrayData) - { - - return this.standardizeArray(this.normal(arrayData)); - -// var result = this._getMaxAndMin(arrayData); -// var min =result[0]; -// var max = result[1]; -// -// -// //los hacemos todos positivos -// for (var i = 0; i< arrayData.length; i++) -// { -// arrayData[i]= Math.abs(min) + parseFloat(arrayData[i]); -// } -// -// var result = this._getMaxAndMin(arrayData); -// var min =result[0]; -// var max = result[1]; -// -// -// var resultArray = new Array(); -// for (var i = 0; i< arrayData.length; i++) -// { -// resultArray.push(arrayData[i]*1/max); -// } -// return resultArray; - }; - - this.normal = function(arrayData){ - var mean = this._getMean(arrayData); - var deviation = this._getStdDeviation(arrayData); - var result = this._getMaxAndMin(arrayData); - var min = result[0]; - var max = result[1]; - - var resultArray = new Array(); - for (var i = 0; i< arrayData.length; i++) { - if (deviation!=0){ - resultArray.push((arrayData[i]-mean)/deviation); - }else{ - resultArray.push(arrayData[i]); - } - } - return resultArray; - }; - - this.standardizeArray = function(arrayData) - { - var result = this._getMaxAndMin(arrayData); - var min = result[0]; - var max = result[1]; - - var offset = ( min <= 0 ) ? Math.abs(min) : (-1 * min); - var resultArray = new Array(); - for (var i = 0; i< arrayData.length; i++) { - if(max + offset!=0){ - resultArray.push((arrayData[i] + offset) / (max + offset)); - }else{ - resultArray.push(arrayData[i]+offset); - } - } - return resultArray; - }; - - - this._getMean = function(arrayData) { - var sum = 0; - for (var i = 0; i< arrayData.length; i++) { - sum = sum + parseFloat(arrayData[i]); - } - return sum/arrayData.length; - }; - - this._getStdDeviation = function(arrayData) { - var mean = this._getMean(arrayData); - var acum = 0.0; - for(var i=0; i max) max = parseFloat(arrayData[i]); - } - - return [min, max]; - }; -}; -function GraphCanvas(componentID, targetNode, args) { - this.args = {}; - /** target */ - this.targetID = targetNode.id; - - /** id manage */ - this.id = componentID; - this.args.idGraph = this.id + "main"; - this.args.idBackgroundNode = this.id + "background"; - - this.args.idEdgesGraph = this.id + "edges"; - this.args.idNodesGraph = this.id + "vertices"; - this.args.idLabelGraph = this.id + "label"; - this.args.idBackground = this.id + "background"; - - /** Objects Graph **/ - this.dataset = null; - this.formatter = null; - this.layout = null; - - /** Drawing **/ - this.circleDefaultRadius = 2; - this.squareDefaultSide = this.circleDefaultRadius * 1.5; - - /** Directed Arrow **/ - this.arrowDefaultSize = this.circleDefaultRadius; - - /** Groups **/ - this.GraphGroup = null; - this.GraphNodeGroup = null; - this.GraphLabelGroup = null; - this.GraphBackground = null; - - /** SETTINGS FLAGS **/ - this.args.draggingCanvasEnabled = false; //Flag to set if the canvas can be dragged - this.args.multipleSelectionEnabled = false; - this.args.interactive = false; - this.args.labeled = false; - this.args.linkEnabled = false; - - /** If numberEdge > maxNumberEdgesMoving then only it will move edges when mouse up **/ - this.args.maxNumberEdgesMoving = 3; - this.args.maxNumberEdgesFiringEvents = 50; - - /** Linking edges **/ - this.args.linking = false; - this.linkStartX = 0; - this.linkStartY = 0; - this.linkSVGNode = null; - this.linkNodeSource = null; - this.linkNodeTarget = null; - - /** Dragging Control **/ - this.draggingElement = null; - this.dragging = false; - this.nMouseOffsetX = 0; - this.nMouseOffsetY = 0; - this.dragStartX = 0; - this.dragStartY = 0; - this.desplazamientoX = 0; - this.desplazamientoY = 0; - - /** Selection Control **/ - this.selecting = false; - this.selectorX = null; - this.selectorY = null; - this.selectorSVGNode = null; - - /** Node status **/ - this.args.isVertexSelected = {}; - this.args.selectedVertices = []; - - /** Edges status **/ - this.args.isEdgeSelected = {}; - //this.args.selectedEdges = []; - - if (args != null) { - if (args.multipleSelectionEnabled != null) { - this.args.multipleSelectionEnabled = args.multipleSelectionEnabled; - this.args.draggingCanvasEnabled = !(this.args.multipleSelectionEnabled); - } - if (args.draggingCanvasEnabled != null) { - this.args.draggingCanvasEnabled = args.draggingCanvasEnabled; - this.args.multipleSelectionEnabled = !(this.args.draggingCanvasEnabled); - } - if (args.interactive != null) { - this.args.interactive = args.interactive; - } - if (args.labeled != null) { - this.args.labeled = args.labeled; - } - - } - - /** Hashmap with the svg node labels **/ - this.svgLabels = {}; - - /** EVENTS **/ - this.onVertexOut = new Event(this); - this.onVertexOver = new Event(this); - this.onVertexSelect = new Event(this); - this.onEdgeSelect = new Event(this); - this.onCanvasClicked = new Event(this); - this.onVertexUp = new Event(this); -} - -GraphCanvas.prototype.showLabels = function(value) { - this.args.labeled = value; - this.removeLabels(); - if (value) { - this.renderLabels(); - } -}; - -GraphCanvas.prototype.getSelectedVertices = function() { - return this.args.selectedVertices; -}; - -GraphCanvas.prototype.getSelectedEdges = function() { - var selected = []; - for ( var selectedEdge in this.args.isEdgeSelected) { - selected.push(selectedEdge); - } - return selected; -}; - -GraphCanvas.prototype.createSVGDom = function(targetID, id, width, height, backgroundColor) { - var container = document.getElementById(targetID); - this._svg = SVG.createSVGCanvas(container, [ - [ "style", "background-color:" + backgroundColor + ";" ], [ "id", id ], [ "dragx", 0 ], [ "dragy", 0 ], - [ "height", this.getFormatter().getHeight() ], [ "width", this.getFormatter().getWidth() ] ]); - return this._svg; -}; - -/** MULTIPLE SELECTION **/ -GraphCanvas.prototype.isMultipleSelectionEnabled = function() { - return this.args.multipleSelectionEnabled; -}; - -GraphCanvas.prototype.setMultipleSelection = function(value) { - this.args.multipleSelectionEnabled = value; - this.args.draggingCanvasEnabled = (!value); -}; - -GraphCanvas.prototype.setSelecting = function(value) { - this.selecting = value; -}; - -/** linking **/ -GraphCanvas.prototype.setLinking = function(value) { - this.args.linkEnabled = value; - this.selecting = !value; - this.dragging = !value; -}; - -/** CANVAS MOVING **/ -GraphCanvas.prototype.setDraggingCanvas = function(value) { - this.args.draggingCanvasEnabled = value; - this.args.multipleSelectionEnabled = !value; -}; - -GraphCanvas.prototype.isDraggingCanvasEnabled = function() { - return this.args.draggingCanvasEnabled; -}; -/** ZOOM **/ -GraphCanvas.prototype.getScale = function() { - return this.getFormatter().getZoomScale(); -}; - -GraphCanvas.prototype.setScale = function(scale) { - var graphNode = document.getElementById(this.args.idGraph); - graphNode.setAttribute("transform", graphNode.getAttribute("transform").replace("scale(" + this.getScale() + ")", "scale(" + scale + ")")); - this.getFormatter().setZoomScale(scale); -}; - -GraphCanvas.prototype.zoomIn = function() { - this.setScale(this.getScale() + this.getFormatter().getZoomScaleStepFactor()); -}; - -GraphCanvas.prototype.zoomOut = function() { - this.setScale(this.getScale() - this.getFormatter().getZoomScaleStepFactor()); - -}; - -/** SVG COORDENATES **/ -GraphCanvas.prototype.getSVGCoordenates = function(evt) { - var p = this._svg.createSVGPoint(); - p.x = evt.clientX; - p.y = evt.clientY; - - var m = this._svg.getScreenCTM(document.documentElement); - p = p.matrixTransform(m.inverse()); - return p; -}; - -/** SVG EVENTS **/ -GraphCanvas.prototype.mouseClick = function(event) { - if (event.button == 0) { - if (!this.args.interactive) { - return; - } - - if (this.isVertex(event.target)) { - this.clickNode(this.getVertexIdFromSVGId(event.target.id)); - } - /** Como el evento mouseClick viene despues del mouse up es aqui donde manejo el tema de deseccionar los elementos que estoy dragging **/ - if (this.dragging) { - this.dragging = false; - } - } -}; - -GraphCanvas.prototype.mouseMove = function(evt) { - if (this.selecting) { - this.clearLabels(); - - var width = (this.getSVGCoordenates(evt).x - this.selectorX); - var height = (this.getSVGCoordenates(evt).y - this.selectorY); - if ((width > 0) && (height > 0)) { - this.displaySelection(this.selectorX, this.selectorY, width, height); - } - if ((width > 0) && (height < 0)) { - this.displaySelection(this.selectorX, this.getSVGCoordenates(evt).y, width, Math.abs(height)); - } - if ((width < 0) && (height < 0)) { - this.displaySelection(this.getSVGCoordenates(evt).x, this.getSVGCoordenates(evt).y, Math.abs(width), Math.abs(height)); - } - if ((width < 0) && (height > 0)) { - this.displaySelection(this.selectorX + width, this.selectorY, Math.abs(width), Math.abs(height)); - } - - var x1 = (parseFloat(this.selectorSVGNode.getAttribute("x")) - DOM.select(this.id).getAttribute("dragx")) / this.getFormatter().getWidth(); - var y1 = (parseFloat(this.selectorSVGNode.getAttribute("y")) - DOM.select(this.id).getAttribute("dragy")) / this.getFormatter().getHeight(); - var x2 = (x1 + parseFloat(this.selectorSVGNode.getAttribute("width") / this.getFormatter().getWidth())); - var y2 = (y1 + parseFloat(this.selectorSVGNode.getAttribute("height") / this.getFormatter().getHeight())); - - this.deselectNodes(this.getLayout()); - var verticesSelected = this.getLayout().getVerticesByArea(x1 / this.getFormatter().getZoomScale(), y1 / this.getFormatter().getZoomScale(), - x2 / this.getFormatter().getZoomScale(), y2 / this.getFormatter().getZoomScale()); - for ( var i = 0; i < verticesSelected.length; i++) { - this.selectNode(verticesSelected[i].getId()); - this.renderLabel(verticesSelected[i].getId()); - } - - } - var p = null; - if (this.args.linking) { - p = this.getSVGCoordenates(evt); - if (this.linkSVGNode != null) { - this.linkSVGNode.setAttribute("x2", p.x - 2); - this.linkSVGNode.setAttribute("y2", p.y - 2); - } - } - - if (this.dragging) { - p = this.getSVGCoordenates(evt); - p.x -= this.nMouseOffsetX; - p.y -= this.nMouseOffsetY; - this.desplazamientoX = (this.getSVGCoordenates(evt).x - this.dragStartX);// + parseFloat(DOM.select(this.id).getAttribute("dragx")); - this.desplazamientoY = (this.getSVGCoordenates(evt).y - this.dragStartY);// + parseFloat(DOM.select(this.id).getAttribute("dragy")); - - if (this.draggingElement != null) { - /** Click sobre el recct del banground que provoca que mueva todo el canvas **/ - if (this.isNodeCanvas(this.draggingElement)) { - - p = this.getSVGCoordenates(evt); - p.x = this.desplazamientoX; - p.y = this.desplazamientoY; - - this.draggingElement.setAttribute("dragx", p.x); - this.draggingElement.setAttribute("dragy", p.y); - this.draggingElement = document.getElementById(this.args.idGraph); - this.draggingElement.setAttribute("transform", "translate(" + p.x + "," + p.y + "), scale(" + this.getScale() + ")"); - - DOM.select(this.id).setAttribute("dragx", p.x); - DOM.select(this.id).setAttribute("dragy", p.y); - - if (this.NodeSVGbackgroundImage != null) { - this.NodeSVGbackgroundImage.setAttribute("dragx", p.x); - this.NodeSVGbackgroundImage.setAttribute("dragy", p.y); - } - } else { - if (this.isVertex(this.draggingElement)) { - this.selectNode(this.getVertexIdFromSVGId(this.draggingElement.id)); - this.desplazamientoX = this.desplazamientoX / this.getFormatter().getZoomScale(); - this.desplazamientoY = this.desplazamientoY / this.getFormatter().getZoomScale(); - this.moveSelectedNodes(this.desplazamientoX, this.desplazamientoY); - - this.dragStartX = this.getSVGCoordenates(evt).x; - this.dragStartY = this.getSVGCoordenates(evt).y; - } else { - if (this.isNodeBackground(this.draggingElement)) { - - this.draggingElement.setAttribute("dragx", p.x); - this.draggingElement.setAttribute("dragy", p.y); - this.draggingElement = document.getElementById(this.args.idGraph); - this.draggingElement.setAttribute("transform", "translate(" + p.x + "," + p.y + "), scale(" + this.getScale() + ")"); - } else { - this.draggingElement.setAttribute("transform", "translate(" + p.x + "," + p.y + ")"); - } - } - } - } - } -}; - -GraphCanvas.prototype.moveSelectedNodes = function(offsetX, offsetY) { - for ( var i = 0; i < this.getSelectedVertices().length; i++) { - - var nodeId = this.getSelectedVertices()[i]; - var svgNodeId = this.getSVGNodeId(nodeId); - - var x = parseFloat(DOM.select(svgNodeId).getAttribute("dragx")) + parseFloat(offsetX);// - parseFloat(DOM.select(this.id).getAttribute("dragx")); - var y = parseFloat(DOM.select(svgNodeId).getAttribute("dragy")) + parseFloat(offsetY);// + parseFloat(DOM.select(this.id).getAttribute("dragy")); - - this._movingNode(DOM.select(svgNodeId), x, y); - } -}; - -GraphCanvas.prototype.mouseDown = function(evt) { - if (event.button == 0) { - - /** if !no interactive mouse events do anything **/ - if (!this.args.interactive) { - return; - } - - var p = this.getSVGCoordenates(evt); - - /** When click on canvas or background deselect all **/ - if (this.isNodeCanvas(evt.target) || this.isNodeBackground(evt.target)) { - this.deselectNodes(); - this.deselectEdges(); - this.onCanvasClicked.notify(); - } - - /** if I am linking vertices **/ - if (this.args.linkEnabled) { - - if (!this.args.linking) { - this.args.linking = true; - if (this.isVertex(evt.target)) { - this.linkStartX = p.x; - this.linkStartY = p.y; - this.linkSVGNode = SVG.drawLine(p.x, p.y, p.x, p.y, this._svg, { - "stroke" : "#FF0000" - }); - this.linkNodeSource = this.getVertexIdFromSVGId(evt.target.id); - } - } else { - this.linkNodeTarget = this.getVertexIdFromSVGId(evt.target.id); - this.args.linking = false; - this.args.linkEnabled = false; - if (this.isVertex(evt.target)) { - this.getDataset().addEdge(this.linkNodeSource + "_" + this.linkNodeTarget, this.linkNodeSource, this.linkNodeTarget, {}); - } - this.linkSVGNode.parentNode.removeChild(this.linkSVGNode); - } - return; - } - - /** Id is a vertex or the canvas **/ - if (this.isVertex(evt.target) || this.isNodeCanvas(evt.target) || this.isNodeBackground(evt.target)) { - this._startDragging(evt); - } - /** if i is edge **/ - if (this.isEdge(evt.target)) { - this.selectEdge(this.getEdgeIdFromSVGId(evt.target.getAttribute("id"))); - } - - if (this.args.multipleSelectionEnabled) { - if (!this.dragging) { - this.setSelecting(true); - this.selectorX = p.x; - this.selectorY = p.y; - this.displaySelection(p.x, p.y, 1, 1); - } - } - - } - if (event.button == 1) { - this.setLinking(false); - this.setMultipleSelection(false); - this.selecting = false; - - /** Id is a vertex or the canvas **/ - if (this.isVertex(evt.target) || this.isNodeCanvas(evt.target) || this.isNodeBackground(evt.target)) { - this._startDragging(evt); - } - } -}; - -GraphCanvas.prototype.mouseUp = function(event) { - if (!this.args.interactive) { - return; - } - - if (this.dragging) { - this._stopDragging(event); - if (this.isVertex(event.target)) { - var vertexId = this.getVertexIdFromSVGId(event.target.id); - if (this.getDataset().getVertexById(vertexId).getEdges().length >= this.args.maxNumberEdgesMoving) { - this.moveEdge(vertexId); - } - } - } - - if (this.selecting) { - this.setSelecting(false); - - var x1 = (parseFloat(this.selectorSVGNode.getAttribute("x")) - DOM.select(this.id).getAttribute("dragx")) / this.getFormatter().getWidth(); - var y1 = (parseFloat(this.selectorSVGNode.getAttribute("y")) - DOM.select(this.id).getAttribute("dragy")) / this.getFormatter().getHeight(); - var x2 = (x1 + parseFloat(this.selectorSVGNode.getAttribute("width") / this.formatter.getWidth())); - var y2 = (y1 + parseFloat(this.selectorSVGNode.getAttribute("height") / this.formatter.getHeight())); - - var verticesSelected = this.getLayout().getVerticesByArea(x1 / this.getFormatter().getZoomScale, y1 / this.getFormatter().getZoomScale, - x2 / this.getFormatter().getZoomScale, y2 / this.getFormatter().getZoomScale); - - for ( var i = 0; i < verticesSelected.length; i++) { - this.selectNode(verticesSelected[i].getId()); - } - - if (this.selectorSVGNode != null) { - this._svg.removeChild(this.selectorSVGNode); - } - - if (this.args.labeled) { - this.clearLabels(); - this.renderLabels(); - } - - this.selectorSVGNode = null; - // this.renderLabels(); - } -}; - -/** SELECTION **/ -GraphCanvas.prototype.displaySelection = function(x, y, width, height) { - if (this.selectorSVGNode != null) { - this.selectorSVGNode.setAttribute("x", x); - this.selectorSVGNode.setAttribute("y", y); - this.selectorSVGNode.setAttribute("width", width); - this.selectorSVGNode.setAttribute("height", height); - } else { - this.selectorSVGNode = SVG.drawRectangle(x, y, width, height, this._svg, { - "fill" : "red", - "stroke" : "black", - "opacity" : "0.2", - "stroke-opacity" : "1" - }); - } -}; - -/** DRAGGING **/ -GraphCanvas.prototype._startDragging = function(evt) { - if (!this.isDraggingCanvasEnabled()) { - if (this.isNodeCanvas(evt.target)) { - this.draggingElement = null; - } - } - - if (this.isVertex(evt.target) || (this.isNodeBackground(evt.target) && (this.isDraggingCanvasEnabled()))|| (this.isNodeCanvas(evt.target) && (this.isDraggingCanvasEnabled()))) { - this.clearLabels(); - this.draggingElement = evt.target; - this.dragging = true; - var p = this.getSVGCoordenates(evt); - - this.nMouseOffsetX = p.x - parseInt(evt.target.getAttribute("dragx")); - this.nMouseOffsetY = p.y - parseInt(evt.target.getAttribute("dragy")); - - if (this.isVertex(evt.target)) { - this.dragStartX = parseInt(this.draggingElement.getAttribute("dragx")) * this.getFormatter().getZoomScale() - + parseFloat(DOM.select(this.id).getAttribute("dragx")); - this.dragStartY = parseInt(this.draggingElement.getAttribute("dragy")) * this.getFormatter().getZoomScale() - + parseFloat(DOM.select(this.id).getAttribute("dragy")); - } else { - this.dragStartX = p.x - parseInt(this.draggingElement.getAttribute("dragx"));// + parseFloat(DOM.select(this.id).getAttribute("dragx")); - this.dragStartY = p.y - parseInt(this.draggingElement.getAttribute("dragy"));// + parseFloat(DOM.select(this.id).getAttribute("dragy")); - } - } -}; - -GraphCanvas.prototype._stopDragging = function(event) { - this.nMouseOffsetX = 0; - this.nMouseOffsetX = 0; - /** despues del evento up viene el evento click entonces acabo el dragging en el mouseclick **/ - this.dragging = false; - this.draggingElement = null; - this.renderLabels(); - - this.setLinking(false); - this.setMultipleSelection(true); - this.selecting = false; - -}; - -/** Move the edges of the vertex with the vertexId indicado **/ -GraphCanvas.prototype.moveEdge = function(vertexId) { - var x = this.getLayout().getNodeById(vertexId).x * this.getFormatter().getWidth(); - var y = this.getLayout().getNodeById(vertexId).y * this.getFormatter().getHeight(); - - /** Moving edges out **/ - for ( var i = 0; i < this.getDataset().getVertexById(vertexId).getEdgesOut().length; i++) { - var edgeId = this.getDataset().getVertexById(vertexId).getEdgesOut()[i].getId(); - var svgEdgeId = this.getSVGEdgeId(edgeId); - var edgeFormatter = this.getFormatter().getEdgeById(edgeId); - if (edgeFormatter instanceof LineEdgeGraphFormatter) { - DOM.select(svgEdgeId + "_shadow").setAttribute("x2", x); - DOM.select(svgEdgeId + "_shadow").setAttribute("y2", y); - DOM.select(svgEdgeId).setAttribute("x2", x); - DOM.select(svgEdgeId).setAttribute("y2", y); - } - - if ((edgeFormatter instanceof DirectedLineEdgeGraphFormatter) || (edgeFormatter instanceof OdirectedLineEdgeGraphFormatter) - || (edgeFormatter instanceof OdotDirectedLineEdgeGraphFormatter) || (edgeFormatter instanceof DotDirectedLineEdgeGraphFormatter) - || (edgeFormatter instanceof CutDirectedLineEdgeGraphFormatter)) { - this.removeEdge(edgeId); - this.renderEdge(edgeId); - } - } - - /** Moving edges in **/ - for ( var i = 0; i < this.getDataset().getVertexById(vertexId).getEdgesIn().length; i++) { - var edgeId = this.getDataset().getVertexById(vertexId).getEdgesIn()[i].getId(); - var svgEdgeId = this.getSVGEdgeId(edgeId); - var edgeFormatter = this.getFormatter().getEdgeById(edgeId); - if (edgeFormatter instanceof LineEdgeGraphFormatter) { - DOM.select(svgEdgeId).setAttribute("x1", x); - DOM.select(svgEdgeId).setAttribute("y1", y); - DOM.select(svgEdgeId + "_shadow").setAttribute("x1", x); - DOM.select(svgEdgeId + "_shadow").setAttribute("y1", y); - } - - if ((edgeFormatter instanceof DirectedLineEdgeGraphFormatter) || (edgeFormatter instanceof OdirectedLineEdgeGraphFormatter) - || (edgeFormatter instanceof OdotDirectedLineEdgeGraphFormatter) || (edgeFormatter instanceof DotDirectedLineEdgeGraphFormatter) - || (edgeFormatter instanceof CutDirectedLineEdgeGraphFormatter)) { - this.removeEdge(edgeId); - this.renderEdge(edgeId); - } - - if (edgeFormatter instanceof BezierEdgeGraphFormatter) { - var radius = this.getFormatter().getVertexById(vertexId).getDefault().getSize() * this.getFormatter().getNodesMaxSize(); - var d = this.calculateCoordenatesBezier(radius, x, y); - DOM.select(svgEdgeId).setAttribute("d", d); - } - } -}; - -GraphCanvas.prototype.moveNode = function(vertexId) { - var x = this.getLayout().getNodeById(vertexId).x * this.getFormatter().getWidth(); - var y = this.getLayout().getNodeById(vertexId).y * this.getFormatter().getHeight(); - var svgNodeElement = DOM.select(this.getSVGNodeId(vertexId)); - - svgNodeElement.setAttribute("dragx", x); - svgNodeElement.setAttribute("dragy", y); - svgNodeElement.setAttribute("transform", "translate(" + x + "," + y + ")"); - - if (this.getDataset().getVertexById(vertexId).getEdges().length < this.args.maxNumberEdgesMoving) { - this.moveEdge(vertexId); - } -}; - -GraphCanvas.prototype._movingNode = function(svgNodeElement, x, y) { - var vertexId = this.getVertexIdFromSVGId(svgNodeElement.getAttribute("id")); - this.getLayout().getNodeById(vertexId).setCoordinates(x / this.getFormatter().getWidth(), y / this.getFormatter().getHeight()); - this.desplazamientoX = 0; - this.desplazamientoY = 0; - this.removeLabel(vertexId); - this.renderLabel(vertexId); -}; - -/** INIT **/ -GraphCanvas.prototype.init = function() { - - this._svg = this.createSVGDom(this.targetID, this.id, this.getFormatter().getWidth(), this.getFormatter().getHeight(), this.getFormatter() - .getBackgroundColor()); - this.GraphGroup = SVG.drawGroup(this._svg, [ [ "id", this.args.idGraph ], [ "transform", "translate(0,0), scale(1)" ] ]); - this.GraphBackground = SVG.drawGroup(this.GraphGroup, [ [ "id", this.args.idBackground ] ]); - this.GraphEdgeGroup = SVG.drawGroup(this.GraphGroup, [ [ "id", this.args.idEdgesGraph ] ]); - this.GraphNodeGroup = SVG.drawGroup(this.GraphGroup, [ [ "id", this.args.idNodesGraph ] ]); - this.GraphLabelGroup = SVG.drawGroup(this.GraphGroup, [ [ "id", this.args.idLabelGraph ] ]); - - if ((this.getFormatter().getBackgroundImage() != null) && (this.getFormatter().getBackgroundImage() != "")) { - this.setBackgroundImage(this.getFormatter().getBackgroundImage()); - } - /** SVG Events listener */ - var _this = this; - this._svg.addEventListener("click", function(event) { - _this.mouseClick(event); - }, false); - this._svg.addEventListener("mousemove", function(event) { - _this.mouseMove(event, _this); - }, false); - this._svg.addEventListener("mousedown", function(event) { - _this.mouseDown(event, _this); - }, false); - this._svg.addEventListener("mouseup", function(event) { - _this.mouseUp(event, _this); - }, false); -}; - -/* - GraphCanvas.prototype.backgroungToSVG = function(){ - var _this = this; - var canvas = document.createElement('canvas'); - canvas.setAttribute("id", "canvas"); - canvas.width = this.formatter.getWidth(); - canvas.height = this.formatter.getHeight(); - - this._svg.parentNode.parentNode.appendChild(canvas); - var ctx = document.getElementById('canvas').getContext('2d'); - var img = new Image(); - - img.src = this.formatter.getBackgroundImage(); - ctx.drawImage(img,0,0 ,_this.formatter.getWidth(), _this.formatter.getHeight()); - - - img.onload = function() { - canvas.parentNode.removeChild(canvas); - } - - this.NodeSVGbackgroundImage.setAttribute("xlink:href", document.getElementById("canvas").toDataURL()); - this.NodeSVGbackgroundImage.removeAttribute("href"); - - // - - };*/ - -GraphCanvas.prototype.setBackgroundImage = function() { - if (this.NodeSVGbackgroundImage != null) { - this.NodeSVGbackgroundImage.parentNode.removeChild(this.NodeSVGbackgroundImage); - } - $('#' + this.targetID).svg(); - $('#' + this.targetID).svg("get"); - - $('#' + this.targetID).svg("get")._svg = document.getElementById(this.id); - - var svg = $('#' + this.targetID).svg("get"); - this.NodeSVGbackgroundImage = svg.image(0, 0, this.getFormatter().getWidth(), this.getFormatter().getHeight(), this.getFormatter() - .getBackgroundImage()); - this.NodeSVGbackgroundImage.setAttribute("id", this.args.idBackgroundNode); - - this.NodeSVGbackgroundImage.setAttribute("x", 0); - this.NodeSVGbackgroundImage.setAttribute("y", 0); - - this.NodeSVGbackgroundImage.setAttribute("dragx", 0); - this.NodeSVGbackgroundImage.setAttribute("dragy", 0); - - if (this.getFormatter().args.backgroundImageHeight != null) { - this.NodeSVGbackgroundImage.setAttribute("height", this.getFormatter().args.backgroundImageHeight); - } - if (this.getFormatter().args.backgroundImageWidth != null) { - this.NodeSVGbackgroundImage.setAttribute("width", this.getFormatter().args.backgroundImageWidth); - } - - if (this.getFormatter().args.backgroundImageX != null) { - this.NodeSVGbackgroundImage.setAttribute("x", this.getFormatter().args.backgroundImageX); - } - if (this.getFormatter().args.backgroundImageY != null) { - this.NodeSVGbackgroundImage.setAttribute("y", this.getFormatter().args.backgroundImageY); - } - - this.GraphBackground.appendChild(this.NodeSVGbackgroundImage); - this.NodeSVGbackgroundImage.removeAttribute("href"); - this.NodeSVGbackgroundImage.setAttribute("xlink:href", this.getFormatter().getBackgroundImage()); -}; - -GraphCanvas.prototype.removeBackgroundImage = function() { - if (this.NodeSVGbackgroundImage != null) { - this.NodeSVGbackgroundImage.parentNode.removeChild(this.NodeSVGbackgroundImage); - } -}; - -GraphCanvas.prototype._setBackgroundColor = function(color) { - var attributes = [ [ "fill", color ] ]; - SVG.drawRectangle(0, 0, this.getFormatter().getWidth(), this.getFormatter().getHeight(), this.GraphBackground, attributes); -}; - -/** Serialize **/ -GraphCanvas.prototype.toJSON = function() { - var json = {}; - json.dataset = {}; - json.formatter = {}; - json.layout = {}; - json.dataset = this.getDataset().toJSON(); - json.formatter = this.getFormatter().toJSON(); - json.layout = this.getLayout().toJSON(); - return json; -}; - -GraphCanvas.prototype.toHTML = function() { - //this.backgroungToSVG(); - var html = this._svg.parentElement.innerHTML; - - var start = html.indexOf("") + 6; - - return html.substr(start, end); -}; - -/** DRAW **/ -GraphCanvas.prototype.draw = function(graphdataset, graphformatter, graphlayout) { - this.setDataset(graphdataset); - this.setFormatter(graphformatter); - this.setLayout(graphlayout); - - var _this = this; - this.getFormatter().changed.attach(function(sender, item) { - _this.removeNode(item.getId()); - _this.renderNode(item.getId()); - if (_this.args.labeled) { - _this.removeLabel(item.getId()); - _this.renderLabel(item.getId()); - } - - }); - //TODO - this.getFormatter().edgeChanged.attach(function(sender, item) { - _this.removeEdge(item.getId()); - _this.renderEdge(item.getId()); - }); - - this.getFormatter().resized.attach(function(sender, item) { - _this.resize(_this.getFormatter().getWidth(), _this.getFormatter().getHeight()); - }); - - this.getFormatter().backgroundImageChanged.attach(function(sender, item) { - _this.setBackgroundImage(_this.getFormatter().getBackgroundImage()); - }); - - this.getFormatter().backgroundColorChanged.attach(function(sender, item) { - _this._setBackgroundColor(_this.getFormatter().getBackgroundColor()); - }); - - this.getLayout().changed.attach(function(sender, item) { - _this.moveNode(item.getId()); - _this.moveEdge(item.getId()); - if (_this.args.labeled) { - _this.removeLabel(item.getId()); - _this.renderLabel(item.getId()); - } - }); - - this.getDataset().newVertex.attach(function(sender, item) { - - _this.renderNode(item.getId()); - if (_this.args.labeled) { - _this.renderLabel(item.getId()); - } - }); - - this.getDataset().newEdge.attach(function(sender, item) { - _this.renderEdge(item.getId()); - }); - - this.getDataset().vertexDeleted.attach(function(sender, item) { - _this.removeNode(item.getId()); - if (_this.args.labeled) { - _this.removeLabel(item.getId()); - } - }); - - this.getDataset().edgeDeleted.attach(function(sender, item) { - _this.removeEdge(item.getId()); - }); - - this.getDataset().vertexNameChanged.attach(function(sender, args) { - if (_this.args.labeled) { - _this.removeLabel(args.item.getId()); - _this.removeLabel(args.item.getId()); - _this.renderLabel(args.item.getId()); - } - }); - this.init(); - this.render(); -}; - -GraphCanvas.prototype.render = function() { - for ( var id in this.getDataset().getVertices()) { - this.renderNode(id); - } - this.renderLabels(); - this.renderEdges(); -}; - -GraphCanvas.prototype.renderLabels = function() { - if (this.args.labeled) { - for ( var id in this.getDataset().getVertices()) { - this.renderLabel(id); - } - } -}; - -GraphCanvas.prototype.removeLabels = function() { - for ( var id in this.getDataset().getVertices()) { - this.removeLabel(id); - } -}; - -/** Utilities method for nodes **/ -GraphCanvas.prototype.isNodeCanvas = function(node) { - return ((node.id == this.args.idGraph) || (node.id == this.id)); -}; - -GraphCanvas.prototype.isNodeBackground = function(node) { - return ((node.id == this.args.idBackgroundNode)); -}; - -GraphCanvas.prototype.isVertex = function(node) { - if (node.getAttribute("id") != null) { - if (node.getAttribute("id").indexOf("_v_") != -1) { - return true; - } - } - return false; -}; - -GraphCanvas.prototype.isLabel = function(node) { - if (node.getAttribute("id") != null) { - if (node.getAttribute("id").indexOf("_l_") != -1) { - return true; - } - } - return false; -}; - -GraphCanvas.prototype.isEdge = function(node) { - if (node.getAttribute("id") != null) { - if (node.getAttribute("id").indexOf("_e_") != -1) { - return true; - } - } - return false; -}; - -/** Resize **/ -GraphCanvas.prototype.resize = function(width, height) { - // this._svg.setAttribute("width", width); - // this._svg.setAttribute("height", height); - if (this.NodeSVGbackgroundImage != null) { - this.NodeSVGbackgroundImage.setAttribute("width", width); - this.NodeSVGbackgroundImage.setAttribute("height", height); - } - - this._svg.setAttribute("width", width); - this._svg.setAttribute("height", height); - - this.clearCanvas(); - this.render(); -}; - -GraphCanvas.prototype.clearCanvas = function() { - DOM.removeChilds(this.GraphEdgeGroup.getAttribute("id")); - DOM.removeChilds(this.GraphNodeGroup.getAttribute("id")); - this.clearLabels(); -}; - -GraphCanvas.prototype.clearLabels = function() { - DOM.removeChilds(this.GraphLabelGroup.getAttribute("id")); -}; - -/** ID'S converter **/ -GraphCanvas.prototype.getSVGNodeId = function(nodeId) { - return this.id + "_v_" + nodeId; -}; - -GraphCanvas.prototype.getSVGEdgeId = function(edgeId) { - return this.id + "_e_" + edgeId; -}; - -GraphCanvas.prototype.getSVGArrowEdgeId = function(edgeId) { - return this.id + "_arrow_" + edgeId; -}; - -GraphCanvas.prototype.getSVGLabelId = function(edgeId) { - return this.id + "_l_" + edgeId; -}; - -GraphCanvas.prototype.blinkVertexById = function(vertexId) { - $("#" + this.getSVGNodeId(vertexId)).fadeIn().fadeOut().fadeIn().fadeOut().fadeIn().fadeOut(); -}; - -GraphCanvas.prototype.getVertexIdFromSVGId = function(svgVertexId) { - return svgVertexId.replace(this.id, "").replace("_v_", ""); -}; - -GraphCanvas.prototype.getEdgeIdFromSVGId = function(svgEdgeId) { - return svgEdgeId.replace(this.id, "").replace("_e_", ""); -}; - -/** VERTEX **/ -GraphCanvas.prototype.getVertexById = function(id) { - return document.getElementById(this.getSVGNodeId(id)); -}; - -GraphCanvas.prototype.renderNodes = function() { - for ( var id in this.getDataset().getVertices()) { - this.renderNode(id); - } -}; - -GraphCanvas.prototype.overNode = function(nodeId) { - if (!this.args.interactive) { - return; - } - /** If selected we don't change the format **/ - if (this.args.isVertexSelected[nodeId] == null) { - var args = this.getFormatter().getVertexById(nodeId).getOver(); - args.args["cursor"] = 'pointer'; - this.changeVertexFormat(nodeId, args); - } -}; - -GraphCanvas.prototype.outNode = function(nodeId) { - if (!this.args.interactive) { - return; - } - - /** If selected we don't change the format **/ - if (this.args.isVertexSelected[nodeId] == null) { - this.changeVertexFormat(nodeId, this.getFormatter().getVertexById(nodeId).getDefault()); - } -}; - -GraphCanvas.prototype.overLabel = function(nodeId) { - this.overNode(nodeId); - // this.svgLabels[nodeId].setAttribute("cursor", "pointer"); -}; - -GraphCanvas.prototype.outLabel = function(nodeId) { - this.outNode(nodeId); - // this.svgLabels[nodeId].setAttribute("cursor", ""); -}; - -GraphCanvas.prototype.clickNode = function(nodeId) { - if (!this.args.interactive) { - return; - } - - /** si el evento se dispara oprque estaba dragging entonces no activo nada **/ - if (this.args.isVertexSelected[nodeId] == null) { - this.selectNode(nodeId); - } else { - this.deselectNode(nodeId); - } -}; - -GraphCanvas.prototype.selectNode = function(nodeId) { - for ( var i = 0; i < this.args.selectedVertices.length; i++) { - var format = this.getFormatter().getVertexById(nodeId).getSelected(); - format.opacity = 0.2; - this.changeVertexFormat(nodeId, this.getFormatter().getVertexById(nodeId).getSelected()); - } - - if (this.args.isVertexSelected[nodeId] == null) { - var format = this.getFormatter().getVertexById(nodeId).getSelected(); - format.opacity = 1; - this.changeVertexFormat(nodeId, this.getFormatter().getVertexById(nodeId).getSelected()); - this.args.selectedVertices.push(nodeId); - this.args.isVertexSelected[nodeId] = this.args.selectedVertices.length - 1; - this.onVertexSelect.notify(nodeId); - } -}; - -GraphCanvas.prototype.selectAllEdges = function() { - this.deselectNodes(); - this.deselectEdges(); - - for ( var edgesId in this.getDataset().edges) { - this.selectEdge(edgesId); - } -}; - -GraphCanvas.prototype.selectAllNodes = function() { - this.deselectNodes(); - this.deselectEdges(); - - for ( var vertexId in this.getDataset().vertices) { - this.selectNode(vertexId); - } -}; - -GraphCanvas.prototype.selectAll = function() { - this.deselectNodes(); - this.deselectEdges(); - - for ( var vertexId in this.getDataset().vertices) { - this.selectNode(vertexId); - } - - for ( var edgesId in this.getDataset().edges) { - this.selectEdge(edgesId); - } -}; - -GraphCanvas.prototype.selectEdge = function(edgeId) { - if (this.args.isEdgeSelected[edgeId] == null) { - this.changeEdgeFormat(edgeId, this.getFormatter().getEdgeById(edgeId).getSelected()); - //this.args.selectedEdges.push(edgeId); - this.args.isEdgeSelected[edgeId] = true; //this.args.selectedEdges.length - 1; - this.onEdgeSelect.notify(edgeId); - } -}; - -GraphCanvas.prototype.selectEdges = function(edges) { - - for ( var i = 0; i < edges.length; i++) { - this.selectEdge(edges[i]); - } -}; - -GraphCanvas.prototype.deselectNode = function(nodeId) { - if (this.args.isVertexSelected[nodeId] != null) { - this.changeVertexFormat(nodeId, this.getFormatter().getVertexById(nodeId).getDefault()); - this.args.selectedVertices.splice(this.args.isVertexSelected[nodeId], 1); - var index = this.args.isVertexSelected[nodeId]; - delete this.args.isVertexSelected[nodeId]; - - for ( var vertex in this.args.isVertexSelected) { - if (this.args.isVertexSelected[vertex] > index) { - this.args.isVertexSelected[vertex] = this.args.isVertexSelected[vertex] - 1; - } - } - } -}; - -GraphCanvas.prototype.deselectNodes = function() { - var selected = JSON.parse(JSON.stringify(this.getSelectedVertices())); - for ( var i = 0; i < selected.length; i++) { - this.deselectNode(selected[i]); - } -}; -GraphCanvas.prototype.selectNodes = function(idNodes) { - - for ( var i = 0; i < idNodes.length; i++) { - this.selectNode(idNodes[i]); - } - - // for ( var vertex in this.args.isVertexSelected) { - // if (this.args.isVertexSelected[vertex] > index){ - // this.args.isVertexSelected[vertex] = this.args.isVertexSelected[vertex] - 1; - // } - // } - -}; - -GraphCanvas.prototype.changeVertexFormat = function(nodeId, format) { - var svgNode = DOM.select(this.getSVGNodeId(nodeId)); - if (svgNode != null) { - var properties = format.toJSON(); - for ( var item in properties) { - svgNode.setAttribute(item, properties[item]); - } - - if (this.getFormatter().getVertexById(nodeId) instanceof CircleVertexGraphFormatter) { - var transform = "translate(" + svgNode.getAttribute("dragx") + "," + svgNode.getAttribute("dragy") + "), scale(" + format.getSize() + ")"; - svgNode.setAttribute("transform", transform); - } - } -}; - -GraphCanvas.prototype.renderLabel = function(nodeId) { - var x = Math.ceil(this.getLayout().getNodeById(nodeId).x * this.getFormatter().getWidth()); - var y = Math.ceil(this.getLayout().getNodeById(nodeId).y * this.getFormatter().getHeight()); - - var svgAttributesNode = JSON.parse(JSON.stringify(this.getFormatter().getVertexById(nodeId).getDefault().toJSON().title)); - svgAttributesNode.id = this.getSVGLabelId(this.getDataset().getVertexById(nodeId).getId()); - svgAttributesNode.dx = (-1) * (this.getDataset().getVertexById(nodeId).getName().length * svgAttributesNode["font-size"]) / 4 - 4; - - svgAttributesNode.dy = parseFloat((this.getFormatter().getVertexById(nodeId).getDefault().getSize())) - + parseFloat(svgAttributesNode["font-size"]) + parseFloat(this.getFormatter().getVertexById(nodeId).getDefault().getStrokeWidth()) - 4; - svgAttributesNode.dragx = Math.ceil(this.getLayout().getNodeById(nodeId).x * this.getFormatter().getWidth()); - - var gragy = parseFloat(this.getFormatter().getVertexById(nodeId).getDefault().getSize()) - + Math.ceil(this.getLayout().getNodeById(nodeId).y * this.getFormatter().getHeight()); - svgAttributesNode.dragy = gragy; - svgAttributesNode.transform = "translate(" + svgAttributesNode.dragx + "," + svgAttributesNode.dragy + ")";//, scale("+this.formatter.getVertexById(nodeId).getDefault().getSize()+")"; - - var nodeSVG = SVG.drawText(0, 0, this.getDataset().getVertexById(nodeId).getName(), this.GraphLabelGroup, svgAttributesNode); - - this.svgLabels[nodeId] = nodeSVG; - - /** Events for the SVG node **/ - var _this = this; - if (nodeSVG != null) { - nodeSVG.addEventListener("mouseover", function() { - _this.overLabel(nodeId); - }, false); - nodeSVG.addEventListener("mouseout", function() { - _this.outLabel(nodeId); - }, false); - } - -}; - -GraphCanvas.prototype.removeLabel = function(labelId) { - if (DOM.select(this.getSVGLabelId(labelId)) != null) { - DOM.select(this.getSVGLabelId(labelId)).parentNode.removeChild(DOM.select(this.getSVGLabelId(labelId))); - } -}; - -GraphCanvas.prototype.renderNode = function(nodeId) { - var svgAttributesNode = JSON.parse(JSON.stringify(this.getFormatter().getVertexById(nodeId).getDefault().toJSON())); - svgAttributesNode.dragx = Math.ceil(this.getLayout().getNodeById(nodeId).x * this.getFormatter().getWidth()); - svgAttributesNode.dragy = Math.ceil(this.getLayout().getNodeById(nodeId).y * this.getFormatter().getHeight()); - svgAttributesNode.transform = "translate(" + svgAttributesNode.dragx + "," + svgAttributesNode.dragy + ")"; - svgAttributesNode.id = this.getSVGNodeId(nodeId); - /*svgAttributesNode["stroke-width"] = 3 ; - svgAttributesNode["stroke-opacity"] = 1 ; - svgAttributesNode["fill-opacity"] = svgAttributesNode["opacity"] ; - svgAttributesNode["opacity"] = 1 ;*/ - this.circleDefaultRadius = this.getFormatter().getVertexById(nodeId).getDefault().getSize(); - var nodeSVG; - - if (this.getFormatter().getVertexById(nodeId) instanceof CircleVertexGraphFormatter) { - nodeSVG = SVG.drawCircle(0, 0, this.circleDefaultRadius, this.GraphNodeGroup, svgAttributesNode); - } - - if (this.getFormatter().getVertexById(nodeId) instanceof SquareVertexGraphFormatter) { - //nodeSVG = SVG.drawRectangle(0 - (this.circleDefaultRadius) ,0 - (this.formatter.getVertexById(nodeId).getDefault().getSize()) , (this.getFormatter().getVertexById(nodeId).getDefault().getSize()*2), (this.getFormatter().getVertexById(nodeId).getDefault().getSize()*2), this.GraphNodeGroup, svgAttributesNode); - nodeSVG = SVG.drawRectangle(0 - (this.circleDefaultRadius), 0 - (this.circleDefaultRadius), (this.circleDefaultRadius * 2), - (this.circleDefaultRadius * 2), this.GraphNodeGroup, svgAttributesNode); - } - - if (this.getFormatter().getVertexById(nodeId) instanceof EllipseVertexGraphFormatter) { - nodeSVG = SVG.drawEllipse(0, 0, this.circleDefaultRadius * 1.5, this.circleDefaultRadius, this.GraphNodeGroup, svgAttributesNode); - } - - if (this.getFormatter().getVertexById(nodeId) instanceof RectangleVertexGraphFormatter) { - //nodeSVG = SVG.drawRectangle(0 - (this.circleDefaultRadius) ,0 - ((this.circleDefaultRadius*2)/2) , (this.circleDefaultRadius*2), (this.circleDefaultRadius), this.GraphNodeGroup, svgAttributesNode); - nodeSVG = SVG.drawRectangle(0 - (this.circleDefaultRadius * 1.5), 0 - (this.circleDefaultRadius), (this.circleDefaultRadius * 2 * 1.5), - (this.circleDefaultRadius * 2), this.GraphNodeGroup, svgAttributesNode); - - } - - if (this.getFormatter().getVertexById(nodeId) instanceof RoundedVertexGraphFormatter) { - svgAttributesNode.ry = 2;// this.formatter.getVertexById(nodeId).getDefault().getSize()/4; - svgAttributesNode.rx = 2;// this.formatter.getVertexById(nodeId).getDefault().getSize()/4; - nodeSVG = SVG.drawRectangle(0 - (this.circleDefaultRadius * 1.5), 0 - (this.circleDefaultRadius), (this.circleDefaultRadius * 2 * 1.5), - (this.circleDefaultRadius * 2), this.GraphNodeGroup, svgAttributesNode); - } - - // - - if (this.getFormatter().getVertexById(nodeId) instanceof OctagonVertexGraphFormatter) { - svgAttributesNode.ry = 2; - svgAttributesNode.rx = 2; - nodeSVG = SVG.drawRectangle(0 - (this.circleDefaultRadius * 1.5), 0 - (this.circleDefaultRadius), (this.circleDefaultRadius * 2 * 1.5), - (this.circleDefaultRadius * 2), this.GraphNodeGroup, svgAttributesNode); - } - - nodeSVG.internalId = nodeId; - // - var _this = this; - - /** Events for the SVG node **/ - if (nodeSVG != null) { - nodeSVG.addEventListener("mouseover", function() { - _this.onVertexOver.notify(nodeId); - _this.overNode(nodeId); - }, false); - nodeSVG.addEventListener("mouseout", function() { - _this.onVertexOut.notify(nodeId); - _this.outNode(nodeId); - }, false); - //nodeSVG.addEventListener("click", function(){_this.clickNode(nodeId);}, false); - // - nodeSVG.addEventListener("mouseup", function() { - _this.onVertexUp.notify(nodeId); - }, false); - } -}; - -GraphCanvas.prototype.removeNode = function(nodeId) { - DOM.select(this.getSVGNodeId(nodeId)).parentNode.removeChild(DOM.select(this.getSVGNodeId(nodeId))); - if (this.args.labeled) { - this.removeLabel(nodeId); - } -}; - -/** REMOVING **/ -GraphCanvas.prototype.removeSelected = function() { - /** El orden importa **/ - this.removeSelectedEdges(); - this.removeSelectedNode(); - -}; - -GraphCanvas.prototype.removeSelectedNode = function() { - var selected = JSON.parse(JSON.stringify(this.getSelectedVertices())); - this.deselectNodes(); - var sorted = selected.sort(function(a, b) { - return a - b - }); - for ( var i = 0; i < sorted.length; i++) { - if (this.getDataset().getVertexById(sorted[i]) != null) { - this.getDataset().getVertexById(sorted[i]).remove(); - } - } -}; - -/** EDGES **/ -GraphCanvas.prototype.removeEdge = function(edgeId) { - if (DOM.select(this.getSVGEdgeId(edgeId)) != null) { - DOM.select(this.getSVGEdgeId(edgeId)).parentNode.removeChild(DOM.select(this.getSVGEdgeId(edgeId))); - } - - if (DOM.select(this.getSVGEdgeId(edgeId) + "_shadow") != null) { - DOM.select(this.getSVGEdgeId(edgeId) + "_shadow").parentNode.removeChild(DOM.select(this.getSVGEdgeId(edgeId) + "_shadow")); - } - - if (DOM.select(this.getSVGArrowEdgeId(edgeId)) != null) { - DOM.select(this.getSVGArrowEdgeId(edgeId)).parentNode.removeChild(DOM.select(this.getSVGArrowEdgeId(edgeId))); - } -}; - -GraphCanvas.prototype.overEdge = function(edgeId) { - if ((!this.args.interactive) || this.dragging || this.selecting) { - return; - } - - /** If selected we don't change the format **/ - if (this.args.isEdgeSelected[edgeId] == null) { - var format = this.getFormatter().getEdgeById(edgeId).getOver(); - format.args["cursor"] = "pointer"; - this.changeEdgeFormat(edgeId, format); - } -}; - -GraphCanvas.prototype.outEdge = function(edgeId) { - if (!this.args.interactive) { - return; - } - - /** If selected we don't change the format **/ - if (this.args.isEdgeSelected[edgeId] == null) { - this.changeEdgeFormat(edgeId, this.getFormatter().getEdgeById(edgeId).getDefault()); - } -}; - -GraphCanvas.prototype.changeEdgeFormat = function(edgeId, format) { - var svgEdge = DOM.select(this.getSVGEdgeId(edgeId) + "_shadow"); - if (svgEdge != null) { - var properties = format.toJSON(); - for ( var item in properties) { - svgEdge.setAttribute(item, properties[item]); - } - } -}; - -GraphCanvas.prototype.deselectEdge = function(edgeID) { - if (this.args.isEdgeSelected[edgeID] != null) { - this.changeEdgeFormat(edgeID, this.getFormatter().getEdgeById(edgeID).getDefault()); - var index = this.args.isEdgeSelected[edgeID]; - delete this.args.isEdgeSelected[edgeID]; - } -}; - -GraphCanvas.prototype.deselectEdges = function() { - var selected = JSON.parse(JSON.stringify(this.getSelectedEdges())); - for ( var i = 0; i < selected.length; i++) { - this.deselectEdge(selected[i]); - } -}; - -GraphCanvas.prototype.removeSelectedEdges = function() { - var selected = JSON.parse(JSON.stringify(this.getSelectedEdges())); - this.deselectEdges(); - for ( var i = 0; i < selected.length; i++) { - if (this.getDataset().getEdgeById(selected[i]) != null) { - this.getDataset().getEdgeById(selected[i]).remove(); - } - } -}; - -GraphCanvas.prototype.renderEdge = function(edgeId) { - var svgAttributesEdge = this.getFormatter().getEdgeById(edgeId).getDefault().toJSON(); - var edge = this.getDataset().getEdgeById(edgeId); - - var svgNodeTarget = this.getVertexById(edge.getNodeTarget().getId()); - var svgNodeSource = this.getVertexById(edge.getNodeSource().getId()); - svgAttributesEdge.id = this.getSVGEdgeId(edge.getId()) + "_shadow"; - - var svgEdge = null; - - if (this.getFormatter().getEdgeById(edgeId) instanceof LineEdgeGraphFormatter) { - var coordenateSourceX = svgNodeSource.getAttribute("dragx"); - var coordenateSourceY = svgNodeSource.getAttribute("dragy"); - var coordenateTargetX = svgNodeTarget.getAttribute("dragx"); - var coordenateTargetY = svgNodeTarget.getAttribute("dragy"); - - SVG.drawLine(coordenateSourceX, coordenateSourceY, coordenateTargetX, coordenateTargetY, this.GraphEdgeGroup, svgAttributesEdge); - var attributesShadow = {}; - attributesShadow.id = this.getSVGEdgeId(edge.getId()); - attributesShadow["stroke-opacity"] = 0; - attributesShadow["stroke-width"] = 4; - attributesShadow["stroke"] = "black"; - svgEdge = SVG.drawLine(svgNodeSource.getAttribute("dragx"), svgNodeSource.getAttribute("dragy"), svgNodeTarget.getAttribute("dragx"), - svgNodeTarget.getAttribute("dragy"), this.GraphEdgeGroup, attributesShadow); - } - - if (this.getFormatter().getEdgeById(edgeId) instanceof BezierEdgeGraphFormatter) { - var nodeId = edge.getNodeTarget().getId(); - var nodeSize = this.formatter.getVertexById(nodeId).getDefault().getSize() * this.getFormatter().getNodesMaxSize(); - svgAttributesEdge.fill = "none"; - svgAttributesEdge.id = this.getSVGEdgeId(edgeId); - var d = this.calculateCoordenatesBezier(nodeSize, svgNodeSource.getAttribute("dragx"), svgNodeSource.getAttribute("dragy")); - svgEdge = SVG.drawPath(d, this.GraphEdgeGroup, svgAttributesEdge); - } - ; - - if ((this.getFormatter().getEdgeById(edgeId) instanceof DirectedLineEdgeGraphFormatter) - || (this.getFormatter().getEdgeById(edgeId) instanceof CutDirectedLineEdgeGraphFormatter) - || (this.getFormatter().getEdgeById(edgeId) instanceof DotDirectedLineEdgeGraphFormatter) - || (this.getFormatter().getEdgeById(edgeId) instanceof OdotDirectedLineEdgeGraphFormatter) - || (this.getFormatter().getEdgeById(edgeId) instanceof OdirectedLineEdgeGraphFormatter)) { - var coordenateSourceX = svgNodeSource.getAttribute("dragx"); - var coordenateSourceY = svgNodeSource.getAttribute("dragy"); - var coordenateTargetX = svgNodeTarget.getAttribute("dragx"); - var coordenateTargetY = svgNodeTarget.getAttribute("dragy"); - - var offset = parseFloat(this.getFormatter().getVertexById(this.getDataset().getEdgeById(edgeId).getNodeTarget().getId()).getDefault() - .getSize() - * this.circleDefaultRadius); - var point = this._calculateEdgePointerPosition(coordenateSourceX, coordenateSourceY, coordenateTargetX, coordenateTargetY, offset); - coordenateTargetX = point.x; - coordenateTargetY = point.y; - SVG.drawLine(coordenateSourceX, coordenateSourceY, coordenateTargetX, coordenateTargetY, this.GraphEdgeGroup, svgAttributesEdge); - - var attributesShadow = {}; - attributesShadow.id = this.getSVGEdgeId(edge.getId()); - attributesShadow["stroke-opacity"] = 0; - attributesShadow["stroke-width"] = 4; - attributesShadow["stroke"] = "black"; - svgEdge = SVG.drawLine(coordenateSourceX, coordenateSourceY, coordenateTargetX, coordenateTargetY, this.GraphEdgeGroup, attributesShadow); - } - - if (this.getFormatter().getEdgeById(edgeId) instanceof DirectedLineEdgeGraphFormatter - || (this.getFormatter().getEdgeById(edgeId) instanceof OdirectedLineEdgeGraphFormatter)) { - var coordenateSourceX = svgNodeSource.getAttribute("dragx"); - var coordenateSourceY = svgNodeSource.getAttribute("dragy"); - var coordenateTargetX = svgNodeTarget.getAttribute("dragx"); - var coordenateTargetY = svgNodeTarget.getAttribute("dragy"); - - var point = this._calculateEdgePointerPosition(coordenateSourceX, coordenateSourceY, coordenateTargetX, coordenateTargetY, offset); - coordenateTargetX = point.x; - coordenateTargetY = point.y; - - var angle = Geometry.toDegree(point.angle) + 90; - this.arrowDefaultSize = this.getFormatter().getEdgeById(edgeId).getArrowSize(); //getDefault().getArrowSize(); - var d = "-" + this.arrowDefaultSize + ",0 0,-" + parseFloat(this.arrowDefaultSize) * 2 + " " + this.arrowDefaultSize + ",0"; - - var attributes; - - if (this.getFormatter().getEdgeById(edgeId) instanceof DirectedLineEdgeGraphFormatter) { - attributes = [ - [ "fill", this.getFormatter().getEdgeById(edgeId).getDefault().getStroke() ], - [ "stroke", this.getFormatter().getEdgeById(edgeId).getDefault().getStroke() ], [ "id", this.getSVGArrowEdgeId(edgeId) ] ]; - } else { - attributes = [ - [ "fill", "#FFFFFF" ], [ "stroke", this.getFormatter().getEdgeById(edgeId).getDefault().getStroke() ], - [ "id", this.getSVGArrowEdgeId(edgeId) ] ]; - } - - var flechaSVGNode = SVG.drawPoligon(d, this.GraphEdgeGroup, attributes);//, ["transform", "rotate("+angle+"), translate(0,0)"]]); - flechaSVGNode.setAttribute("transform", " translate(" + coordenateTargetX + ", " + coordenateTargetY + "), rotate(" + angle + ")"); - } - ; - - if (this.getFormatter().getEdgeById(edgeId) instanceof CutDirectedLineEdgeGraphFormatter) { - var coordenateSourceX = svgNodeSource.getAttribute("dragx"); - var coordenateSourceY = svgNodeSource.getAttribute("dragy"); - var coordenateTargetX = svgNodeTarget.getAttribute("dragx"); - var coordenateTargetY = svgNodeTarget.getAttribute("dragy"); - - var point = this._calculateEdgePointerPosition(coordenateSourceX, coordenateSourceY, coordenateTargetX, coordenateTargetY, offset); - coordenateTargetX = point.x; - coordenateTargetY = point.y; - - var angle = Geometry.toDegree(point.angle) + 90; - - //this.arrowDefaultSize = 2; //getDefault().getArrowSize(); - var d = "-4,0 4,0 4,-2 -4,-2"; - - var flechaSVGNode = SVG.drawPoligon(d, this.GraphEdgeGroup, [ - [ "fill", this.getFormatter().getEdgeById(edgeId).getDefault().getStroke() ], - [ "stroke", this.getFormatter().getEdgeById(edgeId).getDefault().getStroke() ], [ "id", this.getSVGArrowEdgeId(edgeId) ] ]);//, ["transform", "rotate("+angle+"), translate(0,0)"]]); - flechaSVGNode.setAttribute("transform", " translate(" + coordenateTargetX + ", " + coordenateTargetY + "), rotate(" + angle + ")"); - } - ; - - if ((this.getFormatter().getEdgeById(edgeId) instanceof DotDirectedLineEdgeGraphFormatter) - || (this.getFormatter().getEdgeById(edgeId) instanceof OdotDirectedLineEdgeGraphFormatter)) { - var coordenateSourceX = svgNodeSource.getAttribute("dragx"); - var coordenateSourceY = svgNodeSource.getAttribute("dragy"); - var coordenateTargetX = svgNodeTarget.getAttribute("dragx"); - var coordenateTargetY = svgNodeTarget.getAttribute("dragy"); - var point = this._calculateEdgePointerPosition(coordenateSourceX, coordenateSourceY, coordenateTargetX, coordenateTargetY, offset); - coordenateTargetX = point.x; - coordenateTargetY = point.y; - var angle = Geometry.toDegree(point.angle) + 90; - // this.arrowDefaultSize = this.formatter.getEdgeById(edgeId).getArrowSize(); //getDefault().getArrowSize(); - var attributes = []; - if (this.getFormatter().getEdgeById(edgeId) instanceof OdotDirectedLineEdgeGraphFormatter) { - attributes = [ - [ "fill", "#FFFFFF" ], [ "stroke", this.getFormatter().getEdgeById(edgeId).getDefault().getStroke() ], - [ "id", this.getSVGArrowEdgeId(edgeId) ] ]; - } else { - attributes = [ - [ "fill", this.getFormatter().getEdgeById(edgeId).getDefault().getStroke() ], - [ "stroke", this.getFormatter().getEdgeById(edgeId).getDefault().getStroke() ], [ "id", this.getSVGArrowEdgeId(edgeId) ] ]; - } - var flechaSVGNode = SVG.drawCircle(0, 0, 4, this.GraphEdgeGroup, attributes); - flechaSVGNode.setAttribute("transform", " translate(" + coordenateTargetX + ", " + coordenateTargetY + "), rotate(" + angle + ")"); - } - ; - - var _this = this; - /** Events for the SVG edge **/ - if (svgEdge != null) { - if (this.getDataset().getEdgesCount() < this.args.maxNumberEdgesFiringEvents) { - svgEdge.addEventListener("mouseover", function() { - _this.overEdge(edgeId); - }, false); - svgEdge.addEventListener("mouseout", function() { - _this.outEdge(edgeId); - }, false); - } - } -}; - -GraphCanvas.prototype._calculateEdgePointerPosition = function(sourceX, sourceY, targetX, targetY, radius) { - var angle = Geometry.getAngleBetweenTwoPoints(sourceX, sourceY, targetX, targetY); - - /** Suponiendo el node source que este a la derecha **/ - if ((targetX - sourceX) < 0) { - var b = Geometry.getAdjacentSideOfRectangleRight(angle, radius); - targetX = parseFloat(targetX) + parseFloat(b); - arrowX = parseFloat(targetX) + parseFloat(b) + this.arrowDefaultSize / 2; - } else { - var b = Geometry.getAdjacentSideOfRectangleRight(angle, radius); - targetX = parseFloat(targetX) - parseFloat(b); - arrowX = parseFloat(targetX) - parseFloat(b) - this.arrowDefaultSize / 2; - } - - /** Suponiendo el node source que este a la arriba **/ - if ((targetY - sourceY) > 0) { - var a = Geometry.getOppositeSideOfRectangleRight(angle, radius); - targetY = parseFloat(targetY) - parseFloat(a); - arrowY = parseFloat(targetY) - parseFloat(a) - this.arrowDefaultSize / 2; - } else { - var a = Geometry.getOppositeSideOfRectangleRight(angle, radius); - targetY = parseFloat(targetY) + parseFloat(a); - arrowY = parseFloat(targetY) + parseFloat(a) - this.arrowDefaultSize / 2; - - } - - return { - "x" : arrowX, - "y" : arrowY, - "angle" : angle - }; -}; - -GraphCanvas.prototype.calculateCoordenatesBezier = function(nodeSize, x1, y1) { - var x11 = x1 - (nodeSize / 2); - var y11 = y1 - (nodeSize / 2); - - var x12 = parseFloat(x1) + parseFloat(nodeSize / 2); - var y12 = y1 - (nodeSize / 2); - - var curvePointX = (x12 - x11) / 2 + x11; - var curvePointY = y1 - (nodeSize * 2); - var d = "M" + x11 + "," + y11 + " T" + curvePointX + "," + curvePointY + " " + x12 + "," + y12; - return d; - -}; - -GraphCanvas.prototype.renderEdges = function() { - for ( var edge in this.getDataset().getEdges()) { - this.renderEdge(this.getDataset().getEdgeById(edge).getId()); - - } -}; - -GraphCanvas.prototype.getLastSelectedNode = function() { - var node = null; - if (this.getSelectedVertices().length > 0) { - var nodeId = this.getSelectedVertices()[this.getSelectedVertices().length - 1]; - node = this.getDataset().getVertexById(nodeId); - } - return node; -}; -/* - GraphCanvas.prototype.getNodeByNameAndIndex = function(node, index){ - var nodeId = this.getDataset().verticesIndex[node][index]; - var nodeItem = this.getDataset().getVertexById(nodeId); - return nodeItem; - }; - */ - -GraphCanvas.prototype.setDataset = function(dataset) { - this.dataset = dataset; -}; - -GraphCanvas.prototype.setFormatter = function(formatter) { - this.formatter = formatter; -}; - -GraphCanvas.prototype.setLayout = function(layout) { - this.layout = layout; -}; - -/** API **/ -GraphCanvas.prototype.getDataset = function() { - return this.dataset; -}; - -GraphCanvas.prototype.getFormatter = function() { - return this.formatter; -}; - -GraphCanvas.prototype.getLayout = function() { - return this.layout; -}; - -/** API DATASET **/ -GraphCanvas.prototype.addVertex = function(name, args) { - this.getDataset().addNode(name, args); -}; - -GraphCanvas.prototype.removeVertex = function(vertexId) { - this.getDataset().getVertexById(vertexId).remove(); -}; - -GraphCanvas.prototype.addEdge = function(edgeName, nodeSourceId, nodeTargetId, args) { - this.getDataset().addEdge(edgeName, nodeSourceId, nodeTargetId, args); -}; -/* - GraphCanvas.prototype.removeEdge = function(edgeId){ - this.getDataset().getEdgeById(edgeId).remove(); - }; - */ - -/** API FORMATTER **/ -GraphCanvas.prototype.getWidth = function() { - return this.getFormatter().getWidth(); -}; - -GraphCanvas.prototype.getHeight = function() { - return this.getFormatter().getHeight(); -}; - -GraphCanvas.prototype.getBackgroundImage = function() { - return this.getFormatter().getBackgroundImage(); -}; - -//GraphCanvas.prototype.setBackgroundImage = function(value){ -// this.getFormatter().setBackgroundImage(value); -//}; - -GraphCanvas.prototype.getBackgroundColor = function() { - return this.getFormatter().getBackgroundColor(); -}; - -GraphCanvas.prototype.setBackgroundColor = function() { - this.getFormatter().setBackgroundColor(value); -}; - -//GraphCanvas.prototype.setEdgeFill = function(edgeId, value){ -// this.getFormatter().getEdgeById(edgeId).getDefault().setFill(value); -//}; -// -//GraphCanvas.prototype.getEdgeFill = function(edgeId){ -// return this.getFormatter().getEdgeById(edgeId).getDefault().getFill(); -//}; - -/** VERTICES FORMATTER **/ -GraphCanvas.prototype.setVertexSize = function(vertexId, value) { - this.getFormatter().getVertexById(vertexId).getDefault().setSize(value); -}; - -GraphCanvas.prototype.getVertexSize = function(vertexId) { - return this.getFormatter().getVertexById(vertexId).getDefault().getSize(); -}; - -GraphCanvas.prototype.setVertexStroke = function(vertexId, value) { - this.getFormatter().getVertexById(vertexId).getDefault().setStroke(value); -}; - -GraphCanvas.prototype.getVertexStroke = function(vertexId) { - return this.getFormatter().getVertexById(vertexId).getDefault().getStroke(); -}; - -GraphCanvas.prototype.setVertexStrokeOpacity = function(vertexId, value) { - this.getFormatter().getVertexById(vertexId).getDefault().setStrokeOpacity(value); -}; - -GraphCanvas.prototype.getVertexStrokeOpacity = function(vertexId) { - return this.getFormatter().getVertexById(vertexId).getDefault().getStrokeOpacity(); -}; - -GraphCanvas.prototype.setVertexOpacity = function(vertexId, value) { - this.getFormatter().getVertexById(vertexId).getDefault().setOpacity(value); -}; - -GraphCanvas.prototype.getVertexOpacity = function(vertexId) { - return this.getFormatter().getVertexById(vertexId).getDefault().getOpacity(); -}; - -GraphCanvas.prototype.setVertexFill = function(vertexId, color) { - this.getFormatter().getVertexById(vertexId).getDefault().setFill(color); -}; - -GraphCanvas.prototype.getVertexFill = function(vertexId) { - return this.getFormatter().getVertexById(vertexId).getDefault().getFill(); -}; - -/** EDGES FORMATTER **/ -GraphCanvas.prototype.setEdgeSize = function(edgeId, value) { - this.getFormatter().getEdgeById(edgeId).getDefault().setSize(value); -}; - -GraphCanvas.prototype.getEdgeSize = function(edgeId) { - return this.getFormatter().getEdgeById(edgeId).getDefault().getSize(); -}; - -GraphCanvas.prototype.setEdgeStroke = function(edgeId, value) { - this.getFormatter().getEdgeById(edgeId).getDefault().setStroke(value); -}; - -GraphCanvas.prototype.getEdgeStroke = function(edgeId) { - return this.getFormatter().getEdgeById(edgeId).getDefault().getStroke(); -}; - -GraphCanvas.prototype.setEdgeStrokeOpacity = function(edgeId, value) { - this.getFormatter().getEdgeById(edgeId).getDefault().setStrokeOpacity(value); -}; - -GraphCanvas.prototype.getEdgeStrokeOpacity = function(edgeId) { - return this.getFormatter().getEdgeById(edgeId).getDefault().getStrokeOpacity(); -}; - -GraphCanvas.prototype.setEdgeFill = function(edgeId, color) { - this.getFormatter().getEdgeById(edgeId).getDefault().setFill(color); -}; - -GraphCanvas.prototype.getEdgeFill = function(edgeId) { - return this.getFormatter().getEdgeById(edgeId).getDefault().getFill(); -}; - -/** API LAYOUT **/ -GraphCanvas.prototype.setCoordinates = function(vertexId, x, y) { - return this.getLayout().getEdgeById(vertexId).setCoordinates(x, y); -}; - - - -function HPLCGraph(args) { - this.width = 600; - this.height = 600; - this.title = ''; - this.bbar = false; - this.plotInnerPanelPadding = 10; - this.plotPanelPadding = 5; - this.id = BUI.id(); - - this.hidePlots = null; - this.xlabel = ""; - this.scaled = false; - this.xParam = null; - this.showRangeSelector = true; - this.interactionModel = null; - - /** for each stat the max and minimum value when it is scaled in order to show correctly in the legend **/ - this.ranges = {}; - if (args != null) { - if (args.interactionModel != null) { - this.interactionModel = args.interactionModel; - } - if (args.width != null) { - this.width = args.width; - } - if (args.height != null) { - this.height = args.height; - } - if (args.bbar != null) { - this.bbar = args.bbar; - } - if (args.title != null) { - this.title = args.title; - } - if (args.plots != null) { - this.plots = args.plots; - } - - if (args.scaled != null) { - this.scaled = args.scaled; - } - if (args.xlabel != null) { - this.xlabel = args.xlabel; - } - if (args.xParam != null) { - this.xParam = args.xParam; - } - if (args.showRangeSelector != null) { - this.showRangeSelector = args.showRangeSelector; - } - } - - this.onZoomX = new Event(this); - this.onResetZoom = new Event(this); - this.dblclick = new Event(this); -} - -HPLCGraph.prototype.getMenu = function () { - var _this = this; - /** Actions buttons **/ - var actions = []; - - function toggle(item, pressed) { - if (pressed) { - _this.plots[item.param] = true; - } else { - delete _this.plots[item.param]; - } - _this.reloadData(this.hplcData); - } - - for (var i = 0; i < this.hplcData.length; i++) { - if (this.hplcData[i].showOnMenu != false) { - var param = this.hplcData[i].param; - var style = "style='padding:0 0px 0 5px;'"; - actions.push({ - text : "
" + BUI.getRectangleColorDIV(this.hplcData[i].color, 10, 10) + " " + this.hplcData[i].label + "
", - id : _this.id + param, - param : param, - enableToggle : true, - scope : this, - toggleHandler : toggle, - pressed : (_this.plots[param] != null) - }); - } - } - actions.push("-"); - - actions.push({ - text : "Scale", - enableToggle : true, - scope : this, - pressed : this.scaled, - icon : '../images/icon_graph.png', - toggleHandler : function (item, pressed) { - _this.scaled = pressed; - _this.reloadData(this.hplcData); - } - }); - - actions.push("->"); - actions.push({ - text : "Save", - scope : this, - icon : '../images/save.gif', - handler : function (item, pressed) { - var largeImage = document.createElement("img"); - largeImage.style.display = 'block'; - largeImage.style.width = 200 + "px"; - largeImage.style.height = 200 + "px"; - largeImage.setAttribute('src', Dygraph.Export.asCanvas(this.dygraphObject.dygraph).toDataURL()); - window.open(Dygraph.Export.asCanvas(this.dygraphObject.dygraph).toDataURL(), 'Image', ''); - } - }); - - return actions; -}; - -/** Looks for the maximum value and then divide everything but that value **/ -HPLCGraph.prototype.scaledData = function (data) { - for (var i = 0; i < data.length; i++) { - var values = this.getMaxAndMinValue(data[i]); - data[i] = this.divideValuesByMax(data[i], values.max); - this.ranges[data[i].label] = values; - } - return data; -}; - - -/** Given a stat float[] and a max number it will divide each value by max **/ -HPLCGraph.prototype.divideValuesByMax = function (stat, max) { - for (var j = 0; j < stat.data.length; j++) { - if (max != 0) { - stat.data[j] = Number(stat.data[j]) / max; - stat.std[j] = Number(stat.std[j]) / max; - } - } - return stat; -}; - -/** returns max value of a stat **/ -HPLCGraph.prototype.getMaxAndMinValue = function (stat) { - var max = 0; - var min = stat.data[0]; - for (var j = 0; j < stat.data.length; j++) { - if (Number(stat.data[j]) > max) { - max = Number(stat.data[j]); - } - if (Number(stat.std[j]) > max) { - max = Number(stat.std[j]); - } - if (Number(stat.data[j]) < min) { - min = Number(stat.data[j]); - } - } - return { - max : Number(max), - min : Number(min) - }; -}; - -HPLCGraph.prototype.getPoint = function (data, i) { - var point = [ 10, 10, 10 ]; - var y = parseFloat(data.data[i]); - var error = parseFloat(data.std[i]); - if (data.fdata == null) { - return [ y - error, y, y + error ]; - } else { - if (data.fstd != null) { - return [ data.fstd(y - error), data.fdata(y), data.fstd(y + error) ]; - } - return [ data.fdata(y) - error, data.fdata(y), data.fdata(y) + error ]; - } - return point; -}; - -HPLCGraph.prototype.reloadData = function(hplcData) { - this.panel.setLoading(false); - this.hplcData = hplcData; - - var data = hplcData; - - - /** In case of having peaks **/ - if (this.peaks != null) { - for (var peak in this.peaks) { - var values = []; - var std = []; - for (var i = 0; i < this.peaks[peak].length; i++) { - values.push(this.peaks[peak][i][1]); - std.push(this.peaks[peak][i][2]); - } - data.push({ - param : peak, - data : values, - showOnMenu : false, - fdata : function (a) { - var value = (Math.log(parseFloat(a))); - if (isNumber(value)) { - return value; - } - }, - fstd : function (a) { - var value = Math.log(Math.abs(parseFloat(a))); - if (isNumber(value)) - return value; - }, - std : std, - color : this.colorPeak[peak], - label : peak - }); - - } - } - - - if (this.scaled) { - data = this.scaledData(JSON.parse(JSON.stringify(hplcData))); - } - - var paramIndex = {}; - var parsed = []; - var j = 0; - for (var i = 0; i < data[0].data.length - 1; i++) { - var aux = []; - for (j = 0; j < data.length; j++) { - if (this.plots != null) { - if (this.plots[data[j].param] != null) { - aux.push(this.getPoint(data[j], i)); - paramIndex[data[j].param] = aux.length - 1; - } - } else { - aux.push([ data[j].data[i] - data[j].std[i], data[j].data[i], data[j].data[i] + data[j].std[i] ]); - } - } - parsed.push([]); - - var index = i; - if (this.xParam != null) { - index = parseFloat(data[this.xParam].data[i]); - } - - parsed[parsed.length - 1].push(index); - - for (j = 0; j < data.length; j++) { - if (this.plots != null) { - if (this.plots[data[j].param] != null) { - parsed[parsed.length - 1].push(aux[paramIndex[data[j].param]]); - } - } else { - parsed[parsed.length - 1].push(aux[j]); - } - } - } - - var colors = []; - var labels = [ "" ]; - for (j = 0; j < data.length; j++) { - if (this.plots != null) { - if (this.plots[data[j].param] != null) { - colors.push(data[j].color); - labels.push(data[j].label); - } - } else { - parsed[parsed.length - 1].push(aux[j]); - } - } - - this._renderDygraph(parsed, colors, labels); -}; - -HPLCGraph.prototype._renderDygraph = function (parsed, colors, labels) { - this.dygraphObject = new StdDevDyGraph(this.id, { - width : this.width, - height : this.height - 10, - xlabel : this.xlabel, - showRangeSelector : this.showRangeSelector, - interactionModel : this.interactionModel, - scaled : this.scaled, - ranges : this.ranges - }); - this.dygraphObject.draw(parsed, colors, labels); - - var _this = this; - this.dygraphObject.onZoomX.attach(function (sender, args) { - try { - _this.onZoomX.notify(args); - } catch (e) { - } - }); - - this.dygraphObject.onResetZoom.attach(function (sender, args) { - try { - _this.onResetZoom.notify(args); - } catch (e) { - } - }); - - this.dygraphObject.dblclick.attach(function (sender, args) { - try { - _this.dblclick.notify(args); - } catch (e) { - } - }); - -}; - -HPLCGraph.prototype.loadData = function (data) { - var _this = this; - this.reloadData(data); - this.panel.addDocked({ - xtype : 'toolbar', - items : this.getMenu() - }); - - - if (this.bbar == true){ - this.panel.addDocked({ - xtype : 'toolbar', - dock: 'bottom', - items : [ - { - xtype: 'numberfield', - id: 'main_field_start', - fieldLabel: 'Range from', - width: 170, - labelWidth : 70, - value: 0, - minValue: 0 - }, - { - xtype: 'numberfield', - id: 'main_field_end', - fieldLabel: 'to', - width: 130, - labelWidth : 30, - value: 0, - minValue: 0 - }, - { - xtype: 'button', - text: 'Go', - handler: function () { - var start = parseFloat(Ext.getCmp("main_field_start").getValue()); - var end = parseFloat(Ext.getCmp("main_field_end").getValue()); - - if (start < 0) { - start = 0; - } - if (end < 0) { - end = 0; - } - if (start > end) { - var aux = end; - end = start; - start = aux; - } - - _this.dygraphObject.dygraph.updateOptions({ isZoomedIgnoreProgrammaticZoom: true, dateWindow: [start, end] }); - } - } - ] - }); - } -}; - -HPLCGraph.prototype.getPanel = function () { - this.panel = Ext.create('Ext.panel.Panel', { - padding : this.plotPanelPadding, - width : this.width + 4 * this.plotInnerPanelPadding, - height : this.height + 4 * this.plotInnerPanelPadding, - items : [ { - html : "", - id : this.id, - width : this.width, - height : this.height - } ] - }); - - return this.panel; -}; - -HPLCGraph.prototype.input = function () { - return DATADOC.getHPLCData(); -}; - -HPLCGraph.prototype.getDataByFrameNumber = function (frameNumber) { - var data = {}; - data.frameNumber = frameNumber; - for (var key in this.hplcData){ - data[this.hplcData[key].label] = this.hplcData[key].data[frameNumber]; - } - console.log(data); - return data; -}; - - -HPLCGraph.prototype.test = function (targetId) { - var mainPlotPanel = new HPLCGraph({ - title : 'I0', - width : 800, - height : 400, - plots : { - "I0" : true, - "Rg" : true, - "Mass" : true - }, - xlabel : "HPLC Frames", - scaled : this.scaled, - interactionModel : { - 'dblclick' : function (event, g, context) { - } - } - }); - mainPlotPanel.getPanel().render(targetId); - mainPlotPanel.loadData(mainPlotPanel.input()); - -}; - - -function MergesHPLCGraph(args) { - HPLCGraph.prototype.constructor.call(this, args); - -// this.peakColors = ["#00FB42", "#00BA31", "#007C21", "#003E10"]; - this.peakColors = ["#DEBD00", "#6D9100", "#872900", "#0092CC"]; -} - - -MergesHPLCGraph.prototype.scaledData = HPLCGraph.prototype.scaledData; -MergesHPLCGraph.prototype.divideValuesByMax = HPLCGraph.prototype.divideValuesByMax; -MergesHPLCGraph.prototype.getMaxAndMinValue = HPLCGraph.prototype.getMaxAndMinValue; -MergesHPLCGraph.prototype.getPoint = HPLCGraph.prototype.getPoint; -MergesHPLCGraph.prototype.reloadData = HPLCGraph.prototype.reloadData; -MergesHPLCGraph.prototype._renderDygraph = HPLCGraph.prototype._renderDygraph; -MergesHPLCGraph.prototype.loadData = HPLCGraph.prototype.loadData; -MergesHPLCGraph.prototype.getPanel = HPLCGraph.prototype.getPanel; -MergesHPLCGraph.prototype.getDataByFrameNumber = HPLCGraph.prototype.getDataByFrameNumber; - - -MergesHPLCGraph.prototype.setPeaks = function (data) { - this.peaks = data; - /** get size of peaks **/ - this.peakKeys = []; - this.colorPeak = {}; - var colorCount = 1; - for (var key in this.peaks) { - if (this.peaks.hasOwnProperty(key)) { - var color = this.peakColors[colorCount % this.peakColors.length]; - colorCount = colorCount + 1; - this.peakKeys.push(key); - this.colorPeak[key] = color; - } - } - this.peakKeys.sort(); -}; - - -MergesHPLCGraph.prototype.getMenu = function () { - var _this = this; - /** Actions buttons **/ - var actions = []; - - function toggle(item, pressed) { - if (pressed) { - _this.plots[item.param] = true; - } else { - delete _this.plots[item.param]; - } - _this.reloadData(_this.hplcData); - } - - - /** Toolbar for peaks Average **/ - if (this.peaks != null) { - var items = []; - for (var i = 0; i < this.peakKeys.length; i++) { - var color = this.colorPeak[this.peakKeys[i]]; - items.push({ - text: "Peak #" + i + " " + this.peakKeys[i].replace("- ", " to #").replace(".0", "").replace(".0", "") + "", - peakid : this.peakKeys[i], - checked: false, - checkHandler: function (sender, pressed) { - var item = new Object(); - item.param = sender.peakid; - toggle(item, pressed); - } - }); - } - - var menu = Ext.create('Ext.menu.Menu', { - id: 'mainMenu', - style: { - overflow: 'visible' - }, - items: items - }); - var tb = Ext.create('Ext.toolbar.Toolbar'); - tb.add({ - text: 'Peaks Avg.', - menu: menu - }); - actions.push(tb); - } - - - - for (var i = 0; i < this.hplcData.length; i++) { - if (this.hplcData[i].showOnMenu != false) { - var param = this.hplcData[i].param; - var style = "style='padding:0 0px 0 5px;'"; - actions.push({ - text : "
" + BUI.getRectangleColorDIV(this.hplcData[i].color, 10, 10) + " " + this.hplcData[i].label + "
", - id : _this.id + param, - param : param, - enableToggle : true, - scope : this, - margin : 5, - toggleHandler : toggle, - pressed : (_this.plots[param] != null) - }); - } - } - - actions.push("->"); - actions.push({ - text : "Save", - scope : this, - icon : '../images/save.gif', - handler : function (item, pressed) { - var largeImage = document.createElement("img"); - largeImage.style.display = 'block'; - largeImage.style.width = 200 + "px"; - largeImage.style.height = 200 + "px"; - largeImage.setAttribute('src', Dygraph.Export.asCanvas(this.dygraphObject.dygraph).toDataURL()); - window.open(Dygraph.Export.asCanvas(this.dygraphObject.dygraph).toDataURL(), 'Image', ''); - } - }); - - return actions; -}; - - -MergesHPLCGraph.prototype.input = function () { - return DATADOC.getScatteringHPLCFrameData(); -}; - -MergesHPLCGraph.prototype.test = function (targetId) { - var mainPlotPanel = new MergesHPLCGraph({ - title : 'Scattering', - width : this.plotWidth, - height : 500, - showRangeSelector : false, - xParam : 0, - xlabel : "scattering_I", - plots : { - "scattering_I" : true, - "subtracted_I" : true, - "buffer_I" : true - } - }); - mainPlotPanel.getPanel().render(targetId); - mainPlotPanel.loadData(mainPlotPanel.input()); -}; - - -function NetworkWidget(args) { - this.id = "NetworkViewer_" + Math.random().toString().replace(".", "_"); - - this.label = true; - if (args != null) { - if (args.targetId != null) { - this.targetId = args.targetId; - } - if (args.label != null) { - this.label = args.label; - } - } - - this.onVertexOver = new Event(this); - this.onVertexOut = new Event(this); -} - -NetworkWidget.prototype.draw = function(dataset, formatter, layout) { - - this.graphCanvas = new GraphCanvas(this.id, document.getElementById(this.targetId), { - "labeled" : this.label, - "multipleSelectionEnabled" : false, - "draggingCanvasEnabled" : false - }); - this.graphCanvas.draw(dataset, formatter, layout); - - var _this = this; - this.graphCanvas.onVertexOver.attach(function(sender, nodeId) { - _this.onVertexOver.notify(nodeId); - }); - - this.graphCanvas.onVertexOut.attach(function(sender, nodeId) { - _this.onVertexOut.notify(nodeId); - }); -}; - -/** SELECT VERTICES BY NAME * */ -NetworkWidget.prototype.selectVertexByName = function(vertexName) { - var vertices = this.getDataset().getVertexByName(vertexName); - if (vertices != null) { - for ( var nodeId in vertices) { - if (vertices.hasOwnProperty(nodeId)) { - var vertexId = vertices[nodeId].getId(); - this.selectVertexById(vertexId); - } - } - } -}; - -NetworkWidget.prototype.selectVerticesByName = function(verticesName) { - for ( var i = 0; i < verticesName.length; i++) { - this.selectVertexByName(verticesName[i]); - } -}; - -/** SELECT VERTICES BY ID * */ -NetworkWidget.prototype.selectVertexById = function(vertexId) { - this.graphCanvas.selectNode(vertexId); - this.blinkVertexById(vertexId); -}; - -NetworkWidget.prototype.selectVerticesById = function(verticesId) { - for ( var i = 0; i < verticesId.length; i++) { - this.selectVertexById(verticesId[i]); - } -}; - -/** VECINDARIO * */ -NetworkWidget.prototype.selectNeighbourhood = function() { - this.selectEdgesFromVertices(); - this.selectAdjacent(); -}; - -/** DESELECT * */ -NetworkWidget.prototype.deselectNodes = function() { - this.graphCanvas.deselectNodes(); -}; - -/** SELECT ALL NODES * */ -NetworkWidget.prototype.selectAllNodes = function() { - this.getGraphCanvas().selectAllNodes(); -}; - -/** SELECT EVERYTHING * */ -NetworkWidget.prototype.selectAll = function() { - this.getGraphCanvas().selectAll(); -}; - -/** SELECT ALL EDGES * */ -NetworkWidget.prototype.selectAllEdges = function() { - this.getGraphCanvas().selectAllEdges(); -}; - -/** ZOOM * */ -NetworkWidget.prototype.setScale = function(value) { - this.graphCanvas.setScale(value); -}; - -NetworkWidget.prototype.getScale = function() { - return this.graphCanvas.getScale(value); -}; - -/** SELECT ADJACENT VERTICES FROM SELECTED VERTICES * */ -NetworkWidget.prototype.selectAdjacent = function() { - var selectedVertices = this.getGraphCanvas().getSelectedVertices(); - var edges = []; - for ( var i = 0; i < selectedVertices.length; i++) { - edges = edges.concat(this.getGraphCanvas().getDataset().getVertexById(selectedVertices[i]).getEdges()); - } - var vertices = []; - for ( i = 0; i < edges.length; i++) { - vertices.push(edges[i].getNodeSource().getId()); - vertices.push(edges[i].getNodeTarget().getId()); - } - - this.selectVerticesById(vertices); -}; - -/** SELECT EDGES FROM SELECTED VERTICES * */ -NetworkWidget.prototype.selectEdgesFromVertices = function() { - var selectedVertices = this.getGraphCanvas().getSelectedVertices(); - var edges = []; - for ( var i = 0; i < selectedVertices.length; i++) { - edges = edges.concat(this.getGraphCanvas().getDataset().getVertexById(selectedVertices[i]).getEdges()); - } - var edgesId = []; - for ( i = 0; i < edges.length; i++) { - edgesId.push(edges[i].getId()); - } - this.getGraphCanvas().selectEdges(edgesId); -}; - -/** BLINKING * */ -NetworkWidget.prototype.blinkVertexById = function(vertexId) { - this.graphCanvas.blinkVertexById(vertexId); -}; - -/** COMPONENTE CONEXA DE LOS NODOS SELECCIONADOS * */ -NetworkWidget.prototype.selectConnectedComponent = function() { - var elements = this.getConnectedComponent(); - this.selectVerticesById(elements.nodes); - this.graphCanvas.selectEdges(elements.edges); -}; - -NetworkWidget.prototype.getConnectedComponent = function() { - var nodosVisitados = {}; - var aristasVisitadas = {}; - var arrNodos = []; - var arrAristas = []; - var selectedGraphNodesId = this.getGraphCanvas().getSelectedVertices(); - for ( var i = 0; i < selectedGraphNodesId.length; i++) { - this.visitNode(selectedGraphNodesId[i], nodosVisitados, aristasVisitadas, arrNodos, arrAristas); - } - return { - nodes : arrNodos, - edges : arrAristas - }; -}; - -NetworkWidget.prototype.visitNode = function(nodeId, nodosVisitados, aristasVisitadas, arrNodos, arrAristas) { - nodosVisitados[nodeId] = true; - arrNodos.push(nodeId); - var nodeEdges = this.getDataset().getVertexById(nodeId).getEdges(); - for ( var j = 0; j < nodeEdges.length; j++) { - var edge = nodeEdges[j]; - var edgeId = edge.getId(); - if (aristasVisitadas[edgeId] == null) { - aristasVisitadas[edgeId] = true; - arrAristas.push(edgeId); - var nodeTargetId = edge.getNodeTarget().getId(); - if (nodosVisitados[nodeTargetId] == null) { - this.visitNode(nodeTargetId, nodosVisitados, aristasVisitadas, arrNodos, arrAristas); - } - var nodeSourceId = edge.getNodeSource().getId(); - if (nodosVisitados[nodeSourceId] == null) { - this.visitNode(nodeSourceId, nodosVisitados, aristasVisitadas, arrNodos, arrAristas); - } - } - } -}; - -/** COLLAPSE SELECTED VERTICES * */ -NetworkWidget.prototype.collapse = function() { - var selectedVertices = this.getGraphCanvas().getSelectedVertices(); - var xMin = -Infinity; - var xMax = Infinity; - var yMin = -Infinity; - var yMax = Infinity; - - for ( var i = 0; i < selectedVertices.length; i++) { - var vertex = this.getGraphCanvas().getLayout().getNodeById(selectedVertices[i]); - if (xMin < vertex.x) { - xMin = vertex.x; - } - if (xMax > vertex.x) { - xMax = vertex.x; - } - if (yMin < vertex.y) { - yMin = vertex.y; - } - if (yMax > vertex.y) { - yMax = vertex.y; - } - } - - var centerX = xMin - xMax; - var centerY = yMin - yMax; - var radius = (xMax - xMin) / 4; - - var count = selectedVertices.length; - var verticesCoordinates = []; - - for ( i = 0; i < selectedVertices.length; i++) { - x = centerX + radius * Math.sin(i * 2 * Math.PI / count); - y = centerY + radius * Math.cos(i * 2 * Math.PI / count); - verticesCoordinates.push({ - 'x' : x, - 'y' : y - }); - } - - for ( i = 0; i < selectedVertices.length; i++) { - this.getGraphCanvas().getLayout().getNodeById(selectedVertices[i]).setCoordinates(verticesCoordinates[i].x, verticesCoordinates[i].y); - } -}; - -/** SETTER FONT SIZE * */ -NetworkWidget.prototype.setVerticesFontSize = function(value) { - for ( var nodeId in this.getDataset().getVertices()) { - if (this.getDataset().getVertices().hasOwnProperty(nodeId)) { - this.getFormatter().getVertexById(nodeId).getDefault().setFontSize(value); - } - } -}; - -/** GETTERS * */ -NetworkWidget.prototype.getFormatter = function() { - return this.getGraphCanvas().getFormatter(); -}; -NetworkWidget.prototype.getLayout = function() { - return this.getGraphCanvas().getLayout(); -}; - -NetworkWidget.prototype.getDataset = function() { - return this.getGraphCanvas().getDataset(); -}; - -NetworkWidget.prototype.getGraphCanvas = function() { - return this.graphCanvas; -}; - -RangeWhiskerGraph.prototype.cleanArray = GenericGraph.prototype.cleanArray; -RangeWhiskerGraph.prototype.plotAxes = GenericGraph.prototype.plotAxes; -RangeWhiskerGraph.prototype.plotRuler = GenericGraph.prototype.plotRuler; -RangeWhiskerGraph.prototype.drawSVGVerticalText = GenericGraph.prototype.drawSVGVerticalText; -RangeWhiskerGraph.prototype.getClassColor = GenericGraph.prototype.getClassColor; -RangeWhiskerGraph.prototype.getDimensions = GenericGraph.prototype.getDimensions; -RangeWhiskerGraph.prototype.calculate = GenericGraph.prototype.calculate; -RangeWhiskerGraph.prototype.getMedian = GenericGraph.prototype.getMedian; -RangeWhiskerGraph.prototype.pointToPixel = GenericGraph.prototype.pointToPixel; -RangeWhiskerGraph.prototype.maxValueWhisker = GenericGraph.prototype.maxValueWhisker; -RangeWhiskerGraph.prototype.refresh = GenericGraph.prototype.refresh; - -/** - * Subclass of GenericGraph - * - * @plotHorizontalByCluster - */ -function RangeWhiskerGraph(args) { - this.maxBoxWidth = 25; - - if (args == null) { - args = {}; - } - args.plotHorizontalByCluster = false; - - GenericGraph.call(this, args); - - if (args.maxBoxWidth != null) { - this.maxBoxWidth = args.maxBoxWidth; - } -} - -RangeWhiskerGraph.prototype.refresh = function(data) { - document.getElementById(this.targetId).innerHTML = ""; - this.draw(this.targetId, data); -}; - -RangeWhiskerGraph.prototype.isNumber = function(value) { - if (value == "") - return false; - - var d = parseInt(value); - if (!isNaN(d)) - return true; - else - return false; - -}; - -/** - There are several different methods for calculating quartiles.[1] This calculator uses a method described by Moore and McCabe to find quartile values. - The same method also used by The TI-83 to calculate quartile values. - With this method, the first quartile is the median of the numbers below the median, the third quartile is the median of the numbers above the median. - - http://www.miniwebtool.com/quartile-calculator/ - http://www.alcula.com/calculators/statistics/box-plot/ - - **/ -RangeWhiskerGraph.prototype.getQ1 = function(array) { - array = array.slice(0, array.length / 2); - return this.getMedian(array); -}; - -RangeWhiskerGraph.prototype.getQ3 = function(array) { - array = array.slice((array.length + 1) / 2); - return this.getMedian(array); - -}; - -RangeWhiskerGraph.prototype.getQ2 = function(array) { - return this.getMedian(array); -}; - -RangeWhiskerGraph.prototype.getBelowOutliers = function(belowLimit, array) { - var points = []; - for ( var i = 0; i < array.length; i++) { - if (array[i] <= belowLimit) { - points.push(array[i]); - } - } - return points; -}; - -RangeWhiskerGraph.prototype.getAboveOutliers = function(aboveLimit, array) { - var points = []; - for ( var i = 0; i < array.length; i++) { - if (array[i] >= aboveLimit) { - points.push(array[i]); - } - } - return points; -}; - -RangeWhiskerGraph.prototype.minValueWhisker = function(belowLimit, q1, array) { - var points = []; - - for ( var i = 0; i < array.length; i++) { - if ((array[i] < q1) && (array[i]) > belowLimit) { - points.push(array[i]); - } - } - - if (points.length > 0) { - points.sort(function(a, b) { - return a - b; - }); - return points[0]; - } - return null; -}; - -//RangeWhiskerGraph.prototype.maxValueWhisker = function(aboveLimit, q3, array){ -// var points = []; -// for (var i = 0; i < array.length; i++){ -// if ((array[i] > q3) && (array[i]) <= aboveLimit){ -// points.push(array[i]); -// } -// } -// if (points.length > 0){ -// points.sort(function(a, b){return a - b;}); -// return points[points.length - 1]; -// } -// return null; -//}; - -RangeWhiskerGraph.prototype.drawPoints = function(boxProperties) { - if (this.plotPoints) { - for ( var i = 0; i < boxProperties.values.length; i++) { - var value = boxProperties.values[i]; - var toPixel = this.pointToPixel(value, boxProperties); - SVG.drawCircle(boxProperties.x, toPixel, this.pointRadius, this.svg, - [ - [ "fill", "green" ], [ "fill-opacity", this.fillOpacityPoint ], [ 'stroke-opacity', this.strokeOpacityPoint ], - [ "stroke", "black" ] ]); - } - } -}; - -RangeWhiskerGraph.prototype.plotRangeQ1Q3 = function(data, properties) { - var posX = this.left + this.rulerWidth; - - var boxBordersPointsQ1 = []; - var boxBordersPointsQ3 = []; - var Q2 = []; - - for ( var i = 0; i < data.clusters.length; i++) { - var cluster = data.clusters[i]; - /** inter cluster space **/ - posX = posX + this.interClustersSpace; - - for ( var j = 0; j < cluster.classes.length; j++) { - var ratio = properties.width / (properties.xValues.range); - var coorX = (cluster.x - properties.xValues.min) * ratio + this.rulerWidth; - var boxProperties = { - name : cluster.classes[j].name, - values : cluster.classes[j].values, - minPoint : properties.minPoint, - maxPoint : properties.maxPoint, - x : coorX + this.left, - y : this.top, - width : properties.classWidth, - height : this.height - this.top - this.bottom - this.clusterTitleHeight - this.rulerHeight - }; - - var result = this.calculate(boxProperties.values); - var boxColor = this.getClassColor(boxProperties.name); - - if (this.isNumber(result.Q1) && this.isNumber(result.Q3)) { - var x = boxProperties.x; - var y = this.pointToPixel(result.Q1, boxProperties); - if (boxBordersPointsQ1[cluster.classes[j].name] == null) { - boxBordersPointsQ1[cluster.classes[j].name] = []; - } - boxBordersPointsQ1[cluster.classes[j].name].push({ - x : x, - y : y - }); - - x = boxProperties.x; - y = this.pointToPixel(result.Q3, boxProperties); - if (boxBordersPointsQ3[cluster.classes[j].name] == null) { - boxBordersPointsQ3[cluster.classes[j].name] = []; - } - boxBordersPointsQ3[cluster.classes[j].name].push({ - x : x, - y : y - }); - } else { - - if (this.isNumber(result.Q2)) { - - if (boxBordersPointsQ1[cluster.classes[j].name] == null) { - boxBordersPointsQ1[cluster.classes[j].name] = []; - } - - if (boxBordersPointsQ3[cluster.classes[j].name] == null) { - boxBordersPointsQ3[cluster.classes[j].name] = []; - } - - boxBordersPointsQ1[cluster.classes[j].name].push({ - x : boxProperties.x, - y : this.pointToPixel(result.Q2, boxProperties) - }); - boxBordersPointsQ3[cluster.classes[j].name].push({ - x : boxProperties.x, - y : this.pointToPixel(result.Q2, boxProperties) - }); - } - } - } - } - for ( var classe in boxBordersPointsQ1) { - var points = boxBordersPointsQ1[classe]; - var pointsSVG = ""; - for (var k = 0; k < points.length; k++) { - pointsSVG = Number(points[k].x).toFixed(1) + "," + Number(points[k].y).toFixed(1) + " " + pointsSVG; - } - - points = boxBordersPointsQ3[classe]; - for (var z = points.length - 1; z >= 0; z--) { - pointsSVG = Number(points[z].x).toFixed(1) + "," + Number(points[z].y).toFixed(1) + " " + pointsSVG; - } - - SVG.drawPoligon(pointsSVG, this.svg, [ - [ "fill", this.getClassColor(classe) ], [ "opacity", "0.3" ], [ "stroke", "black" ], [ "stroke-width", 1 ] ]); - } -}; - -RangeWhiskerGraph.prototype.plotWhisters = function(data, properties) { - var colors = [ "yellow", "orange", "green" ]; - - this.plotRangeQ1Q3(data, properties); - var Q2 = {}; - - for ( var i = 0; i < data.clusters.length; i++) { - var cluster = data.clusters[i]; - for ( var j = 0; j < cluster.classes.length; j++) { - var ratio = properties.width / (properties.xValues.range); - var coorX = (cluster.x - properties.xValues.min) * ratio + this.left + this.rulerWidth - this.rulerStroke; - var boxProperties = { - name : cluster.classes[j].name, - values : cluster.classes[j].values, - minPoint : properties.minPoint, - maxPoint : properties.maxPoint, - x : coorX, - y : this.top, - width : properties.classWidth, - height : this.height - this.top - this.bottom - this.clusterTitleHeight - this.rulerHeight - }; - - this.drawPoints(boxProperties); - - /** PLOTTING Q2 **/ - var result = this.calculate(boxProperties.values); - var boxColor = this.getClassColor(boxProperties.name); - if (this.isNumber(result.Q2)) { - if (Q2[boxProperties.name] != null) { - SVG.drawLine(boxProperties.x, this.pointToPixel(result.Q2, boxProperties), Q2[boxProperties.name].x, Q2[boxProperties.name].y, - this.svg, [ [ "stroke", boxColor ], [ "stroke-width", "2" ] ]); - } - Q2[boxProperties.name] = { - x : boxProperties.x, - y : this.pointToPixel(result.Q2, boxProperties) - } - } - } - } - -}; - -RangeWhiskerGraph.prototype.draw = function(targetId, data) { - //this.calculate(data); - this.targetId = targetId; - var properties = (this.getDimensions(data)); - this.svg = SVG.createSVGCanvas(document.getElementById(this.targetId), [ [ "width", this.width ], [ "height", this.height ] ]); - this.plotAxes(properties); - this.plotWhisters(data, properties); -}; - -RangeWhiskerGraph.prototype.input = function() { - return DATADOC.getBoxWhikerData(); -}; - -RangeWhiskerGraph.prototype.test = function(targetId) { - var plot = new RangeWhiskerGraph({ - targetId : targetId, - height : 350, - width : 450, - maxBoxWidth : 25 - }); - plot.refresh(this.input()); -}; - -StdDevDyGraph.prototype.dblclick = DygraphWidget.prototype.dblclick; -StdDevDyGraph.prototype._createHTLMWrapper = DygraphWidget.prototype._createHTLMWrapper; -StdDevDyGraph.prototype.draw = DygraphWidget.prototype.draw; - -function StdDevDyGraph(targetId, args) { - this.scaled = false; - if (args == null) { - args = {}; - } - args.customBars = true; - DygraphWidget.prototype.constructor.call(this, targetId, args); -} - -StdDevDyGraph.prototype.input = function () { - return { - data : [ [ 1, [ 2, 3, 3.5 ], [ 4, 4.2, 5 ] ], [ 2, [ 5, 5.5, 5.7 ], [ 4, 4.2, 5 ] ] ], - colors : [ "blue", "red" ], - labels : [ "", 'data1', 'data2' ] - }; -}; - -StdDevDyGraph.prototype.test = function (targetId) { - var dygraphObject = new StdDevDyGraph(targetId, { - width : 500, - height : 400, - xlabel : "xLabel", - showRangeSelector : false - }); - - dygraphObject.draw(dygraphObject.input().data, dygraphObject.input().colors, dygraphObject.input().labels); -}; - - - -function AbinitioGrid(args) { - this.height = null; - this.width = null; - this.id = BUI.id(); - - if (args != null) { - if (args.height != null) { - this.height = args.height; - - if (args.width != null) { - this.width = args.width; - } - } - } - - this.onSelected = new Event(this); -}; - - -AbinitioGrid.prototype.refresh = function(subtractions){ - this.store.loadData(this._prepareData(subtractions)); -}; - -AbinitioGrid.prototype._prepareData = function(subtractions){ - /** Parsing data * */ - var models = []; - for (var l = 0; l < subtractions.length; l++) { - var subtraction = subtractions[l]; - for (var k = 0; k < subtraction.substractionToAbInitioModel3VOs.length; k++) { - var data = subtraction.substractionToAbInitioModel3VOs[k].abinitiomodel3VO; - if (data.averagedModel != null) { - models.push(data.averagedModel); - models[models.length - 1].type = "Reference"; - } - - if (data.shapeDeterminationModel != null) { - models.push(data.shapeDeterminationModel); - models[models.length - 1].type = "Refined"; - } - - if (data.modelList3VO != null) { - if (data.modelList3VO.modeltolist3VOs != null) { - for (var i = 0; i < data.modelList3VO.modeltolist3VOs.length; i++) { - models.push(data.modelList3VO.modeltolist3VOs[i].model3VO); - models[models.length - 1].type = "Model"; - } - } - } - } - } - return models; -}; - -AbinitioGrid.prototype.getPanel = function(){ - var _this = this; - - - var modelFields = [ "modelId", "type", "chiSqrt", "dmax", "firFile", "logFile", "fitFile", "pdbFile", "rfactor", "rg", "volume" ]; - Ext.define('AbinitioModel', { - extend : 'Ext.data.Model', - fields : modelFields - - }); - - /** - * Store in Memory - */ - this.store = Ext.create('Ext.data.Store', { - model : 'AbinitioModel', - autoload : true, - groupField : 'type' - }); - - - var groupingFeature = Ext.create('Ext.grid.feature.Grouping',{ - groupHeaderTpl: '{name} ({rows.length} model{[values.rows.length > 1 ? "s" : ""]})', - startCollapsed: true, - collapsible : true - }); - - this.grid = Ext.create('Ext.grid.Panel', { - collapsible : false, - resizable : true, - features: [groupingFeature], - autoscroll : true, - multiSelect : true, - store : this.store, - height : this.height, - width : this.width, - margin : 10, - columns : [ { - text : "Type", - dataindex : "type", - hidden : true, - renderer : function(a, b, record) { - return record.data.type; - }, - flex : 1 - }, - { - text : "ModelId", - dataindex : "modelId", - hidden : true, - renderer : function(a, b, record) { - return record.data.modelId; - - }, - flex : 1 - }, - - { - text : "chiSqrt", - dataindex : "chiSqrt", - renderer : function(a, b, record) { - if (record.data.dmax != null) { - return BUI.formatValuesUnits(record.data.chiSqrt, "", 12, this.decimals); - } - - }, - flex : 1 - }, - { - text : "Dmax", - dataindex : "dmax", - renderer : function(a, b, record) { - if (record.data.dmax != null) { - return BUI.formatValuesUnits(record.data.dmax, "nm", 12, this.decimals); - } - - }, - flex : 1 - }, { - text : "rFactor", - dataindex : "rfactor", - hidden : true, - renderer : function(a, b, record) { - if (record.data.rfactor != null) { - return record.data.rfactor; - } - }, - flex : 1 - }, { - text : "Rg", - dataindex : "rg", - renderer : function(a, b, record) { - if (record.data.rg != null) { - return BUI.formatValuesUnits(record.data.rg, "nm", 12, this.decimals); - } - - }, - flex : 1 - }, - { - text : "Volume", - dataindex : "volume", - renderer : function(a, b, record) { - if (record.raw.volume != null){ - return BUI.formatValuesUnits(record.raw.volume, '') + " nm3"; - } - }, - flex : 1 - }, - { - text : "PDB", - dataindex : "pdbFile", - renderer : function(a, b, record) { - if (record.data.pdbFile != null){ - return record.data.pdbFile.split("/")[record.data.pdbFile.split("/").length - 1]; - } - }, - flex : 1 - }, { - text : "Fir", - dataindex : "firFile", - renderer : function(a, b, record) { - if (record.data.firFile != null){ - return record.data.firFile.split("/")[record.data.firFile.split("/").length - 1]; - } - }, - flex : 1 - }, { - text : "LOG", - dataindex : "logFile", - hidden : true, - renderer : function(a, b, record) { - if (record.data.logFile != null){ - return record.data.logFile.split("/")[record.data.logFile.split("/").length - 1]; - } - }, - flex : 1 - } - ], - viewConfig : { - enableTextSelection : true, - preserveScrollOnRefresh : true, - stripeRows : true, - listeners : { - 'celldblclick' : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { - }, - 'cellclick' : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { - var models = []; - for (var i = 0; i < grid.getSelectionModel().selected.items.length; i++) { - models.push(grid.getSelectionModel().selected.items[i].raw); - } - _this.onSelected.notify(models); - } - } - } - }); - return this.grid; - -}; - - -function AdditiveGrid(args) { - this.onRemoveButtonClicked = new Event(this); -} - -AdditiveGrid.prototype.getBuffer = function() { - return this.buffer; -}; - -AdditiveGrid.prototype._getActions = function() { - var _this = this; - /** Actions buttons **/ - var actions = []; - - actions.push(Ext.create('Ext.Action', { - icon : '../images/add.png', - text : 'Add', - disabled : true, - alwaysEnabled : true, - handler : function(widget, event) { - _this.buffer.bufferhasadditive3VOs.push(BIOSAXS_BEANS.getBufferhasAdditive3VO()); - _this.refresh(_this.buffer, _this.experiment); - } - })); - - return actions; -}; - -AdditiveGrid.prototype.refresh = function(buffer, experiment) { - this.buffer = buffer; - this.experiment = experiment; - if (buffer) { - if (buffer.bufferhasadditive3VOs) { - this.features = buffer.bufferhasadditive3VOs; - } - } - this.experiment = experiment; - this.store.loadData(this._prepareData(), false); -}; - -AdditiveGrid.prototype.getAdditives = function() { - var additives = []; - for ( var i = 0; i < this.store.getCount(); i++) { - var bufferHasAdditive = BIOSAXS_BEANS.getBufferhasAdditive3VO(); - var data = this.store.getAt(i).getData(); - - bufferHasAdditive.additive3VO.name = data.name; - bufferHasAdditive.additive3VO.comments = data.comments; - bufferHasAdditive.additive3VO.additiveType = data.additiveType; - - bufferHasAdditive.bufferId = this.buffer.bufferId; - bufferHasAdditive.bufferHasAdditiveId = data.bufferHasAdditiveId; - bufferHasAdditive.quantity = data.quantity; - additives.push(bufferHasAdditive); - } - - return additives; -}; - -AdditiveGrid.prototype._getFields = function(buffers) { - var columns = [ - - { - header : 'Name', - dataIndex : 'name', - type : 'string', - editor : { - allowBlank : true - }, - flex : 1 - }, - { - header : 'Type', - name : 'additiveType', - dataIndex : 'additiveType', - editor : { - xtype : 'combobox', - typeAhead : true, - triggerAction : 'all', - selectOnTab : true, - store : BIOSAXS.proposal.getAdditiveTypes(), - lazyRender : true, - listClass : 'x-combo-list-small' - }, - flex : 0.6 - }, - { - header : 'Quantity', - dataIndex : 'quantity', - name : 'quantity', - editor : { - allowBlank : true - }, - type : 'string', - flex : 1 - }, - { - xtype : 'actioncolumn', - items : [ { - icon : '../images/cancel.png', - tooltip : 'Delete additive', - scope : this, - handler : function(grid, rowIndex, colIndex) { - if ((grid.getStore().getAt(rowIndex).data.bufferHasAdditiveId == null)|| (grid.getStore().getAt(rowIndex).data.bufferHasAdditiveId == "")) { - grid.getStore().removeAt(rowIndex); - } else { - this.onRemoveButtonClicked.notify({ - 'bufferId' : this.buffer.bufferId, - 'bufferHasAdditiveId' : this.store.getAt(rowIndex).data.bufferHasAdditiveId - }); - } - } - } ] - } ]; - - return columns; -}; - -AdditiveGrid.prototype._prepareData = function() { - var data = []; - if (this.features == null) { - this.features = []; - } - for (var i = 0; i < this.features.length; i++) { - var object = this.features[i]; - object.name = this.features[i].additive3VO.name; - object.additiveType = this.features[i].additive3VO.additiveType; - object.comments = this.features[i].additive3VO.comments; - object.additiveId = this.features[i].additive3VO.additiveId; - data.push(object); - } - return data; -}; - -AdditiveGrid.prototype.getPanel = function(buffer, experiment) { - this.buffer = buffer; - this.features = buffer.bufferhasadditive3VOs; - this.experiment = experiment; - return this._renderGrid(); -}; - -AdditiveGrid.prototype.getStore = function() { - var _this = this; - var store = Ext.create('Ext.data.Store', { - fields : [ "name", "additiveType", "comments", "additiveId", "bufferHasAdditiveId", "quantity" ], - autoload : false, - data : this._prepareData(), - listeners : { - update : function(store, record) { - record.index = _this.grid.getSelectionModel().getCurrentPosition().row; - _this.buffer.bufferhasadditive3VOs[record.index].additive3VO.name = record.data.name; - _this.buffer.bufferhasadditive3VOs[record.index].additive3VO.additiveType = record.data.additiveType; - _this.buffer.bufferhasadditive3VOs[record.index].additive3VO.comments = record.data.comments; - _this.buffer.bufferhasadditive3VOs[record.index].quantity = record.data.quantity; - } - } - }); - - // store.sort('bufferHasAdditiveId', 'ASC'); - store.loadData(this._prepareData(), false); - return store; -}; - -AdditiveGrid.prototype._renderGrid = function() { - var _this = this; - this.store = this.getStore(); - - var cellEditing = Ext.create('Ext.grid.plugin.CellEditing', { - clicksToEdit : 1 - }); - - this.grid = Ext.create('Ext.grid.Panel', { - dockedItems : [ { - xtype : 'toolbar', - items : this._getActions() - } ], - store : this.store, - height : 230, - title : "Additives", - width : "100%", - columns : _this._getFields(), - plugins : [ cellEditing ], - viewConfig : { - stripeRows : true, - listeners : { - itemcontextmenu : function(view, rec, node, index, e) { - e.stopEvent(); - contextMenu.showAt(e.getXY()); - return false; - } - } - }, - selModel : { - mode : 'SINGLE' - } - }); - - return this.grid; -}; - -AdditiveGrid.prototype.input = function() { -}; - -AdditiveGrid.prototype.test = function(targetId) { - var grid = new AdditiveGrid(); - var panel = grid.getPanel([]); - panel.render(targetId); - -}; - -/** AnalysisGrid **/ -function AnalysisGrid(args) { - var _this = this; - - this.id = BUI.id(); - if (Ext.get("mainPanel")){ - this.width = Ext.get("mainPanel").getWidth(); - } - else{ - this.width = this.maxWidth; - } - this.maxWidth = 1500; - - /** Visibles **/ - this.isGuinierTabVisible = true; - this.isGnomTabVisible = true; - this.isPorodTabVisible = true; - this.isScatteringPlotVisible = true; - this.isAbinitioTabVisible = true; - this.showButtonsVisible = true; - this.isI0Visible = true; - - this.margin = null; - this.grouped = true; - - this.hideNulls = false; - this.decimals = 4; - this.height = null; - this.sorters = [ { - property : 'conc', - direction : 'ASC' - } ]; - - if (args != null) { - if (args.grouped != null) { - this.grouped = args.grouped; - } - - if (args.showButtonsVisible != null) { - this.showButtonsVisible = args.showButtonsVisible; - } - if (args.isI0Visible != null) { - this.isI0Visible = args.isI0Visible; - } - if (args.sorters != null) { - this.sorters = args.sorters; - } - if (args.isScatteringPlotVisible != null) { - this.isScatteringPlotVisible = args.isScatteringPlotVisible; - } - if (args.isGuinierTabVisible != null) { - this.isGuinierTabVisible = args.isGuinierTabVisible; - } - if (args.isGnomTabVisible != null) { - this.isGnomTabVisible = args.isGnomTabVisible; - } - if (args.isPorodTabVisible != null) { - this.isPorodTabVisible = args.isPorodTabVisible; - } - if (args.hideNulls != null) { - this.hideNulls = args.hideNulls; - } - if (args.height != null) { - this.height = args.height; - } - if (args.width != null) { - this.width = args.width; - } - if (args.maxWidth != null) { - this.maxWidth = args.maxWidth; - } - } -} - -AnalysisGrid.prototype.refresh = function(data, args) { - this.store.loadData(this._prepareData(data), false); - if (args != null){ - if (args.experiment != null){ - this.experiment = args.experiment; - } - } -}; - -AnalysisGrid.prototype._getPorod = function() { - return { - text : 'Porod', - name : 'Porod', - columns : [ - { - text : 'Volume', - dataIndex : 'volumeEdna', - width : 75, - sortable : true, - hidden : !this.isPorodTabVisible, - renderer : function(val, y, sample) { - if (sample.raw.volume != null) - return BUI.formatValuesUnits(sample.raw.volume, '') + " nm3"; - } - }, - { - text : 'MM Vol. est.', - dataIndex : 'volumeEdna', - tooltip : '[Volume/2 - Volume/1.5] (Guinier)', - sortable : true, - width : 95, - hidden : !this.isPorodTabVisible, - renderer : function(val, y, sample) { - if (sample.raw.volume != null) - return Number(sample.raw.volume / 2).toFixed(1) + " - " + Number(sample.raw.volume / 1.5).toFixed(1)+ "kD"; - } - } ] - }; -}; - - -AnalysisGrid.prototype._getFramesColumn = function() { - var _this = this; - return { - text : 'Frames (Averaged/Total)', - dataIndex : 'datacollection', - name : 'datacollection', - sortable : true, - width : 150, - renderer : function(dataCollections, y, data) { - /** Bug of Webservices: frames count were swapped with frames averages **/ - if (data.raw.bufferAfterFramesCount){ - if (data.raw.bufferAfterFramesMerged){ - if (parseInt(data.raw.bufferAfterFramesMerged) > parseInt(data.raw.bufferAfterFramesCount)){ - var aux = parseInt(data.raw.bufferAfterFramesCount); - data.raw.bufferAfterFramesCount= data.raw.bufferAfterFramesMerged; - data.raw.bufferAfterFramesMerged = aux; - } - } - } - - if (data.raw.bufferBeforeFramesCount){ - if (data.raw.bufferBeforeFramesMerged){ - if (parseInt(data.raw.bufferBeforeFramesMerged) > parseInt(data.raw.bufferBeforeFramesCount)){ - var aux = parseInt(data.raw.bufferBeforeFramesCount); - data.raw.bufferBeforeFramesCount= data.raw.bufferBeforeFramesMerged; - data.raw.bufferBeforeFramesMerged = aux; - } - } - - } - - var bufferAcronym = data.raw.bufferAcronym; - var macromoleculeAcronym = data.raw.macromoleculeAcronym; - var bbmerges = data.raw.bufferBeforeFramesMerged; - var molmerges = data.raw.framesMerge; - var bamerges = data.raw.bufferAfterFramesMerged; - var totalframes = data.raw.framesCount; - var bufferId = data.raw.bufferId; - var macromoleculeId = data.raw.macromoleculeId; - var macromoleculeColor = null; - if (_this.experiment != null){ - macromoleculeColor = _this.experiment.macromoleculeColors[data.raw.macromoleculeId]; - } - - /** BUG in the database to be fixed **/ - try{ - if (totalframes != null){ - if(molmerges != null){ - if (parseFloat(totalframes) < parseFloat(molmerges)){ - var aux = totalframes; - totalframes = molmerges; - molmerges = aux; - } - } - } - } - catch(e){ - - } - - return BUI.getHTMLTableForFrameAveraged(bufferAcronym, - macromoleculeAcronym, - bbmerges, - molmerges, - bamerges, - totalframes, - bufferId, - macromoleculeId, - macromoleculeColor); - } - }; -}; - -AnalysisGrid.prototype._getColumns = function() { - var _this = this; - return [ - { - name : 'groupeField', - dataIndex : 'groupeField', - hidden : true - - }, - { - "header" : "subtractionId", - hidden : true, - "dataIndex" : "subtractionId", - "name" : "subtractionId" - }, - { - header : "Macromolecule", - dataIndex : "macromoleculeAcronym", - name : "macromoleculeAcronym", - renderer : function(val, y, sample) { - return '
' + val + '
' + - BUI.formatValuesUnits(sample.raw.conc, "mg/ml", 10, this.decimals) + '
' + - BUI.formatValuesUnits(sample.raw.exposureTemperature, "C", 10, this.decimals) + '
' - } - }, - { - header : "Order", - dataIndex : "priorityLevelId", - name : "priorityLevelId", - hidden : true - }, - { - header : "Code", - dataIndex : "code", - name : "code", - hidden : true - }, - { - header : "Conc.", - dataIndex : "conc", - width : 80, - name : "conc", - type : "number", - hidden : true, - renderer : function(val, y, sample) { - return BUI.formatValuesUnits(val, "mg/ml", 10, this.decimals); - } - }, - { - text : 'Scattering', - width : 100, - dataIndex : 'subtractionId', - name : 'subtractionId', - hidden : !this.isScatteringPlotVisible, - renderer : function(val, y, sample) { - var url = BUI.getURL() + '&type=scattering&subtractionId=' + sample.raw.subtractionId; - var event = "OnClick= window.open('" + url + "')"; - return ''; - } - }, - { - text : 'Kratky', - width : 100, - dataIndex : 'subtractionId', - hidden : true, - name : 'subtractionId', - renderer : function(val, y, sample) { - var url = BUI.getURL() + '&type=kratky&subtractionId=' + sample.raw.subtractionId; - var event = "OnClick= window.open('" + url + "')"; - return ''; - } - }, - this._getFramesColumn(), - { - text : 'File Name', - dataIndex : 'averageFilePath', - name : 'averageFilePath', - sortable : true, - width : 150, - hidden : true, - renderer : function(dataCollections, y, data) { - return BUI.getHTMLTableForPrefixes(data.raw.bufferBeforeAverageFilePath, data.raw.averageFilePath, - data.raw.bufferAfterAverageFilePath); - } - }, - - { - text : 'Guinier', - name : 'Guinier', - columns : [ - { - text : 'Rg', - dataIndex : 'rgGuinier', - name : 'rgGuinier', - hidden : !this.isGuinierTabVisible, - width : 75, - tooltip : 'In polymer physics, the radius of gyration is used to describe the dimensions of a polymer chain.', - sortable : true, - renderer : function(val, y, sample) { - if (sample.raw.rgGuinier != null) { - /** Show warning if rgGuinier and rgGnom differ more than 10% **/ - if (Math.abs(sample.raw.rgGuinier - sample.raw.rgGnom) > (sample.raw.rgGuinier * 0.1)) { - return "" + BUI.formatValuesUnits(sample.raw.rgGuinier, "") + ""; - - } - return BUI.formatValuesUnits(sample.raw.rgGuinier, "nm", 12, this.decimals); - } - } - }, - { - text : 'Points', - dataIndex : 'points', - sortable : true, - width : 100, - type : 'string', - hidden : !this.isGuinierTabVisible, - renderer : function(val, y, sample) { - if ((sample.raw.firstPointUsed == "") || (sample.raw.firstPointUsed == null)) - return; - return "" + sample.raw.firstPointUsed + " - " + sample.raw.lastPointUsed + " (" + - (sample.raw.lastPointUsed - sample.raw.firstPointUsed) + " )"; - } - }, - { - text : 'Quality', - dataIndex : 'quality', - hidden : !this.isGuinierTabVisible, - tooltip : 'Estimated data quality. 1.0 - means ideal quality, 0.0 - unusable data. In table format it is given in percent (100% - ideal quality, 0% - unusable data). Please note that this estimation is based only on the Guinier interval (very low angles).', - width : 60, - sortable : true, - renderer : function(val, y, sample) { - if (sample.raw.quality != null) { - val = sample.raw.quality; - if ((val != null) && (val != "")) { - return "" + (Number(val) * 100).toFixed(2) + " %"; - } - } - } - }, { - text : 'I(0)', - dataIndex : 'I0', - sortable : true, - hidden : !this.isI0Visible, - tooltip : 'Extrapolated scattering intensity at zero angle I(0) (forward scattering)', - width : 75, - type : 'string', - renderer : function(val, y, sample) { - if (sample.raw.I0 != null) { - return BUI.formatValuesErrorUnitsScientificFormat(sample.raw.I0, sample.raw.i0stdev, ""); - } - } - }, { - text : 'Aggregated', - tooltip : "If aggregation was detected from the slope of the data curve at low angles the value is '1', otherwise '0'.", - dataIndex : 'isagregated', - hidden : true, - width : 75, - renderer : function(val, y, sample) { - if ((sample.raw.isagregated != null)) { - if (val == true) { - return "Yes"; - } else { - return "No"; - } - } - } - }, { - text : 'Guinier', - sortable : true, - dataIndex : 'subtractionId', - type : 'string', - width : 100, - hidden : true, - renderer : function(val, y, sample) { - - if (sample.raw.subtractionId != null) { - var url = BUI.getURL() + '&type=guinier&subtractionId=' + sample.raw.subtractionId; - var event = "OnClick= window.open('" + url + "')"; - return ''; - } - } - } ] - }, - { - text : 'Gnom', - name : 'Gnom', - - columns : [ { - text : 'Rg', - dataIndex : 'rgGnom', - type : 'string', - width : 65, - hidden : !this.isGnomTabVisible, - sortable : true, - renderer : function(val, y, sample) { - /** Show warning if rgGuinier and rgGnom differ more than 10% **/ - if (sample.raw.rgGnom != null) { - if (Math.abs(sample.raw.rgGuinier - sample.raw.rgGnom) > (sample.raw.rgGuinier * 0.1)) { - return "" + BUI.formatValuesUnits(sample.raw.rgGnom, "") + ""; - - } - return BUI.formatValuesUnits(sample.raw.rgGnom, "nm"); - } - } - }, { - text : 'Total', - dataIndex : 'total', - width : 65, - hidden : !this.isGnomTabVisible, - sortable : true, - renderer : function(val, y, sample) { - if (sample.raw.total != null) - return BUI.formatValuesUnits(sample.raw.total, ''); - } - }, { - text : 'Dmax', - dataIndex : 'dmax', - sortable : true, - width : 75, - hidden : !this.isGnomTabVisible, - renderer : function(val, y, sample) { - if (sample.raw.dmax != null) - return BUI.formatValuesUnits(sample.raw.dmax, "") + " nm"; - } - }, { - text : 'P(r)', - sortable : true, - hidden : true, - width : 100, - dataIndex : 'subtractionId', - type : 'string', - renderer : function(val, y, sample) { - var url = BUI.getURL() + '&type=gnom&subtractionId=' + sample.raw.subtractionId; - var event = "OnClick= window.open('" + url + "')"; - return ''; - } - } ] - }, - this._getPorod(), - { - text : 'AbInitio Modeling', - name : 'AbInitio_modeling', - - columns : [ - { - text : 'NSD', - dataIndex : 'volumeEdna', - width : 100, - sortable : true, - hidden : true, - renderer : function(val, y, sample) { - var url = BUI.getNSDImageURL(sample.raw.modelListId); - var event = "OnClick= window.open('" + url + "')"; - return ''; - } - }, - { - text : 'Chi2', - dataIndex : 'volumeEdna', - sortable : true, - hidden : true, - width : 100, - renderer : function(val, y, sample) { - var url = BUI.getCHI2ImageURL(sample.raw.modelListId); - var event = "OnClick= window.open('" + url + "')"; - return ''; - } - }, - { - text : 'Pdb', - dataIndex : 'volumeEdna', - tooltip : 'Damaver: The program suite DAMAVER is a set of programs to align ab initio models, select the most typical one and build an averaged model. Dammif : Rapid ab initio shape determination by simulated annealing using a single phase dummy atom model. Dammin :Ab initio shape determination by simulated annealing using a single phase dummy atom model', - sortable : true, - width : 125, - hidden : !this.isAbinitioTabVisible, - renderer : function(val, y, sample) { - var html = new String(); - var split = null; - var url = null; - var file = sample.raw.averagedModel; - if (file != null) { - split = file.split("/"); - if (split.length > 0) { - url = BUI.getPdbURL() + '&type=AVERAGED&abInitioModelId=' + sample.raw.abInitioModelId; - html = html + ''+ split[split.length - 1] + '

'; - - } - } - - file = sample.raw.rapidShapeDeterminationModel; - if (file != null) { - split = file.split("/"); - if (split.length > 0) { - url = BUI.getPdbURL() + '&type=RAPIDSHAPEDETERMINATIONMODEL&abInitioModelId=' + sample.raw.abInitioModelId; - html = html + '' + split[split.length - 1] + '

'; - } - } - - file = sample.raw.shapeDeterminationModel; - if (file != null) { - split = file.split("/"); - if (split.length > 0) { - url = BUI.getPdbURL() + '&type=SHAPEDETERMINATIONMODEL&abInitioModelId=' + sample.raw.abInitioModelId; - html = html + ''+ split[split.length - 1] + ''; - } - } - return html; - } - }, - { - text : 'Damaver', - dataIndex : 'volumeEdna', - hidden : true, - tooltip : 'Damaver: The program suite DAMAVER is a set of programs to align ab initio models, select the most typical one and build an averaged model. ', - sortable : true, - width : 125, - renderer : function(val, y, sample) { - var file = sample.raw.averagedModel; - if (file != null) { - var split = file.split("/"); - if (split.length > 0) { - var url = BUI.getPdbURL() + '&type=AVERAGED&abInitioModelId=' + sample.raw.abInitioModelId; - return '' + split[split.length - 1]+ ''; - } - } - return sample.raw.averagedModel; - } - }, - { - text : 'Dammif', - dataIndex : 'volumeEdna', - hidden : true, - tooltip : 'Dammif : Rapid ab initio shape determination by simulated annealing using a single phase dummy atom model', - sortable : true, - width : 125, - renderer : function(val, y, sample) { - var file = sample.raw.rapidShapeDeterminationModel; - if (file != null) { - var split = file.split("/"); - if (split.length > 0) { - var url = BUI.getPdbURL() + '&type=RAPIDSHAPEDETERMINATIONMODEL&abInitioModelId=' + sample.raw.abInitioModelId; - return '' + split[split.length - 1] + ''; - } - } - return sample.raw.averagedModel; - } - }, - { - text : 'Dammin', - dataIndex : 'volumeEdna', - hidden : true, - tooltip : 'Dammin :Ab initio shape determination by simulated annealing using a single phase dummy atom model', - sortable : true, - width : 125, - renderer : function(val, y, sample) { - var file = sample.raw.shapeDeterminationModel; - if (file != null) { - var split = file.split("/"); - if (split.length > 0) { - var url = BUI.getPdbURL() + '&type=SHAPEDETERMINATIONMODEL&abInitioModelId=' + sample.raw.abInitioModelId; - return '' + split[split.length - 1]+ ''; - } - } - return sample.raw.averagedModel; - } - } ] - }, { - text : 'Time', - dataIndex : 'substractionCreationTime', - name : 'substractionCreationTime', - hidden : true, - width : 80, - sortable : true, - renderer : function(value, metaData, record, rowIndex, colIndex, store) { - try { - if (record.raw.substractionCreationTime != null) { - return moment(record.raw.substractionCreationTime).format('h:mm:ss a'); - } - } catch (e) { - return "NA"; - } - } - }, { - text : 'Date', - dataIndex : 'substractionCreationTime', - name : 'substractionCreationTime', - hidden : true, - width : 80, - sortable : true, - renderer : function(value, metaData, record, rowIndex, colIndex, store) { - try { - if (record.raw.substractionCreationTime != null) { - return moment(record.raw.substractionCreationTime).format("LL"); - } - } catch (e) { - return "NA"; - } - } - }, - this._getButtons()]; -}; - -AnalysisGrid.prototype._getButtons = function() { - return { - id : this.id + 'buttonPlot', - name : 'buttonPlot', - hidden : !this.showButtonsVisible, - width : 110, - sortable : false, - renderer : function(value, metaData, sample, rowIndex, colIndex, store) { - var html = ""; - - if (sample.raw.subtractionId) { - html = html + ""; - if (sample.raw.rapidShapeDeterminationModelId != null) { - html = html + ""; - } - } - return html + "
" + BUI.getGreenButton('Calibration', { - height : 20, - width : 100 - }) + "
" + BUI.getGreenButton('Primary Data Proc.', { - height : 20, - width : 100 - }) + "
" + BUI.getGreenButton('AbInitio Modeling', { - height : 20, - width : 100 - }) + "
"; - } - }; -}; -AnalysisGrid.prototype._prepareData = function(data) { - if (this.hideNulls) { - var result = []; - for ( var i = 0; i < data.length; i++) { - if (data[i].subtractionId != null) { - data[i].groupeField = data[i].macromoleculeAcronym + " " + data[i].bufferAcronym; - result.push(data[i]); - } - } - return result; - } else { - return data; - } -}; - -AnalysisGrid.prototype.getPanel = function(data) { - var _this = this; - var columns = this._getColumns(); - - var fields = JSON.parse(JSON.stringify(columns)); - fields.push({ - name : 'experimentId', - dataIndex : 'experimentId' - - }); - this.store = Ext.create('Ext.data.Store', { - fields : fields, - autoload : true, - data : this._prepareData(data), - groupField : 'groupeField' - }); - - this.store.sort(this.sorters); - - var features = []; - - if (this.grouped) { - features.push({ - ftype : 'grouping', - groupHeaderTpl : '{name}', - hideGroupedHeader : false, - startCollapsed : false - }); - } - - this.grid = Ext.create('Ext.grid.Panel', { - style : { - padding : 5 - }, - maxWidth : this.maxWidth, - width : this.width, - height : this.height, - store : this.store, - columns : columns, - resizable : true, - features : features, - viewConfig : { - preserveScrollOnRefresh : true, - stripeRows : true, - listeners : { - cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { - - if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonPlot') { - if (e.target.defaultValue == 'AbInitio Modeling') { - var url = BUI.getPDBVisualizerURL(record.raw.averagedModelId, record.raw.subtractionId, record.raw.experimentId); - window.open(url, "_blank"); - } - - if (e.target.defaultValue == 'Primary Data Proc.') { - _this._edit(record.raw); - } - - if (e.target.defaultValue == 'Calibration') { - _this._showCalibration(record.raw); - } - } - } - } - }, - selModel : { - mode : 'SINGLE' - } - }); - - return this.grid; - -}; - -AnalysisGrid.prototype._openVisualizarBySubstractionId = function(record) { - //experimentId, subtractionId) { - var experimentId = record.experimentId; - var subtractionId = record.subtractionId; - - var _this = this; - var adapter = new BiosaxsDataAdapter(); - this.subtractionId = subtractionId; - - adapter.onSuccess.attach(function(sender, data) { - var experiment = new Experiment(data); - var experimentList = new ExperimentList([ experiment ]); - var subtraction = experiment.getSubtractionById(_this.subtractionId); - _this.grid.setLoading(false); - _this._openVisualizer(experimentList, subtraction, record); - }); - this.grid.setLoading("ISPyB: Fetching experiment"); - adapter.getExperimentById(experimentId, "MEDIUM"); -}; - -AnalysisGrid.prototype._edit = function(record) { - var experimentId = record.experimentId; - var _this = this; - - if (BIOSAXS.proposal.macromolecules == null) { - this.grid.setLoading("ISPyB: Fetching proposal"); - BIOSAXS.proposal.onInitialized.attach(function(sender, data) { - _this._openVisualizarBySubstractionId(record); - }); - BIOSAXS.proposal.init(); - } else { - this._openVisualizarBySubstractionId(record); - } -}; - - -AnalysisGrid.prototype._showCalibration = function(row) { - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, data) { - Ext.create('Ext.window.Window', { - title : 'Calibration', - width : 900, - resizable : true, - height : 400, - modal : true, - frame : false, - draggable : true, - closable : true, - autoscroll : true, - paddin : 5, - layout : { - type : 'vbox', - align : 'stretch' - }, - items : [ new AnalysisGrid({ - isGuinierTabVisible : false, - isGnomTabVisible : false, - isPorodTabVisible : false, - isAbinitioTabVisible : false, - isI0Visible : true, - showButtonsVisible : false, - height : 350, - sorters : [ { - property : 'experimentId', - direction : 'DESC' - } ] - }).getPanel(data) ] - }).show(); - }); - adapter.getAnalysisCalibrationByProposalId(); -}; - -AnalysisGrid.prototype._openVisualizer = function(experimentList, subtraction, record) { - var merges = experimentList.getMergesByDataCollectionId(subtraction.dataCollectionId); - var mergeIdList = []; - for ( var i = 0; i < merges.length; i++) { - mergeIdList.push(merges[i].mergeId); - } - - var dataAdapter = new BiosaxsDataAdapter(); - dataAdapter.onSuccess.attach(function(sender, merges) { - var dataCollectionCurveVisualizer = new DataCollectionCurveVisualizer(); - dataCollectionCurveVisualizer.draw(); - dataCollectionCurveVisualizer.refresh(merges, experimentList, subtraction.dataCollectionId, record); - }); - - dataAdapter.onError.attach(function(sender, error) { - }); - - dataAdapter.getMergesByIdsList(mergeIdList); -}; - -AnalysisGrid.prototype.input = function() { - return { - data : DATADOC.getData_3367().slice(20, 30),//[{"total":"0.447505552082","bufferBeforeMeasurementId":22150,"sampleMergeId":12373,"averagedModelId":43636,"I0":"32.50776","proposalCode":"OPD","averagedModel":"/data/pyarch/bm29/opd29/1137/1d/damaver.pdb","bufferAfterMergeId":12374,"framesCount":"10","bufferBeforeMergeId":12372,"averageFilePath":" /data/pyarch/bm29/opd29/1137/1d/MG386_016_ave.dat","bufferAfterMeasurementId":22152,"rgGuinier":"2.96883","subtractedFilePath":" /data/pyarch/bm29/opd29/1137/1d/MG386_016_sub.dat","firstPointUsed":"30","chi2RgFilePath":"/data/pyarch/bm29/opd29/1137/1d/chi2_R.png","abInitioModelId":272,"macromoleculeId":112,"code":"_20.0_1.25","transmission":"100.0","timeStart":"2013-07-17 17:10:25.743734","bufferAcronym":"MES","quality":"0.87091","shapeDeterminationModelId":43638,"expermientComments":"[BsxCube] Generated from BsxCube","bufferId":707,"isagregated":"False","dmax":"10.390905","bufferBeforeAverageFilePath":" /data/pyarch/bm29/opd29/1137/1d/MG386_015_ave.dat","exposureTemperature":"20.0","subtractionId":5165,"conc":"1.25","rapidShapeDeterminationModel":"/data/pyarch/bm29/opd29/1137/1d/damfilt.pdb","experimentCreationDate":"Jul 17, 2013 5:08:37 PM","lastPointUsed":"92","modelListId":272,"framesMerge":"10","rapidShapeDeterminationModelId":43637,"bufferAfterAverageFilePath":" /data/pyarch/bm29/opd29/1137/1d/MG386_017_ave.dat","nsdFilePath":"/data/pyarch/bm29/opd29/1137/1d/nsd.png","bufferAfterFramesMerged":"10","experimentId":1137,"macromoleculeAcronym":"MG386","i0stdev":"0.05726128","sampleMeasurementId":22151,"measurementComments":"[1] MES","priorityLevelId":2,"bufferBeforeFramesMerged":"10","substractionCreationTime":"Jul 17, 2013 5:12:32 PM","proposalNumber":"29","rgGnom":"3.05242714339","volume":"44.1406","shapeDeterminationModel":"/data/pyarch/bm29/opd29/1137/1d/dammin.pdb","comments":null},{"total":"0.582641941147","bufferBeforeMeasurementId":22152,"sampleMergeId":12375,"averagedModelId":43809,"I0":"31.4366153846","proposalCode":"OPD","averagedModel":"/data/pyarch/bm29/opd29/1137/1d/damaver.pdb","bufferAfterMergeId":12376,"framesCount":"10","bufferBeforeMergeId":12374,"averageFilePath":" /data/pyarch/bm29/opd29/1137/1d/MG386_018_ave.dat","bufferAfterMeasurementId":22155,"rgGuinier":"2.95541","subtractedFilePath":" /data/pyarch/bm29/opd29/1137/1d/MG386_018_sub.dat","firstPointUsed":"21","chi2RgFilePath":"/data/pyarch/bm29/opd29/1137/1d/chi2_R.png","abInitioModelId":273,"macromoleculeId":112,"code":"_20.0_0.65000000000000002","transmission":"100.0","timeStart":"2013-07-17 17:12:55.837462","bufferAcronym":"MES","quality":"0.870164","shapeDeterminationModelId":43811,"expermientComments":"[BsxCube] Generated from BsxCube","bufferId":707,"isagregated":"False","dmax":"10.343935","bufferBeforeAverageFilePath":" /data/pyarch/bm29/opd29/1137/1d/MG386_017_ave.dat","exposureTemperature":"20.0","subtractionId":5166,"conc":"0.65000000000000002","rapidShapeDeterminationModel":"/data/pyarch/bm29/opd29/1137/1d/damfilt.pdb","experimentCreationDate":"Jul 17, 2013 5:08:37 PM","lastPointUsed":"80","modelListId":273,"framesMerge":"10","rapidShapeDeterminationModelId":43810,"bufferAfterAverageFilePath":" /data/pyarch/bm29/opd29/1137/1d/MG386_019_ave.dat","nsdFilePath":"/data/pyarch/bm29/opd29/1137/1d/nsd.png","bufferAfterFramesMerged":"10","experimentId":1137,"macromoleculeAcronym":"MG386","i0stdev":"0.100250461538","sampleMeasurementId":22154,"measurementComments":"[2] MES","priorityLevelId":4,"bufferBeforeFramesMerged":"10","substractionCreationTime":"Jul 17, 2013 5:15:02 PM","proposalNumber":"29","rgGnom":"2.9963404542","volume":"42.2255","shapeDeterminationModel":"/data/pyarch/bm29/opd29/1137/1d/dammin.pdb","comments":null}], - proposal : new ResultsAssemblyGrid().input().proposal - }; -}; - -AnalysisGrid.prototype.test = function(targetId) { - var analysisGrid = new AnalysisGrid(); - BIOSAXS.proposal = new Proposal(analysisGrid.input().proposal); - var panel = analysisGrid.getPanel(analysisGrid.input().data); - panel.render(targetId); -}; - - -function HPLCAnalysisGrid(args) { - AnalysisGrid.prototype.constructor.call(this, args); -} - -HPLCAnalysisGrid.prototype._edit = AnalysisGrid.prototype._edit; -HPLCAnalysisGrid.prototype._getColumns = AnalysisGrid.prototype._getColumns; -HPLCAnalysisGrid.prototype._openVisualizarBySubstractionId = AnalysisGrid.prototype._openVisualizarBySubstractionId; -HPLCAnalysisGrid.prototype._openVisualizer = AnalysisGrid.prototype._openVisualizer; -HPLCAnalysisGrid.prototype._prepareData = AnalysisGrid.prototype._prepareData; -HPLCAnalysisGrid.prototype._showCalibration = AnalysisGrid.prototype._showCalibration; -HPLCAnalysisGrid.prototype.getPanel = AnalysisGrid.prototype.getPanel; -HPLCAnalysisGrid.prototype.input = AnalysisGrid.prototype.input; -HPLCAnalysisGrid.prototype.test = AnalysisGrid.prototype.test; -HPLCAnalysisGrid.prototype.refresh = AnalysisGrid.prototype.refresh; -HPLCAnalysisGrid.prototype._getPorod = AnalysisGrid.prototype._getPorod; - -HPLCAnalysisGrid.prototype._getButtons = function() { - return { - id : this.id + 'buttonPlot', - name : 'buttonPlot', - hidden : !this.showButtonsVisible, - width : 110, - sortable : false, - renderer : function(value, metaData, sample, rowIndex, colIndex, store) { - var html = ""; - - if (sample.raw.subtractionId) { - if (sample.raw.rapidShapeDeterminationModelId != null) { - html = html + ""; - } - } - return html + "
" + BUI.getGreenButton('AbInitio Modeling', { - height : 20, - width : 100 - }) + "
"; - } - }; -}; - -HPLCAnalysisGrid.prototype._getFramesColumn = function() { - return { - text : 'Frames', - dataIndex : 'datacollection', - name : 'datacollection', - sortable : true, - width : 150, - renderer : function(dataCollections, y, data) { - molmerges = data.raw.framesMerge; - totalframes = data.raw.framesCount; - return molmerges +" - "+ totalframes; - } - }; -}; - - - - -/** - * Rigid body grid to show PDB, symmetry and multiplicity - * - * - * #onUploadFile click on upload file - */ -function AprioriRigidBodyGrid(args) { - - this.height = 250; - this.btnEditVisible = true; - this.btnRemoveVisible = true; - - if (args != null) { - if (args.height != null) { - this.height = args.height; - } - if (args.btnEditVisible != null) { - this.btnEditVisible = args.btnEditVisible; - } - if (args.btnRemoveVisible != null) { - this.btnRemoveVisible = args.btnRemoveVisible; - } - } - - /** Events **/ - this.onUploadFile = new Event(this); - this.onRemove = new Event(this); -} - -AprioriRigidBodyGrid.prototype._getColumns = function() { -}; - -AprioriRigidBodyGrid.prototype._getTopButtons = function() { - var _this = this; - /** Actions buttons **/ - var actions = []; - - /** ADD BUTTON **/ - actions.push(Ext.create('Ext.Action', { - icon : '../images/add.png', - text : 'Add', - disabled : false, - handler : function(widget, event) { - _this.onAddButtonClicked.notify(); - } - })); - - return actions; -}; - -AprioriRigidBodyGrid.prototype.refresh = function(macromolecule) { - this.macromolecule = macromolecule; - if (macromolecule != null){ - this.pdbStore.loadData(macromolecule.structure3VOs); - } -}; - -AprioriRigidBodyGrid.prototype._prepareData = function() { - var data = []; - for ( var i = 0; i < this.features.length; i++) { - data.push(this.features[i]); - } - return data; -}; - -AprioriRigidBodyGrid.prototype._getPlugins = function() { - var _this = this; - var plugins = []; - plugins.push(Ext.create('Ext.grid.plugin.RowEditing', { - clicksToEdit : 1, - listeners : { - validateedit : function(grid, e) { - /** Comments are always updatable* */ - e.record.raw.symmetry = e.newValues.symmetry; - e.record.raw.multiplicity = e.newValues.multiplicity; - - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, proposal) { - BIOSAXS.proposal.setItems(proposal); - _this.refresh(BIOSAXS.proposal.getMacromoleculeById(_this.macromolecule.macromoleculeId)); - _this.panel.setLoading(false); - }); - adapter.onError.attach(function() { - alert("Error"); - }); - - _this.panel.setLoading(); - adapter.saveStructure(e.record.raw); - } - } - })); - return plugins; -}; - -AprioriRigidBodyGrid.prototype.getPanel = function() { - var _this = this; - - this.pdbStore = Ext.create('Ext.data.Store', { - fields : [ 'filePath', 'structureId', 'structureType', 'symmetry', 'structureId', 'name', 'multiplicity' ], - groupField : 'structureType', - sorters : { - property : 'structureId', - direction : 'DESC' - } - }); - - var groupingFeature = Ext.create('Ext.grid.feature.Grouping', { - groupHeaderTpl : Ext.create('Ext.XTemplate', - "
{name:this.formatName}
", { - formatName : function(name) { - return name; - } - }), - hideGroupedHeader : true, - startCollapsed : false - }); - - this.panel = Ext.create('Ext.grid.Panel', { - margin : "15 10 0 10", - height : this.height, - store : this.pdbStore, - plugins : _this._getPlugins(), - tbar : [ { - text : 'Add Modeling Option (PDB)', - icon : '../images/add.png', - handler : function() { - _this.onUploadFile.notify('PDB', 'Upload PDB File'); - } - } - - ], - columns : [ - { - text : "structureId", - flex : 0.2, - hidden : true, - dataIndex : 'structureId', - sortable : true - }, - { - text : "File", - flex : 0.5, - dataIndex : 'filePath', - sortable : true, - hidden : true - }, - { - text : "PDB", - flex : 0.4, - dataIndex : 'name', - sortable : true - }, - { - text : "Symmetry", - flex : 0.2, - dataIndex : 'symmetry', - sortable : true, - editor : { - xtype : 'combobox', - typeAhead : true, - triggerAction : 'all', - selectOnTab : true, - store : [ [ "P1", "P1" ], [ "P2", "P2" ], [ "P3", "P3" ], [ "P4", "P4" ], [ "P5", "P5" ], [ "P6", "P6" ], [ "P32", "P32" ], [ "P42", "P42" ], - [ "P52", "P52" ], [ "P62", "P62" ], [ "P222", "P222" ] ], - } - }, { - text : "Multiplicity", - flex : 0.2, - dataIndex : 'multiplicity', - sortable : true, - editor : { - xtype : 'textfield' - } - - }, { - text : "Subunit", - flex : 0.2, - dataIndex : 'isSubunit', - sortable : true, - hidden : true - }, { - text : "Type", - flex : 0.2, - dataIndex : 'structureType', - sortable : true, - hidden : true - }, - - { - id : this.id + 'REMOVE', - flex : 0.2, - sortable : false, - renderer : function(value, metaData, record, rowIndex, colIndex, store) { - return BUI.getRedButton('REMOVE'); - } - }, ], - - listeners : { - itemdblclick : function(dataview, record, item, e) { - _this._editExperiment(record.raw.experimentId); - }, - cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { - - if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'REMOVE') { - var dataAdapter = new BiosaxsDataAdapter(); - dataAdapter.onSuccess.attach(function() { - _this.panel.setLoading(false); - _this.onRemove.notify(); - }); - _this.panel.setLoading("Removing PDB file"); - dataAdapter.removeStructure(record.data.structureId); - } - - } - }, - viewConfig : { - getRowClass : function(record, rowIdx, params, store) { - if (record.raw.isSubunit != null) { - return "blue-row"; - } - } - } - }); - - return this.panel; -}; - - - - -AprioriRigidBodyGrid.prototype.input = function() { - return { - proposal : DATADOC.getProposal_10(), - dewars : DATADOC.getDewars_10() - - }; -}; - -AprioriRigidBodyGrid.prototype.test = function(targetId) { - var AprioriRigidBodyGrid = new AprioriRigidBodyGrid({ - height : 150 - }); - BIOSAXS.proposal = new Proposal(AprioriRigidBodyGrid.input().proposal); - var panel = AprioriRigidBodyGrid.getPanel(AprioriRigidBodyGrid.input().dewars); - panel.render(targetId); - -}; - -/** - * It shows buffer grid with a top bar with "Add" button - * - * @height - * @searchBar - * @collapsed - * @width - */ -function BufferGrid(args) { - this.height = 500; - this.searchBar = false; - this.tbar = false; - this.collapsed = false; - this.id = BUI.id(); - - if (args != null) { - if (args.height != null) { - this.height = args.height; - } - if (args.searchBar != null) { - this.searchBar = args.searchBar; - } - - if (args.tbar != null) { - this.tbar = args.tbar; - } - - if (args.collapsed != null) { - this.collapsed = args.collapsed; - } - - if (args.width != null) { - this.width = args.width; - } - } -} - -BufferGrid.prototype._edit = function(bufferId) { - var _this = this; - var window = new BufferWindow(); - window.onSuccess.attach(function(sender, proposal) { - _this.store.loadData(BIOSAXS.proposal.getBuffers()); - }); - window.draw(BIOSAXS.proposal.getBufferById(bufferId)); -}; - -BufferGrid.prototype.refresh = function(buffers) { - this.store.loadData(this._prepareData(buffers), false); -}; - -BufferGrid.prototype._prepareData = function(buffers) { - return buffers; -}; - -BufferGrid.prototype._getTbar = function() { - var _this = this; - var actions = []; - - actions.push(Ext.create('Ext.Action', { - icon : '../images/add.png', - text : 'Add buffer', - disabled : false, - handler : function(widget, event) { - var window = new BufferWindow(); - window.onSuccess.attach(function(sender) { - _this.store.loadData(BIOSAXS.proposal.getBuffers()); - }); - window.draw({}); - } - })); - return actions; -}; - -BufferGrid.prototype.getPanel = function(buffers) { - var _this = this; - - this.store = Ext.create('Ext.data.Store', { - fields : [ 'bufferId', 'acronym', 'name', 'composition' ], - data : buffers - }); - - this.store.sort('acronym'); - - var type = 'Ext.grid.Panel'; - if (this.searchBar == true) { - type = 'Ext.ux.LiveSearchGridPanel'; - } - - this.grid = Ext.create(type, { - title : 'Buffers', - collapsible : true, - collapsed : this.collapsed, - store : this.store, - height : this.height, - width : this.width, - columns : [ - /*{ - text : '', - dataIndex : 'bufferId', - width : 20, - renderer : function(val, y, sample) { - return BUI.getRectangleColorDIV(BIOSAXS.proposal.bufferColors[val], 10, 10); - } - },*/ - { - text : 'Acronym', - dataIndex : 'acronym', - flex : 1 - }, { - text : 'Name', - dataIndex : 'name', - flex : 1, - hidden : true - }, { - text : 'Composition', - dataIndex : 'composition', - flex : 1, - hidden : true - }, { - id : _this.id + 'buttonEditBuffer', - width : 80, - sortable : false, - renderer : function(value, metaData, record, rowIndex, colIndex, store) { - return BUI.getGreenButton('EDIT'); - } - }, { - id : _this.id + 'buttonRemoveBuffer', - width : 85, - hidden : true, - sortable : false, - renderer : function(value, metaData, record, rowIndex, colIndex, store) { - return BUI.getRedButton('REMOVE'); - } - } ], - flex : 1, - viewConfig : { - stripeRows : true, - listeners : { - 'celldblclick' : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { - _this._edit(record.data.bufferId); - }, - 'cellclick' : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { - if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonEditBuffer') { - _this._edit(record.data.bufferId); - } - - if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonRemoveBuffer') { - BUI.showBetaWarning(); - } - } - - } - } - }); - - /** Adding the tbar **/ - if (this.tbar) { - this.grid.addDocked({ - xtype : 'toolbar', - items : this._getTbar() - }); - } - return this.grid; -}; - -BufferGrid.prototype.input = function() { - return new MacromoleculeGrid().input(); -}; - -BufferGrid.prototype.test = function(targetId) { - var bufferGrid = new BufferGrid({ - width : 800, - height : 350, - collapsed : false, - tbar : true - }); - - BIOSAXS.proposal = new Proposal(bufferGrid.input().proposal); - var panel = bufferGrid.getPanel(BIOSAXS.proposal.macromolecules); - panel.render(targetId); -}; - -/** - * A shipment may contains one or more cases where stock solutions and sample plates are stored - * - * @height - * @btnEditVisible - * @btnRemoveVisible - * - * #onEditButtonClicked - * #onAddButtonClicked - * #onRemoveButtonClicked - * #onDuplicateButtonClicked - */ -function CaseGrid(args) { - - this.height = 100; - this.btnEditVisible = true; - this.btnRemoveVisible = true; - - if (args != null) { - if (args.height != null) { - this.height = args.height; - } - if (args.btnEditVisible != null) { - this.btnEditVisible = args.btnEditVisible; - } - if (args.btnRemoveVisible != null) { - this.btnRemoveVisible = args.btnRemoveVisible; - } - } - - /** Events **/ - this.onEditButtonClicked = new Event(this); - this.onAddButtonClicked = new Event(this); - this.onRemoveButtonClicked = new Event(this); - this.onDuplicateButtonClicked = new Event(this); -} - -CaseGrid.prototype._getColumns = function() { - var _this = this; - - function actionItemRenderer(value, meta, record, rowIx, ColIx, store) { - /*if (record.raw.specimen3VOs.length > 0){ - return 'x-hide-display'; - }*/ - } - - var columns = [ - { - header : 'Code', - dataIndex : 'code', - name : 'code', - type : 'string', - flex : 1 - }, - { - header : 'Bar Code', - dataIndex : 'barCode', - name : 'barCode', - type : 'string', - flex : 1, - hidden : true - }, - { - header : 'BeamLine', - dataIndex : 'barCode', - type : 'string', - flex : 1, - renderer : function(comp, val, record) { - if (record.raw.sessionVO != null) { - return "" + record.raw.sessionVO.beamlineName + ""; - } - } - }, - { - header : 'Session', - dataIndex : 'barCode', - type : 'string', - flex : 1, - renderer : function(comp, val, record) { - if (record.raw.sessionVO != null) { - return "" + moment(record.raw.sessionVO.startDate).format("MMM Do YY") + ""; - } - } - }, - { - header : 'Status', - dataIndex : 'dewarStatus', - name : 'dewarStatus', - type : 'string', - flex : 1, - renderer : function(comp, val, record) { - if (new String(record.raw.dewarStatus).toUpperCase() == 'OPENED') { - return "" + new String(record.raw.dewarStatus).toUpperCase() + ""; - } - return "" + new String(record.raw.dewarStatus).toUpperCase() + ""; - } - }, - { - header : 'Type', - dataIndex : 'type', - name : 'type', - type : 'string', - flex : 1, - hidden : true - }, - { - header : 'Sample Plates', - dataIndex : 'plates', - name : 'plates', - type : 'string', - flex : 1, - hidden : true - }, - { - header : 'Stock Solutions', - dataIndex : 'plates', - name : 'plates', - type : 'string', - width : 100, - renderer : function(comp, val, record) { - var html = "
"; - var stockSolutions = BIOSAXS.proposal.getStockSolutionsByDewarId(record.raw.dewarId); - html = html + "" + stockSolutions.length + " x"; - return html + "
"; - } - }, { - header : 'isStorageDewar', - dataIndex : 'isStorageDewar', - name : 'isStorageDewar', - type : 'string', - flex : 1, - hidden : true - }, { - header : 'Tracking Number From Synchrotron', - dataIndex : 'trackingNumberFromSynchrotron', - name : 'trackingNumberFromSynchrotron', - type : 'string', - flex : 1, - hidden : true - }, { - header : 'Tracking Number To Synchrotron', - dataIndex : 'trackingNumberToSynchrotron', - name : 'trackingNumberToSynchrotron', - type : 'string', - flex : 1, - hidden : true - }, { - header : 'Storage Location', - dataIndex : 'storageLocation', - name : 'storageLocation', - type : 'string', - flex : 1, - hidden : false - }, { - header : 'Comments', - dataIndex : 'comments', - name : 'comments', - type : 'string', - flex : 1, - hidden : false - } - ]; - - if (this.btnEditVisible) { - columns.push({ - id : _this.id + 'buttonEdit', - text : '', - hidden : !_this.btnEditVisible, - width : 85, - sortable : false, - renderer : function(value, metaData, record, rowIndex, colIndex, store) { - return BUI.getGreenButton('EDIT'); - } - }); - } - - if (this.btnRemoveVisible) { - columns.push({ - id : _this.id + 'buttonRemove', - width : 85, - sortable : false, - renderer : function(value, metaData, record, rowIndex, colIndex, store) { - if (_this.btnRemoveVisible) { - return BUI.getRedButton('REMOVE'); - } - } - }); - } - - columns.push({ - dataIndex : 'comments', - type : 'string', - width : 85, - hidden : false, - renderer : function(comp, val, record) { - return BUI.getBlueButton("LABELS", { - href : BUI.getPrintcomponentURL(record.raw.dewarId) - }); - } - }); - - return columns; -}; - -CaseGrid.prototype._getTopButtons = function() { - var _this = this; - /** Actions buttons **/ - var actions = []; - - /** ADD BUTTON **/ - actions.push(Ext.create('Ext.Action', { - icon : '../images/add.png', - text : 'Add', - disabled : false, - handler : function(widget, event) { - _this.onAddButtonClicked.notify(); - } - })); - - return actions; -}; - -CaseGrid.prototype.refresh = function(dewars) { - this.features = dewars; - this.store.loadData(this._prepareData(dewars), false); -}; - -CaseGrid.prototype._sort = function(store) { - store.sort('dewarId', 'DESC'); -}; - -CaseGrid.prototype._prepareData = function() { - var data = []; - for ( var i = 0; i < this.features.length; i++) { - data.push(this.features[i]); - } - return data; -}; - -CaseGrid.prototype.getPanel = function(dewars, plates) { - this.features = dewars; - this.plates = plates; - return this._renderGrid(); -}; - -CaseGrid.prototype._edit = function(dewar) { - var _this = this; - var caseWindow = new CaseWindow(); - /**SAVED **/ - caseWindow.onSaved.attach(function(sender, dewar) { - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, shipment) { - _this.refresh(shipment.dewarVOs); - }); - adapter.saveCase(BIOSAXS.proposal.getShipmentByDewarId(dewar.dewarId).shippingId, dewar); - }); - caseWindow.draw(dewar); -}; - -CaseGrid.prototype._getStoreFields = function(data) { - return [ { - name : 'dewarId', - type : 'string' - }, { - name : 'barCode', - type : 'string' - }, { - name : 'code', - type : 'string' - }, { - name : 'comments', - type : 'string' - }, { - name : 'dewarStatus', - type : 'string' - }, { - name : 'isStorageDewar', - type : 'string' - }, { - name : 'plates', - type : 'string' - }, { - name : 'transportValue', - type : 'string' - }, { - name : 'trackingNumberFromSynchrotron', - type : 'string' - }, { - name : 'trackingNumberToSynchrotron', - type : 'string' - }, { - name : 'timeStamp', - type : 'string' - }, { - name : 'storageLocation', - type : 'string' - }, { - name : 'type', - type : 'string' - } - - ]; -}; - -CaseGrid.prototype._renderGrid = function() { - var _this = this; - - /** Store **/ - var data = this._prepareData(); - this.store = Ext.create('Ext.data.Store', { - fields : this._getStoreFields(data), - autoload : true, - data : data - }); - this._sort(this.store); - - this.store.loadData(data, false); - - this.grid = Ext.create('Ext.grid.Panel', { - style : { - padding : 5 - }, - height : this.height, - store : this.store, - columns : this._getColumns(), - viewConfig : { - stripeRows : true, - listeners : { - itemdblclick : function(dataview, record, item, e) { - _this._edit(record.raw); - }, - cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { - if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonEdit') { - _this._edit(record.raw); - } - - if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonRemove') { - _this.onRemoveButtonClicked.notify(_this.store.getAt(rowIndex).raw.dewarId); - } - } - } - }, - selModel : { - mode : 'SINGLE' - } - }); - - var actions = _this._getTopButtons(); - this.grid.addDocked({ - xtype : 'toolbar', - items : actions - }); - - this.grid.getSelectionModel().on({ - selectionchange : function(sm, selections) { - if (selections.length) { - for ( var i = 0; i < actions.length; i++) { - if (actions[i].enable) { - actions[i].enable(); - } - } - } else { - for ( var i = 0; i < actions.length; i++) { - if (actions[i].alwaysEnabled == false) { - if (actions[i].disable) { - actions[i].disable(); - } - } - } - } - } - }); - return this.grid; -}; - -CaseGrid.prototype.input = function() { - return { - proposal : DATADOC.getProposal_10(), - dewars : DATADOC.getDewars_10() - - }; -}; - -CaseGrid.prototype.test = function(targetId) { - var CaseGrid = new CaseGrid({ - height : 150 - }); - BIOSAXS.proposal = new Proposal(CaseGrid.input().proposal); - var panel = CaseGrid.getPanel(CaseGrid.input().dewars); - panel.render(targetId); - -}; - -/** - * A shipment may contains one or more cases where stock solutions and sample plates are stored - * - * @height - * @btnEditVisible - * @btnRemoveVisible - * - * #onEditButtonClicked - * #onAddButtonClicked - * #onRemoveButtonClicked - * #onDuplicateButtonClicked - */ -function ExampleGrid(args) { - - this.height = 100; - this.btnEditVisible = true; - this.btnRemoveVisible = true; - - if (args != null) { - if (args.height != null) { - this.height = args.height; - } - if (args.btnEditVisible != null) { - this.btnEditVisible = args.btnEditVisible; - } - if (args.btnRemoveVisible != null) { - this.btnRemoveVisible = args.btnRemoveVisible; - } - } - - /** Events **/ - this.onEditButtonClicked = new Event(this); - this.onAddButtonClicked = new Event(this); - this.onRemoveButtonClicked = new Event(this); - this.onDuplicateButtonClicked = new Event(this); -} - -ExampleGrid.prototype._getColumns = function() { - var _this = this; - - function actionItemRenderer(value, meta, record, rowIx, ColIx, store) { - /*if (record.raw.specimen3VOs.length > 0){ - return 'x-hide-display'; - }*/ - } - - var columns = [ - { - header : 'Code', - dataIndex : 'code', - name : 'code', - type : 'string', - flex : 1 - }, - { - header : 'Bar Code', - dataIndex : 'barCode', - name : 'barCode', - type : 'string', - flex : 1, - hidden : true - }, - { - header : 'BeamLine', - dataIndex : 'barCode', - type : 'string', - flex : 1, - renderer : function(comp, val, record) { - if (record.raw.sessionVO != null) { - return "" + record.raw.sessionVO.beamlineName + ""; - } - } - }, - { - header : 'Session', - dataIndex : 'barCode', - type : 'string', - flex : 1, - renderer : function(comp, val, record) { - if (record.raw.sessionVO != null) { - return "" + moment(record.raw.sessionVO.startDate).format("MMM Do YY") + ""; - } - } - }, - { - header : 'Status', - dataIndex : 'dewarStatus', - name : 'dewarStatus', - type : 'string', - flex : 1, - renderer : function(comp, val, record) { - if (new String(record.raw.dewarStatus).toUpperCase() == 'OPENED') { - return "" + new String(record.raw.dewarStatus).toUpperCase() + ""; - } - return "" + new String(record.raw.dewarStatus).toUpperCase() + ""; - } - }, - { - header : 'Type', - dataIndex : 'type', - name : 'type', - type : 'string', - flex : 1, - hidden : true - }, - { - header : 'Sample Plates', - dataIndex : 'plates', - name : 'plates', - type : 'string', - flex : 1, - hidden : true - }, - { - header : 'Stock Solutions', - dataIndex : 'plates', - name : 'plates', - type : 'string', - width : 100, - renderer : function(comp, val, record) { - var html = "
"; - var stockSolutions = BIOSAXS.proposal.getStockSolutionsByDewarId(record.raw.dewarId); - html = html + "" + stockSolutions.length + " x"; - return html + "
"; - } - }, { - header : 'isStorageDewar', - dataIndex : 'isStorageDewar', - name : 'isStorageDewar', - type : 'string', - flex : 1, - hidden : true - }, { - header : 'Tracking Number From Synchrotron', - dataIndex : 'trackingNumberFromSynchrotron', - name : 'trackingNumberFromSynchrotron', - type : 'string', - flex : 1, - hidden : true - }, { - header : 'Tracking Number To Synchrotron', - dataIndex : 'trackingNumberToSynchrotron', - name : 'trackingNumberToSynchrotron', - type : 'string', - flex : 1, - hidden : true - }, { - header : 'Storage Location', - dataIndex : 'storageLocation', - name : 'storageLocation', - type : 'string', - flex : 1, - hidden : false - }, { - header : 'Comments', - dataIndex : 'comments', - name : 'comments', - type : 'string', - flex : 1, - hidden : false - } - ]; - - if (this.btnEditVisible) { - columns.push({ - id : _this.id + 'buttonEdit', - text : '', - hidden : !_this.btnEditVisible, - width : 85, - sortable : false, - renderer : function(value, metaData, record, rowIndex, colIndex, store) { - return BUI.getGreenButton('EDIT'); - } - }); - } - - if (this.btnRemoveVisible) { - columns.push({ - id : _this.id + 'buttonRemove', - width : 85, - sortable : false, - renderer : function(value, metaData, record, rowIndex, colIndex, store) { - if (_this.btnRemoveVisible) { - return BUI.getRedButton('REMOVE'); - } - } - }); - } - - columns.push({ - dataIndex : 'comments', - type : 'string', - width : 85, - hidden : false, - renderer : function(comp, val, record) { - return BUI.getBlueButton("LABELS", { - href : BUI.getPrintcomponentURL(record.raw.dewarId) - }); - } - }); - - return columns; -}; - -ExampleGrid.prototype._getTopButtons = function() { - var _this = this; - /** Actions buttons **/ - var actions = []; - - /** ADD BUTTON **/ - actions.push(Ext.create('Ext.Action', { - icon : '../images/add.png', - text : 'Add', - disabled : false, - handler : function(widget, event) { - _this.onAddButtonClicked.notify(); - } - })); - - return actions; -}; - -ExampleGrid.prototype.refresh = function(dewars) { - this.features = dewars; - this.store.loadData(this._prepareData(dewars), false); -}; - -ExampleGrid.prototype._sort = function(store) { - store.sort('dewarId', 'DESC'); -}; - -ExampleGrid.prototype._prepareData = function() { - var data = []; - for ( var i = 0; i < this.features.length; i++) { - data.push(this.features[i]); - } - return data; -}; - -ExampleGrid.prototype.getPanel = function() { - this.grid = Ext.create('Ext.grid.Panel', { - style : { - padding : 5 - }, - height : this.height, - store : this.store, - columns : this._getColumns(), - viewConfig : { - stripeRows : true, - listeners : { - itemdblclick : function(dataview, record, item, e) { - _this._edit(record.raw); - }, - cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { - if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonEdit') { - _this._edit(record.raw); - } - - if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonRemove') { - _this.onRemoveButtonClicked.notify(_this.store.getAt(rowIndex).raw.dewarId); - } - } - } - }, - selModel : { - mode : 'SINGLE' - } - }); - - var actions = _this._getTopButtons(); - this.grid.addDocked({ - xtype : 'toolbar', - items : actions - }); - - this.grid.getSelectionModel().on({ - selectionchange : function(sm, selections) { - if (selections.length) { - for ( var i = 0; i < actions.length; i++) { - if (actions[i].enable) { - actions[i].enable(); - } - } - } else { - for ( var i = 0; i < actions.length; i++) { - if (actions[i].alwaysEnabled == false) { - if (actions[i].disable) { - actions[i].disable(); - } - } - } - } - } - }); - return this.grid; -}; - - -ExampleGrid.prototype._getStoreFields = function(data) { - return [ { - name : 'dewarId', - type : 'string' - }, { - name : 'barCode', - type : 'string' - }, { - name : 'code', - type : 'string' - }, { - name : 'comments', - type : 'string' - }, { - name : 'dewarStatus', - type : 'string' - }, { - name : 'isStorageDewar', - type : 'string' - }, { - name : 'plates', - type : 'string' - }, { - name : 'transportValue', - type : 'string' - }, { - name : 'trackingNumberFromSynchrotron', - type : 'string' - }, { - name : 'trackingNumberToSynchrotron', - type : 'string' - }, { - name : 'timeStamp', - type : 'string' - }, { - name : 'storageLocation', - type : 'string' - }, { - name : 'type', - type : 'string' - } - - ]; -}; - -ExampleGrid.prototype._renderGrid = function() { - var _this = this; - - /** Store **/ - var data = this._prepareData(); - this.store = Ext.create('Ext.data.Store', { - fields : this._getStoreFields(data), - autoload : true, - data : data - }); - this._sort(this.store); - - this.store.loadData(data, false); - - this.grid = Ext.create('Ext.grid.Panel', { - style : { - padding : 5 - }, - height : this.height, - store : this.store, - columns : this._getColumns(), - viewConfig : { - stripeRows : true, - listeners : { - itemdblclick : function(dataview, record, item, e) { - _this._edit(record.raw); - }, - cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { - if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonEdit') { - _this._edit(record.raw); - } - - if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonRemove') { - _this.onRemoveButtonClicked.notify(_this.store.getAt(rowIndex).raw.dewarId); - } - } - } - }, - selModel : { - mode : 'SINGLE' - } - }); - - var actions = _this._getTopButtons(); - this.grid.addDocked({ - xtype : 'toolbar', - items : actions - }); - - this.grid.getSelectionModel().on({ - selectionchange : function(sm, selections) { - if (selections.length) { - for ( var i = 0; i < actions.length; i++) { - if (actions[i].enable) { - actions[i].enable(); - } - } - } else { - for ( var i = 0; i < actions.length; i++) { - if (actions[i].alwaysEnabled == false) { - if (actions[i].disable) { - actions[i].disable(); - } - } - } - } - } - }); - return this.grid; -}; - -ExampleGrid.prototype.input = function() { - return { - proposal : DATADOC.getProposal_10(), - dewars : DATADOC.getDewars_10() - - }; -}; - -ExampleGrid.prototype.test = function(targetId) { - var ExampleGrid = new ExampleGrid({ - height : 150 - }); - BIOSAXS.proposal = new Proposal(ExampleGrid.input().proposal); - var panel = ExampleGrid.getPanel(ExampleGrid.input().dewars); - panel.render(targetId); - -}; - - -/** - * Shows a list of experiment AKA data acquisitions - * @height - * @sorters - * @minHeight - * @gridType: Ext.ux.LiveSearchGridPanel or Ext.grid.Panel - * @tbar true or false - * @grouping true or false - * @width - * @title - * #onEditButtonClicked - */ -function ExperimentGrid(args) { - this.width = "100%"; - this.height = 700; - this.minHeight = 500; - - this.id = BUI.id(); - this.gridType = 'Ext.grid.Panel'; //'Ext.ux.LiveSearchGridPanel'; - this.tbar = false; - this.hideHeaders = false; - this.grouping = true; - this.title = null; - - this.filtered = null; - - /** maximum row count **/ - this.limit = 100; - - this.sessionIdFilter = null; - - /** if not null filtered by date **/ - this.date = null; - - this.sorters = [ { - property : 'date', - direction : 'DESC' - }, { - property : 'time', - direction : 'DESC' - } ]; - - this.dates = {}; - if (args != null) { - if (args.height != null) { - this.height = args.height; - } - if (args.limit != null) { - this.limit = args.limit; - } - if (args.sorters != null) { - this.sorters = args.sorters; - } - if (args.sessionId != null){ - this.sessionIdFilter = args.sessionId; - } - if (args.filtered != null) { - this.filtered = args.filtered; - } - if (args.minHeight != null) { - this.minHeight = args.minHeight; - } - if (args.gridType != null) { - this.gridType = args.gridType; - } - if (args.tbar != null) { - this.tbar = args.tbar; - } - if (args.grouping != null) { - this.grouping = args.grouping; - } - if (args.width != null) { - this.width = args.width; - } - if (args.title != null) { - this.title = args.title; - } - - } - /** Events **/ - this.onEditButtonClicked = new Event(this); -} - -ExperimentGrid.prototype._getFilterTypes = function() { - return []; -}; - -ExperimentGrid.prototype._prepareData = function(rows) { - var data = []; - var count = 0; - - rows.sort(function(a,b){return b.experimentId - a.experimentId;}); - for ( var i = 0; i < rows.length; i++) { - var row = rows[i]; - this.dates[moment(row.creationDate).format("YYYYMMDD")] = moment(row.creationDate).format("MMM Do YY"); - if ( - ( this.filtered == null || row.experimentType == this.filtered ) && (count < this.limit || this.limit == null ) && (this.sessionIdFilter == null || this.date != null || (this.date == null && this.sessionIdFilter == row.sessionId)) - ){ - - data.push({ - experimentId : row.experimentId, - status : row.status, - dataAcquisitionFilePath : row.dataAcquisitionFilePath, - type : row.experimentType, - name : row.name, - macromolecules_names : row.macromolecules, - percentageAnalysed : { - value : (row.dataCollectionDoneCount / row.dataCollectionCount) * 100, - text : row.dataCollectionDoneCount + " of " + row.dataCollectionCount - }, - percentageCollected : { - value : (row.measurementDoneCount / row.measurementCount) * 100, - text : row.measurementDoneCount + " of " + row.measurementCount - }, - percentageMerged : { - value : (row.measurementAveragedCount / row.measurementCount) * 100, - text : row.measurementAveragedCount + " of " + row.measurementCount - }, - date : moment(row.creationDate).format("YYYYMMDD"), - time : moment(row.creationDate).format("YYYYMMDDHHmmss"), - creationDate : row.creationDate - }); - count ++; - } - } - return data; -}; - -ExperimentGrid.prototype.getPanel = function(experiments) { - this.features = experiments; - return this._renderGrid(experiments); -}; - -ExperimentGrid.prototype.refresh = function(experiments) { - this.experiments = experiments; - var filtered = []; - for ( var i = 0; i < experiments.length; i++) { - if (experiments[i].experimentType != "TEMPLATE") { - filtered.push(experiments[i]); - } - } - - this.parsedData = this._prepareData(filtered); - - var day = {}; - var dates = []; - for ( var i = 0; i < this.experiments.length; i++) { - var date = moment(this.experiments[i].creationDate).format("MMM Do YYYY"); - if (day[date] == null){ - dates.push({ - date : date, - value : moment(this.experiments[i].creationDate) - }); - day[date] = true; - } - } - - this.storeDate.loadData(dates, false); - this.store.loadData(this.parsedData, false); - - /** If it has already been filtered by date we keep the filter **/ - if (this.date != null){ - this._filterByDate(this.date); - } -}; - -ExperimentGrid.prototype._getTopButtons = function() { - var _this = this; - /** Actions buttons **/ - var actions = []; - - actions.push(Ext.create('Ext.Action', { - icon : '../images/calendar_icon.png', - text : 'Show Calendar', - disabled : false, - handler : function(widget, event) { - var window = Ext.create('Ext.window.Window', { - title : 'Calendar', - width : 600, - height : 600, - modal : true, - closable : true, - layout : { - type : 'vbox', - align : 'stretch' - }, - items : [ { - xtype : 'label', - html : 'Click on a data acquisition to select:', - margin : '5 5 5 5' - }, { - html : '
', - margin : '5 5 5 5' - } - - ] - }).show(); - - var calendarWidget = new CalendarWidget({ - height : 450 - }); - var aux = _this.limit; - /** we remove the limit temporarily **/ - _this.limit = null; - _this.sessionIdFilter = null; - calendarWidget.loadData(_this._prepareData(_this.experiments)); - _this.limit = aux; - calendarWidget.draw('calendar'); - calendarWidget.onClick.attach(function(sender, date) { - date = moment(date, "YYYY-MM-DD"); - _this._filterByDate(date); - window.close(); - - }); - - } - })); - this.storeDate = Ext.create('Ext.data.ArrayStore', { - fields: ['date', 'value'], - data : [] - }); - - this.dateMenu = Ext.create('Ext.form.field.ComboBox', { - hideLabel: true, - store: this.storeDate, - displayField: 'date', - typeAhead: true, - queryMode: 'local', - margin : '0 0 0 30', - triggerAction: 'all', - emptyText:'Select a date...', - selectOnFocus:true, - width:135, - listeners:{ - scope: this, - 'select': function (a,b,c){ - _this.limit = null; - _this._filterByDate(moment(b[0].raw.value, "YYYY-MM-DD")); - } - } - }); - - actions.push(this.dateMenu); - - actions.push("->"); - if (_this.filtered != null){ - actions.push({ - html : "Experiment Type: " + _this.filtered +"" - }); - } - else{ - actions.push({ - html : "Experiment Type: ALL" - }); - } - return actions; -}; - -/** - * Date format: "YYYY-MM-DD" - */ -ExperimentGrid.prototype._filterByDate = function(date) { - var experimentsFiltered = []; - /** Getting all the experiments of date**/ - for ( var i = 0; i < this.experiments.length; i++) { - var experiment = this.experiments[i]; - if (experiment.creationDate != null) { - var experimentDate = moment(experiment.creationDate); - if (experimentDate.year() == date.year()) { - if (experimentDate.month() == date.month()) { - if (experimentDate.date() == date.date()) { - experimentsFiltered.push(experiment); - } - } - } - } - } - var parsedData = this._prepareData(experimentsFiltered); - this.store.loadData(parsedData); - this.date = date; -}; - -/** Only for templates **/ -ExperimentGrid.prototype._removeExperimentById = function(experimentId) { - var _this = this; - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(evt, args) { - _this.grid.setLoading(false); - document.getElementById(BIOSAXS.targetId).innerHTML = ""; - BIOSAXS.start(BIOSAXS.targetId); - }); - this.grid.setLoading("Removing experiment "); - adapter.removeExperimentById(experimentId); -}; - -ExperimentGrid.prototype._renderGrid = function() { - var _this = this; - - /** Store **/ - this.store = Ext.create('Ext.data.Store', { - fields : this._getColumns(), - groupField : 'date', - autoload : true, - data : [], - remoteSort: false, - sorters : this.sorters - }); - - - - - var groupingFeature = Ext.create('Ext.grid.feature.Grouping', { - groupHeaderTpl : Ext.create('Ext.XTemplate', - "
{name:this.formatName}
", { - formatName : function(name) { - return _this.dates[name]; - } - }), - hideGroupedHeader : true, - startCollapsed : false - }); - - this.features = []; - if (this.grouping) { - this.features.push(groupingFeature); - } - - /** Grid **/ - this.grid = Ext.create(this.gridType, { - hideHeaders : this.hideHeaders, - resizable : true, - title : this.title, - width : this.width, - minHeight : this.minHeight, - height : this.height, - features : this.features, - store : this.store, - columns : this._getColumns(), - selModel : { - mode : 'SINGLE' - }, - viewConfig : { - stripeRows : true, - getRowClass : function(record, rowIdx, params, store) { - if (record.raw.type == "TEMPLATE") { - return "template-color-row"; - } - if ((record.raw.type == "CALIBRATION") && (record.raw.status == "FINISHED")) { - return "blue-row"; - } - }, - listeners : { - itemdblclick : function(dataview, record, item, e) { - _this._editExperiment(record.raw.experimentId); - }, - cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { - if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'GO') { - _this._editExperiment(record.raw.experimentId); - } - - if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'REMOVE') { - _this._removeExperimentById(record.raw.experimentId); - } - - } - } - } - }); - - var actions = _this._getTopButtons(); - - if (this.tbar) { - this.grid.addDocked({ - xtype : 'toolbar', - items : actions - }); - this.grid.getSelectionModel().on({ - selectionchange : function(sm, selections) { - if (selections.length) { - for ( var i = 0; i < actions.length; i++) { - if (actions[i].enable) { - actions[i].enable(); - } - } - } else { - for ( var i = 0; i < actions.length; i++) { - if (actions[i].alwaysEnabled == false) { - if (actions[i].disable) { - actions[i].disable(); - } - } - } - } - } - }); - } - - return this.grid; -}; - -ExperimentGrid.prototype._getColumns = function() { - var _this = this; - function actionItemRenderer(value, meta, record, rowIx, ColIx, store) { - if (record.raw.buffer3VOs != null) { - if (record.raw.buffer3VOs.length > 0) { - return 'x-hide-display'; - } - } - - if (record.data.platesCount > 0) { - return 'x-hide-display'; - } - } - - return [ - { - text : 'experimentId', - dataIndex : 'experimentId', - name : 'experimentId', - type : 'string', - hidden : true - }, - { - xtype : 'rownumberer', - width : 40 - }, - { - text : 'Name', - dataIndex : 'name', - name : 'name', - type : 'string', - flex : 1 - }, - - { - text : 'Type', - dataIndex : 'type', - name : 'type', - type : 'string', - flex : 1, - renderer : function(val) { - if (val == "CALIBRATION") { - return "" + val + ""; - } - - return val; - } - }, - { - text : 'Macromolecules', - name : 'macromolecules_names', - dataIndex : 'macromolecules_names', - flex : 1, - renderer : function(val) { - if (val != null) { - return " " + val + ""; - } - return " Information not available"; - } - }, - { - text : 'Buffers', - dataIndex : 'buffer_names', - name : 'buffer_names', - flex : 1, - hidden : true, - renderer : function(val) { - return "Buffer/s: " + val + ""; - } - }, - { - text : 'Status', - dataIndex : 'status', - name : 'status', - type : 'string', - flex : 1, - renderer : function(val, x, sample) { - if (sample.raw.type == "TEMPLATE") { - return "READY"; - } - if (sample.raw.status == "ABORTED") { - return "" + val + ""; - } - return "" + val + ""; - } - }, - { - text : 'Download', - dataIndex : 'creationDate', - name : 'creationDate', - renderer : function(val, x, sample) { - if (sample != null) { - if (sample.raw.type == "HPLC") { - return; - } - return BUI.getZipHTMLByExperimentId(sample.raw.experimentId, sample.raw.name); - } - }, - width : 100 - - }, - { - header : 'Measurements', - dataIndex : 'percentageCollected', - name : 'percentageCollected', - type : 'string', - renderer : function(val, y, sample) { - if ((sample.raw.type == "TEMPLATE") || (sample.raw.type == "HPLC")) { - return; - } - return "
" + BUI.getProgessBar(sample.raw.percentageCollected.value, sample.raw.percentageCollected.text) + "
"; - }, - width : 100 - }, - { - header : 'Averaged', - dataIndex : 'percentageMerged', - name : 'percentageMerged', - type : 'string', - renderer : function(val, y, sample) { - if ((sample.raw.type == "TEMPLATE") || (sample.raw.type == "HPLC")) { - return; - } - return "
" + BUI.getProgessBar(sample.raw.percentageMerged.value, sample.raw.percentageMerged.text) + "
"; - }, - width : 100 - }, - { - header : 'Subtractions', - dataIndex : 'percentageAnalysed', - name : 'percentageAnalysed', - type : 'string', - renderer : function(val, y, sample) { - if ((sample.raw.type == "TEMPLATE") || (sample.raw.type == "HPLC")) { - return; - } - return "
" + BUI.getProgessBar(sample.raw.percentageAnalysed.value, sample.raw.percentageAnalysed.text) + "
"; - }, - width : 100 - }, - - { - text : 'time', - dataIndex : 'time', - name : 'time', - hidden : true, - renderer : function(val) { - return val; - }, - width : 100 - - }, { - text : 'Date', - dataIndex : 'date', - name : 'date', - renderer : function(val) { - return val; - }, - width : 100 - - }, - - { - text : 'Time', - dataIndex : 'creationDate', - name : 'creationDate', - renderer : function(val) { - return moment(val).format(" HH:mm:ss"); - }, - width : 100 - - }, { - id : _this.id + 'GO', - width : 80, - sortable : false, - renderer : function(value, metaData, record, rowIndex, colIndex, store) { - return BUI.getGreenButton('GO'); - } - } ]; -}; - -/** Changes location.href in order to edit the experiment **/ -ExperimentGrid.prototype._editExperiment = function(experimentId) { - if (Ext.urlDecode(window.location.href).sessionId != null) { - location.href = 'viewProjectList.do?reqCode=display&experimentId=' + experimentId + '&sessionId='+ Ext.urlDecode(window.location.href).sessionId; - } else { - location.href = 'viewProjectList.do?reqCode=display&experimentId=' + experimentId; - } -}; - -ExperimentGrid.prototype.input = function() { - var experiments = DATADOC.getExperimentList_10(); - return { - experiments : experiments, - proposal : new MeasurementGrid().input().proposal - - }; -}; - -ExperimentGrid.prototype.test = function(targetId) { - var experimentGrid = new ExperimentGrid({ - height : 350, - minHeight : 350, - width : 1000 - - }); - BIOSAXS.proposal = new Proposal(experimentGrid.input().proposal); - var panel = experimentGrid.getPanel(experimentGrid.input().experiments); - experimentGrid.refresh(experimentGrid.input().experiments); - panel.render(targetId); -}; - -function FitStructureToExperimentDataGrid(args) { - this.height = null; - this.width = null; - this.id = BUI.id(); - - if (args != null) { - if (args.height != null) { - this.height = args.height; - - if (args.width != null) { - this.width = args.width; - } - } - } - - this.onSelected = new Event(this); -} - -FitStructureToExperimentDataGrid.prototype.getStructuresByMacromolecule = function(macromolecule) { - var structures = []; - if (macromolecule.structure3VOs != null) { - if (macromolecule.structure3VOs.length > 0) { - for (var i = 0; i < macromolecule.structure3VOs.length; i++) { - structures.push(macromolecule.structure3VOs[i]); - } - } - } - return structures; -}; - -FitStructureToExperimentDataGrid.prototype._prepareData = function(subtractions) { - var data = []; - for (var i = 0; i < subtractions.length; i++) { - - for (var j = 0; j < subtractions[i].fitStructureToExperimentalData3VOs.length; j++) { - var fit = subtractions[i].fitStructureToExperimentalData3VOs[j]; - data.push({ - fit : fit.fitFilePath, - fitStructureToExperimentalDataId : fit.fitStructureToExperimentalDataId, - mixtureToStructure3VOs : fit.mixtureToStructure3VOs, - subtractedFile : subtractions[i].substractedFilePath - }); - } - } - return data; - -}; - -FitStructureToExperimentDataGrid.prototype.refresh = function(subtractions) { - this.store.loadData(this._prepareData(subtractions), false); -}; - -FitStructureToExperimentDataGrid.prototype.getPanel = function() { - var _this = this; - this.store = Ext.create('Ext.data.Store', { - fields : [ 'fit', 'subtractedFile', 'structureId', 'fitFilePath', 'mixtureToStructure3VOs' ], - data : [] - }); - - this.store.sort([ { - property : 'name', - direction : 'ASC' - } ]); - - this.selModel = Ext.create('Ext.selection.RowModel', { - allowDeselect : true, - mode : 'MULTI', - listeners : { - selectionchange : function(sm, selections) { - var selected = []; - for (var i = 0; i < selections.length; i++) { - selected.push(selections[i].raw); - } - _this.onSelected.notify(selected); - } - } - }); - - this.panel = Ext.create('Ext.grid.Panel', { - store : this.store, - deferredRender : false, - width : this.width, - height : this.height, - margin : 10, - selModel : this.selModel, - columns : [ { - text : 'Name', - dataIndex : 'fit', - flex : 1, - renderer : function(val, b, record) { - return BUI.getFileName(val); - } - }, { - text : 'PDB', - dataIndex : 'mixtureToStructure3VOs', - flex : 1, - renderer : function(values, b, record) { - var html = ""; - for (var i = 0; i < values.length; i++) { - var structure = BIOSAXS.proposal.getStructuresById(values[i].structureId); - html = html + ""; - if (structure != null){ - html = html + ""; - } - html = html + ""; - } - return html + "
" + structure.name + "
"; - } - }, { - text : 'Volume Fraction', - dataIndex : 'mixtureToStructure3VOs', - flex : 1, - renderer : function(values, b, record) { - var html = ""; - for (var i = 0; i < values.length; i++) { - html = html + ""; - html = html + ""; - html = html + ""; - } - - return html + "
" + values[i].volumeFraction + "
"; - } - },{ - text : 'Subtraction', - dataIndex : 'subtractedFile', - flex : 1, - renderer : function(values, b, record) { - return values.split("/")[values.split("/").length - 1]; - } - },], - - viewConfig : { - enableTextSelection : true, - preserveScrollOnRefresh : true - }, - listeners : { - cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { - }, - afterrender : function() { - } - } - }); - return this.panel; -}; - -/** Static method **/ -FitStructureToExperimentDataGrid.prototype.RUNFitScattering = function(subtractionId, structureId) { - var _this = this; - /** Add to Workflow **/ - var adapter = new BiosaxsDataAdapter(); - var workflow = { - 'workflowTitle' : 'FitExperimentalDatatoStructure', - 'comments' : 'FitExperimentalDatatoStructure run from ISPyB for subtractionId: ' + subtractionId + ' and structureId ' + structureId - } - - var inputs = [ { - name : 'subtractionId', - value : subtractionId - }, { - name : 'structureId', - value : structureId - } ]; - - adapter.onSuccess.attach(function(sender, data) { - /** Add to Fit **/ - var adapter2 = new BiosaxsDataAdapter(); - var fit = { - 'workflowId' : data.workflowId, - 'subtractionId' : subtractionId, - 'structureId' : structureId, - 'comments' : 'Sent to workflow engine' - } - adapter2.onSuccess.attach(function(sender, fit) { - _this.refresh(_this.subtractionId, _this.macromolecule); - }); - adapter2.addFitStructureData(fit); - - }); - adapter.addWorkflow(workflow, inputs); - -}; +/** + * Example form + * + * @witdh + * @height + */ +function MolarityForm(args) { + this.id = BUI.id(); + this.width = 700; + this.height = 500; + + if (args != null) { + if (args.width != null) { + this.width = args.width; + } + if (args.height != null) { + this.height = args.height; + } + } + + this.onSave = new Event(this); + this.onClose = new Event(this); +} + -/** - * It shows buffer grid with a top bar with "Add" button - * - * @height - * @searchBar - * @collapsed - * @width - */ -function FrameGrid(args) { - this.height = 500; - this.width = 500; - this.searchBar = false; - this.tbar = false; - this.collapsed = false; - - - if (args != null) { - if (args.height != null) { - this.height = args.height; - } - - if (args.searchBar != null) { - this.searchBar = args.searchBar; - } - - if (args.tbar != null) { - this.tbar = args.tbar; - } - - if (args.collapsed != null) { - this.collapsed = args.collapsed; - } - - if (args.width != null) { - this.width = args.width; - } - } -} - -FrameGrid.prototype._edit = function(bufferId) { - var _this = this; - var window = new BufferWindow(); - window.onSuccess.attach(function(sender, proposal) { - _this.store.loadData(BIOSAXS.proposal.getBuffers()); - }); - window.draw(BIOSAXS.proposal.getBufferById(bufferId)); -}; - -FrameGrid.prototype.refresh = function(buffers, experimentId) { - this.experimentId = experimentId; - this.store.loadData(this._prepareData(buffers), false); -}; - -FrameGrid.prototype._prepareData = function(buffers) { - return buffers; -}; - -FrameGrid.prototype._getTbar = function() { - var _this = this; - var actions = []; - - actions.push(Ext.create('Ext.Action', { - icon : '../images/add.png', - text : 'Add Buffer', - disabled : false, - handler : function(widget, event) { - var window = new BufferWindow(); - window.onSuccess.attach(function(sender) { - _this.store.loadData(BIOSAXS.proposal.getBuffers()); - }); - window.draw({}); - } - })); - return actions; -}; - -FrameGrid.prototype.getPanel = function(buffers) { - var _this = this; - - this.store = Ext.create('Ext.data.Store', { - fields : [ 'frameNumber', 'I0', 'Rg', 'Mass', 'Vc', 'Qr', 'quality'], - data : buffers - }); - - this.store.sort('frameNumber'); - - var type = 'Ext.grid.Panel'; - if (this.searchBar == true) { - type = 'Ext.ux.LiveSearchGridPanel'; - } - - this.grid = Ext.create(type, { - store : this.store, - height : this.height, - width : this.width, - margin : 5, - columns : [{ - text : 'Frame', - dataIndex : 'frameNumber', - flex : 1 - },{ - text : 'I0', - dataIndex : 'I0', - flex : 1, - renderer : function(val, y, sample) { - return BUI.formatValuesUnits(sample.data.I0, "", 12, 3); - } - },{ - text : 'Rg', - dataIndex : 'Rg', - flex : 1, - renderer : function(val, y, sample) { - return BUI.formatValuesUnits(sample.data.Rg, "nm", 12, 3); - } - },{ - text : 'Mass', - dataIndex : 'Mass', - flex : 1, - renderer : function(val, y, sample) { - return BUI.formatValuesUnits(sample.data.Mass, "", 12, 3); - } - },{ - text : 'Vc', - dataIndex : 'Vc', - flex : 1, - renderer : function(val, y, sample) { - return BUI.formatValuesUnits(sample.data.Vc, "", 12, 3); - } - },{ - text : 'Qr', - dataIndex : 'Qr', - flex : 1, - renderer : function(val, y, sample) { - return BUI.formatValuesUnits(sample.data.Qr, "", 12, 3); - } - },{ - text : 'Quality', - dataIndex : 'quality', - flex : 1, - renderer : function(val, y, sample) { - return (Number(sample.data.quality) * 100).toFixed(2) + "%"; - } - }, - { - text : '', - renderer : function(val, x, sample) { - if (sample != null) { - return BUI.getZipHTMLByFrameRangeId(_this.experimentId, sample.raw.frameNumber, sample.raw.frameNumber); - } - }, - width : 100 - }], - flex : 1, - viewConfig : { - stripeRows : true - } - }); - - /** Adding the tbar **/ - if (this.tbar) { - this.grid.addDocked({ - xtype : 'toolbar', - items : this._getTbar() - }); - } - return this.grid; -}; - -FrameGrid.prototype.input = function() { - return []; -}; - -FrameGrid.prototype.test = function(targetId) { - var frameGrid = new FrameGrid({ - width : 800, - height : 350, - collapsed : false, - tbar : true - }); - - var panel = frameGrid.getPanel([]); - panel.render(targetId); -}; - - -function PeakGrid(args) { - this.height = 500; - this.searchBar = false; - this.tbar = false; - this.collapsed = false; - this.width = 500; - this.showExtendedColumns = false; - - if (args != null) { - if (args.showExtendedColumns != null) { - this.showExtendedColumns = args.showExtendedColumns; - } - if (args.height != null) { - this.height = args.height; - } - if (args.searchBar != null) { - this.searchBar = args.searchBar; - } - - if (args.tbar != null) { - this.tbar = args.tbar; - } - - if (args.collapsed != null) { - this.collapsed = args.collapsed; - } - - if (args.width != null) { - this.width = args.width; - } - } -} - - -PeakGrid.prototype.refresh = function(buffers) { - this.store.loadData(this._prepareData(buffers), false); -}; - -PeakGrid.prototype._prepareData = function(buffers) { - -// for ( var i = 0; i < buffers.length; i++) { -// buffers[i].name = "Peak #" + (i+1); +MolarityForm.prototype._getNumericWithHelp = function(type, fieldLabel, fieldName, help) { + return Ext.create('Ext.container.Container', { + margin : "10 0 0 10", + items : [ { + xtype : type, + fieldLabel : fieldLabel, + name : fieldName, + id : this.id + fieldName, + decimalPrecision : 6 + }, { + xtype : 'label', + forId : 'myFieldId', + text : help, + margin : "5 0 0 105", + cls : "inline-help" + } ] + }); +}; + + +MolarityForm.prototype._getItems = function() { + this.macromoleculeCombo = BIOSAXS_COMBOMANAGER.getComboMacromoleculeByMacromolecules(this.getMacromuleculesCandidates(this.macromolecule), { + width : 250, + labelWidth : 100, + margin : 10 + }); + + return [ { + xtype : 'container', + flex : 1, + margin : '10 0 0 10', + border : 0, + layout : 'anchor', + defaultType : 'requiredtext', + items : [ this.macromoleculeCombo, + this._getNumericWithHelp("textfield", "Ratio", "ratio", "Number in assymmetric units") + ] + } ]; +}; + +MolarityForm.prototype._persist = function() { + var _this = this; + var macromoleculeId = this.macromoleculeCombo.getValue(); + var ratio = Ext.getCmp(this.id + "ratio").getValue(); + var comments = "Not used yet"; + var dataAdapter = new BiosaxsDataAdapter(); + this.panel.setLoading("Saving"); + dataAdapter.onSuccess.attach(function(sender, args) { + _this.onSave.notify(); + }); + dataAdapter.saveStoichiometry(this.macromolecule.macromoleculeId, macromoleculeId, ratio, comments); +}; + +MolarityForm.prototype._getButtons = function() { + var _this = this; + + function onClose() { + _this.onClose.notify(); + } + + return [ { + text : 'Save', + handler : function() { + _this._persist(); + } + }, { + text : 'Cancel', + handler : function() { + onClose(); + } + } ]; +}; + +MolarityForm.prototype.getPanel = function() { + this.panel = Ext.create('Ext.form.Panel', { +// width : null, + height : this.height, + margin : 2, + border : 1, + defaultType : 'requiredtext', + items : this._getItems(), + buttons : this._getButtons() + }); + return this.panel; +}; + +/** macromolecules contains all macromolecules except this one **/ +MolarityForm.prototype.getMacromuleculesCandidates = function(macromolecule) { + var macromolecules = []; + if ( BIOSAXS.proposal.macromolecules){ + for (var i = 0; i < BIOSAXS.proposal.macromolecules.length; i++) { + var m = BIOSAXS.proposal.macromolecules[i]; + if (this.macromolecule != null){ + if (m.macromoleculeId != this.macromolecule.macromoleculeId) { + macromolecules.push(m); + } + } + } + } + return macromolecules; +}; + + +/** It populates the form **/ +MolarityForm.prototype.refresh = function(macromolecule) { + this.macromolecule = macromolecule; + +}; + + +MolarityForm.prototype.input = function() { + return {}; +}; + + +/** It populates the form **/ +MolarityForm.prototype.test = function(targetId) { + var macromoleculeForm = new MolarityForm(); + var panel = macromoleculeForm.getPanel(); + panel.render(targetId); +}; + +///** +// * +// * @witdh +// * @height +// */ +//function MacromoleculeForm(args) { +// this.id = BUI.id(); +// this.width = 700; +// this.height = 500; +// +// if (args != null) { +// if (args.width != null) { +// this.width = args.width; +// } +// if (args.height != null) { +// this.height = args.height; +// } +// } +// +// this.onClose = new Event(this); +//} +// +//MacromoleculeForm.prototype.getMacromolecule = function() { +// this.macromolecule.name = Ext.getCmp(this.panel.getItemId()).getValues().name; +// this.macromolecule.acronym = Ext.getCmp(this.panel.getItemId()).getValues().acronym; +// this.macromolecule.comments = Ext.getCmp(this.panel.getItemId()).getValues().comments; +// this.macromolecule.extintionCoefficient = Ext.getCmp(this.panel.getItemId()).getValues().extintionCoefficient; +// this.macromolecule.molecularMass = Ext.getCmp(this.panel.getItemId()).getValues().molecularMass; +// return this.macromolecule; +//}; +// +//MacromoleculeForm.prototype.setMacromolecule = function(macromolecule) { +// this.pdbStore.loadData(macromolecule.structure3VOs); +// +//}; +// +//MacromoleculeForm.prototype.getForm = function(macromolecule) { +// this.panel = Ext.createWidget('form', { +// frame : false, +// border : 0, +// padding : 15, +// width : 550, +// height : 350, +// items : [ { +// xtype : 'container', +// layout : 'hbox', +// items : [ { +// xtype : 'container', +// flex : 1, +// border : false, +// layout : 'anchor', +// defaultType : 'requiredtext', +// items : [ { +// fieldLabel : 'Name', +// name : 'name', +// anchor : '95%', +// tooltip : "Name of the macromolecule", +// value : macromolecule.name +// }, { +// fieldLabel : 'Acronym', +// name : 'acronym', +// anchor : '95%', +// value : macromolecule.acronym +// } ] +// }, { +// xtype : 'container', +// flex : 1, +// layout : 'anchor', +// defaultType : 'textfield', +// items : [ { +// xtype : 'numberfield', +// fieldLabel : 'Mol. Mass (Da)', +// name : 'molecularMass', +// value : macromolecule.molecularMass, +// decimalPrecision : 6 +// }, { +// xtype : 'numberfield', +// fieldLabel : 'Extinction coef.', +// name : 'extintionCoefficient', +// value : macromolecule.extintionCoefficient, +// decimalPrecision : 6 +// } ] +// } ] +// }, { +// xtype : 'textareafield', +// name : 'comments', +// fieldLabel : 'Comments', +// value : macromolecule.comments, +// width : this.width - 10 +// }, +// +// { +// html : BUI.getWarningHTML("The macromolecule is unsaved. Save the macromolecule to get access to other tabs"), +// margin : "150 10 10 10", +// id : this.id + "unsavedWarning", +// hidden : !(!macromolecule.macromoleculeId) +// } +// +// ] +// }); +// return this.panel; +//}; +// +//MacromoleculeForm.prototype.save = function() { +// var _this = this; +// +// var adapter = new BiosaxsDataAdapter(); +// adapter.onSuccess.attach(function(sender, proposal) { +// BIOSAXS.proposal.setItems(proposal); +// _this.panel.setLoading(false); +// +// Ext.getCmp(_this.id + "assembly").enable() +// Ext.getCmp(_this.id + "advanced").enable(); +// Ext.getCmp(_this.id + "unsavedWarning").hide(); +// }); +// +// if (this.getMacromolecule().name == "") { +// BUI.showError("Name field is mandatory"); +// return; +// } +// if (this.getMacromolecule().acronym == "") { +// BUI.showError("Acroynm field is mandatory"); +// return; +// } +// +// /** Check if acronym is unique * */ +// if (this.getMacromolecule().macromoleculeId == null) { +// if (BIOSAXS.proposal.getMacromoleculeByAcronym(this.getMacromolecule().acronym) == null) { +// this.panel.setLoading("ISPyB: Saving Macromolecule") +// adapter.saveMacromolecule(this.getMacromolecule()); +// } else { +// alert("There is already an existing macromolecule with the same acronym"); +// +// } +// } else { +// this.panel.setLoading("ISPyB: Saving Macromolecule") +// adapter.saveMacromolecule(this.getMacromolecule()); +// } +// +//}; +// +//MacromoleculeForm.prototype.getPanel = function(macromolecule) { +// var _this = this; +// this.macromolecule = macromolecule; +// return Ext.createWidget('tabpanel', { +// height : this.height, +// margin : 5, +// plain : true, +// style : { +// padding : 5 +// }, +// items : [ { +// tabConfig : { +// title : "General", +// disabled : false +// }, +// items : [ this.getForm(macromolecule) ], +// bbar : [ "->", { +// text : 'Save', +// cls : 'btn-with-border', +// style : { +// +// border : 1 +// }, +// handler : function() { +// _this.save(); +// } +// }, { +// text : 'Close', +// cls : 'btn-with-border', +// handler : function() { +// _this.onClose.notify(); +// } +// } ] +// }, { +// tabConfig : { +// id : this.id + "assembly", +// title : "Assembly", +// tooltip : 'Description of subunits present in the macromolecule', +// // hidden : (!macromolecule.macromoleculeId), +// disabled : (!macromolecule.macromoleculeId) +// }, +// items : [ this.getMolarityGrid(macromolecule) ] +// }, { +// tabConfig : { +// id : this.id + "advanced", +// title : "Advanced Modeling", +// // hidden : (!macromolecule.macromoleculeId), +// tooltip : 'Definition of the description contacts and symetries', +// disabled : (!macromolecule.macromoleculeId) +// }, +// items : [ this.getRigidBodyForm(macromolecule) ] +// } ] +// }); +//}; +// +//MacromoleculeForm.prototype.getRigidBodyForm = function(macromolecule) { +// var _this = this; +// +// // [P1, P2, P3, P4 ,P5 ,P6 ,32, P42, P52, P62] + P222 +// var symmetry = Ext.create('Ext.data.Store', { +// fields : [ 's' ], +// data : [ { +// "s" : "P1" +// }, { +// "s" : "P2" +// }, { +// "s" : "P3" +// }, { +// "s" : "P4" +// }, { +// "s" : "P5" +// }, { +// "s" : "P6" +// }, { +// "s" : "P32" +// }, { +// "s" : "P42" +// }, { +// "s" : "P52" +// }, { +// "s" : "P62" +// }, { +// "s" : "P222" +// } ] +// }); +// +// if (macromolecule.symmetry == null) { +// macromolecule.symmetry = "P1"; +// } +// var comboBox = Ext.create('Ext.form.ComboBox', { +// fieldLabel : 'Symmetry', +// store : symmetry, +// id : 'comboSym', +// queryMode : 'local', +// displayField : 's', +// valueField : 's', +// value : macromolecule.symmetry, +// margin : "10 0 0 0", +// listeners : { +// change : function(combo, newValue, oldValue, eOpts) { +// macromolecule.symmetry = newValue; +// } +// } +// }); +// +// this.rigidBodyPanel = Ext.createWidget('form', { +// frame : false, +// border : 0, +// padding : 10, +// width : 550, +// height : 400, +// items : [ { +// xtype : 'container', +// layout : 'hbox', +// items : [ { +// xtype : 'container', +// border : false, +// layout : 'hbox', +// items : [ { +// xtype : 'label', +// forId : 'myFieldId', +// text : 'Contact Desc:', +// width : 105, +// margin : '0 0 0 0' +// }, { +// xtype : 'textfield', +// hideLabel : true, +// id : "contactsDescriptionFilePath", +// margin : '0 0 0 0', +// width : 300, +// value : macromolecule.contactsDescriptionFilePath +// }, { +// text : 'Upload', +// xtype : 'button', +// margin : "0 0 0 20", +// width : 100, +// handler : function() { +// _this._openUploadDialog(macromolecule.macromoleculeId, "CONTACTS", "Upload Contact Description File"); +// } +// } ] +// } ] +// }, +// +// comboBox, { +// xtype : 'checkbox', +// margin : '10 0 0 5', +// boxLabel : "I want rigid body modeling run on this stuff", +// checked : true, +// width : 300 +// }, _this.getPDBGrid(macromolecule) ] +// }); +// return this.rigidBodyPanel; +//}; +// +//MacromoleculeForm.prototype.update = function() { +// var _this = this; +// BIOSAXS.proposal.onInitialized.attach(function() { +// if (BIOSAXS.proposal != null) { +// var macromolecules = BIOSAXS.proposal.macromolecules; +// for (var i = 0; i < macromolecules.length; i++) { +// if (macromolecules[i].macromoleculeId == _this.macromolecule.macromoleculeId) { +// _this.macromolecule = macromolecules[i]; +// _this.setMacromolecule(_this.macromolecule); +// _this.molarityStore.loadData(_this.parseMolarityData(_this.macromolecule)); +// _this.pdbGrid.setLoading(false); +// Ext.getCmp("contactsDescriptionFilePath").setValue(_this.macromolecule.contactsDescriptionFilePath) +// _this.molarityGrid.setLoading(false); +// +// } +// } +// } +// }); +// this.molarityGrid.setLoading("Updating"); +// this.pdbGrid.setLoading("Updating"); +// BIOSAXS.proposal.init(); +//}; +// +///******************************************************************************* +// * MOLARITY GRID +// ******************************************************************************/ +// +//MacromoleculeForm.prototype.parseMolarityData = function(macromolecule) { +// var data = []; +// if (macromolecule.stoichiometry != null) { +// for (var i = 0; i < macromolecule.stoichiometry.length; i++) { +// data.push({ +// ratio : macromolecule.stoichiometry[i].ratio, +// acronym : macromolecule.stoichiometry[i].macromolecule3VO.acronym, +// comments : macromolecule.stoichiometry[i].macromolecule3VO.comments, +// stoichiometryId : macromolecule.stoichiometry[i].stoichiometryId, +// name : macromolecule.stoichiometry[i].macromolecule3VO.name +// }); +// } +// } +// return data; +//}; +// +//MacromoleculeForm.prototype.getMolarityGrid = function(macromolecule) { +// var _this = this; +// +// this.molarityStore = Ext.create('Ext.data.Store', { +// fields : [ 'acronym', 'ratio', 'comments', 'stoichiometryId', 'name' ], +// data : this.parseMolarityData(macromolecule), +// sorters : { +// property : 'ratio', +// direction : 'DESC' +// } +// }); +// +// this.molarityGrid = Ext.create('Ext.grid.Panel', { +// store : this.molarityStore, +// height : 350, +// padding : 5, +// columns : [ +// +// { +// text : 'Subunit', +// columns : [ { +// text : "Acronym", +// width : 100, +// hidden : false, +// dataIndex : 'acronym', +// sortable : true +// }, { +// text : "Name", +// width : 100, +// hidden : false, +// dataIndex : 'name', +// sortable : true +// }, { +// text : "Comments", +// width : 100, +// dataIndex : 'comments', +// sortable : true +// } ] +// }, { +// text : "Number
in assymmetric units", +// width : 150, +// dataIndex : 'ratio', +// tooltip : 'Number of times this subunit is present in the macromolecule', +// sortable : true +// }, { +// id : this.id + 'MOLARITY_REMOVE', +// flex : 0.1, +// sortable : false, +// renderer : function(value, metaData, record, rowIndex, colIndex, store) { +// return BUI.getRedButton('REMOVE'); +// } +// } ], +// listeners : { +// cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { +// if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'MOLARITY_REMOVE') { +// var dataAdapter = new BiosaxsDataAdapter(); +// dataAdapter.onSuccess.attach(function() { +// _this.molarityGrid.setLoading(false); +// _this.update(); +// }); +// dataAdapter.removeStoichiometry(record.data.stoichiometryId); +// _this.molarityGrid.setLoading("Removing Structure"); +// } +// } +// }, +// buttons : [ { +// text : 'Add molarity', +// handler : function() { +// +// function onClose() { +// w.destroy(); +// _this.update(); +// } +// var w = Ext.create('Ext.window.Window', { +// title : 'Molarity', +// height : 300, +// width : 500, +// modal : true, +// buttons : [ { +// text : 'Save', +// handler : function() { +// var macromoleculeId = (_this.macromoleculeCombo.getValue()); +// var ratio = Ext.getCmp(_this.id + "ratio").getValue(); +// var comments = ""; +// var dataAdapter = new BiosaxsDataAdapter(); +// dataAdapter.onSuccess.attach(function(sender, args) { +// _this.update(); +// w.destroy(); +// }); +// dataAdapter.saveStoichiometry(_this.macromolecule.macromoleculeId, macromoleculeId, ratio, comments); +// } +// }, { +// text : 'Cancel', +// handler : function() { +// onClose(); +// } +// } ], +// items : [ _this.getMolarityForm(macromolecule) ], +// listeners : { +// onEsc : function() { +// onClose(); +// }, +// close : function() { +// onClose(); +// } +// } +// }).show(); +// } +// } ] +// }); +// return this.molarityGrid; +//}; +// +///******************************************************************************* +// * PDB GRID +// ******************************************************************************/ +// +//MacromoleculeForm.prototype._openUploadDialog = function(macromoleculeId, type, title) { +// var _this = this; +// function onClose() { +// w.destroy(); +// _this.update(); +// } +// +// var w = Ext.create('Ext.window.Window', { +// title : title, +// height : 200, +// width : 400, +// modal : true, +// buttons : [ { +// text : 'Close', +// handler : function() { +// onClose(); +// } +// } ], +// layout : 'fit', +// items : { +// html : "" +// }, +// listeners : { +// onEsc : function() { +// onClose(); +// }, +// close : function() { +// onClose(); +// } +// } +// }).show(); +//}; +// +//MacromoleculeForm.prototype._getPlugins = function() { +// var _this = this; +// var plugins = []; +// // if (this.updateRowEnabled) { +// plugins.push(Ext.create('Ext.grid.plugin.RowEditing', { +// clicksToEdit : 1, +// listeners : { +// validateedit : function(grid, e) { +// /** Comments are always updatable* */ +// e.record.raw.symmetry = e.newValues.symmetry; +// e.record.raw.multiplicity = e.newValues.multiplicity; +// +// var adapter = new BiosaxsDataAdapter(); +// adapter.onSuccess.attach(function(sender, measurement) { +// // _this.grid.setLoading(false); +// }); +// adapter.onError.attach(function() { +// alert("Error"); +// // _this.grid.setLoading(false); +// }); +// +// // _this.grid.setLoading(); +// adapter.saveStructure(e.record.raw); +// } +// } +// })); +// // } +// return plugins; +//}; +// +//MacromoleculeForm.prototype.getPDBGrid = function(macromolecule) { +// var _this = this; +// +// var data = macromolecule.structure3VOs; +// +// // /** Getting PDB from subunits **/ +// // if (macromolecule != null){ +// // if (macromolecule.stoichiometry != null){ +// // for (var i =0; i < macromolecule.stoichiometry.length; i++){ +// // var stoichiometry = macromolecule.stoichiometry[i]; +// // if (stoichiometry.macromolecule3VO != null){ +// // if (stoichiometry.macromolecule3VO.structure3VOs != null){ +// // for (var j = 0; j < stoichiometry.macromolecule3VO.structure3VOs.length; +// // j++) { +// // var structure = stoichiometry.macromolecule3VO.structure3VOs[j]; +// // structure["isSubunit"] = stoichiometry.macromolecule3VO.acronym; +// // data.push(structure) +// // } +// // } +// // } +// // } +// // } +// // } +// +// this.pdbStore = Ext.create('Ext.data.Store', { +// fields : [ 'filePath', 'structureId', 'structureType', 'symmetry', 'structureId', 'name', 'multiplicity' ], +// data : macromolecule.structure3VOs, +// groupField : 'structureType', +// sorters : { +// property : 'structureId', +// direction : 'DESC' +// } +// }); +// +// var groupingFeature = Ext.create('Ext.grid.feature.Grouping', { +// groupHeaderTpl : Ext.create('Ext.XTemplate', +// "
{name:this.formatName}
", { +// formatName : function(name) { +// return name; +// } +// }), +// hideGroupedHeader : true, +// startCollapsed : false +// }); +// +// this.pdbGrid = Ext.create('Ext.grid.Panel', { +// margin : "15 0 0 5", +// height : 250, +// store : this.pdbStore, +// plugins : _this._getPlugins(), +// buttons : [ { +// // text : 'Add PDB file', +// text : 'Add Modeling Option', +// handler : function() { +// _this._openUploadDialog(macromolecule.macromoleculeId, "PDB", 'Upload PDB File'); +// } +// } +// +// ], +// columns : [ +// { +// text : "structureId", +// flex : 0.2, +// hidden : true, +// dataIndex : 'structureId', +// sortable : true +// }, +// { +// text : "File", +// flex : 0.5, +// dataIndex : 'filePath', +// sortable : true, +// hidden : true +// }, +// { +// text : "PDB", +// flex : 0.4, +// dataIndex : 'name', +// sortable : true +// }, +// { +// text : "Symmetry", +// flex : 0.4, +// dataIndex : 'symmetry', +// sortable : true, +// editor : { +// xtype : 'combobox', +// typeAhead : true, +// triggerAction : 'all', +// selectOnTab : true, +// store : [ [ "P1", "P1" ], [ "P2", "P2" ], [ "P3", "P3" ], [ "P4", "P4" ], [ "P5", "P5" ], [ "P6", "P6" ], [ "P32", "P32" ], [ "P42", "P42" ], +// [ "P52", "P52" ], [ "P62", "P62" ], [ "P222", "P222" ] ], +// } +// }, { +// text : "Multiplicity", +// flex : 0.4, +// dataIndex : 'multiplicity', +// sortable : true, +// editor : { +// xtype : 'textfield' +// } +// +// }, { +// text : "Subunit", +// flex : 0.2, +// dataIndex : 'isSubunit', +// sortable : true, +// hidden : true +// }, { +// text : "Type", +// flex : 0.2, +// dataIndex : 'structureType', +// sortable : true, +// hidden : true +// }, +// +// { +// id : this.id + 'REMOVE', +// flex : 0.2, +// hidden : true, +// sortable : false, +// renderer : function(value, metaData, record, rowIndex, colIndex, store) { +// return BUI.getRedButton('REMOVE'); +// } +// }, ], +// +// listeners : { +// itemdblclick : function(dataview, record, item, e) { +// _this._editExperiment(record.raw.experimentId); +// }, +// cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { +// +// if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'REMOVE') { +// var dataAdapter = new BiosaxsDataAdapter(); +// dataAdapter.onSuccess.attach(function() { +// _this.pdbGrid.setLoading(false); +// _this.update(); +// }); +// dataAdapter.removeStructure(record.data.structureId); +// _this.pdbGrid.setLoading("Removing PDB file"); +// } +// +// } +// }, +// viewConfig : { +// getRowClass : function(record, rowIdx, params, store) { +// if (record.raw.isSubunit != null) { +// return "blue-row"; +// } +// } +// } +// }); +// +// return this.pdbGrid; +//}; +// +//MacromoleculeForm.prototype.getMolarityForm = function(macromolecule) { +// var _this = this; +// var data = []; +// for (var i = 0; i < BIOSAXS.proposal.macromolecules.length; i++) { +// var m = BIOSAXS.proposal.macromolecules[i]; +// if (m.macromoleculeId != macromolecule.macromoleculeId) { +// data.push(m); +// } +// // } - /** Adding information of buffer **/ - if (buffers.length > 0){ - buffers.unshift({ - name : "BUFFER", - start : 0, - experimentId : buffers[0].experimentId, - end : buffers[0].start - 1 - }); - } - - - return buffers; -}; - -PeakGrid.prototype._getTbar = function() { - var _this = this; - var actions = []; +// this.macromoleculeCombo = BIOSAXS_COMBOMANAGER.getComboMacromoleculeByMacromolecules(data, { +// width : 250, +// labelWidth : 100, +// margin : 10 +// }); +// +// return Ext.createWidget('form', { +// +// frame : false, +// border : 0, +// // padding : 15, +// width : 550, +// height : 350, +// items : [ { +// xtype : 'container', +// flex : 1, +// border : false, +// layout : 'anchor', +// defaultType : 'requiredtext', +// items : [ this.macromoleculeCombo, { +// xtype : 'numberfield', +// name : 'Ratio', +// id : _this.id + "ratio", +// fieldLabel : 'Number in assymmetric units', +// value : 1, +// decimalPrecision : 6, +// margin : 10 +// }, { +// xtype : 'textareafield', +// name : 'comments', +// fieldLabel : 'Comments', +// margin : 10, +// width : 400, +// value : "" +// +// } ] +// } ] +// }); +// +//}; +// +///******************************************************************************* +// * JAVASCRIPT DOC +// ******************************************************************************/ +//MacromoleculeForm.prototype.input = function() { +// return { +// macromolecule : DATADOC.getMacromolecule_10() +// }; +//}; +// +//MacromoleculeForm.prototype.test = function(targetId) { +// var macromoleculeForm = new MacromoleculeForm(); +// var panel = macromoleculeForm.getPanel(macromoleculeForm.input().macromolecule); +// panel.render(targetId); +//}; + +function ResultSummaryForm() { + this.radiationDamageThreshold = BUI.getRadiationDamageThreshold(); + this.qualityThreshold = BUI.getQualityThreshold(); - actions.push(Ext.create('Ext.Action', { - icon : '../images/add.png', - text : 'Add Buffer', - disabled : false, - handler : function(widget, event) { - var window = new BufferWindow(); - window.onSuccess.attach(function(sender) { - _this.store.loadData(BIOSAXS.proposal.getBuffers()); - }); - window.draw({}); - } - })); - return actions; -}; + /** Data clusters with bufers **/ + this.clusterByBuffers = false; + + /** Visualization are groupeb by concenttrations. Ex: 4.5mg/ml and 4.7mg/ml will be clusterized together **/ + this.collapseConcentrations = true; + + this.summaryHeight = 350; + this.id = BUI.id(); -PeakGrid.prototype.getPanel = function(buffers) { var _this = this; - this.peakCount = 0; - this.store = Ext.create('Ext.data.Store', { - fields : ['name', {name:'start', type : 'int'}, {name:'end', type : 'int'}], - data : buffers - - }); - - this.store.sort('start'); - var type = 'Ext.grid.Panel'; - if (this.searchBar == true) { - type = 'Ext.ux.LiveSearchGridPanel'; - } - this.grid = Ext.create(type, { - store : this.store, - height : this.height, - width : this.width, - margin : 5, - columns : [ - { - text : '', - dataIndex : 'name', - width : 100, - hidden : _this.showExtendedColumns, - renderer : function(val, y, sample) { - if (sample.data.name == "BUFFER") - return "BUFFER"; - _this.peakCount++; - return "Peak #" + _this.peakCount; - } - - - }, - { - text : 'Peaks', - dataIndex : 'start', - sortable : true, - width : 100, - hidden: !_this.showExtendedColumns, - renderer : function(val, y, sample) { - return "From " + Number(sample.raw.start) + " to " + Number(sample.raw.end); - } - }, - { - text : 'Frames', - hidden : _this.showExtendedColumns, - columns : [ - { - text : 'Start', - dataIndex : 'start', - sortable : true, - hidden : _this.showExtendedColumns, - width : 100, - renderer : function(val, y, sample) { - return Number(val); - } - },{ - text : 'End', - dataIndex : 'end', - width : 100, - hidden : _this.showExtendedColumns, - renderer : function(val, y, sample) { - return Number(val); - } - },{ - text : 'Total', - dataIndex : 'end', - width : 100, - hidden : _this.showExtendedColumns, - renderer : function(val, y, sample) { - return "" + (sample.raw.end - sample.raw.start) + " frames"; - } - } - ] - } -// , -// { -// text : '', -// hidden : _this.showExtendedColumns, -// renderer : function(val, x, sample) { -// if (sample != null) { -// return BUI.getZipHTMLByFrameRangeId(sample.raw.experimentId, sample.raw.start, sample.raw.end); -// } -// }, -// width : 100 -// } - ], - flex : 1, - viewConfig : { - stripeRows : true, - getRowClass : function(record, rowIdx, params, store) { - if (record.raw.name == "BUFFER") { - return "blue-row"; - } - } - } + this.qualityControlResultsWidget = new QualityControlResultsWidget({ + qualityThreshold : this.radiationDamageThreshold, + radiationDamageThreshold : this.qualityThreshold + + }); + this.qualityControlResultsWidget.onQualityChanged.attach(function(sender, value) { + _this.qualityThreshold = value; + _this.thresholdsChanged(); }); - /** Adding the tbar **/ - if (this.tbar) { - this.grid.addDocked({ - xtype : 'toolbar', - items : this._getTbar() - }); - } - return this.grid; -}; + this.qualityControlResultsWidget.onRadiationDatamageChanged.attach(function(sender, value) { + _this.radiationDamageThreshold = value; + _this.thresholdsChanged(); -PeakGrid.prototype.input = function() { - return []; -}; + }); + this.concentrationHTMLTableWidget = new ConcentrationHTMLTableWidget({ + height : 250, + showBufferColumns : this.clusterByBuffers + }); -PeakGrid.prototype.test = function(targetId) { - var PeakGrid = new PeakGrid({ - width : 800, + this.plot = new BoxWhiskerGraph({ + targetId : _this.id + '_boxPlot', height : 350, - collapsed : false, - tbar : true + width : 450, + maxBoxWidth : 25 }); - var panel = PeakGrid.getPanel([]); - panel.render(targetId); -}; - -/** - * Macromolecule Grid showing macromolecules and adding anb updating buttons - * - * @height - * @maxHeight - * @width - * @cssFontStyle - * @searchBar makes this grid as Ext.ux.LiveSearchGridPanel - * @tbar top bar containing "Add" and "Update From SMIS" button - * @collapsed - * @collapsible - * @btnEditVisible - * @btnRemoveVisible - * @multiselect makes it multiselect using Ext.selection.CheckboxModel - * - * #onSelected - * #onMacromoleculesChanged - */ -function MacromoleculeGrid(args) { - this.height = 500; - this.width = 500; - this.id = BUI.id(); - this.maxHeight = this.height; + this.rangePlot = new RangeWhiskerGraph({ + targetId : _this.id + '_rangePlot', + height : 350, + width : 450, + maxBoxWidth : 25 + }); +} - this.searchBar = false; - this.tbar = false; +ResultSummaryForm.prototype.thresholdsChanged = function() { + var parsedData = this.prepareData(this.rawData); - this.collapsible = true; - this.collapsed = true; + var filtered = JSON.parse(JSON.stringify(parsedData)); + filtered.concentration.concentrations = []; + for ( var conc in parsedData.concentration.concentrations) { + if (parsedData.concentration.concentrations[conc].calculation.rgGnom.validNumber > 0) { + filtered.concentration.concentrations.push(parsedData.concentration.concentrations[conc]); + } + } - this.btnEditVisible = true; - this.btnRemoveVisible = false; - this.multiselect = false; + this.plotWhisker(filtered); + this.plotRange(filtered); - /** Font style applied to the acronym column **/ - this.cssFontStyle = null; + this.concentrationHTMLTableWidget.refresh(parsedData); +}; - if (args != null) { - if (args.height != null) { - this.height = args.height; - this.maxHeight = this.height; - } - if (args.maxHeight != null) { - this.maxHeight = args.maxHeight; - } - if (args.width != null) { - this.width = args.width; - } - if (args.cssFontStyle != null) { - this.cssFontStyle = args.cssFontStyle; - } +/** Given a frame object (object returned by analyzeFrames()) and depending of the threshold indicates if a datacollection has radiation damage **/ +ResultSummaryForm.prototype.hasRadiationDamage = function(frameObject) { + var bb = frameObject.bufferBeforeFramesMerged / frameObject.framesCount; + var ba = frameObject.bufferAfterFramesMerged / frameObject.framesCount; + var mol = frameObject.framesMerge / frameObject.framesCount; + return !((bb >= this.radiationDamageThreshold) && (ba >= this.radiationDamageThreshold) && (mol >= this.radiationDamageThreshold)); +}; - if (args.searchBar != null) { - this.searchBar = args.searchBar; - } - if (args.tbar != null) { - this.tbar = args.tbar; - } - if (args.collapsible != null) { - this.collapsible = args.collapsible; - } - if (args.collapsed != null) { - this.collapsed = args.collapsed; - } - if (args.btnEditVisible != null) { - this.btnEditVisible = args.btnEditVisible; - } - if (args.btnRemoveVisible != null) { - this.btnRemoveVisible = args.btnRemoveVisible; - } - if (args.multiselect != null) { - this.multiselect = args.multiselect; - } +/** Return (frameObject) an object with the information about the frames for a data collection**/ +ResultSummaryForm.prototype.analyzeFrames = function(dataCollectionRow) { + return { + bufferAfterFramesMerged : dataCollectionRow.bufferAfterFramesMerged, + bufferBeforeFramesMerged : dataCollectionRow.bufferBeforeFramesMerged, + framesCount : dataCollectionRow.framesCount, + framesMerge : dataCollectionRow.framesMerge + }; +}; + +ResultSummaryForm.prototype.detectDataCollectionWarnings = function(dataCollectionRow) { + var frameObject = this.analyzeFrames(dataCollectionRow); + var warnings = { + count : 0, + type : [] + }; + + if (this.hasRadiationDamage(frameObject)) { + warnings.count = warnings.count + 1; + warnings.type.push("RADIATION DAMAGE"); } - this.onSelected = new Event(); - - this.onMacromoleculesChanged = new Event(); -} + if (Number(dataCollectionRow.quality) < this.qualityThreshold) { + warnings.count = 1;//warnings.count + 1; + warnings.type.push("Quality <" + this.qualityThreshold); + } + return warnings; +}; +/** Return array composed by {concentration} objects **/ +ResultSummaryForm.prototype.getConcentrations = function(data) { + var concentrationsId = {}; + for ( var i = 0; i < data.length; i++) { + var warning = this.detectDataCollectionWarnings(data[i]); + var id = data[i].conc;// + "_" + data[i].bufferId; + if (this.clusterByBuffers) { + id = data[i].conc + "_" + data[i].bufferId; + } + if (concentrationsId[id] == null) { + concentrationsId[id] = { + id : id, + concentration : Number(data[i].conc).toFixed(2), + bufferId : data[i].bufferId, + bufferAcronym : data[i].bufferAcronym, + rgGuinier : [], + i0Guinier : [], + rgGnom : [], + dMax : [], + quality : [], + frames : [], + frames_warning : warning.count + }; + } else { + concentrationsId[id].frames_warning = concentrationsId[id].frames_warning + warning.count; + } + concentrationsId[id].frames.push(data[i]); -MacromoleculeGrid.prototype.edit = function(macromolecule) { - var _this = this; - var window = new MacromoleculeWindow(); - window.onSave.attach(function(sender) { - _this.store.loadData(BIOSAXS.proposal.getMacromolecules()); - _this.onMacromoleculesChanged.notify(); - }); - window.draw(macromolecule); -}; + if (warning.count == 0) { + concentrationsId[id].rgGuinier.push(data[i].rgGuinier); + concentrationsId[id].i0Guinier.push(data[i].I0); + concentrationsId[id].quality.push(data[i].quality); + concentrationsId[id].rgGnom.push(data[i].rgGnom); + concentrationsId[id].dMax.push(data[i].dmax); + } -MacromoleculeGrid.prototype.getTbar = function() { - var _this = this; - var actions = []; + } + var concentrations = []; + for ( var item in concentrationsId) { + if (concentrationsId.hasOwnProperty(item)) { + concentrations.push({ + concentration : concentrationsId[item].concentration, + id : item, + bufferId : Number(concentrationsId[item].bufferId).toFixed(2), + bufferAcronym : concentrationsId[item].bufferAcronym, + rgGuinier : concentrationsId[item].rgGuinier, + /** Frames **/ + frames : concentrationsId[item].frames, + frames_warning : concentrationsId[item].frames_warning, + /** Calculation **/ + calculation : { + rgGuinier : BUI.getStandardDeviation(concentrationsId[item].rgGuinier), + i0Guinier : BUI.getStandardDeviation(concentrationsId[item].i0Guinier), + quality : BUI.getStandardDeviation(concentrationsId[item].quality), + rgGnom : BUI.getStandardDeviation(concentrationsId[item].rgGnom), + dMax : BUI.getStandardDeviation(concentrationsId[item].dMax) - actions.push(Ext.create('Ext.Action', { - icon : '../images/add.png', - text : 'Add macromolecule', - disabled : false, - handler : function(widget, event) { - _this.edit(); - } - })); - actions.push("->"); - actions.push(Ext.create('Ext.Action', { - icon : '../images/folder_go.png', - text : 'Update From SMIS', - tooltip : "Retrieve all the macromolecules of your proposal from SMIS database", - disabled : false, - handler : function(widget, event) { - _this.grid.setLoading("Connecting to SMIS"); - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, data) { - BIOSAXS.proposal.setMacromolecules(data.macromolecules); - _this.refresh(BIOSAXS.proposal.macromolecules); - _this.grid.setLoading(false); - }); - adapter.onError.attach(function(sender, data) { - _this.grid.setLoading(false); + } }); - adapter.updateDataBaseFromSMIS(); } - })); - return actions; -}; + } -MacromoleculeGrid.prototype.deselectAll = function() { - this.grid.getSelectionModel().deselectAll(); + return { + concentrations : concentrations + }; }; -MacromoleculeGrid.prototype.selectById = function(macromoleculeId) { - this.grid.getSelectionModel().deselectAll(); - for ( var i = 0; i < this.grid.getStore().data.items.length; i++) { - var item = this.grid.getStore().data.items[i].raw; - if (item.macromoleculeId == macromoleculeId) { - this.grid.getSelectionModel().select(i); +ResultSummaryForm.prototype.prepareData = function(data) { + /** Return array composed by {acronym, bufferId} objects **/ + function getBuffers(data) { + var buffersId = {}; + for ( var i = 0; i < data.length; i++) { + buffersId[data[i].bufferId] = data[i].acronym; + } + var buffers = []; + for ( var id in buffersId) { + if (buffersId.hasOwnProperty(id)) { + buffers.push({ + acronym : buffersId[id], + bufferId : id + }); + } } + return buffers; } -}; -MacromoleculeGrid.prototype.refresh = function(macromolecules) { - this.store.loadData(macromolecules, false); + /** Get a string with all the concentrations **/ + function getConcentrationString(parseConcentrations) { + var concentrations = []; + for ( var i = 0; i < parseConcentrations.concentrations.length; i++) { + concentrations.push(parseConcentrations.concentrations[i].concentration + " mg/ml "); + } + return concentrations.toString(); + } + + var parseConcentrations = this.getConcentrations(data); + + return { + dataCollectionCount : data.length, + buffers : getBuffers(data), + concentration : parseConcentrations, + concentrationLabel : getConcentrationString(parseConcentrations) + }; }; -MacromoleculeGrid.prototype.getColumns = function() { - var _this = this; - var columns = [ - { - text : 'Acronym', - dataIndex : 'acronym', - id : this.id + "acronym", - flex : 1, - renderer : function(value, metaData, record, rowIndex, colIndex, store) { - if (_this.cssFontStyle != null) { - return "" + value + ""; +ResultSummaryForm.prototype.getDataForWhisker = function(data) { + var clusters = []; + + var concentrations = {}; + var i = 0; + var conc = 0; + if (this.collapseConcentrations) { + for (i = 0; i < data.concentration.concentrations.length; i++) { + conc = data.concentration.concentrations[i]; + var concentration = Number(conc.concentration).toFixed(0); + if (concentrations[concentration] == null) { + concentrations[concentration] = {}; + concentrations[concentration].concentration = concentration; + concentrations[concentration].calculation = {}; + concentrations[concentration].calculation.rgGuinier = {}; + concentrations[concentration].calculation.rgGuinier.values = []; + concentrations[concentration].calculation.rgGuinier.values = conc.calculation.rgGuinier.values; + concentrations[concentration].calculation.rgGnom = {}; + concentrations[concentration].calculation.rgGnom.values = []; + concentrations[concentration].calculation.rgGnom.values = conc.calculation.rgGnom.values; + } else { + concentrations[concentration].calculation.rgGuinier.values = concentrations[concentration].calculation.rgGuinier.values + .concat(conc.calculation.rgGuinier.values); + concentrations[concentration].calculation.rgGnom.values = concentrations[concentration].calculation.rgGnom.values + .concat(conc.calculation.rgGnom.values); } - return value; } - }, { - text : 'Name', - dataIndex : 'name', - id : this.id + "name", - flex : 1, - hidden : true - } ]; - if (this.btnEditVisible) { - columns.push({ - id : _this.id + 'buttonEditMacromolecule', - width : 85, - sortable : false, - renderer : function(value, metaData, record, rowIndex, colIndex, store) { - if (_this.btnEditVisible) { - return BUI.getGreenButton('EDIT'); - } - return null; + /** From object to array **/ + var array = []; + for ( var key in concentrations) { + if (concentrations.hasOwnProperty(key)) { + array.push(concentrations[key]); } + } + data.concentration.concentrations = array; + } + + for (i = 0; i < data.concentration.concentrations.length; i++) { + conc = data.concentration.concentrations[i]; + clusters.push({ + name : conc.concentration + "mg/ml", + concentration : Number(conc.concentration), + x : Number(conc.concentration), + classes : [] + }); + clusters[clusters.length - 1].classes.push({ + name : "Guinier", + color : '#9A2EFE', + values : conc.calculation.rgGuinier.values + + }); + clusters[clusters.length - 1].classes.push({ + name : "P(r)", + color : '#2E64FE', + values : conc.calculation.rgGnom.values + }); } - if (this.btnRemoveVisible) { - columns.push({ - id : _this.id + 'buttonRemoveMacromolecule', - width : 85, - sortable : false, - renderer : function(value, metaData, record, rowIndex, colIndex, store) { - if (_this.btnRemoveVisible) { - return BUI.getRedButton('REMOVE'); - } - return null; - } + return { + clusters : clusters.sort(function(a, b) { + return a.concentration - b.concentration; + }) + }; +}; + +ResultSummaryForm.prototype.getDataForWhiskerQuality = function(data) { + var clusters = []; + + for ( var i = 0; i < data.concentration.concentrations.length; i++) { + var conc = data.concentration.concentrations[i]; + clusters.push({ + name : conc.concentration + "mg/ml", + classes : [] + }); + clusters[clusters.length - 1].classes.push({ + name : "Quality", + values : conc.calculation.quality.values + }); } - return columns; + return { + clusters : clusters + }; +}; + +ResultSummaryForm.prototype.plotQualityWhisker = function(parsedData) { + this.qualityPlot.refresh(this.getDataForWhiskerQuality(parsedData)); }; +ResultSummaryForm.prototype.plotWhisker = function(parsedData) { + this.plot.refresh(this.getDataForWhisker(parsedData)); +}; -/** Returns the grid **/ -MacromoleculeGrid.prototype.getPanel = function() { +ResultSummaryForm.prototype.plotRange = function(parsedData) { + this.rangePlot.refresh(this.getDataForWhisker(parsedData)); +}; + +ResultSummaryForm.prototype.getPanel = function(data) { var _this = this; + _this.rawData = data; + var parsedData = this.prepareData(data); - this.store = Ext.create('Ext.data.Store', { - fields : [ 'macromoleculeId', 'name', 'acronym' ] - }); + this.panel = Ext + .createWidget( + 'form', + { + bodyPadding : 20, + frame : false, + border : 0, + width : this.width, + height : this.summaryHeight + 1000, + items : [ { + xtype : 'container', + layout : 'hbox', + items : [ { + xtype : 'container', + flex : 1, + border : false, + layout : 'anchor', + defaultType : 'textfield', + items : [ + _this.qualityControlResultsWidget.getPanel(), + { + xtype : 'fieldset', + width : 950, + margin : "20, 0 0 0", + height : this.summaryHeight + 500, + items : [ + { + html : "
Concentration Analysis
", + border : 0, + width : 900 - var type = 'Ext.grid.Panel'; - if (this.searchBar == true) { - type = 'Ext.ux.LiveSearchGridPanel'; - } + }, + { + html : this.concentrationHTMLTableWidget.getPanel(parsedData), + border : 0, + width : 900, + margin : "10, 0 0 10" - this.selModel = Ext.create('Ext.selection.RowModel', { -// allowDeselect : true, -// mode : 'MULTI', - listeners : { - selectionchange : function(sm, selections) { - var selected = []; - for (var i = 0; i < selections.length; i++) { - selected.push(selections[i].raw); - } - _this.onSelected.notify(selected); - } - } - }); + }, + { + xtype : 'container', + layout : 'vbox', + border : 5, + items : [ + { + html : "
Rg based on Guinier and Gnom
", + border : 8, + width : 900, + margin : "20 0 0 10" - this.grid = Ext.create(type, { - id : this.id, - title : 'Macromolecules', - collapsible : this.collapsible, - collapsed : this.collapsed, - store : this.store, - height : this.height, - maxHeight : this.maxHeight, - selModel : this.selModel, - columns : this.getColumns(), - width : this.width, - viewConfig : { - stripeRows : true, - listeners : { - 'celldblclick' : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { - _this.edit(BIOSAXS.proposal.getMacromoleculeById(record.data.macromoleculeId)); - }, - 'cellclick' : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { - if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonEditMacromolecule') { - _this.edit(BIOSAXS.proposal.getMacromoleculeById(record.data.macromoleculeId)); - } - if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonRemoveMacromolecule') { - BUI.showBetaWarning(); - } - } + }, { + xtype : 'container', + layout : 'hbox', + items : [ { + xtype : 'container', + layout : 'vbox', + items : [ { + html : "
", + border : 0, + width : this.plot.width, + padding : "10 0 0 20", + listeners : { + afterrender : function() { + _this.plotWhisker(parsedData); - } - } - }); + } + } + }, { + html : "
Concentration (mg/ml)
", + width : 450, + border : 0, + margin : "-50 0 0 0" + + } ] + }, { + xtype : 'container', + layout : 'vbox', + items : [ { + html : "
", + border : 0, + width : this.rangePlot.width, + padding : "10 0 0 10", + listeners : { + afterrender : function() { + _this.plotRange(parsedData); + } + } + }, { + html : "
Concentration (mg/ml)
", + width : 450, + border : 0, + margin : "-50 0 0 0" + + } ] + } ] + } ] + } ] + } ] + } + + ] + } ] + }); + return this.panel; - /** Adding the tbar **/ - if (this.tbar) { - this.grid.addDocked({ - xtype : 'toolbar', - items : this.getTbar() - }); - } - return this.grid; }; -MacromoleculeGrid.prototype.input = function() { +ResultSummaryForm.prototype.input = function() { return { - proposal : DATADOC.getProposal_10() + data : DATADOC.getData_3367() }; }; -MacromoleculeGrid.prototype.test = function(targetId) { - var macromoleculeGrid = new MacromoleculeGrid({ - width : 800, - height : 350, - collapsed : false, - tbar : true - }); - - BIOSAXS.proposal = new Proposal(macromoleculeGrid.input().proposal); - var panel = macromoleculeGrid.getPanel(BIOSAXS.proposal.macromolecules); +ResultSummaryForm.prototype.test = function(targetId) { + var resultSummaryForm = new ResultSummaryForm(); + var panel = resultSummaryForm.getPanel(resultSummaryForm.input().data); panel.render(targetId); + }; -/** - * Shows measurements with their attributes as specimen, exposure temperature, - * volume to load, transmission etc... - * - * @addBtnMultipleEdit: if true add a button for changing measurements' - * parameters by choosing multiple measurements. It opens - * MultipleEditMeasurementGridWindow - * @collapsed: if true it doesn't show buffer's measurements - * @editor: hashmap containing columns to be edited. It is an array of a json - * like editor of Ext - * @selModel - * @collapseBtnEnable - * @removeBtnEnabled Ext.selection.RowModel - * @addBtnEnable - * @updateRowEnabled true/false if update plugin is set to the grid - * @showTitle - * @title - * @isStatusColumnHidden - * @isPriorityColumnHidden - * @isTimeColumnHidden - * @estimateTime - * @margin - * @tbar - * @height - * @maxHeight - * @minHeight - * @width - * @maxWidth - * @resizable - * @experimentColorBased when colors for buffers and macromolecules are not - * selected by the proposal but by experiment, so the - * number of colors is smaller +/** + * Example form + * + * @witdh + * @height + */ +function RigibBodyModelingForm(args) { + this.id = BUI.id(); + this.width = 700; + this.height = 500; + + if (args != null) { + if (args.width != null) { + this.width = args.width; + } + if (args.height != null) { + this.height = args.height; + } + } + + var _this = this; + this.rigidBodyGrid = new AprioriRigidBodyGrid(); + + this.rigidBodyGrid.onUploadFile.attach(function(sender, type, title){ + _this._openUploadDialog(_this.macromolecule.macromoleculeId, type, title); + }); + + this.rigidBodyGrid.onRemove.attach(function(sender, type, title){ + _this._update(); + }); + + this.onSave = new Event(this); + +} + +RigibBodyModelingForm.prototype._getItems = function() { + var _this = this; + + + return [ { + xtype : 'label', + forId : 'myFieldId', + text : 'Information for model fit, mixture analysis and rigid body modeling', + margin : '15 0 20 10', + cls : "inline-help" + }, + this.rigidBodyGrid.getPanel(), + { + xtype : 'label', + forId : 'myFieldId', + text : 'Distance restraints may be imposed on the model using contacts conditions file (OPTIONAL)', + margin : '25 0 5 10', + cls : "inline-help" + }, + { + xtype : 'container', + layout : 'hbox', + items : [ { + xtype : 'container', + border : false, + layout : 'hbox', + items : [ { + xtype : 'label', + forId : 'myFieldId', + text : 'Contact Description File: (Optional)', + width : 150, + margin : '10 0 0 10' + }, { + id : this.id + "contactsDescriptionFilePath", + xtype : 'textfield', + hideLabel : true, + margin : '10 0 0 0', + width : 200, + disabled: true + }, { + text : 'Upload', + xtype : 'button', + margin : "10 0 0 10", + width : 80, + handler : function() { + + _this._openUploadDialog(_this.macromolecule.macromoleculeId, "CONTACTS", "Upload Contact Description File"); + } + }, { + text : 'Remove', + id : _this.id + "_remove", + xtype : 'button', + margin : "10 0 0 10", + width : 80, + handler : function() { + _this.macromolecule.contactsDescriptionFilePath = null; + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, proposal) { + /** Updating the proposal global object **/ + BIOSAXS.proposal.setItems(proposal); + _this.panel.setLoading(false); + + var saved = BIOSAXS.proposal.getMacromoleculeByAcronym(_this.macromolecule.acronym); + /** Refreshing to check that everything is ok **/ + _this.refresh(saved); + _this.onSave.notify(saved); + }); + + _this.panel.setLoading("Saving Macromolecule") + adapter.saveMacromolecule(_this.macromolecule); + } + } ] + } ] + }, { + xtype : 'panel', + html : "Go to SASREF manual for further information", + margin : "10 0 0 160", + border : 0 + + }, + { + xtype : 'checkbox', + margin : '10 0 0 5', + boxLabel : "I want rigid body modeling run on this stuff", + checked : true, + width : 300 + } ] + +}; + +/** Because update is a jsp page we don't know if the user has uploaded a file or not then we need to refresh **/ +RigibBodyModelingForm.prototype._update = function(macromoleculeId, type, title) { + var _this = this; + BIOSAXS.proposal.onInitialized.attach(function() { + if (BIOSAXS.proposal != null) { + _this.refresh(BIOSAXS.proposal.getMacromoleculeById(_this.macromolecule.macromoleculeId)); + _this.panel.setLoading(false); + } + }); + this.panel.setLoading(); + BIOSAXS.proposal.init(); +}; + +RigibBodyModelingForm.prototype._openUploadDialog = function(macromoleculeId, type, title) { + var _this = this; + function onClose() { + w.destroy(); + _this._update(); + + } + + var w = Ext.create('Ext.window.Window', { + title : title, + height : 200, + width : 400, + modal : true, + buttons : [ { + text : 'Close', + handler : function() { + onClose(); + } + } ], + layout : 'fit', + items : { + html : "" + }, + listeners : { + onEsc : function() { + onClose(); + }, + close : function() { + onClose(); + } + } + }).show(); +}; + +RigibBodyModelingForm.prototype._getButtons = function() { + return []; +}; + +RigibBodyModelingForm.prototype.getPanel = function() { + var _this = this; + this.panel = Ext.create('Ext.form.Panel', { + width : this.width, + height : this.height, + margin : 10, + border : 1, + defaultType : 'textfield', + items : this._getItems(), + buttons : this._getButtons(), + listeners : { + afterrender : function(){ + _this._populate(); + + } + } + }); + return this.panel; +}; + +/** Populates could be call when the DOM is not filled yet **/ +RigibBodyModelingForm.prototype._populate = function() { + if (this.macromolecule != null){ + if (Ext.getCmp(this.id + "contactsDescriptionFilePath") != null){ + if (this.macromolecule.contactsDescriptionFilePath != null){ + Ext.getCmp(this.id + "contactsDescriptionFilePath").setValue(this.macromolecule.contactsDescriptionFilePath); + Ext.getCmp(this.id + "_remove").enable(); + } + else{ + Ext.getCmp(this.id + "_remove").disable(); + Ext.getCmp(this.id + "contactsDescriptionFilePath").setValue(""); + + } + } + } +}; + +/** It populates the form * */ +RigibBodyModelingForm.prototype.refresh = function(macromolecule) { + this.macromolecule = macromolecule; + this.rigidBodyGrid.refresh(macromolecule); + this._populate(); +}; + +RigibBodyModelingForm.prototype.input = function() { + return {}; +}; + + +/** It populates the form **/ +RigibBodyModelingForm.prototype.test = function(targetId) { + var macromoleculeForm = new RigibBodyModelingForm(); + var panel = macromoleculeForm.getPanel(); + panel.render(targetId); +}; + + +/** + * Same form as MX part * - * #onClick #onSelected #onRemoved #onUpdateTime #onMeasurementChanged - * #onExperimentChanged + * @creationMode if true a create button appears instead of save + * @showTitle true or false */ -function MeasurementGrid(args) { +function ShipmentForm(args) { this.id = BUI.id(); - this.height = 500; - this.width = 900; - - this.maxWidth = 1200; - this.maxHeight = 600; - this.minHeight = 500; - - this.unitsFontSize = 9; - this.title = "Measurements"; - this.estimateTime = false; - this.collapsed = true; - this.tbar = true; - + this.creationMode = false; this.showTitle = true; - this.resizable = true; - this.updateRowEnabled = true; - - /** - * Hash map containing the keys of the editable columns. Ex: - * 'exposureTemperature' * - */ - this.editor = { - comments : { - xtype : 'textfield', - allowBlank : true + if (args != null) { + if (args.creationMode != null) { + this.creationMode = args.creationMode; } - }; + if (args.showTitle != null) { + this.showTitle = args.showTitle; + } + } - this.isTimeColumnHidden = false; - this.isStatusColumnHidden = false; - this.isPriorityColumnHidden = true; - this.margin = "0 0 0 0"; + this.onSaved = new Event(this); +} - this.addBtnEnable = true; - this.sorter = [ { - property : 'priority', - direction : 'ASC' - } ]; +ShipmentForm.prototype.fillStores = function() { + this.panel.setLoading("Loading Labcontacts from database"); + this.labContactForSendingStore.loadData(BIOSAXS.proposal.getLabcontacts(), false); + this.labContactForReturnStore.loadData(BIOSAXS.proposal.getLabcontacts(), false); + this.panel.setLoading(false); + if (this.shipment != null) { + this.setShipment(this.shipment); + } +}; - this.removeBtnEnabled = false; - this.collapseBtnEnable = true; - this.addBtnMultipleEdit = false; - this.sortingBtnEnable = false; +ShipmentForm.prototype.draw = function(targetId) { + this.getPanel().render(targetId); +}; +ShipmentForm.prototype.setShipment = function(shipment) { + this.shipment = shipment; var _this = this; - this.selModel = Ext.create('Ext.selection.RowModel', { - allowDeselect : true, - mode : 'MULTI', - listeners : { - selectionchange : function(sm, selections) { - var selected = []; - for (var i = 0; i < selections.length; i++) { - selected.push(selections[i].raw); - } - _this.onSelected.notify(selected); - } - } - }); - - if (args != null) { - - if (args.selModel != null) { - this.selModel = args.selModel; - } + Ext.getCmp(_this.id + "shippingName").setValue(shipment.json.shippingName); + Ext.getCmp(_this.id + "shippingStatus").setValue(shipment.json.shippingStatus); + Ext.getCmp(_this.id + "comments").setValue(shipment.json.comments); + if (shipment.json.sendingLabContactVO != null) { + this.labContactsSendingCombo.setValue(shipment.json.sendingLabContactVO.labContactId); + } + if (shipment.json.returnLabContactVO != null) { + this.labContactsReturnCombo.setValue(shipment.json.returnLabContactVO.labContactId); + } - if (args.removeBtnEnabled != null) { - this.removeBtnEnabled = args.removeBtnEnabled; - } +}; - if (args.addBtnMultipleEdit != null) { - this.addBtnMultipleEdit = args.addBtnMultipleEdit; - } +ShipmentForm.prototype._saveShipment = function() { + var _this = this; + var shippingId = null; + if (this.shipment != null) { + shippingId = this.shipment.json.shippingId; + } + var json = { + shippingId : shippingId, + name : Ext.getCmp(_this.id + "shippingName").getValue(), + status : Ext.getCmp(_this.id + "shippingStatus").getValue(), + sendingLabContactId : Ext.getCmp(_this.id + "shipmentform_sendingLabContactId").getValue(), + returnLabContactId : Ext.getCmp(_this.id + "returnLabContactId").getValue(), + returnCourier : Ext.getCmp(_this.id + "returnCourier").getValue(), + courierAccount : Ext.getCmp(_this.id + "courierAccount").getValue(), + BillingReference : Ext.getCmp(_this.id + "BillingReference").getValue(), + dewarAvgCustomsValue : Ext.getCmp(_this.id + "dewarAvgCustomsValue").getValue(), + dewarAvgTransportValue : Ext.getCmp(_this.id + "dewarAvgTransportValue").getValue(), + comments : Ext.getCmp(_this.id + "comments").getValue() + }; - // if (args.experimentColorBased != null) { - // this.experimentColorBased = args.experimentColorBased; - // } + var dataAdapter = new BiosaxsDataAdapter(); + dataAdapter.onSuccess.attach(function(sender, shipment) { + window.location = BUI.getShippingURL(shipment.shippingId); + }); - if (args.collapsed != null) { - this.collapsed = args.collapsed; - } - if (args.resizable != null) { - this.resizable = args.resizable; - } + dataAdapter.onError.attach(function(sender, error) { + _this.onError.notify(error); + }); - if (args.editor != null) { - this.editor = args.editor; - } + /** Cheking params **/ + if (json.name == "") { + BUI.showError("Name field is mandatory"); + return; + } - if (args.collapseBtnEnable != null) { - this.collapseBtnEnable = args.collapseBtnEnable; - } + if (json.sendingLabContactId == null) { + BUI.showError("Lab contact for sending field is mandatory"); + return; + } - if (args.addBtnEnable != null) { - this.addBtnEnable = args.addBtnEnable; - } - if (args.sortingBtnEnable != null) { - this.sortingBtnEnable = args.sortingBtnEnable; - } + if (json.returnLabContactId == null) { + BUI.showError("Lab contact for return field is mandatory"); + return; + } - if (args.isPriorityColumnHidden != null) { - this.isPriorityColumnHidden = args.isPriorityColumnHidden; - } + dataAdapter.createShipment(json.shippingId, json.name, json.status, json.comments, json.sendingLabContactId, json.returnLabContactId, + json.returnCourier, json.courierAccount, json.BillingReference, json.dewarAvgCustomsValue, json.dewarAvgTransportValue); - if (args.width != null) { - this.width = args.width; - } +}; - if (args.updateRowEnabled != null) { - this.updateRowEnabled = args.updateRowEnabled; - } +ShipmentForm.prototype.getPanel = function(shipment) { + var _this = this; + this.shipment = shipment; + var required = '*'; + var buttons = []; - if (args.showTitle != null) { - this.showTitle = args.showTitle; - if (this.showTitle == false) { - this.title = null; + if (_this.creationMode) { + buttons.push({ + text : 'Create', + scope : this, + handler : function() { + _this._saveShipment(); } - } - - if (args.height != null) { - this.height = args.height; - } - - if (args.maxHeight != null) { - this.maxHeight = args.maxHeight; - } - - if (args.minHeight != null) { - this.minHeight = args.minHeight; - } + }); + } else { + buttons.push({ + text : 'Save', + scope : this, + handler : function() { + _this._saveShipment(); + } + }); - if (args.maxWidth != null) { - this.maxWidth = args.maxWidth; - } + } - if (args.isStatusColumnHidden != null) { - this.isStatusColumnHidden = args.isStatusColumnHidden; - } - if (args.isTimeColumnHidden != null) { - this.isTimeColumnHidden = args.isTimeColumnHidden; - } + this.labContactForSendingStore = Ext.create('Ext.data.Store', { + fields : [ 'cardName', 'labContactId' ] + }); - if (args.title != null) { - this.title = args.title; - } - if (args.estimateTime != null) { - this.estimateTime = args.estimateTime; - } + this.labContactForReturnStore = Ext.create('Ext.data.Store', { + fields : [ 'cardName', 'labContactId' ] + }); - if (args.margin != null) { - this.margin = args.margin; - } + // Create the combo box, attached to the states data store + this.labContactsSendingCombo = Ext.create('Ext.form.ComboBox', { + id : _this.id + "shipmentform_sendingLabContactId", + fieldLabel : 'Lab contact for sending', + afterLabelTextTpl : required, + store : this.labContactForSendingStore, + queryMode : 'local', + labelWidth : 200, + displayField : 'cardName', + valueField : 'labContactId' + }); - if (args.tbar != null) { - this.tbar = args.tbar; + this.labContactsReturnCombo = Ext.create('Ext.form.ComboBox', { + id : _this.id + "returnLabContactId", + fieldLabel : 'If No, Lab-Contact for Return', + afterLabelTextTpl : required, + store : this.labContactForReturnStore, + queryMode : 'local', + labelWidth : 200, + displayField : 'cardName', + valueField : 'labContactId', + listeners : { + change : function(x, newValue) { + for ( var i = 0; i < x.getStore().data.items.length; i++) { + if (x.getStore().data.items[i].raw.labContactId == newValue) { + Ext.getCmp(_this.id + "returnCourier").setValue(x.getStore().data.items[i].raw.defaultCourrierCompany); + Ext.getCmp(_this.id + "courierAccount").setValue(x.getStore().data.items[i].raw.courierAccount); + Ext.getCmp(_this.id + "BillingReference").setValue(x.getStore().data.items[i].raw.billingReference); + Ext.getCmp(_this.id + "dewarAvgCustomsValue").setValue(x.getStore().data.items[i].raw.dewarAvgCustomsValue); + Ext.getCmp(_this.id + "dewarAvgTransportValue").setValue(x.getStore().data.items[i].raw.dewarAvgTransportValue); + } + } + } } + }); - if (args.sorter != null) { - this.sorter = args.sorter; - } + if (this.panel == null) { + this.panel = Ext.create('Ext.form.Panel', { + bodyPadding : 5, + width : 600, + border : 1, + items : [ { + xtype : 'fieldset', + title : 'Details', + collapsible : false, + defaultType : 'textfield', + layout : 'anchor', + defaults : { + anchor : '100%' + }, + items : [ { + xtype : 'requiredtext', + fieldLabel : 'Shipment Label', + allowBlank : false, + name : 'shippingName', + id : _this.id + 'shippingName', + value : '', + anchor : '50%' + }, { + xtype : 'textareafield', + name : 'comments', + id : _this.id + 'comments', + fieldLabel : 'Comments', + value : '' + }, { + fieldLabel : 'Status', + readOnly : true, + id : _this.id + 'shippingStatus', + value : 'Opened', + anchor : '50%' + } ] + }, { + xtype : 'fieldset', + title : 'Lab-Contacts', + collapsible : false, + defaultType : 'textfield', + layout : 'anchor', + defaults : { + anchor : '100%' + }, + items : [ this.labContactsSendingCombo, this.labContactsReturnCombo ] + }, { + border : 0, + html : BUI.getWarningHTML("These informations are relevant for all shipments") + }, { + xtype : 'fieldset', + title : 'Courier accounts details for return', + collapsible : false, + layout : 'anchor', + defaults : { + anchor : '100%' + }, + items : [ { + xtype : 'textfield', + labelWidth : 400, + fieldLabel : 'Courier company for return (if ESRF sends a dewar back)', + id : _this.id + 'returnCourier', + value : '' + }, { + xtype : 'textfield', + labelWidth : 400, + fieldLabel : 'Courier account', + id : _this.id + 'courierAccount', + value : '' + }, { + xtype : 'textfield', + labelWidth : 400, + fieldLabel : 'Billing reference', + id : _this.id + 'BillingReference', + value : '' + }, { + xtype : 'numberfield', + labelWidth : 400, + fieldLabel : 'Average Customs value of a dewar (Euro)', + id : _this.id + 'dewarAvgCustomsValue', + value : '' + }, { + xtype : 'numberfield', + labelWidth : 400, + fieldLabel : 'Average Transport value of a dewar (Euro)', + id : _this.id + 'dewarAvgTransportValue', + value : '' + } ] + } ], + buttons : buttons + }); } - this.onClick = new Event(this); - this.onSelected = new Event(this); - this.onRemoved = new Event(this); - this.onUpdateTime = new Event(this); - this.onMeasurementChanged = new Event(this); - this.onExperimentChanged = new Event(this); -} - -MeasurementGrid.prototype._sortBy = function(sort) { - var _this = this; - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, data) { - _this.onExperimentChanged.notify(data); - _this.grid.setLoading(false); - // wizardWidget.window.close(); - }); - adapter.onError.attach(function(sender, data) { - _this.grid.setLoading(false); - alert("Oops, there was a problem"); - }); - _this.grid.setLoading("Sorting"); - adapter.sortMeasurements(this.experiments.experiments[0].experimentId, sort); + this.fillStores(); + if (this.showTitle) { + this.panel.setTitle('Create a new Shipment'); + } + return this.panel; }; -MeasurementGrid.prototype._getMenu = function() { - var _this = this; - if (this.tbar) { +ShipmentForm.prototype.input = function() { + return { + proposal : DATADOC.getProposal_10() - var items = []; - if (_this.addBtnEnable) { - items.push({ - icon : '../images/add.png', - text : 'Add measurements', - handler : function() { - _this._openAddMeasurementWindow(); - } - }); - } + }; +}; - if (_this.addBtnMultipleEdit) { - items.push({ - icon : '../images/Edit_16x16_01.png', - text : 'Multiple Edit', - handler : function() { - var multipleEditMeasurementGridWindow = new MultipleEditMeasurementGridWindow(); - multipleEditMeasurementGridWindow.onExperimentChanged.attach(function(sender, data) { - _this.onExperimentChanged.notify(data); - }); +ShipmentForm.prototype.test = function(targetId) { + var shipmentForm = new ShipmentForm({ + creationMode : true - multipleEditMeasurementGridWindow.draw(_this.measurements, _this.experiments); + }); + BIOSAXS.proposal = new Proposal(shipmentForm.input().proposal); + shipmentForm.getPanel().render(targetId); +}; + +/** + * #onSaved + */ +function StockSolutionForm(args) { + this.id = BUI.id(); + this.actions = []; - } - }); + if (args != null) { + if (args.actions != null) { + this.actions = args.actions; } + } + this.onSaved = new Event(this); +} - items.push("->"); +StockSolutionForm.prototype.getStockSolution = function() { + if (this.stockSolution != null) { + this.stockSolution.concentration = Ext.getCmp(this.id + "stockSolution_concentration").getValue(); + this.stockSolution.storageTemperature = Ext.getCmp(this.id + "stockSolution_storageTemperature").getValue(); + this.stockSolution.volume = Ext.getCmp(this.id + "stockSolution_volume").getValue(); + this.stockSolution.comments = Ext.getCmp(this.id + "stockSolution_comments").getValue(); + this.stockSolution.name = Ext.getCmp(this.id + "stockSolution_name").getValue(); + this.stockSolution.bufferId = this.bufferCombo.getValue(); - if (_this.sortingBtnEnable) { - var split = Ext.create('Ext.button.Split', { - text : 'Sort by', - // handle a click on the button itself - handler : function() { - // alert("The button was clicked"); - }, - menu : new Ext.menu.Menu({ - items : [ - { - text : 'First Created First Measured', - handler : function() { - _this._sortBy("FIFO"); - } - }, "-", { - text : 'Default', - handler : function() { - _this._sortBy("DEFAULT"); - } - } ] - }) - }); - items.push(split); + if (this.macromoleculeCombo.getValue() != null) { + this.stockSolution.macromoleculeId = this.macromoleculeCombo.getValue(); + } else { + this.stockSolution.macromolecule3VO = null; } - if (_this.collapseBtnEnable) { - items.push({ - text : 'Collapse buffers', - enableToggle : true, - scope : this, - toggleHandler : function(item, pressed) { - this.collapsed = pressed; - this.grid.getStore().loadData(this._prepareData(this.measurements, this.experiments), false); - }, - pressed : this.collapsed - }); + } else { + return { + concentration : Ext.getCmp(this.id + "stockSolution_concentration").getValue(), + storageTemperature : Ext.getCmp(this.id + "stockSolution_storageTemperature").getValue(), + volume : Ext.getCmp(this.id + "stockSolution_volume").getValue(), + comments : Ext.getCmp(this.id + "stockSolution_comments").getValue(), + name : Ext.getCmp(this.id + "stockSolution_name").getValue(), + bufferId : this.bufferCombo.getValue(), + macromoleculeId : this.macromoleculeCombo.getValue() + }; + } + return this.stockSolution; +}; + +StockSolutionForm.prototype.setStockSolution = function(stockSolution) { + if (stockSolution != null) { + if (stockSolution.macromoleculeId != null) { + this.macromoleculeCombo.setValue(stockSolution.macromoleculeId); } - var tb = Ext.create('Ext.toolbar.Toolbar', { - items : items - }); - return tb; + this.bufferCombo.setValue(stockSolution.bufferId); + Ext.getCmp(this.id + "stockSolution_concentration").setValue(this.stockSolution.concentration); + Ext.getCmp(this.id + "stockSolution_storageTemperature").setValue(this.stockSolution.storageTemperature); + Ext.getCmp(this.id + "stockSolution_volume").setValue(this.stockSolution.volume); + Ext.getCmp(this.id + "stockSolution_name").setValue(this.stockSolution.name); + Ext.getCmp(this.id + "stockSolution_comments").setValue(this.stockSolution.comments); } - return null; }; -/** Opens WizardWidget for adding new measurements * */ -MeasurementGrid.prototype._openAddMeasurementWindow = function(measurements, experiments) { - var _this = this; - var wizardWidget = new WizardWidget(); - wizardWidget.onFinished.attach(function(sender, result) { - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, data) { - _this.onExperimentChanged.notify(data); - _this.grid.setLoading(false); - wizardWidget.window.close(); - }); - wizardWidget.current.setLoading("ISPyB: Adding measurements"); - adapter.addMeasurements(result.name, "comments", result.data, _this.experiments.experiments[0].experimentId); +StockSolutionForm.prototype.getBufferCombo = function() { + this.bufferCombo = BIOSAXS_COMBOMANAGER.getComboBuffers(BIOSAXS.proposal.getBuffers(), { + labelWidth : 120, + margin : '0 0 10 0', + width : 220 + }); + return this.bufferCombo; +}; - wizardWidget.draw(null, new MeasurementCreatorStepWizardForm(BIOSAXS.proposal.getMacromolecules(), { - noNext : true - })); +StockSolutionForm.prototype.getMacromoleculeCombo = function() { + this.macromoleculeCombo = BIOSAXS_COMBOMANAGER.getComboMacromoleculeByMacromolecules(BIOSAXS.proposal.getMacromolecules(), { + labelWidth : 120, + margin : '0 0 10 0', + width : 220 + + }); + return this.macromoleculeCombo; }; -/******************************************************************************* - * Opens WizardWidget for adding new measurements - * - * @Measurements - * @Experiments experimentList Object - ******************************************************************************/ -MeasurementGrid.prototype._prepareData = function(measurements, experiments) { - var data = []; - for (var i = 0; i < measurements.length; i++) { - var measurement = measurements[i]; - var specimen = experiments.getSampleById(measurement.specimenId); - var buffer = experiments.getBufferById(specimen.bufferId); - measurement.buffer_acronym = buffer.acronym; - measurement.bufferId = buffer.bufferId; - measurement.volume = specimen.volume; - if (specimen.macromolecule3VO != null) { - measurement.acronym = specimen.macromolecule3VO.acronym; - measurement.macromoleculeId = specimen.macromolecule3VO.macromoleculeId; - } - measurement.concentration = specimen.concentration; - if (measurement.run3VO != null) { - measurement.energy = measurement.run3VO.energy; - measurement.expExposureTemperature = measurement.run3VO.exposureTemperature; - measurement.storageTemperature = measurement.run3VO.storageTemperature; - measurement.timePerFrame = measurement.run3VO.timePerFrame; - measurement.radiationAbsolute = measurement.run3VO.radiationAbsolute; - measurement.radiationRelative = measurement.run3VO.radiationRelative; - measurement.status = "DONE"; +StockSolutionForm.prototype.refresh = function() { +}; - try { - if (measurement.run3VO.timeStart != null) { - if (measurement.run3VO.timeStart != "") { - measurement.miliseconds = moment(measurement.run3VO.timeStart).format("X"); - } - } - } catch (E) { - console.log(E); - } - } +StockSolutionForm.prototype._getTopPanel = function() { + return { + xtype : 'container', + layout : 'hbox', + border : 0, + items : [ { + xtype : 'container', + layout : 'hbox', + items : [ { + xtype : 'container', + flex : 1, + border : false, + layout : 'anchor', + defaultType : 'textfield', + items : [ - if (experiments.getDataCollectionByMeasurementId(measurement.measurementId).length > 0) { - var measurementtodatacollection3VOs = experiments.getDataCollectionByMeasurementId(measurement.measurementId)[0].measurementtodatacollection3VOs; - for (var k = 0; k < measurementtodatacollection3VOs.length; k++) { - if (measurementtodatacollection3VOs[k].dataCollectionOrder == 1) { - var specimenBuffer = experiments.getSampleById(experiments.getMeasurementById(measurementtodatacollection3VOs[k].measurementId).specimenId); - if (specimenBuffer.sampleplateposition3VO != null) { - measurement.bufferSampleplateposition3VO = specimenBuffer.sampleplateposition3VO; - measurement.bufferSampleplate = (experiments.getSamplePlateById(specimenBuffer.sampleplateposition3VO.samplePlateId)); - } - } - } - } + this.getMacromoleculeCombo(), { + xtype : 'requiredtext', + id : this.id + 'stockSolution_name', + fieldLabel : 'Acronym', + labelWidth : 120, + width : 250 + }, { + xtype : 'requiredtext', + id : this.id + 'stockSolution_concentration', + fieldLabel : 'Conc. (mg/ml)', + labelWidth : 120, + width : 250 + }, - if (this.collapsed) { - /** If collapsed only the samples * */ - if (specimen.macromolecule3VO != null) { - data.push(measurement); - } - } else { - data.push(measurement); - } + { + id : this.id + 'stockSolution_storageTemperature', + fieldLabel : 'Storage Temp.(C)', + labelWidth : 120, + width : 250 + }, { + xtype : 'requiredtext', + id : this.id + 'stockSolution_volume', + fieldLabel : 'Volume in Well (µl)', + labelWidth : 120, + width : 250 + } ] + } ] + }, { + xtype : 'container', + flex : 1, + layout : 'anchor', + defaultType : 'textfield', + margin : '0 0 0 10', + items : [ this.getBufferCombo() ] + } ] + }; - } - return data; }; -/** - * Refresh data grid with the measurements and the experiments - * - * @measurements array with measurement3VO objects - * @experiments array with experiments objects - */ -MeasurementGrid.prototype.refresh = function(measurements, experiments) { - this.experiments = experiments; - this.measurements = measurements; - this.store.loadData(this._prepareData(measurements, experiments), false); +StockSolutionForm.prototype.getPanel = function(stockSolution) { + this.stockSolution = stockSolution; + this.panel = Ext.createWidget({ + xtype : 'container', + layout : 'vbox', + border : 0, + style : { + padding : '10px' + }, + fieldDefaults : { + labelAlign : 'left', + labelWidth : 50 + }, + items : [ this._getTopPanel(stockSolution), { + id : this.id + 'stockSolution_comments', + xtype : 'textareafield', + name : 'comments', + fieldLabel : 'Comments', + labelWidth : 120, + width : '100%' + } ] + }); + + this.setStockSolution(stockSolution); + return this.panel; +}; + +StockSolutionForm.prototype.input = function() { + return { + stock : { + "stockSolutionId" : 6, + "proposalId" : 3124, + "macromoleculeId" : 5933, + "bufferId" : 811, + "instructionSet3VO" : null, + "boxId" : 305861, + "storageTemperature" : "20", + "volume" : "300", + "concentration" : "1.2", + "comments" : "Buffer EDTA with A", + "name" : "A_EDTA_1.2", + "samples" : [], + "buffer" : "EDTA", + "macromolecule" : "A" + }, + proposal : new MeasurementGrid().input().proposal + }; +}; + +StockSolutionForm.prototype.test = function(targetId) { + var stockSolutionForm = new StockSolutionForm(); + BIOSAXS.proposal = new Proposal(stockSolutionForm.input().proposal); + var panel = stockSolutionForm.getPanel(new Shipment(stockSolutionForm.input().stock)); + panel.render(targetId); }; + + +BoxWhiskerGraph.prototype.cleanArray = GenericGraph.prototype.cleanArray; +BoxWhiskerGraph.prototype.plotAxes = GenericGraph.prototype.plotAxes; +BoxWhiskerGraph.prototype.plotRuler = GenericGraph.prototype.plotRuler; +BoxWhiskerGraph.prototype.drawSVGVerticalText = GenericGraph.prototype.drawSVGVerticalText; +BoxWhiskerGraph.prototype.getClassColor = GenericGraph.prototype.getClassColor; +BoxWhiskerGraph.prototype.getDimensions = GenericGraph.prototype.getDimensions; +BoxWhiskerGraph.prototype.calculate = GenericGraph.prototype.calculate; +BoxWhiskerGraph.prototype.getMedian = GenericGraph.prototype.getMedian; +BoxWhiskerGraph.prototype.pointToPixel = GenericGraph.prototype.pointToPixel; +BoxWhiskerGraph.prototype.maxValueWhisker = GenericGraph.prototype.maxValueWhisker; +BoxWhiskerGraph.prototype.refresh = GenericGraph.prototype.refresh; /** - * Set status bar to busy (refreshing icon) + * Subclass of GenericGraph * - * @msg message to be displayed on the bar + * @maxBoxWidth */ -MeasurementGrid.prototype._showStatusBarBusy = function(msg) { - var statusBar = Ext.getCmp(this.id + 'basic-statusbar'); - statusBar.setStatus({ - text : msg, - iconCls : 'x-status-busy', - clear : false - }); +function BoxWhiskerGraph(args){ + this.maxBoxWidth = 25; + + if (args == null){ + args = new Object(); + } + args["plotHorizontalByCluster"] = true; + + GenericGraph.call(this, args); + + if (args.maxBoxWidth != null){ + this.maxBoxWidth = args.maxBoxWidth; + } }; + + /** - * Set status bar to ready (ok icon) - * - * @msg message to be displayed on the bar - */ -MeasurementGrid.prototype._showStatusBarReady = function(msg) { - var statusBar = Ext.getCmp(this.id + 'basic-statusbar'); - statusBar.setStatus({ - text : msg, - iconCls : 'x-status-valid', - clear : false - }); +There are several different methods for calculating quartiles.[1] This calculator uses a method described by Moore and McCabe to find quartile values. +The same method also used by The TI-83 to calculate quartile values. +With this method, the first quartile is the median of the numbers below the median, the third quartile is the median of the numbers above the median. + +http://www.miniwebtool.com/quartile-calculator/ +http://www.alcula.com/calculators/statistics/box-plot/ +**/ +BoxWhiskerGraph.prototype.getQ1 = function(array){ + array = array.slice(0, array.length/2); + return this.getMedian(array); }; -/** - * If updateRowEnabled returns an array with Ext.grid.plugin.RowEditing - */ -MeasurementGrid.prototype._getPlugins = function() { - var _this = this; - var plugins = []; - if (this.updateRowEnabled) { - plugins.push(Ext.create('Ext.grid.plugin.RowEditing', { - clicksToEdit : 1, - listeners : { - validateedit : function(grid, e) { - /** Setting values * */ - for ( var key in _this.editor) { - e.record.raw[key] = e.newValues[key]; - } - /** Comments are always updatable* */ - e.record.raw.comments = e.newValues.comments; +BoxWhiskerGraph.prototype.getQ3 = function(array){ + array = array.slice((array.length + 1)/2); + return this.getMedian(array); + +}; - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, measurement) { - _this.onMeasurementChanged.notify(measurement); - _this.grid.setLoading(false); - }); - adapter.onError.attach(function() { - alert("Error"); - _this.grid.setLoading(false); - }); +BoxWhiskerGraph.prototype.getQ2 = function(array){ + return this.getMedian(array); +}; - _this.grid.setLoading(); - adapter.saveMeasurement(e.record.raw, _this.experiments.experiments[0]); - } - } - })); +BoxWhiskerGraph.prototype.getBelowOutliers = function(belowLimit, array){ + var points = new Array(); + for (var i = 0; i < array.length; i++){ + if (array[i] <= belowLimit){ + points.push(array[i]); + } } - return plugins; + return points; }; -/** - * @key name of the columns mathing the this.editor[key] - */ -MeasurementGrid.prototype._getEditor = function(key) { - if (this.editor[key] != null) { - return this.editor[key]; +BoxWhiskerGraph.prototype.getAboveOutliers = function(aboveLimit, array){ + var points = new Array(); + for (var i = 0; i < array.length; i++){ + if (array[i] >= aboveLimit){ + points.push(array[i]); + } } - return null; + return points; }; -MeasurementGrid.prototype.getPanel = function(measurements, experiments) { - this.experiments = experiments; - this.measurements = measurements; - var _this = this; - this.store = Ext.create('Ext.data.Store', { - fields : [ { - name : 'miliseconds', - type : 'int' - }, 'priority', 'bufferId', { - name : 'exposureTemperature', - type : 'numeric' - }, 'volumeToLoad','code', 'transmission', 'viscosity', 'waitTime', 'flow', 'buffer_acronym', 'macromoleculeId', 'acronym', 'concentration', 'extraFlowTime', 'volume', 'energy', - 'expExposureTemperature', 'storageTemperature', 'timePerFrame', 'radiationAbsolute', 'radiationRelative', 'status', 'comments' ], - data : this._prepareData(measurements, experiments) - }); - this.store.sort(this.sorter); - var bbar = {}; - try { - bbar = Ext.create('Ext.ux.StatusBar', { - id : _this.id + 'basic-statusbar', - defaultText : 'Ready', - text : 'Ready', - iconCls : 'x-status-valid', - items : [] - }); - } catch (exp) { - console.log("bbar error"); + +BoxWhiskerGraph.prototype.minValueWhisker = function(belowLimit, q1, array){ + var points = new Array(); + + for (var i = 0; i < array.length; i++){ + if ((array[i] < q1) && (array[i]) > belowLimit){ + points.push(array[i]); + } + } + + if (points.length > 0){ + points.sort(function(a, b){return a - b;}); + return points[0]; } + return null; +}; - this.grid = Ext - .create( - 'Ext.grid.Panel', - { - id : this.id, - title : this.title, - store : this.store, - selModel : this.selModel, - plugins : this._getPlugins(), - resizable : this.resizable, - margin : this.margin, - maxHeight : this.maxHeight, - minHeight : this.minHeight, - maxWidth : this.maxWidth, - width : this.width, - tbar : this._getMenu(), - columns : [ - { - text : 'Order', - dataIndex : 'priority', - width : 50, - hidden : _this.isPriorityColumnHidden, - sortable : true +BoxWhiskerGraph.prototype.isNumber = function(value){ + if (value=="") return false; - }, - { - text : 'Run Number', - dataIndex : 'code', - width : 50, - hidden : true, - sortable : true + var d = parseInt(value); + if (!isNaN(d)) return true; else return false; - }, +}; - { - text : 'Specimen', - columns : [ +BoxWhiskerGraph.prototype.plotBoxWhisker = function(boxProperties){ + if (this.maxBoxWidth != null){ + if (boxProperties.width > this.maxBoxWidth){ + boxProperties.x = boxProperties.x + (boxProperties.width/2) - (this.maxBoxWidth/2); + boxProperties.width = this.maxBoxWidth; + } + + } - { - text : '', - dataIndex : 'macromoleculeId', - width : 30, - renderer : function(val, y, sample) { - if (val != null) { - if (_this.experiments == null) { - return BUI.getRectangleColorDIV(BIOSAXS.proposal.macromoleculeColors[val], 10, 10); - } else { - return BUI.getRectangleColorDIV(_this.experiments.macromoleculeColors[val], 10, 10); - } - } - }, - sortable : true - }, - { - text : 'Macromo.', - dataIndex : 'acronym', - width : 80, - renderer : function(val, y, sample) { - return val; - }, - sortable : true - }, - { - text : 'Conc. ', - dataIndex : 'concentration', - width : 80, - renderer : function(val, y, sample) { - if (sample.raw.macromoleculeId == null) { - return ""; - } - if (isNaN(val)) - return val; + if (this.plotPoints){ + for(var i = 0; i < boxProperties.values.length; i++){ + var value = boxProperties.values[i]; + var toPixel = this.pointToPixel(value, boxProperties); + SVG.drawCircle(boxProperties.x + (boxProperties.width/2) + 2, toPixel, this.pointRadius, this.svg, [["fill", "green"], ["fill-opacity", this.fillOpacityPoint],['stroke-opacity', this.strokeOpacityPoint], ["stroke", "black"]]); + } + } + - if (val != 0) { - return BUI.formatValuesUnits(val, '', { - fontSize : 16, - decimals : 3, - unitsFontSize : this.unitsFontSize - }); - } else { - return; - } + var boxColor = this.getClassColor(boxProperties.name); + var result = this.calculate(boxProperties.values); + /** Q1 **/ + + if (this.isNumber(result.Q1)){ + SVG.drawLine(boxProperties.x, this.pointToPixel(result.Q1, boxProperties), boxProperties.x + boxProperties.width, this.pointToPixel(result.Q1, boxProperties), this.svg, [["stroke", boxColor]]); + } + /** Q2 **/ + if (this.isNumber(result.Q2)){ + SVG.drawLine(boxProperties.x, this.pointToPixel(result.Q2, boxProperties), boxProperties.x + boxProperties.width, this.pointToPixel(result.Q2, boxProperties), this.svg, [["stroke", "blue"], ["stroke-width", "2"]]); + } + + /** Q3 **/ + if (this.isNumber(result.Q3)){ + SVG.drawLine(boxProperties.x, this.pointToPixel(result.Q3, boxProperties), boxProperties.x + boxProperties.width, this.pointToPixel(result.Q3, boxProperties), this.svg, [["stroke", boxColor]]); + } + + /** Concenting Q1 and Q3 **/ + if (this.isNumber(result.Q1)&&this.isNumber(result.Q3)){ + SVG.drawLine(boxProperties.x, this.pointToPixel(result.Q1, boxProperties), boxProperties.x, this.pointToPixel(result.Q3, boxProperties), this.svg, [["stroke", boxColor]]); + SVG.drawLine(boxProperties.x + boxProperties.width, this.pointToPixel(result.Q1, boxProperties), boxProperties.x + boxProperties.width, this.pointToPixel(result.Q3, boxProperties), this.svg, [["stroke", boxColor]]); + } + + /** min-whisker **/ + if (result["min-whisker"] != null){ + if (this.isNumber(result.Q1)){ + SVG.drawLine(boxProperties.x + (boxProperties.width/2) + 2, this.pointToPixel(result.Q1, boxProperties), boxProperties.x + (boxProperties.width/2) + 2, this.pointToPixel(result["min-whisker"], boxProperties), this.svg, [["stroke", boxColor]]); + } + SVG.drawLine(boxProperties.x,this.pointToPixel(result["min-whisker"], boxProperties), boxProperties.x + boxProperties.width, this.pointToPixel(result["min-whisker"], boxProperties) , this.svg, [["stroke", boxColor]]); + + } + + /** max-whisker **/ + if (result["max-whisker"] != null){ + if (this.isNumber(result.Q3)){ + SVG.drawLine(boxProperties.x + (boxProperties.width/2) + 2, this.pointToPixel(result.Q3, boxProperties), boxProperties.x + (boxProperties.width/2) + 2, this.pointToPixel(result["max-whisker"], boxProperties), this.svg, [["stroke", boxColor]]); + } + SVG.drawLine(boxProperties.x , this.pointToPixel(result["max-whisker"], boxProperties), boxProperties.x + boxProperties.width, this.pointToPixel(result["max-whisker"], boxProperties), this.svg, [["stroke", boxColor]]); + + } + + /** outliners **/ + if (result["above-outliers"] != null){ + for (var point in result["above-outliers"]){ + SVG.drawCircle(boxProperties.x + (boxProperties.width/2) + 2, this.pointToPixel(result["above-outliers"][point], boxProperties), this.pointRadius, this.svg, [["fill", "red"], ["fill-opacity", "1"]]); + //SVG.drawText(boxProperties.x + (boxProperties.width/2) - 2, this.pointToPixel(result["above-outliers"][point], boxProperties) + 2, "x",this.svg, [["fill", "black"], ["font-size", "x-small"]]); + } + } + if (result["below-outliers"] != null){ + for (var point in result["below-outliers"]){ + SVG.drawCircle(boxProperties.x + (boxProperties.width/2) + 2, this.pointToPixel(result["below-outliers"][point], boxProperties), this.pointRadius, this.svg, [["fill", "red"], ["fill-opacity", "1"]]); + //SVG.drawText(boxProperties.x + (boxProperties.width/2) - 2, this.pointToPixel(result["below-outliers"][point], boxProperties) + 2, "x",this.svg, [["fill", "black"], ["font-size", "x-small"]]); + } + } +}; - }, - sortable : true - }, - { - text : '', - dataIndex : 'bufferId', - width : 30, - hidden : false, - renderer : function(val, y, sample) { - if (val != null) { - var color = '#FFCCFF'; - if (_this.experiments != null) { - var dc = _this.experiments.getDataCollectionByMeasurementId(sample.raw.measurementId); - if (dc != null) { - if (dc.length > 0) { - color = _this.experiments.getSpecimenColorByBufferId(_this.experiments - .getMeasurementById(dc[0].measurementtodatacollection3VOs[0].measurementId).specimenId); - } - } - } else { - color = BIOSAXS.proposal.bufferColors[val]; - } - return BUI.getRectangleColorDIV(color, 10, 10); - } - }, - sortable : true - }, - { - text : 'Buffer', - dataIndex : 'buffer_acronym', - width : 120, - renderer : function(val, y, sample) { - if (sample.raw.bufferSampleplateposition3VO != null) { - return BIOSAXS.proposal.getBufferById(sample.raw.bufferId).acronym + " Plate: [" - + sample.raw.bufferSampleplate.slotPositionColumn + ", " - + BUI.getSamplePlateLetters()[sample.raw.bufferSampleplateposition3VO.rowNumber - 1] + "-" - + sample.raw.bufferSampleplateposition3VO.columnNumber + "]"; - } - return val; - }, - sortable : true - }, { - text : 'Position', - width : 100, - hidden : true, - renderer : function(val, y, sample) { - if (_this.experiments != null) { - return BUI.getSamplePositionHTML(_this.experiments.getSampleById(sample.raw.specimenId), _this.experiments.experiments[0]); - } - } - } ] - }, - { - text : 'Parameters', - columns : [ - { - text : 'Ex. Flow. time (s)', - dataIndex : 'extraFlowTime', - width : 100, - hidden : true, - renderer : function(val, y, sample) { - return BUI.formatValuesUnits(val, 's', { - fontSize : 12, - decimals : 0, - unitsFontSize : this.unitsFontSize - }); - } - }, - { - text : 'Exp. Temp.', - dataIndex : 'exposureTemperature', - width : 70, - renderer : function(val, y, sample) { - if (Number(val)) { - return BUI.formatValuesUnits(val, 'C', { - fontSize : 12, - decimals : 2, - unitsFontSize : this.unitsFontSize - }); - } - return null; - }, - sortable : true, - editor : this._getEditor("exposureTemperature") - }, - { - text : 'Vol. Load', - dataIndex : 'volumeToLoad', - width : 60, - hidden : false, - editor : this._getEditor("volumeToLoad"), - renderer : function(val, y, sample) { - // return val+ " µl"; - return BUI.formatValuesUnits(val, 'µl', { - fontSize : 12, - decimals : 2, - unitsFontSize : this.unitsFontSize - }); - } - }, - { - text : 'Volume in Well', - dataIndex : 'volume', - hidden : true, - editor : this._getEditor("volume"), - width : 80, - renderer : function(val, y, sample) { - // return val + "(µl)"; - return BUI.formatValuesUnits(val, 'µl', { - fontSize : 12, - decimals : 2, - unitsFontSize : this.unitsFontSize - }); - } - }, - { - text : 'Trans.', - dataIndex : 'transmission', - width : 60, - editor : this._getEditor("transmission"), - renderer : function(val, y, sample) { - return BUI.formatValuesUnits(val, '%', { - fontSize : 12, - decimals : 0, - unitsFontSize : this.unitsFontSize - }); - } - }, - { - text : 'Wait T.', - dataIndex : 'waitTime', - editor : this._getEditor("waitTime"), - width : 50, - renderer : function(val, y, sample) { - // if (val != 0) { - return BUI.formatValuesUnits(val, 's', { - fontSize : 12, - decimals : 0, - unitsFontSize : this.unitsFontSize - }); - // } - } - }, - { - text : 'Flow', - dataIndex : 'flow', - editor : this._getEditor("flow"), - width : 50, - renderer : function(val, y, sample) { - if (val == true) { - return "yes"; - } - return null; - } - }, - { - text : 'Viscosity', - dataIndex : 'viscosity', - tooltip : 'The viscosity of a fluid is a measure of its resistance to gradual deformation by shear stress or tensile stress. For liquids, it corresponds to the informal notion of "thickness"', - editor : this._getEditor("viscosity"), - width : 50, - renderer : function(val, y, sample) { - return val; - } - } ] - }, { - text : 'Status', - dataIndex : 'status', - width : 50, - hidden : _this.isStatusColumnHidden, - renderer : function(val, y, sample) { - if (val != null) { - if (val == 'DONE') { - return "" + val + " "; - } - } - } - }, { - text : 'Time', - dataIndex : 'time', - width : 80, - hidden : _this.isTimeColumnHidden, - renderer : function(val, y, sample) { - if (sample.raw.run3VO != null) { - if (sample.raw.run3VO.timeStart != null) { - if (sample.raw.run3VO.timeStart != "") { - var m = moment(sample.raw.run3VO.timeStart); - return m.format("hh:mm:ss a"); - } - } - } - } - }, { - text : 'Energy', - dataIndex : 'energy', - width : 100, - hidden : true - }, { - text : 'Real Exp. Temp.(C)', - width : 100, - dataIndex : 'expExposureTemperature', - hidden : true - }, { - text : 'Storage Temp.(C)', - width : 100, - dataIndex : 'storageTemperature', - hidden : true - }, { - text : 'Time/Frame (s)', - width : 100, - dataIndex : 'timePerFrame', - hidden : true - }, { - text : 'Radiation Relative', - dataIndex : 'radiationRelative', - width : 100, - hidden : true - }, { - text : 'Radiation Absolute', - dataIndex : 'radiationAbsolute', - width : 100, - hidden : true - }, { - text : 'Comments', - dataIndex : 'comments', - flex : 1, - hidden : false, - editor : this._getEditor("comments") - }, { - id : _this.id + 'buttonRemoveSample', - text : '', - hidden : !_this.removeBtnEnabled, - width : 100, - sortable : false, - renderer : function(value, metaData, record, rowIndex, colIndex, store) { - if (record.raw.macromoleculeId != null) { - if (_this.removeBtnEnabled) { - return BUI.getRedButton('REMOVE'); - } - } - } - } ], - bbar : bbar, - viewConfig : { - stripeRows : true, - getRowClass : function(record, index, rowParams, store) { - if (record.data.status == "DONE") { - return 'green-row'; - } - }, - listeners : { - 'itemclick' : function(grid, record, item, index, e, eOpts) { - _this.onClick.notify({ - specimen : record.raw - }); - }, - 'cellclick' : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { - if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonRemoveSample') { - grid.getStore().removeAt(rowIndex); +BoxWhiskerGraph.prototype.plotClusterTitles = function(properties){ + /** Cluster Titles **/ + var posX = this.left + this.rulerWidth; + var data = this.data; + for(var i = 0; i < data.clusters.length; i++ ){ + /** title for the clusters **/ + posX = posX + this.interClustersSpace; + + /** Drawing title of classes **/ + var cluster = data.clusters[i]; + var posXClasses = posX; + for(var j = 0; j < cluster.classes.length; j++){ + //SVG.drawRectangle(posXClasses, this.height - (this.bottom + this.clusterTitleHeight + this.rulerHeight), properties.classWidth, this.rulerHeight, this.svg, [["fill", "green"]]); +// SVG.drawText(posXClasses + 1/6*properties.classWidth, this.height - (this.bottom + this.clusterTitleHeight + (this.rulerHeight*1/4)), cluster.classes[j].name, this.svg, [["style", "font-size:xx-small"]]); + var color = cluster.classes[j].color; + this.drawSVGVerticalText(posXClasses + 1/2*(properties.classWidth), this.height - (this.bottom - this.clusterTitleHeight + (this.rulerHeight)), cluster.classes[j].name , [["style", "font-size:x-small;"], ["fill", color]]); + + // SVG.drawRectangle(posXClasses, this.height - (this.bottom + this.clusterTitleHeight), properties.classWidth, 2, this.svg, [["fill", "black"]]); + posXClasses = posXClasses + properties.classWidth + this.interClassesSpace; + } + + var clusterTitleSpace = (data.clusters[i].classes.length * properties.classWidth) + ((data.clusters[i].classes.length - 1) * this.interClassesSpace); + //SVG.drawRectangle(posX, this.height - (this.bottom + this.clusterTitleHeight), clusterTitleSpace, this.clusterTitleHeight, this.svg, [["fill", "pink"]]); + SVG.drawRectangle(posX, this.height - (this.clusterTitleHeight+this.bottom), clusterTitleSpace, 1, this.svg, []); + +// var transform = ["translate", "(" + posX + 1/3*clusterTitleSpace +", " + this.height - (this.bottom + (this.clusterTitleHeight*1/4)) +")"], ["transform", "rotate(180)"]; +// var transform = ["transform", "translate(" + (posX + 1/3*clusterTitleSpace) +", " + (this.height - (this.bottom + (this.clusterTitleHeight*1/4))) + "), rotate(-90) "]; +// this.drawSVGVerticalText(posX + 1/3*clusterTitleSpace, this.height - (this.bottom + (this.clusterTitleHeight*1/4)),data.clusters[i].name, [["style", "font-size:x-small"]]); + SVG.drawText(posX , this.height - (this.bottom + (this.clusterTitleHeight*1/4)),data.clusters[i].name ,this.svg, [["style", "font-size:x-small"]]); + posX = posX + clusterTitleSpace; + } +}; + +BoxWhiskerGraph.prototype.plotWhisters = function(data, properties){ + var colors = ["yellow", "orange", "green"]; + var posX = this.left + this.rulerWidth; + for(var i = 0; i < data.clusters.length; i++ ){ + var cluster = data.clusters[i]; + /** inter cluster space **/ + //SVG.drawRectangle(posX, this.top, this.interClustersSpace, this.height - this.top - this.bottom - this.clusterTitleHeight - this.rulerHeight, this.svg, [["fill", "black"]]); + posX = posX + this.interClustersSpace; + + for (var j = 0; j < cluster.classes.length; j ++){ + + //SVG.drawRectangle(posX, this.top, properties.classWidth, this.height - this.top - this.bottom - this.clusterTitleHeight - this.rulerHeight, this.svg, [["fill", colors[j]]]); + this.plotBoxWhisker( + { + name : cluster.classes[j].name, + values : cluster.classes[j].values, + minPoint : properties.minPoint, + maxPoint : properties.maxPoint, + x : posX, + y : this.top, + width : properties.classWidth, + height : this.height - this.top - this.bottom - this.clusterTitleHeight - this.rulerHeight + + + } + ); + posX = posX + properties.classWidth; + //SVG.drawRectangle(posX, this.top, this.interClassesSpace, this.height - this.top - this.bottom - this.clusterTitleHeight - this.rulerHeight, this.svg, [["fill", "black"]]); + if (j < cluster.classes.length - 1){ + posX = posX + this.interClassesSpace; + } + } + } +}; + +BoxWhiskerGraph.prototype.draw = function(targetId, data){ + this.targetId = targetId; + var properties = (this.getDimensions(data)); + this.svg = SVG.createSVGCanvas(document.getElementById(this.targetId), [["width", this.width], ["height", this.height]]); + this.plotAxes(properties); + this.plotClusterTitles(properties); + this.plotWhisters(data,properties); +}; + +BoxWhiskerGraph.prototype.input = function(){ + return DATADOC.getBoxWhikerData(); +}; + +BoxWhiskerGraph.prototype.test = function(targetId){ + var plot = new BoxWhiskerGraph( + { + targetId : targetId, + height : 350, + width : 450, + maxBoxWidth : 25 + } + ); + plot.refresh(this.input()); +}; + + + + + + + +function DataSet(){ + this.json = null; + +}; + + +DataSet.prototype.loadFromJSON = function(json){ + if(json != null) { + if(this.validate(json)) { + this.json = json; + } + } +}; + + +DataSet.prototype.toJSON = function(json){ + return this.json; +}; + + +/** Abstract method to be override on childs classes **/ +DataSet.prototype.validate = function(json){ + if (true){ + return true; + } + /*else{ + throw "Data validation failed"; + }*/ +}; + + + + +Edge.prototype.getName = GraphItem.prototype.getName; +Edge.prototype.setName = GraphItem.prototype.setName; +Edge.prototype.getId = GraphItem.prototype.getId; + +function Edge(id, name, nodeSource, nodeTarget, args){ + GraphItem.prototype.constructor.call(this, id, name, args); + + this.sourceNode = nodeSource; + this.targetNode = nodeTarget; + +}; + +Edge.prototype.toJSON = function(){ + return {"index": this.id,"sourceIndex":this.sourceNode.getId(),"targetIndex":this.targetNode.getId(),"args":this.args}; +}; + +Edge.prototype.getNodeSource = function(){ + return this.sourceNode; +}; + +Edge.prototype.getNodeTarget = function(){ + return this.targetNode; +}; + +Edge.prototype.remove = function(){ + //Remove edge object in the nodes + this.getNodeSource().removeEdge(this); + this.getNodeTarget().removeEdge(this); + + this.deleted.notify(this); +}; + + +EdgeGraphFormatter.prototype.setProperties = ItemGraphFormatter.prototype.setProperties; +EdgeGraphFormatter.prototype.getDefault = ItemGraphFormatter.prototype.getDefault; +EdgeGraphFormatter.prototype.getSelected = ItemGraphFormatter.prototype.getSelected; +EdgeGraphFormatter.prototype.getOver = ItemGraphFormatter.prototype.getOver; +EdgeGraphFormatter.prototype.getDragging = ItemGraphFormatter.prototype.getDragging; +EdgeGraphFormatter.prototype.getId = ItemGraphFormatter.prototype.getId; +EdgeGraphFormatter.prototype.toJSON = ItemGraphFormatter.prototype.toJSON; +EdgeGraphFormatter.prototype.loadFromJSON = ItemGraphFormatter.prototype.loadFromJSON; +EdgeGraphFormatter.prototype._setEvents = ItemGraphFormatter.prototype._setEvents; + +function EdgeGraphFormatter(id, defaultFormat, selectedFormat, overFormat, dragginFormat){ + ItemGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, dragginFormat); + + this.getDefault().args.type = "EdgeGraphFormatter"; +}; + + + + +LineEdgeGraphFormatter.prototype.setProperties = EdgeGraphFormatter.prototype.setProperties; +LineEdgeGraphFormatter.prototype.getDefault = EdgeGraphFormatter.prototype.getDefault; +LineEdgeGraphFormatter.prototype.getSelected = EdgeGraphFormatter.prototype.getSelected; +LineEdgeGraphFormatter.prototype.getOver = EdgeGraphFormatter.prototype.getOver; +LineEdgeGraphFormatter.prototype.getDragging = EdgeGraphFormatter.prototype.getDragging; +LineEdgeGraphFormatter.prototype.getId = EdgeGraphFormatter.prototype.getId; +LineEdgeGraphFormatter.prototype.toJSON = EdgeGraphFormatter.prototype.toJSON; +LineEdgeGraphFormatter.prototype.loadFromJSON = EdgeGraphFormatter.prototype.loadFromJSON; +LineEdgeGraphFormatter.prototype._setEvents = EdgeGraphFormatter.prototype._setEvents; + +function LineEdgeGraphFormatter(id, defaultFormat, selectedFormat, overFormat, dragginFormat){ + EdgeGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, dragginFormat); + this.args.type = "LineEdgeGraphFormatter"; +}; + +/** DIRECTED **/ +DirectedLineEdgeGraphFormatter.prototype.setProperties = EdgeGraphFormatter.prototype.setProperties; +DirectedLineEdgeGraphFormatter.prototype.getDefault = EdgeGraphFormatter.prototype.getDefault; +DirectedLineEdgeGraphFormatter.prototype.getSelected = EdgeGraphFormatter.prototype.getSelected; +DirectedLineEdgeGraphFormatter.prototype.getOver = EdgeGraphFormatter.prototype.getOver; +DirectedLineEdgeGraphFormatter.prototype.getDragging = EdgeGraphFormatter.prototype.getDragging; +DirectedLineEdgeGraphFormatter.prototype.getId = EdgeGraphFormatter.prototype.getId; +DirectedLineEdgeGraphFormatter.prototype.toJSON = EdgeGraphFormatter.prototype.toJSON; +DirectedLineEdgeGraphFormatter.prototype.loadFromJSON = EdgeGraphFormatter.prototype.loadFromJSON; +DirectedLineEdgeGraphFormatter.prototype._setEvents = EdgeGraphFormatter.prototype._setEvents; + +function DirectedLineEdgeGraphFormatter(id, defaultFormat, selectedFormat, overFormat, dragginFormat){ + EdgeGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, dragginFormat); + this.args.type = "DirectedLineEdgeGraphFormatter"; + this.args.arrowSize = "3"; +}; +DirectedLineEdgeGraphFormatter.prototype.getArrowSize = function(){ + return this.args.arrowSize; +}; + +OdirectedLineEdgeGraphFormatter.prototype.setProperties = EdgeGraphFormatter.prototype.setProperties; +OdirectedLineEdgeGraphFormatter.prototype.getDefault = EdgeGraphFormatter.prototype.getDefault; +OdirectedLineEdgeGraphFormatter.prototype.getSelected = EdgeGraphFormatter.prototype.getSelected; +OdirectedLineEdgeGraphFormatter.prototype.getOver = EdgeGraphFormatter.prototype.getOver; +OdirectedLineEdgeGraphFormatter.prototype.getDragging = EdgeGraphFormatter.prototype.getDragging; +OdirectedLineEdgeGraphFormatter.prototype.getId = EdgeGraphFormatter.prototype.getId; +OdirectedLineEdgeGraphFormatter.prototype.toJSON = EdgeGraphFormatter.prototype.toJSON; +OdirectedLineEdgeGraphFormatter.prototype.loadFromJSON = EdgeGraphFormatter.prototype.loadFromJSON; +OdirectedLineEdgeGraphFormatter.prototype._setEvents = EdgeGraphFormatter.prototype._setEvents; + +function OdirectedLineEdgeGraphFormatter(id, defaultFormat, selectedFormat, overFormat, dragginFormat){ + EdgeGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, dragginFormat); + this.args.type = "DirectedLineEdgeGraphFormatter"; + this.args.arrowSize = "3"; +}; + +OdirectedLineEdgeGraphFormatter.prototype.getArrowSize = function(){ + return this.args.arrowSize; +}; + + +/** CUT (inhibition) **/ +CutDirectedLineEdgeGraphFormatter.prototype.setProperties = EdgeGraphFormatter.prototype.setProperties; +CutDirectedLineEdgeGraphFormatter.prototype.getDefault = EdgeGraphFormatter.prototype.getDefault; +CutDirectedLineEdgeGraphFormatter.prototype.getSelected = EdgeGraphFormatter.prototype.getSelected; +CutDirectedLineEdgeGraphFormatter.prototype.getOver = EdgeGraphFormatter.prototype.getOver; +CutDirectedLineEdgeGraphFormatter.prototype.getDragging = EdgeGraphFormatter.prototype.getDragging; +CutDirectedLineEdgeGraphFormatter.prototype.getId = EdgeGraphFormatter.prototype.getId; +CutDirectedLineEdgeGraphFormatter.prototype.toJSON = EdgeGraphFormatter.prototype.toJSON; +CutDirectedLineEdgeGraphFormatter.prototype.loadFromJSON = EdgeGraphFormatter.prototype.loadFromJSON; +CutDirectedLineEdgeGraphFormatter.prototype._setEvents = EdgeGraphFormatter.prototype._setEvents; + +function CutDirectedLineEdgeGraphFormatter(id, defaultFormat, selectedFormat, overFormat, dragginFormat){ + EdgeGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, dragginFormat); + this.args.type = "CutDirectedLineEdgeGraphFormatter"; +}; + + + + +BezierEdgeGraphFormatter.prototype.setProperties = EdgeGraphFormatter.prototype.setProperties; +BezierEdgeGraphFormatter.prototype.setProperties = EdgeGraphFormatter.prototype.setProperties; +BezierEdgeGraphFormatter.prototype.getDefault = EdgeGraphFormatter.prototype.getDefault; +BezierEdgeGraphFormatter.prototype.getSelected = EdgeGraphFormatter.prototype.getSelected; +BezierEdgeGraphFormatter.prototype.getOver = EdgeGraphFormatter.prototype.getOver; +BezierEdgeGraphFormatter.prototype.getDragging = EdgeGraphFormatter.prototype.getDragging; +BezierEdgeGraphFormatter.prototype.getId = EdgeGraphFormatter.prototype.getId; +BezierEdgeGraphFormatter.prototype.toJSON = EdgeGraphFormatter.prototype.toJSON; +BezierEdgeGraphFormatter.prototype.loadFromJSON = EdgeGraphFormatter.prototype.loadFromJSON; +BezierEdgeGraphFormatter.prototype._setEvents = EdgeGraphFormatter.prototype._setEvents; + +function BezierEdgeGraphFormatter(id, defaultFormat, selectedFormat, overFormat, dragginFormat){ + EdgeGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, dragginFormat); + this.args.type = "BezierEdgeGraphFormatter"; +}; + + + +DotDirectedLineEdgeGraphFormatter.prototype.setProperties = EdgeGraphFormatter.prototype.setProperties; +DotDirectedLineEdgeGraphFormatter.prototype.getDefault = EdgeGraphFormatter.prototype.getDefault; +DotDirectedLineEdgeGraphFormatter.prototype.getSelected = EdgeGraphFormatter.prototype.getSelected; +DotDirectedLineEdgeGraphFormatter.prototype.getOver = EdgeGraphFormatter.prototype.getOver; +DotDirectedLineEdgeGraphFormatter.prototype.getDragging = EdgeGraphFormatter.prototype.getDragging; +DotDirectedLineEdgeGraphFormatter.prototype.getId = EdgeGraphFormatter.prototype.getId; +DotDirectedLineEdgeGraphFormatter.prototype.toJSON = EdgeGraphFormatter.prototype.toJSON; +DotDirectedLineEdgeGraphFormatter.prototype.loadFromJSON = EdgeGraphFormatter.prototype.loadFromJSON; +DotDirectedLineEdgeGraphFormatter.prototype._setEvents = EdgeGraphFormatter.prototype._setEvents; + +function DotDirectedLineEdgeGraphFormatter(id, defaultFormat, selectedFormat, overFormat, dragginFormat){ + EdgeGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, dragginFormat); + this.args.type = "DotDirectedLineEdgeGraphFormatter"; +}; + + +OdotDirectedLineEdgeGraphFormatter.prototype.setProperties = EdgeGraphFormatter.prototype.setProperties; +OdotDirectedLineEdgeGraphFormatter.prototype.getDefault = EdgeGraphFormatter.prototype.getDefault; +OdotDirectedLineEdgeGraphFormatter.prototype.getSelected = EdgeGraphFormatter.prototype.getSelected; +OdotDirectedLineEdgeGraphFormatter.prototype.getOver = EdgeGraphFormatter.prototype.getOver; +OdotDirectedLineEdgeGraphFormatter.prototype.getDragging = EdgeGraphFormatter.prototype.getDragging; +OdotDirectedLineEdgeGraphFormatter.prototype.getId = EdgeGraphFormatter.prototype.getId; +OdotDirectedLineEdgeGraphFormatter.prototype.toJSON = EdgeGraphFormatter.prototype.toJSON; +OdotDirectedLineEdgeGraphFormatter.prototype.loadFromJSON = EdgeGraphFormatter.prototype.loadFromJSON; +OdotDirectedLineEdgeGraphFormatter.prototype._setEvents = EdgeGraphFormatter.prototype._setEvents; + +function OdotDirectedLineEdgeGraphFormatter(id, defaultFormat, selectedFormat, overFormat, dragginFormat){ + EdgeGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, dragginFormat); + this.args.type = "OdotDirectedLineEdgeGraphFormatter"; +}; + + + +function GraphDataset(){ + DataSet.prototype.constructor.call(this); + this.edges = new Object(); + this.vertices = new Object(); + this.verticesIndex = new Object(); + + //Events + this.newVertex = new Event(this); + this.vertexNameChanged = new Event(this); + this.vertexDeleted = new Event(this); + + this.newEdge = new Event(this); + this.edgeNameChanged = new Event(this); + this.edgeDeleted = new Event(this); + + this.json = new Object(); + this.json.vertices = new Array(); + this.json.edges = new Array(); + this.json.relations = new Array(); +}; + +GraphDataset.prototype.loadFromJSON = DataSet.prototype.loadFromJSON; +GraphDataset.prototype.toJSON = DataSet.prototype.toJSON; +GraphDataset.prototype.validate = DataSet.prototype.validate; + +/** Devuelve el numero de edges incidentes sobre el nodo con mas edges **/ +GraphDataset.prototype.getMaxClass = function(){ + var maxClassNode = 0; + for ( var node in this.vertices) { + if (this.vertices[node].getEdgesCount() > maxClassNode){ + maxClassNode = this.vertices[node].getEdgesCount(); + } + } + return maxClassNode; +}; + +/** Devuelve el numero de edges incidentes sobre el nodo con mas edges **/ +GraphDataset.prototype.getMinClass = function(){ + var minClassNode = Math.min(); + for ( var node in this.vertices) { + if (this.vertices[node].getEdgesCount() < minClassNode){ + minClassNode = this.vertices[node].getEdgesCount(); + } + } + return minClassNode; +}; + +GraphDataset.prototype.getVertexByName = function(nodeName){ + var results = new Array(); + + for (var vertexId in this.verticesIndex[nodeName]){ + var vertexByid = this.getVertexById(this.verticesIndex[nodeName][vertexId]); + results.push(vertexByid); + //* añadido nuevo porque fallaba el anterior codigo + return vertexByid + } + + if (results <= 1){ + return this.getVertexById(this.verticesIndex[nodeName]); + } + else{ + return results; + } +}; + +GraphDataset.prototype.getVertexById = function(id){ + return this.vertices[id]; +}; + +GraphDataset.prototype.toSIF = function(){ + var sifDataAdapter = new SifFileDataAdapter(); + return sifDataAdapter.toSIF(this); +}; + +GraphDataset.prototype.toSIFID = function(){ + var sifDataAdapter = new SifFileDataAdapter(); + return sifDataAdapter.toSIFID(this); +}; + +GraphDataset.prototype.toDOT = function(){ + var dotFileDataAdapter = new DotFileDataAdapter(); + return dotFileDataAdapter.toDOT(this); +}; + +GraphDataset.prototype.toDOTID = function(){ + var dotFileDataAdapter = new DotFileDataAdapter(); + return dotFileDataAdapter.toDOTID(this); +}; + +GraphDataset.prototype._addNode = function(nodeName, args){ + return new Vertex(this._getVerticesCount()-1, nodeName, args); +}; + +GraphDataset.prototype.addNode = function(nodeName, args){ + this.json.vertices.push(nodeName); + this._addVerticesIndex(nodeName, this._getVerticesCount() - 1); + var vertex = this._addNode(nodeName, args); + this.vertices[this._getVerticesCount()-1] = vertex; + this._setNodeEvents(vertex); + this.newVertex.notify(vertex); +}; + +GraphDataset.prototype._addVerticesIndex = function(nodeName, id){ + if (this.verticesIndex[nodeName] == null){ + this.verticesIndex[nodeName] = new Array(); + } + this.verticesIndex[nodeName].push(id); +}; + +GraphDataset.prototype.addEdge = function(edgeName, nodeSourceId, nodeTargetId, args){ + this.json.edges.push(edgeName); + var nodeSource = this.getVertexById(nodeSourceId); + var nodeTarget = this.getVertexById(nodeTargetId); + var index = this.getEdgesCount() - 1; + this.edges[index] = new Edge(index, edgeName, nodeSource, nodeTarget, args); + this.json.relations.push({"index": index, "sourceIndex": nodeSourceId, "targetIndex": nodeTargetId, "args": args }); + + nodeSource.addEdge(this.edges[index]); + nodeTarget.addEdge(this.edges[index]); + this._setEdgeEvents(this.edges[index]); + this.newEdge.notify(this.edges[index]); +}; + +GraphDataset.prototype.getVertices = function(){ + return this.vertices; +}; + +GraphDataset.prototype.getEdges = function(){ + return this.edges; +}; + +GraphDataset.prototype.getEdgeById = function(edgeId){ + return this.edges[edgeId]; +}; + +GraphDataset.prototype._getVerticesCount = function(){ + return this.json.vertices.length; +}; + + +GraphDataset.prototype.getVerticesCount = function(){ + var count = 0; + for ( var vertex in this.getVertices()) { + count ++; + } + return count; +}; + + +GraphDataset.prototype.getEdgesCount = function(){ + return this.json.edges.length; +}; + +GraphDataset.prototype.init = function(){ + this.edges = new Object(); + this.vertices = new Object(); +}; + +GraphDataset.prototype._setNodeEvents = function(node){ + var _this = this; + //NODE EVENTS + node.deleted.attach(function (sender, node){ + _this._removeNode(node); + }); + + node.nameChanged.attach(function (sender, args){ + var item = args.item; + var newName = item.name; + var indexes = _this.verticesIndex[args.previousName]; + for(var i = 0; i < indexes.length; i++){ + if(indexes[i] == item.id) + indexes.splice(i,1); + } + if(indexes.length == 0){ + delete _this.verticesIndex[args.previousName]; + } + _this._addVerticesIndex(newName, item.id); + _this.json.vertices[item.id] = newName; + _this.vertexNameChanged.notify(args); + }); +}; + +GraphDataset.prototype._setEdgeEvents = function(edge){ + var _this = this; + //EDGE EVENTS + edge.nameChanged.attach(function (sender, edge){ + _this.edgeNameChanged.notify(edge); + + }); + + edge.deleted.attach(function (sender, edge){ + _this._removeEdge(edge); + }); +}; + +GraphDataset.prototype._connectVerticesByName = function(nodeNameSource, nodeNameTarget){ + var source = this.getVertexByName(nodeNameSource); + var target = this.getVertexByName(nodeNameTarget); + + if ((source != null)&&(target!=null)){ + this.addEdge(source.getName() +"_" + target.getName(), source.getId(), target.getId(), {}); + } + else{ + if (source == null){ + console.log("No encontrado: " + nodeNameSource) + } + if (target == null){ + console.log("No encontrado: " + nodeNameTarget) + } + } +}; + +GraphDataset.prototype.loadFromJSON = function(json){ + var json = json; + this.init(); + this.json = new Object(); + this.json.vertices = new Array(); + this.json.edges = new Array(); + this.json.relations = new Array(); + + for ( var i = 0; i < json.nodes.length; i++) { + if (json.nodes[i] != null){ + var name = json.nodes[i]; + this.addNode(name); + } + else{ + this.json.vertices.push(null); + } + } + + for ( var i = 0; i < json.edges.length; i++) { + if (json.edges[i] != null){ + if (json.relations[i] != null){ + var name = json.edges[i]; + this.addEdge(name, json.relations[i].sourceIndex, json.relations[i].targetIndex, json.relations[i].args); + } + } + else{ + this.json.edges.push(null); + this.json.relations.push(null); + } + } +}; + +GraphDataset.prototype.prettyPrint = function(){ + for ( var node in this.vertices) { + console.log(this.vertices[node].getName() ); + for ( var j = 0; j < this.vertices[node].getEdgesIn().length; j++) { + console.log(" --> " + this.vertices[node].getEdgesIn()[j].getNodeTarget().getName() ); + } + } +}; + +GraphDataset.prototype._removeEdge = function(edge){ + this.json.edges[edge.getId()] = null; + this.json.relations[edge.getId()] = null; + + delete this.edges[edge.getId()]; + this.edgeDeleted.notify(edge); + + +}; + +GraphDataset.prototype._removeNode = function(node){ + this.json.vertices[node.getId()] = null; + delete this.vertices[node.getId()]; + this.vertexDeleted.notify(node); +}; + +GraphDataset.prototype.toJSON = function(){ + var json = new Object(); + var nodes = new Array(); + json.nodes = this.json.vertices; //nodes; + json.edges = this.json.edges; //edges; + json.relations = this.json.relations; + return json; +}; + +GraphDataset.prototype.clone = function(){ + var dsDataset = new GraphDataset(); + dsDataset.loadFromJSON(this.toJSON()); + return dsDataset; +}; +//GraphDataset.prototype.test = function(){ +// this.loadFromJSON(this.toJSON()); +//}; + +function labels(){ + var names = new Array(); + + var dataset = interactomeViewer.graphEditorWidget.dataset; + var layout = interactomeViewer.graphEditorWidget.layout; + + for ( var vertexId in interactomeViewer.graphEditorWidget.dataset.getVertices()) { + names.push(interactomeViewer.graphEditorWidget.dataset.getVertexById(vertexId).getName()); + } + + var sorted = (names.sort()); + console.log(sorted) + var distance = 0.01; + var altura = 0.6; + for ( var i = 0; i < names.length; i++) { + var id =dataset.getVertexByName(names[i]).getId(); + + layout.getNodeById(id).setCoordenates(distance, altura); + + + distance = parseFloat(distance) + parseFloat(0.03); + + altura = parseFloat(altura) + parseFloat(0.02); + + if (parseFloat(altura) == 0.9800000000000003){ + + altura = 0.6; + distance = distance - 0.51; + } + + } + + +}; + +function GraphItem(id, name, args){ + this.id = id; + this.name = name; + this.type = "NONE"; + + this.args = new Object(); + + + if (args!=null){ + this.args = args; + if (args.type !=null){ + this.type = args.type; + } + } + + //Events + this.nameChanged = new Event(this); + this.deleted = new Event(this); +} + +GraphItem.prototype.getName = function(){ + return this.name; +}; + +GraphItem.prototype.getId = function(){ + return this.id; +}; + +GraphItem.prototype.setName = function(name){ + var oldName = this.getName(); + this.name = name; + this.nameChanged.notify({"item": this, "previousName" : oldName}); +}; + + + + + +function LayoutDataset(){ + this.dataset = null; + this.vertices = new Object(); + this.changed = new Event(this); + + + this.args = new Object(); + + //RANDOM, CIRCLE + this.args.type = "CIRCLE"; +}; + +LayoutDataset.prototype.loadFromJSON = function(dataset, json){ + var _this = this; + this.vertices = new Object(); + this.dataset = dataset; //new GraphDataset(); + for ( var vertex in json) { + this.vertices[vertex] = new NodeLayout(vertex, json[vertex].x, json[vertex].y); + this.vertices[vertex].changed.attach(function (sender, item){ + _this.changed.notify(item); + }); + } + this._attachDatasetEvents(); +}; + + +LayoutDataset.prototype.toJSON = function(){ + var serialize = new Object(); + for ( var vertex in this.vertices) { + serialize[vertex] = new Object(); + serialize[vertex].x = this.vertices[vertex].x; + serialize[vertex].y = this.vertices[vertex].y; + } + serialize.dataset = new Object(); + serialize.dataset =this.dataset.toJSON(); + return serialize; +}; + +LayoutDataset.prototype.dataBind = function(graphDataset){ + this.dataset = graphDataset; + this._attachDatasetEvents(); + this._calculateLayout(); +}; + +LayoutDataset.prototype._removeVertex = function(vertexId){ + delete this.vertices[vertexId]; +}; + +LayoutDataset.prototype._attachDatasetEvents = function(){ + var _this = this; + + this.dataset.vertexDeleted.attach(function (sender, item){ + _this._removeVertex(item.getId()); + }); + + this.dataset.newVertex.attach(function (sender, item){ + _this.vertices[item.getId()] = new NodeLayout(item.getId(), 0.5, 0.5); + _this.vertices[item.getId()].changed.attach(function (sender, item){ + _this.changed.notify(item); + }); + }); +}; + +LayoutDataset.prototype.getType = function(){ + return this.args.type; +}; + +LayoutDataset.prototype._calculateLayoutVertices = function(type, count){ + + if (type == "CIRCLE"){ + var radius = 0.4; + var centerX = 0.5; + var centerY = 0.5; + var verticesCoordinates = new Array(); + for(var i = 0; i < count; i++){ + x = centerX + radius * Math.sin(i * 2 * Math.PI/count); + y = centerY + radius * Math.cos(i * 2 * Math.PI/count); + verticesCoordinates.push({'x':x,'y':y}); + } + return verticesCoordinates; + } +}; + + +LayoutDataset.prototype._calculateLayout = function(){ + var _this = this; + if (this.getType() == "RANDOM"){ + for ( var vertex in this.dataset.getVertices()) { + if (this.vertices[vertex] == null){ + this.vertices[vertex] = new NodeLayout(vertex, 0, 0); + } + this.vertices[vertex].setCoordinates(Math.random(), Math.random()); + this.vertices[vertex].changed.attach(function (sender, item){ + _this.changed.notify(item); + }); + } + } + + if ( this.getType() == "CIRCLE"){ + + var count = this.dataset._getVerticesCount(); + var verticesCoordinates = this._calculateLayoutVertices(this.getType(), count); + + var aux = 0; + for ( var vertex in this.dataset.getVertices()) { + if (this.vertices[vertex] == null){ + this.vertices[vertex] = new NodeLayout(vertex, 0, 0); + } + this.vertices[vertex].setCoordinates(verticesCoordinates[aux].x, verticesCoordinates[aux].y);//{"x":, "y":}; + aux++; + this.vertices[vertex].changed.attach(function (sender, item){ + _this.changed.notify(item); + }); + } + } + + + if (this.getType() == "SQUARE"){ + + var count = this.dataset._getVerticesCount(); + var xMin = 0.1; + var xMax = 0.9; + var yMin = 0.1; + var yMax = 0.9; + + var rows = Math.sqrt(count); + var step = (xMax - xMin) / rows; + + var verticesCoordinates = new Array(); + for(var i = 0; i < rows; i ++){ + for ( var j = 0; j < rows; j++) { + x = i * step + xMin; + y = j * step + yMin; + verticesCoordinates.push({'x':x,'y':y}); + } + } + + var aux = 0; + for ( var vertex in this.dataset.getVertices()) { + if (this.vertices[vertex] == null){ + this.vertices[vertex] = new NodeLayout(vertex, 0, 0); + } + this.vertices[vertex].setCoordinates(verticesCoordinates[aux].x, verticesCoordinates[aux].y);//{"x":, "y":}; + aux++; + this.vertices[vertex].changed.attach(function (sender, item){ + _this.changed.notify(item); + }); + } + } + +}; + +LayoutDataset.prototype.getNodeById = function(id){ + return this.vertices[id]; +}; + +LayoutDataset.prototype.getVerticesByArea = function(x1, y1, x2, y2){ + var vertices = new Array(); + for ( var vertex in this.dataset.getVertices()) { + if ((this.vertices[vertex].x >= x1)&&(this.vertices[vertex].x <= x2)){ + if ((this.vertices[vertex].y >= y1)&&(this.vertices[vertex].y <= y2)){ + vertices.push(this.vertices[vertex]); + } + } + } + return vertices; +}; + + + + +LayoutDataset.prototype.getLayout = function(type){ + + if (type == "CIRCLE"){ + this.args.type = "CIRCLE"; + this._calculateLayout(); + return; + } + + if (type == "SQUARE"){ + this.args.type = "SQUARE"; + this._calculateLayout(); + return; + } + + if (type == "RANDOM"){ + this.args.type = "RANDOM"; + this._calculateLayout(); + return; + } + + + var dotText = this.dataset.toDOTID(); + var url = "http://bioinfo.cipf.es/utils/ws/rest/network/layout/"+type+".coords"; + var _this = this; + + $.ajax({ + async: true, + type: "POST", + url: url, + dataType: "text", + data: { + dot :dotText + }, + cache: false, + success: function(data){ + var response = JSON.parse(data); + for ( var vertexId in response) { + _this.vertices[vertexId].setCoordinates(response[vertexId].x, response[vertexId].y); + } + } + }); + +// $.ajax({ +// async: true, +// type: "POST", +// url: url, +// dataType: "script", +// data: { +// dot :dotText +// }, +// cache: false, +// success: function(data){ +// var response = JSON.parse(data); +// for ( var vertexId in response) { +// _this.vertices[vertexId].setCoordinates(response[vertexId].x, response[vertexId].y); +// } +// } +// }); + +}; + +function NodeLayout(id, x, y, args){ + this.id = id; + this.x = x; + this.y = y; + this.changed = new Event(this); +}; + +NodeLayout.prototype.getId = function(id){ + return this.id; +}; + +NodeLayout.prototype.setCoordinates = function(x, y){ + this.x = x; + this.y = y; + this.changed.notify(this); +}; + + + +function NetworkDataSetFormatter(vertexFormatProperties, defaultEdgeProperties, args){ + DataSet.prototype.constructor.call(this); + + this.args = new Object(); + + this.vertices = new Object(); + this.edges = new Object(); + this.dataset = null; + this.maxClass = 0; + this.minClass = 0; + + /** Coordenates with default Setting */ + this.args.width = 1100; + this.args.height = 500; + + /** Optional parameters */ + this.args.backgroundColor = "#FFFFFF"; + this.args.backgroundImage = null; + this.args.backgroundImageHeight = null; + this.args.backgroundImageWidth = null; + this.args.backgroundImageX = null; + this.args.backgroundImageY = null; + + + this.args.balanceNodes = false; + this.args.nodesMaxSize = 3; + this.args.nodesMinSize = 0.5; + + + /** Zoom **/ + this.args.zoomScale = 1; + this.args.zoomScaleStepFactor = 0.4; + + if (args != null){ + if (args.backgroundImage != null){ + this.args.backgroundImage = args.backgroundImage; + } + + if (args.width != null){ + this.args.width = args.width; + } + + if (args.height != null){ + this.args.height = args.height; + this.args.svgHeight = args.height; + } + + if (args.svgHeight != null){ + this.args.svgHeight = args.svgHeight; + } + + if (args.backgroundColor != null){ + this.args.backgroundColor = args.backgroundColor; + } + + if(args.balanceNodes != null){ + this.args.balanceNodes = args.balanceNodes; + } + + if(args.nodesMaxSize != null){ + this.args.nodesMaxSize = args.nodesMaxSize; + } + if(args.nodesMinSize != null){ + this.args.nodesMinSize = args.nodesMinSize; + } + } + + this.args.defaultEdgeProperties = {"fill":"#000000", "radius":"1", "stroke":"#000000", "size":1, "title":{"fontSize":10, "fontColor":"#000000"}}; + this.args.vertexFormatProperties = { "fill":"#000000", "stroke":"#000000", "stroke-opacity":"#000000"}; + + if (vertexFormatProperties!= null){ + this.args.vertexFormatProperties = vertexFormatProperties; + } + if (defaultEdgeProperties!= null){ + this.args.defaultEdgeProperties = defaultEdgeProperties; + } + + /** Events **/ + this.changed = new Event(this); + this.edgeChanged = new Event(this); + this.resized = new Event(this); + this.backgroundImageChanged= new Event(this); + this.backgroundColorChanged= new Event(this); +}; + +NetworkDataSetFormatter.prototype.loadFromJSON = function(dataset, json){ + this.args = new Object(); + this.vertices = new Object(); + this.args = json; + this._setDataset(dataset); + var _this = this; + + for ( var vertex in json.vertices) { + this.addVertex(vertex, json.vertices[vertex]); + } + + for ( var edgeId in json.edges) { + this.addEdge(edgeId, json.edges[edgeId]); + } +}; + + +NetworkDataSetFormatter.prototype.toJSON = function(){ + + +// this.args.vertices = new Object(); +// this.args.edges = new Object(); +// +// for ( var vertex in this.vertices) { +// this.args.vertices[vertex] = this.getVertexById(vertex).toJSON(); +// } +// for ( var edge in this.edges) { +// this.args.edges[edge] = this.getEdgeById(edge).toJSON(); +// } +// +// return (this.args); + + + var serialize = new Object(); + serialize = JSON.parse(JSON.stringify(this.args)); + serialize.vertices = new Object(); + serialize.edges = new Object(); + + for ( var vertex in this.vertices) { + serialize.vertices[vertex] = this.getVertexById(vertex).toJSON(); + } + for ( var edge in this.edges) { + serialize.edges[edge] = this.getEdgeById(edge).toJSON(); + } + + return (serialize); +}; + + + +NetworkDataSetFormatter.prototype._getNodeSize = function(nodeId){ + if (this.isVerticesBalanced()){ + var total = this.maxClass - this.minClass; + if (total == 0){ total = 1;} + var nodeConnection = this.dataset.getVertexById(nodeId).getEdges().length; + return ((nodeConnection*this.args.nodesMaxSize)/total) + this.args.nodesMinSize; + } + return this.getVertexById(nodeId).getDefault().getSize(); +}; + +NetworkDataSetFormatter.prototype._recalculateSize = function(){ + if (this.isVerticesBalanced()){ + this.maxClass = this.dataset.getMaxClass(); + this.minClass = this.dataset.getMinClass(); + for ( var vertexIdAll in this.vertices) { + var size = this._getNodeSize(vertexIdAll); + this.vertices[vertexIdAll].getDefault().setSize(size); + this.vertices[vertexIdAll].getSelected().setSize(size); + this.vertices[vertexIdAll].getOver().setSize(size); + } + } +}; + + +NetworkDataSetFormatter.prototype.addVertex = function(vertexId, json){ + + + if (json == null){ + this.vertices[vertexId] = new CircleVertexGraphFormatter(vertexId, this.args.vertexFormatProperties.defaultFormat, this.args.vertexFormatProperties.selected, this.args.vertexFormatProperties.over, this.args.vertexFormatProperties.dragging); + } + else{ + + /** Cargo los attributos desde el json **/ + if (json.type == null){ + this.vertices[vertexId] = new CircleVertexGraphFormatter(vertexId, this.args.vertexFormatProperties.defaultFormat, this.args.vertexFormatProperties.selected, this.args.vertexFormatProperties.over, this.args.vertexFormatProperties.dragging); + } + + if ((json.type == "SquareVertexGraphFormatter")||(json.type == "SquareVertexNetworkFormatter")){ + this.vertices[vertexId] = new SquareVertexGraphFormatter(vertexId); + this.vertices[vertexId].loadFromJSON(json); + } + + if ((json.type == "CircleVertexGraphFormatter")||(json.type == "CircleVertexNetworkFormatter")){ + this.vertices[vertexId] = new CircleVertexGraphFormatter(vertexId); + this.vertices[vertexId].loadFromJSON(json); + } + + if ((json.type == "EllipseVertexGraphFormatter")||(json.type == "EllipseVertexNetworkFormatter")){ + this.vertices[vertexId] = new EllipseVertexGraphFormatter(vertexId); + this.vertices[vertexId].loadFromJSON(json); + } + + if ((json.type == "RectangleVertexGraphFormatter")||(json.type == "RectangleVertexNetworkFormatter")){ + this.vertices[vertexId] = new RectangleVertexGraphFormatter(vertexId); + this.vertices[vertexId].loadFromJSON(json); + } + + if ((json.type == "RoundedVertexGraphFormatter")||(json.type == "RoundedVertexNetworkFormatter")){ + this.vertices[vertexId] = new RoundedVertexGraphFormatter(vertexId); + this.vertices[vertexId].loadFromJSON(json); + } + + } + + + var _this = this; + this.vertices[vertexId].stateChanged.attach(function (sender, item){ + _this.changed.notify(item); + }); + + var size = this._getNodeSize(vertexId); + this.vertices[vertexId].defaultFormat.args.size = size; + this.vertices[vertexId].selected.args.size = size; + this.vertices[vertexId].over.args.size = size; + +}; + +NetworkDataSetFormatter.prototype.addEdge = function(edgeId, json){ + + /** Es un edge nuevo que le doy los atributos por defecto **/ + if (json == null){ + if (this.dataset.getEdgeById(edgeId).getNodeSource().getId() == this.dataset.getEdgeById(edgeId).getNodeTarget().getId()){ + this.edges[edgeId] = new BezierEdgeGraphFormatter(edgeId, this.args.defaultEdgeProperties.defaultFormat, this.args.defaultEdgeProperties.selected, this.args.defaultEdgeProperties.over, this.args.defaultEdgeProperties.dragging); + }else{ + this.edges[edgeId] = new DirectedLineEdgeGraphFormatter(edgeId, this.args.defaultEdgeProperties.defaultFormat, this.args.defaultEdgeProperties.selected, this.args.defaultEdgeProperties.over, this.args.defaultEdgeProperties.dragging); + } + } + else{ + /** Cargo los attributos desde el json **/ + if (json.type == null){ + this.edges[edgeId] = new LineEdgeGraphFormatter(edgeId, this.args.defaultEdgeProperties.defaultFormat, this.args.defaultEdgeProperties.selected, this.args.defaultEdgeProperties.over, this.args.defaultEdgeProperties.dragging); + } + + if ((json.type == "LineEdgeGraphFormatter")||(json.type == "LineEdgeNetworkFormatter")){ + this.edges[edgeId] = new LineEdgeGraphFormatter(edgeId); + this.edges[edgeId].loadFromJSON(json); + } + if ((json.type == "DirectedLineEdgeGraphFormatter")||(json.type == "DirectedLineEdgeNetworkFormatter")){ + this.edges[edgeId] = new DirectedLineEdgeGraphFormatter(edgeId); + this.edges[edgeId].loadFromJSON(json); + } + + if ((json.type == "BezierEdgeGraphFormatter")||(json.type == "BezierEdgeNetworkFormatter")){ + this.edges[edgeId] = new BezierEdgeGraphFormatter(edgeId); + this.edges[edgeId].loadFromJSON(json); + } + + + if ((json.type == "CutDirectedLineEdgeGraphFormatter")||(json.type == "CutDirectedLineEdgeNetworkFormatter")){ + this.edges[edgeId] = new CutDirectedLineEdgeGraphFormatter(edgeId); + this.edges[edgeId].loadFromJSON(json); + } + + if ((json.type == "DotDirectedLineEdgeGraphFormatter")||(json.type == "DotDirectedLineEdgeNetworkFormatter")){ + this.edges[edgeId] = new DotDirectedLineEdgeGraphFormatter(edgeId); + this.edges[edgeId].loadFromJSON(json); + } + if ((json.type == "OdotDirectedLineEdgeGraphFormatter")||(json.type == "OdotDirectedLineEdgeNetworkFormatter")){ + this.edges[edgeId] = new OdotDirectedLineEdgeGraphFormatter(edgeId); + this.edges[edgeId].loadFromJSON(json); + } + + if ((json.type == "OdirectedLineEdgeGraphFormatter")||(json.type == "OdirectedLineEdgeNetworkFormatter")){ + this.edges[edgeId] = new OdirectedLineEdgeGraphFormatter(edgeId); + this.edges[edgeId].loadFromJSON(json); + } + } + + var _this = this; + this.edges[edgeId].stateChanged.attach(function (sender, item){ + _this.edgeChanged.notify(item); + }); + + this._recalculateSize(); +}; + +NetworkDataSetFormatter.prototype._setDataset = function(dataset){ + this.dataset = dataset; + this.maxClass = dataset.getMaxClass(); + this.minClass = dataset.getMinClass(); + this._attachDatasetEvents(); +}; + +NetworkDataSetFormatter.prototype.changeEdgeType = function(edgeId, type){ + if ((type == "LineEdgeGraphFormatter")||(type == "LineEdgeNetworkFormatter")){ + this.edges[edgeId] = new LineEdgeGraphFormatter(edgeId, this.args.defaultEdgeProperties.defaultFormat, this.args.defaultEdgeProperties.selected, this.args.defaultEdgeProperties.over, this.args.defaultEdgeProperties.dragging); + + } + if ((type == "DirectedLineEdgeGraphFormatter")||(type == "DirectedLineEdgeNetworkFormatter")){ + this.edges[edgeId] = new DirectedLineEdgeGraphFormatter(edgeId, this.args.defaultEdgeProperties.defaultFormat, this.args.defaultEdgeProperties.selected, this.args.defaultEdgeProperties.over, this.args.defaultEdgeProperties.dragging); + } + + if ((type == "CutDirectedLineEdgeGraphFormatter")||(type == "CutDirectedLineEdgeNetworkFormatter")){ + this.edges[edgeId] = new CutDirectedLineEdgeGraphFormatter(edgeId, this.args.defaultEdgeProperties.defaultFormat, this.args.defaultEdgeProperties.selected, this.args.defaultEdgeProperties.over, this.args.defaultEdgeProperties.dragging); + } + + if ((type == "DotDirectedLineEdgeGraphFormatter")||(type == "DotDirectedLineEdgeNetworkFormatter")){ + this.edges[edgeId] = new DotDirectedLineEdgeGraphFormatter(edgeId, this.args.defaultEdgeProperties.defaultFormat, this.args.defaultEdgeProperties.selected, this.args.defaultEdgeProperties.over, this.args.defaultEdgeProperties.dragging); + } + + if ((type == "OdotDirectedLineEdgeGraphFormatter")||(type == "OdotDirectedLineEdgeNetworkFormatter")){ + this.edges[edgeId] = new OdotDirectedLineEdgeGraphFormatter(edgeId, this.args.defaultEdgeProperties.defaultFormat, this.args.defaultEdgeProperties.selected, this.args.defaultEdgeProperties.over, this.args.defaultEdgeProperties.dragging); + } + + if ((type == "OdirectedLineEdgeGraphFormatter")||(type == "OdirectedLineEdgeNetworkFormatter")){ + this.edges[edgeId] = new OdirectedLineEdgeGraphFormatter(edgeId, this.args.defaultEdgeProperties.defaultFormat, this.args.defaultEdgeProperties.selected, this.args.defaultEdgeProperties.over, this.args.defaultEdgeProperties.dragging); + } + + + + var _this = this; + this.edges[edgeId].stateChanged.attach(function (sender, item){ + _this.edgeChanged.notify(item); + }); + _this.edgeChanged.notify(this.edges[edgeId]); +}; +/* +classe = "SquareNetworkNodeFormatter"; +} +if (value == "circle"){ + classe = "CircleNetworkNodeFormatter"; + **/ +NetworkDataSetFormatter.prototype.changeNodeType = function(vertexId, type){ + var defaultFormat = JSON.parse(JSON.stringify(this.vertices[vertexId].getDefault())); + var selectedFormat = JSON.parse(JSON.stringify(this.vertices[vertexId].getSelected())); + var defaultFormat = JSON.parse(JSON.stringify(this.vertices[vertexId].getDefault())); + var defaultFormat = JSON.parse(JSON.stringify(this.vertices[vertexId].getDefault())); + + if ((type == "SquareVertexGraphFormatter")||(type == "SquareVertexNetworkFormatter")){ + this.vertices[vertexId] = new SquareVertexGraphFormatter(vertexId,defaultFormat, this.args.vertexFormatProperties.selected, this.args.vertexFormatProperties.over, this.args.vertexFormatProperties.dragging); + } + + if ((type == "CircleVertexGraphFormatter")||(type == "CircleVertexNetworkFormatter")){ + this.vertices[vertexId] = new CircleVertexGraphFormatter(vertexId, defaultFormat, this.args.vertexFormatProperties.selected, this.args.vertexFormatProperties.over, this.args.vertexFormatProperties.dragging); + } + + if ((type == "EllipseVertexGraphFormatter")||(type == "EllipseVertexNetworkFormatter")){ + this.vertices[vertexId] = new EllipseVertexGraphFormatter(vertexId, defaultFormat, this.args.vertexFormatProperties.selected, this.args.vertexFormatProperties.over, this.args.vertexFormatProperties.dragging); + } + + if ((type == "RectangleVertexGraphFormatter")||(type == "RectangleVertexNetworkFormatter")){ + this.vertices[vertexId] = new RectangleVertexGraphFormatter(vertexId, defaultFormat, this.args.vertexFormatProperties.selected, this.args.vertexFormatProperties.over, this.args.vertexFormatProperties.dragging); + } + + if ((type == "RoundedVertexGraphFormatter")||(type == "RoundedVertexNetworkFormatter")){ + this.vertices[vertexId] = new RoundedVertexGraphFormatter(vertexId, defaultFormat, this.args.vertexFormatProperties.selected, this.args.vertexFormatProperties.over, this.args.vertexFormatProperties.dragging); + } + + if ((type == "OctagonVertexGraphFormatter")||(type == "OctagonVertexNetworkhFormatter")){ + this.vertices[vertexId] = new OctagonVertexGraphFormatter(vertexId, defaultFormat, this.args.vertexFormatProperties.selected, this.args.vertexFormatProperties.over, this.args.vertexFormatProperties.dragging); + } + + + var _this = this; + this.vertices[vertexId].stateChanged.attach(function (sender, item){ + _this.changed.notify(item); + }); + _this.changed.notify(this.vertices[vertexId]); +}; + + + +NetworkDataSetFormatter.prototype.dataBind = function(networkDataSet){ + var _this = this; + this._setDataset(networkDataSet); + + for ( var vertex in this.dataset.getVertices()) { + this.addVertex(vertex); + } + + for ( var edge in this.dataset.getEdges()) { + this.addEdge(edge); + } +}; + +NetworkDataSetFormatter.prototype._removeEdge = function(edgeId){ + delete this.edges[edgeId]; +}; + +NetworkDataSetFormatter.prototype._removeVertex = function(vertexId){ + delete this.vertices[vertexId]; +}; + +NetworkDataSetFormatter.prototype._attachDatasetEvents = function(id){ + var _this = this; + this.dataset.vertexDeleted.attach(function (sender, item){ + _this._removeVertex(item.getId()); + }); + + this.dataset.edgeDeleted.attach(function (sender, item){ + _this._removeEdge(item.getId()); + }); + + this.dataset.newVertex.attach(function (sender, item){ + _this.addVertex(item.getId()); + }); + + this.dataset.newEdge.attach(function (sender, item){ + _this.addEdge(item.getId()); + }); +}; + + +NetworkDataSetFormatter.prototype.getVertexById = function(id){ + return this.vertices[id]; +}; + +NetworkDataSetFormatter.prototype.getEdgeById = function(id){ + return this.edges[id]; +}; + +NetworkDataSetFormatter.prototype.makeLabelsBigger = function(){ +for ( var vertexId in this.vertices) { + var fontSize = this.vertices[vertexId].getDefault().getFontSize() + 2; + this.vertices[vertexId].getDefault().setFontSize(fontSize); + } +}; + +NetworkDataSetFormatter.prototype.makeLabelsSmaller = function(){ + for ( var vertexId in this.vertices) { + var fontSize = this.vertices[vertexId].getDefault().getFontSize() - 2; + this.vertices[vertexId].getDefault().setFontSize(fontSize); + } +}; + + +NetworkDataSetFormatter.prototype.resize = function(width, height){ + this.args.width = width; + this.args.height = height; + this.resized.notify(); +}; + +/** ZOOM GETTERS **/ +NetworkDataSetFormatter.prototype.getZoomScaleStepFactor = function(){return this.args.zoomScaleStepFactor;}; +NetworkDataSetFormatter.prototype.setZoomScaleStepFactor = function(scaleFactor){this.args.zoomScaleStepFactor = scaleFactor;}; +NetworkDataSetFormatter.prototype.getZoomScale = function(){return this.args.zoomScale;}; +NetworkDataSetFormatter.prototype.setZoomScale = function(scale){this.args.zoomScale = scale;}; + +NetworkDataSetFormatter.prototype.getNodesMaxSize = function(){return this.args.nodesMaxSize;}; +NetworkDataSetFormatter.prototype.getNodesMinSize = function(){return this.args.nodesMinSize;}; + +/** SIZE SETTERS AND GETTERS **/ +NetworkDataSetFormatter.prototype.isVerticesBalanced = function(){return this.args.balanceNodes;}; +NetworkDataSetFormatter.prototype.getWidth = function(){return this.args.width;}; +NetworkDataSetFormatter.prototype.getHeight = function(){return this.args.height;}; + +/** OPTIONAL PARAMETERS GETTERS AND SETTERS **/ +NetworkDataSetFormatter.prototype.getBackgroundImage = function(){return this.args.backgroundImage;}; +NetworkDataSetFormatter.prototype.setBackgroundImage = function(value){this.args.backgroundImage = value; this.backgroundImageChanged.notify(this);}; + + +NetworkDataSetFormatter.prototype.getBackgroundImageWidth = function(){return this.args.backgroundImageWidth;}; +NetworkDataSetFormatter.prototype.setBackgroundImageWidth = function(value){this.args.backgroundImageWidth = value; this.backgroundImageChanged.notify(this);}; + +NetworkDataSetFormatter.prototype.getBackgroundImageHeight = function(){return this.args.backgroundImageHeight;}; +NetworkDataSetFormatter.prototype.setBackgroundImageHeight = function(value){this.args.backgroundImageHeight = value; this.backgroundImageChanged.notify(this);}; + +NetworkDataSetFormatter.prototype.getBackgroundImageX = function(){return this.args.backgroundImageX;}; +NetworkDataSetFormatter.prototype.setBackgroundImageX = function(value){this.args.backgroundImageX = value; this.backgroundImageChanged.notify(this);}; + +NetworkDataSetFormatter.prototype.getBackgroundImageY = function(){return this.args.backgroundImageX;}; +NetworkDataSetFormatter.prototype.setBackgroundImageY = function(value){this.args.backgroundImageY = value; this.backgroundImageChanged.notify(this);}; + + + +NetworkDataSetFormatter.prototype.getBackgroundColor = function(){return this.args.backgroundColor;}; +NetworkDataSetFormatter.prototype.setBackgroundColor = function(value){this.args.backgroundColor = value; this.backgroundColorChanged.notify(this);}; + +NetworkDataSetFormatter.prototype.getWidth = function(){return this.args.width;}; +NetworkDataSetFormatter.prototype.setWidth = function(value){this.args.width = value;}; + +NetworkDataSetFormatter.prototype.getHeight = function(){return this.args.height;}; +NetworkDataSetFormatter.prototype.setHeight = function(value){this.args.height = value;}; + + + + +Vertex.prototype.getName = GraphItem.prototype.getName; +Vertex.prototype.setName = GraphItem.prototype.setName; +Vertex.prototype.getId = GraphItem.prototype.getId; + +function Vertex(id, name, args){ + GraphItem.prototype.constructor.call(this, id, name, args); + this.edgesIn= new Array(); + this.edgesOut= new Array(); +}; + +Vertex.prototype.getEdges = function(){ + return this.edgesIn.concat(this.edgesOut); +}; + +Vertex.prototype.getEdgesCount = function(){ + return this.getEdges().length; +}; + +Vertex.prototype.getEdgesIn = function(){ + return this.edgesIn; +}; + +Vertex.prototype.getEdgesOut = function(){ + return this.edgesOut; +}; + +Vertex.prototype.addEdge = function(edge){ + if (edge.getNodeSource().getId() == this.id){ + this.edgesIn.push(edge); + } + else{ + this.edgesOut.push(edge); + } +}; + +Vertex.prototype.removeEdge = function(edge){ + for ( var i = 0; i < this.getEdgesIn().length; i++) { + var edgeIn = this.edgesIn[i]; + if (edgeIn.getId() == edge.getId()){ + this.edgesIn.splice(i, 1); + } + } + for ( var i = 0; i < this.getEdgesOut().length; i++) { + var edgeIn = this.edgesOut[i]; + if (edgeIn.getId() == edge.getId()){ + this.edgesOut.splice(i, 1); + } + } +}; + +Vertex.prototype.remove = function(){ + var edges = this.getEdges(); + for ( var i = 0; i < edges.length; i++) { + var edge = edges[i]; + edge.remove(); + } + this.deleted.notify(this); +}; + + + + + +VertexGraphFormatter.prototype.getDefault = ItemGraphFormatter.prototype.getDefault; +VertexGraphFormatter.prototype.getSelected = ItemGraphFormatter.prototype.getSelected; +VertexGraphFormatter.prototype.getOver = ItemGraphFormatter.prototype.getOver; +VertexGraphFormatter.prototype.getDragging = ItemGraphFormatter.prototype.getDragging; +VertexGraphFormatter.prototype.getId = ItemGraphFormatter.prototype.getId; +VertexGraphFormatter.prototype.toJSON = ItemGraphFormatter.prototype.toJSON; +VertexGraphFormatter.prototype.loadFromJSON = ItemGraphFormatter.prototype.loadFromJSON; +VertexGraphFormatter.prototype._setEvents = ItemGraphFormatter.prototype._setEvents; + + +function VertexGraphFormatter(defaultFormat, selectedFormat, overFormat, draggingFormat){ + ItemGraphFormatter.prototype.constructor.call(this, defaultFormat, selectedFormat, overFormat, draggingFormat); +}; + + +CircleVertexGraphFormatter.prototype.getDefault = VertexGraphFormatter.prototype.getDefault; +CircleVertexGraphFormatter.prototype.getSelected = VertexGraphFormatter.prototype.getSelected; +CircleVertexGraphFormatter.prototype.getOver = VertexGraphFormatter.prototype.getOver; +CircleVertexGraphFormatter.prototype.getDragging = VertexGraphFormatter.prototype.getDragging; +CircleVertexGraphFormatter.prototype.getId = VertexGraphFormatter.prototype.getId; +CircleVertexGraphFormatter.prototype.toJSON = VertexGraphFormatter.prototype.toJSON; +CircleVertexGraphFormatter.prototype.loadFromJSON = VertexGraphFormatter.prototype.loadFromJSON; +CircleVertexGraphFormatter.prototype._setEvents = VertexGraphFormatter.prototype._setEvents; + +function CircleVertexGraphFormatter(id, defaultFormat, selectedFormat, overFormat, draggingFormat){ + VertexGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, draggingFormat); + this.args.type = "CircleVertexGraphFormatter"; + if (defaultFormat != null){ + if (defaultFormat.radius != null){ + this.defaultFormat.args.radius = defaultFormat.radius; + } + } + + if (selectedFormat != null){ + if (selectedFormat.radius != null){ + this.selected.args.radius = selectedFormat.radius; + } + } + + if (overFormat != null){ + if (overFormat.radius != null){ + this.over.args.radius = overFormat.radius; + } + } + + if (draggingFormat != null){ + if (draggingFormat.radius != null){ + this.dragging.args.draggingFormat = draggingFormat.radius; + } + } +}; + + + +SquareVertexGraphFormatter.prototype.getDefault = VertexGraphFormatter.prototype.getDefault; +SquareVertexGraphFormatter.prototype.getSelected = VertexGraphFormatter.prototype.getSelected; +SquareVertexGraphFormatter.prototype.getOver = VertexGraphFormatter.prototype.getOver; +SquareVertexGraphFormatter.prototype.getDragging = VertexGraphFormatter.prototype.getDragging; +SquareVertexGraphFormatter.prototype.getId = VertexGraphFormatter.prototype.getId; +SquareVertexGraphFormatter.prototype.toJSON = VertexGraphFormatter.prototype.toJSON; +SquareVertexGraphFormatter.prototype.loadFromJSON = VertexGraphFormatter.prototype.loadFromJSON; +SquareVertexGraphFormatter.prototype._setEvents = VertexGraphFormatter.prototype._setEvents; + +function SquareVertexGraphFormatter(id, defaultFormat, selectedFormat, overFormat, draggingFormat){ + VertexGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, draggingFormat); + this.args.type = "SquareVertexGraphFormatter"; + if (defaultFormat != null){ + if (defaultFormat.radius != null){ + this.defaultFormat.args.radius = defaultFormat.radius; + } + } + + if (selectedFormat != null){ + if (selectedFormat.radius != null){ + this.selected.args.radius = selectedFormat.radius; + } + } + + if (overFormat != null){ + if (overFormat.radius != null){ + this.over.args.radius = overFormat.radius; + } + } + + if (draggingFormat != null){ + if (draggingFormat.radius != null){ + this.dragging.args.draggingFormat = draggingFormat.radius; + } + } +}; + + +EllipseVertexGraphFormatter.prototype.getDefault = VertexGraphFormatter.prototype.getDefault; +EllipseVertexGraphFormatter.prototype.getSelected = VertexGraphFormatter.prototype.getSelected; +EllipseVertexGraphFormatter.prototype.getOver = VertexGraphFormatter.prototype.getOver; +EllipseVertexGraphFormatter.prototype.getDragging = VertexGraphFormatter.prototype.getDragging; +EllipseVertexGraphFormatter.prototype.getId = VertexGraphFormatter.prototype.getId; +EllipseVertexGraphFormatter.prototype.toJSON = VertexGraphFormatter.prototype.toJSON; +EllipseVertexGraphFormatter.prototype.loadFromJSON = VertexGraphFormatter.prototype.loadFromJSON; +EllipseVertexGraphFormatter.prototype._setEvents = VertexGraphFormatter.prototype._setEvents; + +function EllipseVertexGraphFormatter(id, defaultFormat, selectedFormat, overFormat, draggingFormat){ + VertexGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, draggingFormat); + this.args.type = "EllipseVertexGraphFormatter"; + if (defaultFormat != null){ + if (defaultFormat.radius != null){ + this.defaultFormat.args.radius = defaultFormat.radius; + } + } + + if (selectedFormat != null){ + if (selectedFormat.radius != null){ + this.selected.args.radius = selectedFormat.radius; + } + } + + if (overFormat != null){ + if (overFormat.radius != null){ + this.over.args.radius = overFormat.radius; + } + } + + if (draggingFormat != null){ + if (draggingFormat.radius != null){ + this.dragging.args.draggingFormat = draggingFormat.radius; + } + } +}; + + + +RectangleVertexGraphFormatter.prototype.getDefault = ItemGraphFormatter.prototype.getDefault; +RectangleVertexGraphFormatter.prototype.getSelected = ItemGraphFormatter.prototype.getSelected; +RectangleVertexGraphFormatter.prototype.getOver = ItemGraphFormatter.prototype.getOver; +RectangleVertexGraphFormatter.prototype.getDragging = ItemGraphFormatter.prototype.getDragging; +RectangleVertexGraphFormatter.prototype.getId = ItemGraphFormatter.prototype.getId; +RectangleVertexGraphFormatter.prototype.toJSON = ItemGraphFormatter.prototype.toJSON; +RectangleVertexGraphFormatter.prototype.loadFromJSON = ItemGraphFormatter.prototype.loadFromJSON; +RectangleVertexGraphFormatter.prototype._setEvents = ItemGraphFormatter.prototype._setEvents; + +function RectangleVertexGraphFormatter(id, defaultFormat, selectedFormat, overFormat, draggingFormat){ + VertexGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, draggingFormat); + this.args.type = "RectangleVertexGraphFormatter"; + if (defaultFormat != null){ + if (defaultFormat.radius != null){ + this.defaultFormat.args.radius = defaultFormat.radius; + } + } + + if (selectedFormat != null){ + if (selectedFormat.radius != null){ + this.selected.args.radius = selectedFormat.radius; + } + } + + if (overFormat != null){ + if (overFormat.radius != null){ + this.over.args.radius = overFormat.radius; + } + } + + if (draggingFormat != null){ + if (draggingFormat.radius != null){ + this.dragging.args.draggingFormat = draggingFormat.radius; + } + } +}; + + + +RoundedVertexGraphFormatter.prototype.getDefault = ItemGraphFormatter.prototype.getDefault; +RoundedVertexGraphFormatter.prototype.getSelected = ItemGraphFormatter.prototype.getSelected; +RoundedVertexGraphFormatter.prototype.getOver = ItemGraphFormatter.prototype.getOver; +RoundedVertexGraphFormatter.prototype.getDragging = ItemGraphFormatter.prototype.getDragging; +RoundedVertexGraphFormatter.prototype.getId = ItemGraphFormatter.prototype.getId; +RoundedVertexGraphFormatter.prototype.toJSON = ItemGraphFormatter.prototype.toJSON; +RoundedVertexGraphFormatter.prototype.loadFromJSON = ItemGraphFormatter.prototype.loadFromJSON; +RoundedVertexGraphFormatter.prototype._setEvents = ItemGraphFormatter.prototype._setEvents; + +function RoundedVertexGraphFormatter(id, defaultFormat, selectedFormat, overFormat, draggingFormat){ + VertexGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, draggingFormat); + this.args.type = "RoundedVertexGraphFormatter"; + if (defaultFormat != null){ + if (defaultFormat.radius != null){ + this.defaultFormat.args.radius = defaultFormat.radius; + } + } + + if (selectedFormat != null){ + if (selectedFormat.radius != null){ + this.selected.args.radius = selectedFormat.radius; + } + } + + if (overFormat != null){ + if (overFormat.radius != null){ + this.over.args.radius = overFormat.radius; + } + } + + if (draggingFormat != null){ + if (draggingFormat.radius != null){ + this.dragging.args.draggingFormat = draggingFormat.radius; + } + } +}; + + + +OctagonVertexGraphFormatter.prototype.getDefault = ItemGraphFormatter.prototype.getDefault; +OctagonVertexGraphFormatter.prototype.getSelected = ItemGraphFormatter.prototype.getSelected; +OctagonVertexGraphFormatter.prototype.getOver = ItemGraphFormatter.prototype.getOver; +OctagonVertexGraphFormatter.prototype.getDragging = ItemGraphFormatter.prototype.getDragging; +OctagonVertexGraphFormatter.prototype.getId = ItemGraphFormatter.prototype.getId; +OctagonVertexGraphFormatter.prototype.toJSON = ItemGraphFormatter.prototype.toJSON; +OctagonVertexGraphFormatter.prototype.loadFromJSON = ItemGraphFormatter.prototype.loadFromJSON; +OctagonVertexGraphFormatter.prototype._setEvents = ItemGraphFormatter.prototype._setEvents; + +function OctagonVertexGraphFormatter(id, defaultFormat, selectedFormat, overFormat, draggingFormat){ + VertexGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, draggingFormat); + this.args.type = "OctagonVertexGraphFormatter"; + if (defaultFormat != null){ + if (defaultFormat.radius != null){ + this.defaultFormat.args.radius = defaultFormat.radius; + } + } + + if (selectedFormat != null){ + if (selectedFormat.radius != null){ + this.selected.args.radius = selectedFormat.radius; + } + } + + if (overFormat != null){ + if (overFormat.radius != null){ + this.over.args.radius = overFormat.radius; + } + } + + if (draggingFormat != null){ + if (draggingFormat.radius != null){ + this.dragging.args.draggingFormat = draggingFormat.radius; + } + } +}; + + + +var Colors = new function() +{ + this.hashColor = []; + this.getColorByScoreArrayValue = function (arrayScore) + { + var array = new Array(); + + for (var i = 0; i< arrayScore.length; i++) + { + + var color = this.getColorByScoreValue(arrayScore[i]) + array.push( color); + + } + return array; + }; + + this.getHexStringByScoreArrayValue = function (arrayScore) + { + var arrayColor = this.getColorByScoreArrayValue(arrayScore); + var arrayHex = new Array(); + for (var i = 0; i< arrayColor.length; i++) + { + arrayHex.push( arrayColor[i].HexString()); + } + return arrayHex; + }; + + this.getColorByScoreValue = function (score) + { + + var truncate = score.toString().substr(0,4); + if (this.hashColor[truncate]!=null) + { + return this.hashColor[truncate]; + } + + + if(isNaN(score)) { + return Colors.ColorFromRGB(0,0,0); + } + var value; + + var from, to; + if(score < 0.5) { + from = Colors.ColorFromRGB(0,0,255); + to = Colors.ColorFromRGB(255,255,255); + value = (score * 2); + } else { + from = Colors.ColorFromRGB(255,255,255); + to = Colors.ColorFromRGB(255,0,0); + value = (score * 2) - 1; + } + + var x = value; + var y = 1.0 - value; + var color = Colors.ColorFromRGB(y * from.Red() + x * to.Red(), y * from.Green() + x * to.Green(), y * from.Blue() + x * to.Blue()); + + this.hashColor[truncate] = color; + + return color; + }; + + this.ColorFromHSV = function(hue, sat, val) + { + var color = new Color(); + color.SetHSV(hue,sat,val); + return color; + }; + + this.ColorFromRGB = function(r, g, b) + { + var color = new Color(); + color.SetRGB(r,g,b); + return color; + }; + + this.ColorFromHex = function(hexStr) + { + var color = new Color(); + color.SetHexString(hexStr); + return color; + }; + + function Color() { + //Stored as values between 0 and 1 + var red = 0; + var green = 0; + var blue = 0; + + //Stored as values between 0 and 360 + var hue = 0; + + //Strored as values between 0 and 1 + var saturation = 0; + var value = 0; + + this.SetRGB = function(r, g, b) + { + red = r/255.0; + green = g/255.0; + blue = b/255.0; + calculateHSV(); + }; + + this.Red = function() { return Math.round(red*255); }; + + this.Green = function() { return Math.round(green*255); }; + + this.Blue = function() { return Math.round(blue*255); }; + + this.SetHSV = function(h, s, v) + { + hue = h; + saturation = s; + value = v; + calculateRGB(); + }; + + this.Hue = function() + { return hue; }; + + this.Saturation = function() + { return saturation; }; + + this.Value = function() + { return value; }; + + this.SetHexString = function(hexString) + { + if(hexString == null || typeof(hexString) != "string") + { + this.SetRGB(0,0,0); + return; + } + + if (hexString.substr(0, 1) == '#') + hexString = hexString.substr(1); + + if(hexString.length != 6) + { + this.SetRGB(0,0,0); + return; + } + + var r = parseInt(hexString.substr(0, 2), 16); + var g = parseInt(hexString.substr(2, 2), 16); + var b = parseInt(hexString.substr(4, 2), 16); + if (isNaN(r) || isNaN(g) || isNaN(b)) + { + this.SetRGB(0,0,0); + return; + } + + this.SetRGB(r,g,b); + }; + + this.HexString = function() + { + + var rStr = this.Red().toString(16); + if (rStr.length == 1) + rStr = '0' + rStr; + var gStr = this.Green().toString(16); + if (gStr.length == 1) + gStr = '0' + gStr; + var bStr = this.Blue().toString(16); + if (bStr.length == 1) + bStr = '0' + bStr; + return ('#' + rStr + gStr + bStr).toUpperCase(); + }; + + this.Complement = function() + { + var newHue = (hue >= 180) ? hue - 180 : hue + 180; + var newVal = (value * (saturation - 1) + 1); + var newSat = (value*saturation) / newVal; + var newColor = new Color(); + newColor.SetHSV(newHue, newSat, newVal); + return newColor; + } ; + + function calculateHSV() + { + var max = Math.max(Math.max(red, green), blue); + var min = Math.min(Math.min(red, green), blue); + + value = max; + + saturation = 0; + if(max != 0) + saturation = 1 - min/max; + + hue = 0; + if(min == max) + return; + + var delta = (max - min); + if (red == max) + hue = (green - blue) / delta; + else if (green == max) + hue = 2 + ((blue - red) / delta); + else + hue = 4 + ((red - green) / delta); + hue = hue * 60; + if(hue < 0) + hue += 360; + } + + function calculateRGB() + { + red = value; + green = value; + blue = value; + + if(value == 0 || saturation == 0) + return; + + var tHue = (hue / 60); + var i = Math.floor(tHue); + var f = tHue - i; + var p = value * (1 - saturation); + var q = value * (1 - saturation * f); + var t = value * (1 - saturation * (1 - f)); + switch(i) + { + case 0: + red = value; green = t; blue = p; + break; + case 1: + red = q; green = value; blue = p; + break; + case 2: + red = p; green = value; blue = t; + break; + case 3: + red = p; green = q; blue = value; + break; + case 4: + red = t; green = p; blue = value; + break; + default: + red = value; green = p; blue = q; + break; + } + } + } +} +(); +/* + * Clase gestiona la insercción, eliminación de los elementos en el DOM + * + * Vital hacerla SIEMPRE compatible hacia atrás + * + * Last update: 28-10-2010 + * + */ + + +var DOM = {}; + +DOM.createNewElement = function(type, nodeParent, attributes) +{ + + var node = document.createElement(type); + for (var i=0; i0) + { + parent.removeChild(parent.childNodes[0]); + + } +}; + +DOM.select = function(targetID) +{ + return document.getElementById(targetID); +// return $("#"+targetID); +}; +var Geometry = +{ + + /** From tow points obtains the angles formed with the cartesian side **/ + getAngleBetweenTwoPoints : function(x1, y1, x2, y2){ + //var m = (y1 - y2)/ (x1 - x2); + return Math.atan2(y2-y1, x2-x1); + }, + + getAdjacentSideOfRectangleRight : function(angle, hypotenuse){ + return Math.abs(Math.cos(angle)*hypotenuse); + }, + + getOppositeSideOfRectangleRight : function(angle, hypotenuse){ + return Math.abs(Math.sin(angle)*hypotenuse); + }, + + toDegree: function(radian){ + return radian*180/Math.PI; + + } + + +}; + +var SVG = +{ + svgns : 'http://www.w3.org/2000/svg', + xlinkns : "http://www.w3.org/1999/xlink", + + createSVGCanvas: function(parentNode, attributes) + { + + attributes.push( ['xmlns', SVG.svgns], ['xmlns:xlink', 'http://www.w3.org/1999/xlink']); + var svg = document.createElementNS("http://www.w3.org/2000/svg", "svg"); + this._setProperties(svg, attributes); + parentNode.appendChild( svg); + return svg; + + }, + + createRectangle : function (x, y, width, height, attributes){ + var rect = document.createElementNS(this.svgns, "rect"); + rect.setAttribute("x",x); + rect.setAttribute("y",y); + rect.setAttribute("width",width); + rect.setAttribute("height",height); + SVG._setProperties(rect, attributes); + return rect; + }, + + drawImage64 : function (x, y, width, height, base64, svgNode, attributes) { + var node = SVG.createImage64(x, y, width, height, base64, attributes); + svgNode.appendChild(node); + return node; + }, + + createImage64 : function (x, y, width, height, base64, attributes) { + var img = document.createElementNS(this.svgns, "image"); + img.setAttribute("x",x); + img.setAttribute("y",y); + img.setAttribute("width",width); + img.setAttribute("height",height); + img.setAttribute("xlink:href",base64); + SVG._setProperties(img, attributes); + return img; + }, + + createLine: function (x1, y1, x2, y2, attributes){ + var line = document.createElementNS(this.svgns,"line"); + line.setAttribute("x1",x1); + line.setAttribute("y1",y1); + line.setAttribute("x2", x2); + line.setAttribute("y2", y2); + SVG._setProperties(line, attributes); + return line; + }, + + createClip: function (id, nodeToClip, attributes){ + var clip = document.createElementNS(this.svgns,"clipPath"); + clip.setAttribute("id",id); + clip.appendChild(nodeToClip); + return clip; + }, + + drawClip : function (id, nodeToClip, svgNode) { + var node = SVG.createClip(id, nodeToClip); + svgNode.appendChild(node); + return node; + }, + + drawRectangle : function (cx, cy, width, height, svgNode, attributes) { + try{ + var node = SVG.createRectangle(cx, cy, width, height, attributes); + svgNode.appendChild(node); + } + catch(e){ + + console.log("-------------------- "); + console.log("Error on drawRectangle " + e); + console.log(attributes); + console.log("-------------------- "); + } + return node; + }, + + createEllipse : function (x, y, rx, ry, attributes){ + var rect = document.createElementNS(this.svgns, "ellipse"); + rect.setAttribute("cx",x); + rect.setAttribute("cy",y); + rect.setAttribute("rx",rx); + rect.setAttribute("ry",ry); + SVG._setProperties(rect, attributes); + return rect; + }, + + drawEllipse : function (cx, cy, rx, ry, svgNode, attributes) { + var node = SVG.createEllipse(cx, cy, rx, ry, attributes); + svgNode.appendChild(node); + return node; + }, + + drawImage : function (x, y, canvasSVG, attributes) { + var image = document.createElementNS(this.svgns, "image"); + image.setAttribute("x",x); + image.setAttribute("y",y); + canvasSVG.appendChild(image); + SVG._setProperties(image, attributes); + }, + + drawLine : function (x1, y1, x2, y2, nodeSVG, attributes) { + try{ + var line = SVG.createLine(x1, y1, x2, y2, attributes); + nodeSVG.appendChild(line); + }catch(e){ + } + return line; + }, + + + drawPath: function (d, nodeSVG, attributes) { + var path = SVG.createPath(d, attributes); + nodeSVG.appendChild(path); + return path; + }, + + createPoligon : function (points, attributes){ + var poligon = document.createElementNS(this.svgns, "polygon"); + poligon.setAttribute("points",points); + SVG._setProperties(poligon, attributes); + return poligon; + }, + + drawPoligon : function (points, canvasSVG, attributes){ + var poligon = SVG.createPoligon(points, attributes); + canvasSVG.appendChild(poligon); + return poligon; + }, + // + + createPath : function (d, attributes){ + var path = document.createElementNS(this.svgns, "path"); + path.setAttribute("d",d); + SVG._setProperties(path, attributes); + return path; + }, + + drawCircle : function (x, y, radio, canvasSVG, attributes) { + + var newText = document.createElementNS(this.svgns,"circle"); + newText.setAttribute("cx",x); + newText.setAttribute("cy",y); + newText.setAttribute("r",radio); + + canvasSVG.appendChild(newText); + attributes["cursor"] = "pointer"; + this._setProperties(newText, attributes); + return newText; + }, + + + _setProperties: function(node, attributes) + { + if (attributes instanceof Array){ + for (var i=0; i< attributes.length; i++) + { + node.setAttribute(attributes[i][0], attributes[i][1]); + } + } + else{ + for ( var key in attributes){ + node.setAttribute(key, attributes[key]); + } + } + }, + +/* drawPath: function(pointsArray, canvasSVG, attributes){ + var path = document.createElementNS(this.svgns,"polyline"); + path.setAttribute ('id', id); + + var d= pointsArray[0].x+ " "+ pointsArray[0].y; + for (var i=1; i< pointsArray.length; i++) + { + d=d+" "+pointsArray[i].x+" "+pointsArray[i].y; + } + path.setAttribute ('points', d); + canvasSVG.appendChild(path); + },*/ + + createText : function (x, y, text, attributes) { + var node = document.createElementNS(this.svgns,"text"); + node.setAttributeNS(null , "x",x); + node.setAttributeNS(null, "y",y); + + var textNode = document.createTextNode(text); + node.appendChild(textNode); + + this._setProperties(node, attributes); + return node; + }, + + drawText : function (x, y, text, canvasSVG, attributes) { + var text = SVG.createText(x, y, text, attributes); + canvasSVG.appendChild(text); + return text; + }, + + + + drawGroup: function(svgNode, attributes) + { + var group = SVG.createGroup(attributes); + svgNode.appendChild(group); + return group; + }, + + createGroup: function(attributes){ + var group = document.createElementNS(this.svgns,"g"); + this._setProperties(group, attributes); + return group; + } + +}; + + + +var CanvasToSVG = { + + convert: function(sourceCanvas, targetSVG, x, y, id, attributes) { + + var img = this._convert(sourceCanvas, targetSVG, x, y, id); + + for (var i=0; i< attributes.length; i++) + { + img.setAttribute(attributes[i][0], attributes[i][1]); + } + }, + + _convert: function(sourceCanvas, targetSVG, x, y, id) { + var svgNS = "http://www.w3.org/2000/svg"; + var xlinkNS = "http://www.w3.org/1999/xlink"; + // get base64 encoded png from Canvas + var image = sourceCanvas.toDataURL(); + + // must be careful with the namespaces + var svgimg = document.createElementNS(svgNS, "image"); + + svgimg.setAttribute('id', id); + + //svgimg.setAttribute('class', class); + //svgimg.setAttribute('xlink:href', image); + svgimg.setAttributeNS(xlinkNS, 'xlink:href', image); + + + + + svgimg.setAttribute('x', x ? x : 0); + svgimg.setAttribute('y', y ? y : 0); + svgimg.setAttribute('width', sourceCanvas.width); + svgimg.setAttribute('height', sourceCanvas.height); + //svgimg.setAttribute('cursor', 'pointer'); + svgimg.imageData = image; + + targetSVG.appendChild(svgimg); + return svgimg; + }, + + importSVG: function(sourceSVG, targetCanvas) { + svg_xml = sourceSVG;//(new XMLSerializer()).serializeToString(sourceSVG); + var ctx = targetCanvas.getContext('2d'); + + var img = new Image(); + img.src = "data:image/svg+xml;base64," + btoa(svg_xml); +// img.onload = function() { + ctx.drawImage(img, 0, 0); +// }; + } + +}; +/* +Graph.prototype.importSVG = function(sourceSVG, targetCanvas) { + sourceSVG = this._svg; + targetCanvas = document.createElementNS('canvas'); + // https://developer.mozilla.org/en/XMLSerializer + svg_xml = (new XMLSerializer()).serializeToString(sourceSVG); + var ctx = targetCanvas.getContext('2d'); + // this is just a JavaScript (HTML) image + var img = new Image(); + // http://en.wikipedia.org/wiki/SVG#Native_support + // https://developer.mozilla.org/en/DOM/window.btoa + img.src = "data:image/svg+xml;base64," + btoa(svg_xml); + img.onload = function() { + // after this, Canvas’ origin-clean is DIRTY + ctx.drawImage(img, 0, 0); + } +}; +*/ + +/* + +Normalizacion de datos para dibujar colores +Issues: + No sé como debería llamarse esta libreria + No sé si ya existe una funciçon en javascript que lo haga + + +*/ + + +var Normalizer = new function() +{ + this.normalizeArray = function (arrayData) + { + + return this.standardizeArray(this.normal(arrayData)); + +// var result = this._getMaxAndMin(arrayData); +// var min =result[0]; +// var max = result[1]; +// +// +// //los hacemos todos positivos +// for (var i = 0; i< arrayData.length; i++) +// { +// arrayData[i]= Math.abs(min) + parseFloat(arrayData[i]); +// } +// +// var result = this._getMaxAndMin(arrayData); +// var min =result[0]; +// var max = result[1]; +// +// +// var resultArray = new Array(); +// for (var i = 0; i< arrayData.length; i++) +// { +// resultArray.push(arrayData[i]*1/max); +// } +// return resultArray; + }; + + this.normal = function(arrayData){ + var mean = this._getMean(arrayData); + var deviation = this._getStdDeviation(arrayData); + var result = this._getMaxAndMin(arrayData); + var min = result[0]; + var max = result[1]; + + var resultArray = new Array(); + for (var i = 0; i< arrayData.length; i++) { + if (deviation!=0){ + resultArray.push((arrayData[i]-mean)/deviation); + }else{ + resultArray.push(arrayData[i]); + } + } + return resultArray; + }; + + this.standardizeArray = function(arrayData) + { + var result = this._getMaxAndMin(arrayData); + var min = result[0]; + var max = result[1]; + + var offset = ( min <= 0 ) ? Math.abs(min) : (-1 * min); + var resultArray = new Array(); + for (var i = 0; i< arrayData.length; i++) { + if(max + offset!=0){ + resultArray.push((arrayData[i] + offset) / (max + offset)); + }else{ + resultArray.push(arrayData[i]+offset); + } + } + return resultArray; + }; + + + this._getMean = function(arrayData) { + var sum = 0; + for (var i = 0; i< arrayData.length; i++) { + sum = sum + parseFloat(arrayData[i]); + } + return sum/arrayData.length; + }; + + this._getStdDeviation = function(arrayData) { + var mean = this._getMean(arrayData); + var acum = 0.0; + for(var i=0; i max) max = parseFloat(arrayData[i]); + } + + return [min, max]; + }; +}; +function GraphCanvas(componentID, targetNode, args) { + this.args = {}; + /** target */ + this.targetID = targetNode.id; + + /** id manage */ + this.id = componentID; + this.args.idGraph = this.id + "main"; + this.args.idBackgroundNode = this.id + "background"; + + this.args.idEdgesGraph = this.id + "edges"; + this.args.idNodesGraph = this.id + "vertices"; + this.args.idLabelGraph = this.id + "label"; + this.args.idBackground = this.id + "background"; + + /** Objects Graph **/ + this.dataset = null; + this.formatter = null; + this.layout = null; + + /** Drawing **/ + this.circleDefaultRadius = 2; + this.squareDefaultSide = this.circleDefaultRadius * 1.5; + + /** Directed Arrow **/ + this.arrowDefaultSize = this.circleDefaultRadius; + + /** Groups **/ + this.GraphGroup = null; + this.GraphNodeGroup = null; + this.GraphLabelGroup = null; + this.GraphBackground = null; + + /** SETTINGS FLAGS **/ + this.args.draggingCanvasEnabled = false; //Flag to set if the canvas can be dragged + this.args.multipleSelectionEnabled = false; + this.args.interactive = false; + this.args.labeled = false; + this.args.linkEnabled = false; + + /** If numberEdge > maxNumberEdgesMoving then only it will move edges when mouse up **/ + this.args.maxNumberEdgesMoving = 3; + this.args.maxNumberEdgesFiringEvents = 50; + + /** Linking edges **/ + this.args.linking = false; + this.linkStartX = 0; + this.linkStartY = 0; + this.linkSVGNode = null; + this.linkNodeSource = null; + this.linkNodeTarget = null; + + /** Dragging Control **/ + this.draggingElement = null; + this.dragging = false; + this.nMouseOffsetX = 0; + this.nMouseOffsetY = 0; + this.dragStartX = 0; + this.dragStartY = 0; + this.desplazamientoX = 0; + this.desplazamientoY = 0; + + /** Selection Control **/ + this.selecting = false; + this.selectorX = null; + this.selectorY = null; + this.selectorSVGNode = null; + + /** Node status **/ + this.args.isVertexSelected = {}; + this.args.selectedVertices = []; + + /** Edges status **/ + this.args.isEdgeSelected = {}; + //this.args.selectedEdges = []; + + if (args != null) { + if (args.multipleSelectionEnabled != null) { + this.args.multipleSelectionEnabled = args.multipleSelectionEnabled; + this.args.draggingCanvasEnabled = !(this.args.multipleSelectionEnabled); + } + if (args.draggingCanvasEnabled != null) { + this.args.draggingCanvasEnabled = args.draggingCanvasEnabled; + this.args.multipleSelectionEnabled = !(this.args.draggingCanvasEnabled); + } + if (args.interactive != null) { + this.args.interactive = args.interactive; + } + if (args.labeled != null) { + this.args.labeled = args.labeled; + } + + } + + /** Hashmap with the svg node labels **/ + this.svgLabels = {}; + + /** EVENTS **/ + this.onVertexOut = new Event(this); + this.onVertexOver = new Event(this); + this.onVertexSelect = new Event(this); + this.onEdgeSelect = new Event(this); + this.onCanvasClicked = new Event(this); + this.onVertexUp = new Event(this); +} + +GraphCanvas.prototype.showLabels = function(value) { + this.args.labeled = value; + this.removeLabels(); + if (value) { + this.renderLabels(); + } +}; + +GraphCanvas.prototype.getSelectedVertices = function() { + return this.args.selectedVertices; +}; + +GraphCanvas.prototype.getSelectedEdges = function() { + var selected = []; + for ( var selectedEdge in this.args.isEdgeSelected) { + selected.push(selectedEdge); + } + return selected; +}; + +GraphCanvas.prototype.createSVGDom = function(targetID, id, width, height, backgroundColor) { + var container = document.getElementById(targetID); + this._svg = SVG.createSVGCanvas(container, [ + [ "style", "background-color:" + backgroundColor + ";" ], [ "id", id ], [ "dragx", 0 ], [ "dragy", 0 ], + [ "height", this.getFormatter().getHeight() ], [ "width", this.getFormatter().getWidth() ] ]); + return this._svg; +}; + +/** MULTIPLE SELECTION **/ +GraphCanvas.prototype.isMultipleSelectionEnabled = function() { + return this.args.multipleSelectionEnabled; +}; + +GraphCanvas.prototype.setMultipleSelection = function(value) { + this.args.multipleSelectionEnabled = value; + this.args.draggingCanvasEnabled = (!value); +}; + +GraphCanvas.prototype.setSelecting = function(value) { + this.selecting = value; +}; + +/** linking **/ +GraphCanvas.prototype.setLinking = function(value) { + this.args.linkEnabled = value; + this.selecting = !value; + this.dragging = !value; +}; + +/** CANVAS MOVING **/ +GraphCanvas.prototype.setDraggingCanvas = function(value) { + this.args.draggingCanvasEnabled = value; + this.args.multipleSelectionEnabled = !value; +}; + +GraphCanvas.prototype.isDraggingCanvasEnabled = function() { + return this.args.draggingCanvasEnabled; +}; +/** ZOOM **/ +GraphCanvas.prototype.getScale = function() { + return this.getFormatter().getZoomScale(); +}; + +GraphCanvas.prototype.setScale = function(scale) { + var graphNode = document.getElementById(this.args.idGraph); + graphNode.setAttribute("transform", graphNode.getAttribute("transform").replace("scale(" + this.getScale() + ")", "scale(" + scale + ")")); + this.getFormatter().setZoomScale(scale); +}; + +GraphCanvas.prototype.zoomIn = function() { + this.setScale(this.getScale() + this.getFormatter().getZoomScaleStepFactor()); +}; + +GraphCanvas.prototype.zoomOut = function() { + this.setScale(this.getScale() - this.getFormatter().getZoomScaleStepFactor()); + +}; + +/** SVG COORDENATES **/ +GraphCanvas.prototype.getSVGCoordenates = function(evt) { + var p = this._svg.createSVGPoint(); + p.x = evt.clientX; + p.y = evt.clientY; + + var m = this._svg.getScreenCTM(document.documentElement); + p = p.matrixTransform(m.inverse()); + return p; +}; + +/** SVG EVENTS **/ +GraphCanvas.prototype.mouseClick = function(event) { + if (event.button == 0) { + if (!this.args.interactive) { + return; + } + + if (this.isVertex(event.target)) { + this.clickNode(this.getVertexIdFromSVGId(event.target.id)); + } + /** Como el evento mouseClick viene despues del mouse up es aqui donde manejo el tema de deseccionar los elementos que estoy dragging **/ + if (this.dragging) { + this.dragging = false; + } + } +}; + +GraphCanvas.prototype.mouseMove = function(evt) { + if (this.selecting) { + this.clearLabels(); + + var width = (this.getSVGCoordenates(evt).x - this.selectorX); + var height = (this.getSVGCoordenates(evt).y - this.selectorY); + if ((width > 0) && (height > 0)) { + this.displaySelection(this.selectorX, this.selectorY, width, height); + } + if ((width > 0) && (height < 0)) { + this.displaySelection(this.selectorX, this.getSVGCoordenates(evt).y, width, Math.abs(height)); + } + if ((width < 0) && (height < 0)) { + this.displaySelection(this.getSVGCoordenates(evt).x, this.getSVGCoordenates(evt).y, Math.abs(width), Math.abs(height)); + } + if ((width < 0) && (height > 0)) { + this.displaySelection(this.selectorX + width, this.selectorY, Math.abs(width), Math.abs(height)); + } + + var x1 = (parseFloat(this.selectorSVGNode.getAttribute("x")) - DOM.select(this.id).getAttribute("dragx")) / this.getFormatter().getWidth(); + var y1 = (parseFloat(this.selectorSVGNode.getAttribute("y")) - DOM.select(this.id).getAttribute("dragy")) / this.getFormatter().getHeight(); + var x2 = (x1 + parseFloat(this.selectorSVGNode.getAttribute("width") / this.getFormatter().getWidth())); + var y2 = (y1 + parseFloat(this.selectorSVGNode.getAttribute("height") / this.getFormatter().getHeight())); + + this.deselectNodes(this.getLayout()); + var verticesSelected = this.getLayout().getVerticesByArea(x1 / this.getFormatter().getZoomScale(), y1 / this.getFormatter().getZoomScale(), + x2 / this.getFormatter().getZoomScale(), y2 / this.getFormatter().getZoomScale()); + for ( var i = 0; i < verticesSelected.length; i++) { + this.selectNode(verticesSelected[i].getId()); + this.renderLabel(verticesSelected[i].getId()); + } + + } + var p = null; + if (this.args.linking) { + p = this.getSVGCoordenates(evt); + if (this.linkSVGNode != null) { + this.linkSVGNode.setAttribute("x2", p.x - 2); + this.linkSVGNode.setAttribute("y2", p.y - 2); + } + } + + if (this.dragging) { + p = this.getSVGCoordenates(evt); + p.x -= this.nMouseOffsetX; + p.y -= this.nMouseOffsetY; + this.desplazamientoX = (this.getSVGCoordenates(evt).x - this.dragStartX);// + parseFloat(DOM.select(this.id).getAttribute("dragx")); + this.desplazamientoY = (this.getSVGCoordenates(evt).y - this.dragStartY);// + parseFloat(DOM.select(this.id).getAttribute("dragy")); + + if (this.draggingElement != null) { + /** Click sobre el recct del banground que provoca que mueva todo el canvas **/ + if (this.isNodeCanvas(this.draggingElement)) { + + p = this.getSVGCoordenates(evt); + p.x = this.desplazamientoX; + p.y = this.desplazamientoY; + + this.draggingElement.setAttribute("dragx", p.x); + this.draggingElement.setAttribute("dragy", p.y); + this.draggingElement = document.getElementById(this.args.idGraph); + this.draggingElement.setAttribute("transform", "translate(" + p.x + "," + p.y + "), scale(" + this.getScale() + ")"); + + DOM.select(this.id).setAttribute("dragx", p.x); + DOM.select(this.id).setAttribute("dragy", p.y); + + if (this.NodeSVGbackgroundImage != null) { + this.NodeSVGbackgroundImage.setAttribute("dragx", p.x); + this.NodeSVGbackgroundImage.setAttribute("dragy", p.y); + } + } else { + if (this.isVertex(this.draggingElement)) { + this.selectNode(this.getVertexIdFromSVGId(this.draggingElement.id)); + this.desplazamientoX = this.desplazamientoX / this.getFormatter().getZoomScale(); + this.desplazamientoY = this.desplazamientoY / this.getFormatter().getZoomScale(); + this.moveSelectedNodes(this.desplazamientoX, this.desplazamientoY); + + this.dragStartX = this.getSVGCoordenates(evt).x; + this.dragStartY = this.getSVGCoordenates(evt).y; + } else { + if (this.isNodeBackground(this.draggingElement)) { + + this.draggingElement.setAttribute("dragx", p.x); + this.draggingElement.setAttribute("dragy", p.y); + this.draggingElement = document.getElementById(this.args.idGraph); + this.draggingElement.setAttribute("transform", "translate(" + p.x + "," + p.y + "), scale(" + this.getScale() + ")"); + } else { + this.draggingElement.setAttribute("transform", "translate(" + p.x + "," + p.y + ")"); + } + } + } + } + } +}; + +GraphCanvas.prototype.moveSelectedNodes = function(offsetX, offsetY) { + for ( var i = 0; i < this.getSelectedVertices().length; i++) { + + var nodeId = this.getSelectedVertices()[i]; + var svgNodeId = this.getSVGNodeId(nodeId); + + var x = parseFloat(DOM.select(svgNodeId).getAttribute("dragx")) + parseFloat(offsetX);// - parseFloat(DOM.select(this.id).getAttribute("dragx")); + var y = parseFloat(DOM.select(svgNodeId).getAttribute("dragy")) + parseFloat(offsetY);// + parseFloat(DOM.select(this.id).getAttribute("dragy")); + + this._movingNode(DOM.select(svgNodeId), x, y); + } +}; + +GraphCanvas.prototype.mouseDown = function(evt) { + if (event.button == 0) { + + /** if !no interactive mouse events do anything **/ + if (!this.args.interactive) { + return; + } + + var p = this.getSVGCoordenates(evt); + + /** When click on canvas or background deselect all **/ + if (this.isNodeCanvas(evt.target) || this.isNodeBackground(evt.target)) { + this.deselectNodes(); + this.deselectEdges(); + this.onCanvasClicked.notify(); + } + + /** if I am linking vertices **/ + if (this.args.linkEnabled) { + + if (!this.args.linking) { + this.args.linking = true; + if (this.isVertex(evt.target)) { + this.linkStartX = p.x; + this.linkStartY = p.y; + this.linkSVGNode = SVG.drawLine(p.x, p.y, p.x, p.y, this._svg, { + "stroke" : "#FF0000" + }); + this.linkNodeSource = this.getVertexIdFromSVGId(evt.target.id); + } + } else { + this.linkNodeTarget = this.getVertexIdFromSVGId(evt.target.id); + this.args.linking = false; + this.args.linkEnabled = false; + if (this.isVertex(evt.target)) { + this.getDataset().addEdge(this.linkNodeSource + "_" + this.linkNodeTarget, this.linkNodeSource, this.linkNodeTarget, {}); + } + this.linkSVGNode.parentNode.removeChild(this.linkSVGNode); + } + return; + } + + /** Id is a vertex or the canvas **/ + if (this.isVertex(evt.target) || this.isNodeCanvas(evt.target) || this.isNodeBackground(evt.target)) { + this._startDragging(evt); + } + /** if i is edge **/ + if (this.isEdge(evt.target)) { + this.selectEdge(this.getEdgeIdFromSVGId(evt.target.getAttribute("id"))); + } + + if (this.args.multipleSelectionEnabled) { + if (!this.dragging) { + this.setSelecting(true); + this.selectorX = p.x; + this.selectorY = p.y; + this.displaySelection(p.x, p.y, 1, 1); + } + } + + } + if (event.button == 1) { + this.setLinking(false); + this.setMultipleSelection(false); + this.selecting = false; + + /** Id is a vertex or the canvas **/ + if (this.isVertex(evt.target) || this.isNodeCanvas(evt.target) || this.isNodeBackground(evt.target)) { + this._startDragging(evt); + } + } +}; + +GraphCanvas.prototype.mouseUp = function(event) { + if (!this.args.interactive) { + return; + } + + if (this.dragging) { + this._stopDragging(event); + if (this.isVertex(event.target)) { + var vertexId = this.getVertexIdFromSVGId(event.target.id); + if (this.getDataset().getVertexById(vertexId).getEdges().length >= this.args.maxNumberEdgesMoving) { + this.moveEdge(vertexId); + } + } + } + + if (this.selecting) { + this.setSelecting(false); + + var x1 = (parseFloat(this.selectorSVGNode.getAttribute("x")) - DOM.select(this.id).getAttribute("dragx")) / this.getFormatter().getWidth(); + var y1 = (parseFloat(this.selectorSVGNode.getAttribute("y")) - DOM.select(this.id).getAttribute("dragy")) / this.getFormatter().getHeight(); + var x2 = (x1 + parseFloat(this.selectorSVGNode.getAttribute("width") / this.formatter.getWidth())); + var y2 = (y1 + parseFloat(this.selectorSVGNode.getAttribute("height") / this.formatter.getHeight())); + + var verticesSelected = this.getLayout().getVerticesByArea(x1 / this.getFormatter().getZoomScale, y1 / this.getFormatter().getZoomScale, + x2 / this.getFormatter().getZoomScale, y2 / this.getFormatter().getZoomScale); + + for ( var i = 0; i < verticesSelected.length; i++) { + this.selectNode(verticesSelected[i].getId()); + } + + if (this.selectorSVGNode != null) { + this._svg.removeChild(this.selectorSVGNode); + } + + if (this.args.labeled) { + this.clearLabels(); + this.renderLabels(); + } + + this.selectorSVGNode = null; + // this.renderLabels(); + } +}; + +/** SELECTION **/ +GraphCanvas.prototype.displaySelection = function(x, y, width, height) { + if (this.selectorSVGNode != null) { + this.selectorSVGNode.setAttribute("x", x); + this.selectorSVGNode.setAttribute("y", y); + this.selectorSVGNode.setAttribute("width", width); + this.selectorSVGNode.setAttribute("height", height); + } else { + this.selectorSVGNode = SVG.drawRectangle(x, y, width, height, this._svg, { + "fill" : "red", + "stroke" : "black", + "opacity" : "0.2", + "stroke-opacity" : "1" + }); + } +}; + +/** DRAGGING **/ +GraphCanvas.prototype._startDragging = function(evt) { + if (!this.isDraggingCanvasEnabled()) { + if (this.isNodeCanvas(evt.target)) { + this.draggingElement = null; + } + } + + if (this.isVertex(evt.target) || (this.isNodeBackground(evt.target) && (this.isDraggingCanvasEnabled()))|| (this.isNodeCanvas(evt.target) && (this.isDraggingCanvasEnabled()))) { + this.clearLabels(); + this.draggingElement = evt.target; + this.dragging = true; + var p = this.getSVGCoordenates(evt); + + this.nMouseOffsetX = p.x - parseInt(evt.target.getAttribute("dragx")); + this.nMouseOffsetY = p.y - parseInt(evt.target.getAttribute("dragy")); + + if (this.isVertex(evt.target)) { + this.dragStartX = parseInt(this.draggingElement.getAttribute("dragx")) * this.getFormatter().getZoomScale() + + parseFloat(DOM.select(this.id).getAttribute("dragx")); + this.dragStartY = parseInt(this.draggingElement.getAttribute("dragy")) * this.getFormatter().getZoomScale() + + parseFloat(DOM.select(this.id).getAttribute("dragy")); + } else { + this.dragStartX = p.x - parseInt(this.draggingElement.getAttribute("dragx"));// + parseFloat(DOM.select(this.id).getAttribute("dragx")); + this.dragStartY = p.y - parseInt(this.draggingElement.getAttribute("dragy"));// + parseFloat(DOM.select(this.id).getAttribute("dragy")); + } + } +}; + +GraphCanvas.prototype._stopDragging = function(event) { + this.nMouseOffsetX = 0; + this.nMouseOffsetX = 0; + /** despues del evento up viene el evento click entonces acabo el dragging en el mouseclick **/ + this.dragging = false; + this.draggingElement = null; + this.renderLabels(); + + this.setLinking(false); + this.setMultipleSelection(true); + this.selecting = false; + +}; + +/** Move the edges of the vertex with the vertexId indicado **/ +GraphCanvas.prototype.moveEdge = function(vertexId) { + var x = this.getLayout().getNodeById(vertexId).x * this.getFormatter().getWidth(); + var y = this.getLayout().getNodeById(vertexId).y * this.getFormatter().getHeight(); + + /** Moving edges out **/ + for ( var i = 0; i < this.getDataset().getVertexById(vertexId).getEdgesOut().length; i++) { + var edgeId = this.getDataset().getVertexById(vertexId).getEdgesOut()[i].getId(); + var svgEdgeId = this.getSVGEdgeId(edgeId); + var edgeFormatter = this.getFormatter().getEdgeById(edgeId); + if (edgeFormatter instanceof LineEdgeGraphFormatter) { + DOM.select(svgEdgeId + "_shadow").setAttribute("x2", x); + DOM.select(svgEdgeId + "_shadow").setAttribute("y2", y); + DOM.select(svgEdgeId).setAttribute("x2", x); + DOM.select(svgEdgeId).setAttribute("y2", y); + } + + if ((edgeFormatter instanceof DirectedLineEdgeGraphFormatter) || (edgeFormatter instanceof OdirectedLineEdgeGraphFormatter) + || (edgeFormatter instanceof OdotDirectedLineEdgeGraphFormatter) || (edgeFormatter instanceof DotDirectedLineEdgeGraphFormatter) + || (edgeFormatter instanceof CutDirectedLineEdgeGraphFormatter)) { + this.removeEdge(edgeId); + this.renderEdge(edgeId); + } + } + + /** Moving edges in **/ + for ( var i = 0; i < this.getDataset().getVertexById(vertexId).getEdgesIn().length; i++) { + var edgeId = this.getDataset().getVertexById(vertexId).getEdgesIn()[i].getId(); + var svgEdgeId = this.getSVGEdgeId(edgeId); + var edgeFormatter = this.getFormatter().getEdgeById(edgeId); + if (edgeFormatter instanceof LineEdgeGraphFormatter) { + DOM.select(svgEdgeId).setAttribute("x1", x); + DOM.select(svgEdgeId).setAttribute("y1", y); + DOM.select(svgEdgeId + "_shadow").setAttribute("x1", x); + DOM.select(svgEdgeId + "_shadow").setAttribute("y1", y); + } + + if ((edgeFormatter instanceof DirectedLineEdgeGraphFormatter) || (edgeFormatter instanceof OdirectedLineEdgeGraphFormatter) + || (edgeFormatter instanceof OdotDirectedLineEdgeGraphFormatter) || (edgeFormatter instanceof DotDirectedLineEdgeGraphFormatter) + || (edgeFormatter instanceof CutDirectedLineEdgeGraphFormatter)) { + this.removeEdge(edgeId); + this.renderEdge(edgeId); + } + + if (edgeFormatter instanceof BezierEdgeGraphFormatter) { + var radius = this.getFormatter().getVertexById(vertexId).getDefault().getSize() * this.getFormatter().getNodesMaxSize(); + var d = this.calculateCoordenatesBezier(radius, x, y); + DOM.select(svgEdgeId).setAttribute("d", d); + } + } +}; + +GraphCanvas.prototype.moveNode = function(vertexId) { + var x = this.getLayout().getNodeById(vertexId).x * this.getFormatter().getWidth(); + var y = this.getLayout().getNodeById(vertexId).y * this.getFormatter().getHeight(); + var svgNodeElement = DOM.select(this.getSVGNodeId(vertexId)); + + svgNodeElement.setAttribute("dragx", x); + svgNodeElement.setAttribute("dragy", y); + svgNodeElement.setAttribute("transform", "translate(" + x + "," + y + ")"); + + if (this.getDataset().getVertexById(vertexId).getEdges().length < this.args.maxNumberEdgesMoving) { + this.moveEdge(vertexId); + } +}; + +GraphCanvas.prototype._movingNode = function(svgNodeElement, x, y) { + var vertexId = this.getVertexIdFromSVGId(svgNodeElement.getAttribute("id")); + this.getLayout().getNodeById(vertexId).setCoordinates(x / this.getFormatter().getWidth(), y / this.getFormatter().getHeight()); + this.desplazamientoX = 0; + this.desplazamientoY = 0; + this.removeLabel(vertexId); + this.renderLabel(vertexId); +}; + +/** INIT **/ +GraphCanvas.prototype.init = function() { + + this._svg = this.createSVGDom(this.targetID, this.id, this.getFormatter().getWidth(), this.getFormatter().getHeight(), this.getFormatter() + .getBackgroundColor()); + this.GraphGroup = SVG.drawGroup(this._svg, [ [ "id", this.args.idGraph ], [ "transform", "translate(0,0), scale(1)" ] ]); + this.GraphBackground = SVG.drawGroup(this.GraphGroup, [ [ "id", this.args.idBackground ] ]); + this.GraphEdgeGroup = SVG.drawGroup(this.GraphGroup, [ [ "id", this.args.idEdgesGraph ] ]); + this.GraphNodeGroup = SVG.drawGroup(this.GraphGroup, [ [ "id", this.args.idNodesGraph ] ]); + this.GraphLabelGroup = SVG.drawGroup(this.GraphGroup, [ [ "id", this.args.idLabelGraph ] ]); + + if ((this.getFormatter().getBackgroundImage() != null) && (this.getFormatter().getBackgroundImage() != "")) { + this.setBackgroundImage(this.getFormatter().getBackgroundImage()); + } + /** SVG Events listener */ + var _this = this; + this._svg.addEventListener("click", function(event) { + _this.mouseClick(event); + }, false); + this._svg.addEventListener("mousemove", function(event) { + _this.mouseMove(event, _this); + }, false); + this._svg.addEventListener("mousedown", function(event) { + _this.mouseDown(event, _this); + }, false); + this._svg.addEventListener("mouseup", function(event) { + _this.mouseUp(event, _this); + }, false); +}; + +/* + GraphCanvas.prototype.backgroungToSVG = function(){ + var _this = this; + var canvas = document.createElement('canvas'); + canvas.setAttribute("id", "canvas"); + canvas.width = this.formatter.getWidth(); + canvas.height = this.formatter.getHeight(); + + this._svg.parentNode.parentNode.appendChild(canvas); + var ctx = document.getElementById('canvas').getContext('2d'); + var img = new Image(); + + img.src = this.formatter.getBackgroundImage(); + ctx.drawImage(img,0,0 ,_this.formatter.getWidth(), _this.formatter.getHeight()); + + + img.onload = function() { + canvas.parentNode.removeChild(canvas); + } + + this.NodeSVGbackgroundImage.setAttribute("xlink:href", document.getElementById("canvas").toDataURL()); + this.NodeSVGbackgroundImage.removeAttribute("href"); + + // + + };*/ + +GraphCanvas.prototype.setBackgroundImage = function() { + if (this.NodeSVGbackgroundImage != null) { + this.NodeSVGbackgroundImage.parentNode.removeChild(this.NodeSVGbackgroundImage); + } + $('#' + this.targetID).svg(); + $('#' + this.targetID).svg("get"); + + $('#' + this.targetID).svg("get")._svg = document.getElementById(this.id); + + var svg = $('#' + this.targetID).svg("get"); + this.NodeSVGbackgroundImage = svg.image(0, 0, this.getFormatter().getWidth(), this.getFormatter().getHeight(), this.getFormatter() + .getBackgroundImage()); + this.NodeSVGbackgroundImage.setAttribute("id", this.args.idBackgroundNode); + + this.NodeSVGbackgroundImage.setAttribute("x", 0); + this.NodeSVGbackgroundImage.setAttribute("y", 0); + + this.NodeSVGbackgroundImage.setAttribute("dragx", 0); + this.NodeSVGbackgroundImage.setAttribute("dragy", 0); + + if (this.getFormatter().args.backgroundImageHeight != null) { + this.NodeSVGbackgroundImage.setAttribute("height", this.getFormatter().args.backgroundImageHeight); + } + if (this.getFormatter().args.backgroundImageWidth != null) { + this.NodeSVGbackgroundImage.setAttribute("width", this.getFormatter().args.backgroundImageWidth); + } + + if (this.getFormatter().args.backgroundImageX != null) { + this.NodeSVGbackgroundImage.setAttribute("x", this.getFormatter().args.backgroundImageX); + } + if (this.getFormatter().args.backgroundImageY != null) { + this.NodeSVGbackgroundImage.setAttribute("y", this.getFormatter().args.backgroundImageY); + } + + this.GraphBackground.appendChild(this.NodeSVGbackgroundImage); + this.NodeSVGbackgroundImage.removeAttribute("href"); + this.NodeSVGbackgroundImage.setAttribute("xlink:href", this.getFormatter().getBackgroundImage()); +}; + +GraphCanvas.prototype.removeBackgroundImage = function() { + if (this.NodeSVGbackgroundImage != null) { + this.NodeSVGbackgroundImage.parentNode.removeChild(this.NodeSVGbackgroundImage); + } +}; + +GraphCanvas.prototype._setBackgroundColor = function(color) { + var attributes = [ [ "fill", color ] ]; + SVG.drawRectangle(0, 0, this.getFormatter().getWidth(), this.getFormatter().getHeight(), this.GraphBackground, attributes); +}; + +/** Serialize **/ +GraphCanvas.prototype.toJSON = function() { + var json = {}; + json.dataset = {}; + json.formatter = {}; + json.layout = {}; + json.dataset = this.getDataset().toJSON(); + json.formatter = this.getFormatter().toJSON(); + json.layout = this.getLayout().toJSON(); + return json; +}; + +GraphCanvas.prototype.toHTML = function() { + //this.backgroungToSVG(); + var html = this._svg.parentElement.innerHTML; + + var start = html.indexOf("") + 6; + + return html.substr(start, end); +}; + +/** DRAW **/ +GraphCanvas.prototype.draw = function(graphdataset, graphformatter, graphlayout) { + this.setDataset(graphdataset); + this.setFormatter(graphformatter); + this.setLayout(graphlayout); + + var _this = this; + this.getFormatter().changed.attach(function(sender, item) { + _this.removeNode(item.getId()); + _this.renderNode(item.getId()); + if (_this.args.labeled) { + _this.removeLabel(item.getId()); + _this.renderLabel(item.getId()); + } + + }); + //TODO + this.getFormatter().edgeChanged.attach(function(sender, item) { + _this.removeEdge(item.getId()); + _this.renderEdge(item.getId()); + }); + + this.getFormatter().resized.attach(function(sender, item) { + _this.resize(_this.getFormatter().getWidth(), _this.getFormatter().getHeight()); + }); + + this.getFormatter().backgroundImageChanged.attach(function(sender, item) { + _this.setBackgroundImage(_this.getFormatter().getBackgroundImage()); + }); + + this.getFormatter().backgroundColorChanged.attach(function(sender, item) { + _this._setBackgroundColor(_this.getFormatter().getBackgroundColor()); + }); + + this.getLayout().changed.attach(function(sender, item) { + _this.moveNode(item.getId()); + _this.moveEdge(item.getId()); + if (_this.args.labeled) { + _this.removeLabel(item.getId()); + _this.renderLabel(item.getId()); + } + }); + + this.getDataset().newVertex.attach(function(sender, item) { + + _this.renderNode(item.getId()); + if (_this.args.labeled) { + _this.renderLabel(item.getId()); + } + }); + + this.getDataset().newEdge.attach(function(sender, item) { + _this.renderEdge(item.getId()); + }); + + this.getDataset().vertexDeleted.attach(function(sender, item) { + _this.removeNode(item.getId()); + if (_this.args.labeled) { + _this.removeLabel(item.getId()); + } + }); + + this.getDataset().edgeDeleted.attach(function(sender, item) { + _this.removeEdge(item.getId()); + }); + + this.getDataset().vertexNameChanged.attach(function(sender, args) { + if (_this.args.labeled) { + _this.removeLabel(args.item.getId()); + _this.removeLabel(args.item.getId()); + _this.renderLabel(args.item.getId()); + } + }); + this.init(); + this.render(); +}; + +GraphCanvas.prototype.render = function() { + for ( var id in this.getDataset().getVertices()) { + this.renderNode(id); + } + this.renderLabels(); + this.renderEdges(); +}; + +GraphCanvas.prototype.renderLabels = function() { + if (this.args.labeled) { + for ( var id in this.getDataset().getVertices()) { + this.renderLabel(id); + } + } +}; + +GraphCanvas.prototype.removeLabels = function() { + for ( var id in this.getDataset().getVertices()) { + this.removeLabel(id); + } +}; + +/** Utilities method for nodes **/ +GraphCanvas.prototype.isNodeCanvas = function(node) { + return ((node.id == this.args.idGraph) || (node.id == this.id)); +}; + +GraphCanvas.prototype.isNodeBackground = function(node) { + return ((node.id == this.args.idBackgroundNode)); +}; + +GraphCanvas.prototype.isVertex = function(node) { + if (node.getAttribute("id") != null) { + if (node.getAttribute("id").indexOf("_v_") != -1) { + return true; + } + } + return false; +}; + +GraphCanvas.prototype.isLabel = function(node) { + if (node.getAttribute("id") != null) { + if (node.getAttribute("id").indexOf("_l_") != -1) { + return true; + } + } + return false; +}; + +GraphCanvas.prototype.isEdge = function(node) { + if (node.getAttribute("id") != null) { + if (node.getAttribute("id").indexOf("_e_") != -1) { + return true; + } + } + return false; +}; + +/** Resize **/ +GraphCanvas.prototype.resize = function(width, height) { + // this._svg.setAttribute("width", width); + // this._svg.setAttribute("height", height); + if (this.NodeSVGbackgroundImage != null) { + this.NodeSVGbackgroundImage.setAttribute("width", width); + this.NodeSVGbackgroundImage.setAttribute("height", height); + } + + this._svg.setAttribute("width", width); + this._svg.setAttribute("height", height); + + this.clearCanvas(); + this.render(); +}; + +GraphCanvas.prototype.clearCanvas = function() { + DOM.removeChilds(this.GraphEdgeGroup.getAttribute("id")); + DOM.removeChilds(this.GraphNodeGroup.getAttribute("id")); + this.clearLabels(); +}; + +GraphCanvas.prototype.clearLabels = function() { + DOM.removeChilds(this.GraphLabelGroup.getAttribute("id")); +}; + +/** ID'S converter **/ +GraphCanvas.prototype.getSVGNodeId = function(nodeId) { + return this.id + "_v_" + nodeId; +}; + +GraphCanvas.prototype.getSVGEdgeId = function(edgeId) { + return this.id + "_e_" + edgeId; +}; + +GraphCanvas.prototype.getSVGArrowEdgeId = function(edgeId) { + return this.id + "_arrow_" + edgeId; +}; + +GraphCanvas.prototype.getSVGLabelId = function(edgeId) { + return this.id + "_l_" + edgeId; +}; + +GraphCanvas.prototype.blinkVertexById = function(vertexId) { + $("#" + this.getSVGNodeId(vertexId)).fadeIn().fadeOut().fadeIn().fadeOut().fadeIn().fadeOut(); +}; + +GraphCanvas.prototype.getVertexIdFromSVGId = function(svgVertexId) { + return svgVertexId.replace(this.id, "").replace("_v_", ""); +}; + +GraphCanvas.prototype.getEdgeIdFromSVGId = function(svgEdgeId) { + return svgEdgeId.replace(this.id, "").replace("_e_", ""); +}; + +/** VERTEX **/ +GraphCanvas.prototype.getVertexById = function(id) { + return document.getElementById(this.getSVGNodeId(id)); +}; + +GraphCanvas.prototype.renderNodes = function() { + for ( var id in this.getDataset().getVertices()) { + this.renderNode(id); + } +}; + +GraphCanvas.prototype.overNode = function(nodeId) { + if (!this.args.interactive) { + return; + } + /** If selected we don't change the format **/ + if (this.args.isVertexSelected[nodeId] == null) { + var args = this.getFormatter().getVertexById(nodeId).getOver(); + args.args["cursor"] = 'pointer'; + this.changeVertexFormat(nodeId, args); + } +}; + +GraphCanvas.prototype.outNode = function(nodeId) { + if (!this.args.interactive) { + return; + } + + /** If selected we don't change the format **/ + if (this.args.isVertexSelected[nodeId] == null) { + this.changeVertexFormat(nodeId, this.getFormatter().getVertexById(nodeId).getDefault()); + } +}; + +GraphCanvas.prototype.overLabel = function(nodeId) { + this.overNode(nodeId); + // this.svgLabels[nodeId].setAttribute("cursor", "pointer"); +}; + +GraphCanvas.prototype.outLabel = function(nodeId) { + this.outNode(nodeId); + // this.svgLabels[nodeId].setAttribute("cursor", ""); +}; + +GraphCanvas.prototype.clickNode = function(nodeId) { + if (!this.args.interactive) { + return; + } + + /** si el evento se dispara oprque estaba dragging entonces no activo nada **/ + if (this.args.isVertexSelected[nodeId] == null) { + this.selectNode(nodeId); + } else { + this.deselectNode(nodeId); + } +}; + +GraphCanvas.prototype.selectNode = function(nodeId) { + for ( var i = 0; i < this.args.selectedVertices.length; i++) { + var format = this.getFormatter().getVertexById(nodeId).getSelected(); + format.opacity = 0.2; + this.changeVertexFormat(nodeId, this.getFormatter().getVertexById(nodeId).getSelected()); + } + + if (this.args.isVertexSelected[nodeId] == null) { + var format = this.getFormatter().getVertexById(nodeId).getSelected(); + format.opacity = 1; + this.changeVertexFormat(nodeId, this.getFormatter().getVertexById(nodeId).getSelected()); + this.args.selectedVertices.push(nodeId); + this.args.isVertexSelected[nodeId] = this.args.selectedVertices.length - 1; + this.onVertexSelect.notify(nodeId); + } +}; + +GraphCanvas.prototype.selectAllEdges = function() { + this.deselectNodes(); + this.deselectEdges(); + + for ( var edgesId in this.getDataset().edges) { + this.selectEdge(edgesId); + } +}; + +GraphCanvas.prototype.selectAllNodes = function() { + this.deselectNodes(); + this.deselectEdges(); + + for ( var vertexId in this.getDataset().vertices) { + this.selectNode(vertexId); + } +}; + +GraphCanvas.prototype.selectAll = function() { + this.deselectNodes(); + this.deselectEdges(); + + for ( var vertexId in this.getDataset().vertices) { + this.selectNode(vertexId); + } + + for ( var edgesId in this.getDataset().edges) { + this.selectEdge(edgesId); + } +}; + +GraphCanvas.prototype.selectEdge = function(edgeId) { + if (this.args.isEdgeSelected[edgeId] == null) { + this.changeEdgeFormat(edgeId, this.getFormatter().getEdgeById(edgeId).getSelected()); + //this.args.selectedEdges.push(edgeId); + this.args.isEdgeSelected[edgeId] = true; //this.args.selectedEdges.length - 1; + this.onEdgeSelect.notify(edgeId); + } +}; + +GraphCanvas.prototype.selectEdges = function(edges) { + + for ( var i = 0; i < edges.length; i++) { + this.selectEdge(edges[i]); + } +}; + +GraphCanvas.prototype.deselectNode = function(nodeId) { + if (this.args.isVertexSelected[nodeId] != null) { + this.changeVertexFormat(nodeId, this.getFormatter().getVertexById(nodeId).getDefault()); + this.args.selectedVertices.splice(this.args.isVertexSelected[nodeId], 1); + var index = this.args.isVertexSelected[nodeId]; + delete this.args.isVertexSelected[nodeId]; + + for ( var vertex in this.args.isVertexSelected) { + if (this.args.isVertexSelected[vertex] > index) { + this.args.isVertexSelected[vertex] = this.args.isVertexSelected[vertex] - 1; + } + } + } +}; + +GraphCanvas.prototype.deselectNodes = function() { + var selected = JSON.parse(JSON.stringify(this.getSelectedVertices())); + for ( var i = 0; i < selected.length; i++) { + this.deselectNode(selected[i]); + } +}; +GraphCanvas.prototype.selectNodes = function(idNodes) { + + for ( var i = 0; i < idNodes.length; i++) { + this.selectNode(idNodes[i]); + } + + // for ( var vertex in this.args.isVertexSelected) { + // if (this.args.isVertexSelected[vertex] > index){ + // this.args.isVertexSelected[vertex] = this.args.isVertexSelected[vertex] - 1; + // } + // } + +}; + +GraphCanvas.prototype.changeVertexFormat = function(nodeId, format) { + var svgNode = DOM.select(this.getSVGNodeId(nodeId)); + if (svgNode != null) { + var properties = format.toJSON(); + for ( var item in properties) { + svgNode.setAttribute(item, properties[item]); + } + + if (this.getFormatter().getVertexById(nodeId) instanceof CircleVertexGraphFormatter) { + var transform = "translate(" + svgNode.getAttribute("dragx") + "," + svgNode.getAttribute("dragy") + "), scale(" + format.getSize() + ")"; + svgNode.setAttribute("transform", transform); + } + } +}; + +GraphCanvas.prototype.renderLabel = function(nodeId) { + var x = Math.ceil(this.getLayout().getNodeById(nodeId).x * this.getFormatter().getWidth()); + var y = Math.ceil(this.getLayout().getNodeById(nodeId).y * this.getFormatter().getHeight()); + + var svgAttributesNode = JSON.parse(JSON.stringify(this.getFormatter().getVertexById(nodeId).getDefault().toJSON().title)); + svgAttributesNode.id = this.getSVGLabelId(this.getDataset().getVertexById(nodeId).getId()); + svgAttributesNode.dx = (-1) * (this.getDataset().getVertexById(nodeId).getName().length * svgAttributesNode["font-size"]) / 4 - 4; + + svgAttributesNode.dy = parseFloat((this.getFormatter().getVertexById(nodeId).getDefault().getSize())) + + parseFloat(svgAttributesNode["font-size"]) + parseFloat(this.getFormatter().getVertexById(nodeId).getDefault().getStrokeWidth()) - 4; + svgAttributesNode.dragx = Math.ceil(this.getLayout().getNodeById(nodeId).x * this.getFormatter().getWidth()); + + var gragy = parseFloat(this.getFormatter().getVertexById(nodeId).getDefault().getSize()) + + Math.ceil(this.getLayout().getNodeById(nodeId).y * this.getFormatter().getHeight()); + svgAttributesNode.dragy = gragy; + svgAttributesNode.transform = "translate(" + svgAttributesNode.dragx + "," + svgAttributesNode.dragy + ")";//, scale("+this.formatter.getVertexById(nodeId).getDefault().getSize()+")"; + + var nodeSVG = SVG.drawText(0, 0, this.getDataset().getVertexById(nodeId).getName(), this.GraphLabelGroup, svgAttributesNode); + + this.svgLabels[nodeId] = nodeSVG; + + /** Events for the SVG node **/ + var _this = this; + if (nodeSVG != null) { + nodeSVG.addEventListener("mouseover", function() { + _this.overLabel(nodeId); + }, false); + nodeSVG.addEventListener("mouseout", function() { + _this.outLabel(nodeId); + }, false); + } + +}; + +GraphCanvas.prototype.removeLabel = function(labelId) { + if (DOM.select(this.getSVGLabelId(labelId)) != null) { + DOM.select(this.getSVGLabelId(labelId)).parentNode.removeChild(DOM.select(this.getSVGLabelId(labelId))); + } +}; + +GraphCanvas.prototype.renderNode = function(nodeId) { + var svgAttributesNode = JSON.parse(JSON.stringify(this.getFormatter().getVertexById(nodeId).getDefault().toJSON())); + svgAttributesNode.dragx = Math.ceil(this.getLayout().getNodeById(nodeId).x * this.getFormatter().getWidth()); + svgAttributesNode.dragy = Math.ceil(this.getLayout().getNodeById(nodeId).y * this.getFormatter().getHeight()); + svgAttributesNode.transform = "translate(" + svgAttributesNode.dragx + "," + svgAttributesNode.dragy + ")"; + svgAttributesNode.id = this.getSVGNodeId(nodeId); + /*svgAttributesNode["stroke-width"] = 3 ; + svgAttributesNode["stroke-opacity"] = 1 ; + svgAttributesNode["fill-opacity"] = svgAttributesNode["opacity"] ; + svgAttributesNode["opacity"] = 1 ;*/ + this.circleDefaultRadius = this.getFormatter().getVertexById(nodeId).getDefault().getSize(); + var nodeSVG; + + if (this.getFormatter().getVertexById(nodeId) instanceof CircleVertexGraphFormatter) { + nodeSVG = SVG.drawCircle(0, 0, this.circleDefaultRadius, this.GraphNodeGroup, svgAttributesNode); + } + + if (this.getFormatter().getVertexById(nodeId) instanceof SquareVertexGraphFormatter) { + //nodeSVG = SVG.drawRectangle(0 - (this.circleDefaultRadius) ,0 - (this.formatter.getVertexById(nodeId).getDefault().getSize()) , (this.getFormatter().getVertexById(nodeId).getDefault().getSize()*2), (this.getFormatter().getVertexById(nodeId).getDefault().getSize()*2), this.GraphNodeGroup, svgAttributesNode); + nodeSVG = SVG.drawRectangle(0 - (this.circleDefaultRadius), 0 - (this.circleDefaultRadius), (this.circleDefaultRadius * 2), + (this.circleDefaultRadius * 2), this.GraphNodeGroup, svgAttributesNode); + } + + if (this.getFormatter().getVertexById(nodeId) instanceof EllipseVertexGraphFormatter) { + nodeSVG = SVG.drawEllipse(0, 0, this.circleDefaultRadius * 1.5, this.circleDefaultRadius, this.GraphNodeGroup, svgAttributesNode); + } + + if (this.getFormatter().getVertexById(nodeId) instanceof RectangleVertexGraphFormatter) { + //nodeSVG = SVG.drawRectangle(0 - (this.circleDefaultRadius) ,0 - ((this.circleDefaultRadius*2)/2) , (this.circleDefaultRadius*2), (this.circleDefaultRadius), this.GraphNodeGroup, svgAttributesNode); + nodeSVG = SVG.drawRectangle(0 - (this.circleDefaultRadius * 1.5), 0 - (this.circleDefaultRadius), (this.circleDefaultRadius * 2 * 1.5), + (this.circleDefaultRadius * 2), this.GraphNodeGroup, svgAttributesNode); + + } + + if (this.getFormatter().getVertexById(nodeId) instanceof RoundedVertexGraphFormatter) { + svgAttributesNode.ry = 2;// this.formatter.getVertexById(nodeId).getDefault().getSize()/4; + svgAttributesNode.rx = 2;// this.formatter.getVertexById(nodeId).getDefault().getSize()/4; + nodeSVG = SVG.drawRectangle(0 - (this.circleDefaultRadius * 1.5), 0 - (this.circleDefaultRadius), (this.circleDefaultRadius * 2 * 1.5), + (this.circleDefaultRadius * 2), this.GraphNodeGroup, svgAttributesNode); + } + + // + + if (this.getFormatter().getVertexById(nodeId) instanceof OctagonVertexGraphFormatter) { + svgAttributesNode.ry = 2; + svgAttributesNode.rx = 2; + nodeSVG = SVG.drawRectangle(0 - (this.circleDefaultRadius * 1.5), 0 - (this.circleDefaultRadius), (this.circleDefaultRadius * 2 * 1.5), + (this.circleDefaultRadius * 2), this.GraphNodeGroup, svgAttributesNode); + } + + nodeSVG.internalId = nodeId; + // + var _this = this; + + /** Events for the SVG node **/ + if (nodeSVG != null) { + nodeSVG.addEventListener("mouseover", function() { + _this.onVertexOver.notify(nodeId); + _this.overNode(nodeId); + }, false); + nodeSVG.addEventListener("mouseout", function() { + _this.onVertexOut.notify(nodeId); + _this.outNode(nodeId); + }, false); + //nodeSVG.addEventListener("click", function(){_this.clickNode(nodeId);}, false); + // + nodeSVG.addEventListener("mouseup", function() { + _this.onVertexUp.notify(nodeId); + }, false); + } +}; + +GraphCanvas.prototype.removeNode = function(nodeId) { + DOM.select(this.getSVGNodeId(nodeId)).parentNode.removeChild(DOM.select(this.getSVGNodeId(nodeId))); + if (this.args.labeled) { + this.removeLabel(nodeId); + } +}; + +/** REMOVING **/ +GraphCanvas.prototype.removeSelected = function() { + /** El orden importa **/ + this.removeSelectedEdges(); + this.removeSelectedNode(); + +}; + +GraphCanvas.prototype.removeSelectedNode = function() { + var selected = JSON.parse(JSON.stringify(this.getSelectedVertices())); + this.deselectNodes(); + var sorted = selected.sort(function(a, b) { + return a - b + }); + for ( var i = 0; i < sorted.length; i++) { + if (this.getDataset().getVertexById(sorted[i]) != null) { + this.getDataset().getVertexById(sorted[i]).remove(); + } + } +}; + +/** EDGES **/ +GraphCanvas.prototype.removeEdge = function(edgeId) { + if (DOM.select(this.getSVGEdgeId(edgeId)) != null) { + DOM.select(this.getSVGEdgeId(edgeId)).parentNode.removeChild(DOM.select(this.getSVGEdgeId(edgeId))); + } + + if (DOM.select(this.getSVGEdgeId(edgeId) + "_shadow") != null) { + DOM.select(this.getSVGEdgeId(edgeId) + "_shadow").parentNode.removeChild(DOM.select(this.getSVGEdgeId(edgeId) + "_shadow")); + } + + if (DOM.select(this.getSVGArrowEdgeId(edgeId)) != null) { + DOM.select(this.getSVGArrowEdgeId(edgeId)).parentNode.removeChild(DOM.select(this.getSVGArrowEdgeId(edgeId))); + } +}; + +GraphCanvas.prototype.overEdge = function(edgeId) { + if ((!this.args.interactive) || this.dragging || this.selecting) { + return; + } + + /** If selected we don't change the format **/ + if (this.args.isEdgeSelected[edgeId] == null) { + var format = this.getFormatter().getEdgeById(edgeId).getOver(); + format.args["cursor"] = "pointer"; + this.changeEdgeFormat(edgeId, format); + } +}; + +GraphCanvas.prototype.outEdge = function(edgeId) { + if (!this.args.interactive) { + return; + } + + /** If selected we don't change the format **/ + if (this.args.isEdgeSelected[edgeId] == null) { + this.changeEdgeFormat(edgeId, this.getFormatter().getEdgeById(edgeId).getDefault()); + } +}; + +GraphCanvas.prototype.changeEdgeFormat = function(edgeId, format) { + var svgEdge = DOM.select(this.getSVGEdgeId(edgeId) + "_shadow"); + if (svgEdge != null) { + var properties = format.toJSON(); + for ( var item in properties) { + svgEdge.setAttribute(item, properties[item]); + } + } +}; + +GraphCanvas.prototype.deselectEdge = function(edgeID) { + if (this.args.isEdgeSelected[edgeID] != null) { + this.changeEdgeFormat(edgeID, this.getFormatter().getEdgeById(edgeID).getDefault()); + var index = this.args.isEdgeSelected[edgeID]; + delete this.args.isEdgeSelected[edgeID]; + } +}; + +GraphCanvas.prototype.deselectEdges = function() { + var selected = JSON.parse(JSON.stringify(this.getSelectedEdges())); + for ( var i = 0; i < selected.length; i++) { + this.deselectEdge(selected[i]); + } +}; + +GraphCanvas.prototype.removeSelectedEdges = function() { + var selected = JSON.parse(JSON.stringify(this.getSelectedEdges())); + this.deselectEdges(); + for ( var i = 0; i < selected.length; i++) { + if (this.getDataset().getEdgeById(selected[i]) != null) { + this.getDataset().getEdgeById(selected[i]).remove(); + } + } +}; + +GraphCanvas.prototype.renderEdge = function(edgeId) { + var svgAttributesEdge = this.getFormatter().getEdgeById(edgeId).getDefault().toJSON(); + var edge = this.getDataset().getEdgeById(edgeId); + + var svgNodeTarget = this.getVertexById(edge.getNodeTarget().getId()); + var svgNodeSource = this.getVertexById(edge.getNodeSource().getId()); + svgAttributesEdge.id = this.getSVGEdgeId(edge.getId()) + "_shadow"; + + var svgEdge = null; + + if (this.getFormatter().getEdgeById(edgeId) instanceof LineEdgeGraphFormatter) { + var coordenateSourceX = svgNodeSource.getAttribute("dragx"); + var coordenateSourceY = svgNodeSource.getAttribute("dragy"); + var coordenateTargetX = svgNodeTarget.getAttribute("dragx"); + var coordenateTargetY = svgNodeTarget.getAttribute("dragy"); + + SVG.drawLine(coordenateSourceX, coordenateSourceY, coordenateTargetX, coordenateTargetY, this.GraphEdgeGroup, svgAttributesEdge); + var attributesShadow = {}; + attributesShadow.id = this.getSVGEdgeId(edge.getId()); + attributesShadow["stroke-opacity"] = 0; + attributesShadow["stroke-width"] = 4; + attributesShadow["stroke"] = "black"; + svgEdge = SVG.drawLine(svgNodeSource.getAttribute("dragx"), svgNodeSource.getAttribute("dragy"), svgNodeTarget.getAttribute("dragx"), + svgNodeTarget.getAttribute("dragy"), this.GraphEdgeGroup, attributesShadow); + } + + if (this.getFormatter().getEdgeById(edgeId) instanceof BezierEdgeGraphFormatter) { + var nodeId = edge.getNodeTarget().getId(); + var nodeSize = this.formatter.getVertexById(nodeId).getDefault().getSize() * this.getFormatter().getNodesMaxSize(); + svgAttributesEdge.fill = "none"; + svgAttributesEdge.id = this.getSVGEdgeId(edgeId); + var d = this.calculateCoordenatesBezier(nodeSize, svgNodeSource.getAttribute("dragx"), svgNodeSource.getAttribute("dragy")); + svgEdge = SVG.drawPath(d, this.GraphEdgeGroup, svgAttributesEdge); + } + ; + + if ((this.getFormatter().getEdgeById(edgeId) instanceof DirectedLineEdgeGraphFormatter) + || (this.getFormatter().getEdgeById(edgeId) instanceof CutDirectedLineEdgeGraphFormatter) + || (this.getFormatter().getEdgeById(edgeId) instanceof DotDirectedLineEdgeGraphFormatter) + || (this.getFormatter().getEdgeById(edgeId) instanceof OdotDirectedLineEdgeGraphFormatter) + || (this.getFormatter().getEdgeById(edgeId) instanceof OdirectedLineEdgeGraphFormatter)) { + var coordenateSourceX = svgNodeSource.getAttribute("dragx"); + var coordenateSourceY = svgNodeSource.getAttribute("dragy"); + var coordenateTargetX = svgNodeTarget.getAttribute("dragx"); + var coordenateTargetY = svgNodeTarget.getAttribute("dragy"); + + var offset = parseFloat(this.getFormatter().getVertexById(this.getDataset().getEdgeById(edgeId).getNodeTarget().getId()).getDefault() + .getSize() + * this.circleDefaultRadius); + var point = this._calculateEdgePointerPosition(coordenateSourceX, coordenateSourceY, coordenateTargetX, coordenateTargetY, offset); + coordenateTargetX = point.x; + coordenateTargetY = point.y; + SVG.drawLine(coordenateSourceX, coordenateSourceY, coordenateTargetX, coordenateTargetY, this.GraphEdgeGroup, svgAttributesEdge); + + var attributesShadow = {}; + attributesShadow.id = this.getSVGEdgeId(edge.getId()); + attributesShadow["stroke-opacity"] = 0; + attributesShadow["stroke-width"] = 4; + attributesShadow["stroke"] = "black"; + svgEdge = SVG.drawLine(coordenateSourceX, coordenateSourceY, coordenateTargetX, coordenateTargetY, this.GraphEdgeGroup, attributesShadow); + } + + if (this.getFormatter().getEdgeById(edgeId) instanceof DirectedLineEdgeGraphFormatter + || (this.getFormatter().getEdgeById(edgeId) instanceof OdirectedLineEdgeGraphFormatter)) { + var coordenateSourceX = svgNodeSource.getAttribute("dragx"); + var coordenateSourceY = svgNodeSource.getAttribute("dragy"); + var coordenateTargetX = svgNodeTarget.getAttribute("dragx"); + var coordenateTargetY = svgNodeTarget.getAttribute("dragy"); + + var point = this._calculateEdgePointerPosition(coordenateSourceX, coordenateSourceY, coordenateTargetX, coordenateTargetY, offset); + coordenateTargetX = point.x; + coordenateTargetY = point.y; + + var angle = Geometry.toDegree(point.angle) + 90; + this.arrowDefaultSize = this.getFormatter().getEdgeById(edgeId).getArrowSize(); //getDefault().getArrowSize(); + var d = "-" + this.arrowDefaultSize + ",0 0,-" + parseFloat(this.arrowDefaultSize) * 2 + " " + this.arrowDefaultSize + ",0"; + + var attributes; + + if (this.getFormatter().getEdgeById(edgeId) instanceof DirectedLineEdgeGraphFormatter) { + attributes = [ + [ "fill", this.getFormatter().getEdgeById(edgeId).getDefault().getStroke() ], + [ "stroke", this.getFormatter().getEdgeById(edgeId).getDefault().getStroke() ], [ "id", this.getSVGArrowEdgeId(edgeId) ] ]; + } else { + attributes = [ + [ "fill", "#FFFFFF" ], [ "stroke", this.getFormatter().getEdgeById(edgeId).getDefault().getStroke() ], + [ "id", this.getSVGArrowEdgeId(edgeId) ] ]; + } + + var flechaSVGNode = SVG.drawPoligon(d, this.GraphEdgeGroup, attributes);//, ["transform", "rotate("+angle+"), translate(0,0)"]]); + flechaSVGNode.setAttribute("transform", " translate(" + coordenateTargetX + ", " + coordenateTargetY + "), rotate(" + angle + ")"); + } + ; + + if (this.getFormatter().getEdgeById(edgeId) instanceof CutDirectedLineEdgeGraphFormatter) { + var coordenateSourceX = svgNodeSource.getAttribute("dragx"); + var coordenateSourceY = svgNodeSource.getAttribute("dragy"); + var coordenateTargetX = svgNodeTarget.getAttribute("dragx"); + var coordenateTargetY = svgNodeTarget.getAttribute("dragy"); + + var point = this._calculateEdgePointerPosition(coordenateSourceX, coordenateSourceY, coordenateTargetX, coordenateTargetY, offset); + coordenateTargetX = point.x; + coordenateTargetY = point.y; + + var angle = Geometry.toDegree(point.angle) + 90; + + //this.arrowDefaultSize = 2; //getDefault().getArrowSize(); + var d = "-4,0 4,0 4,-2 -4,-2"; + + var flechaSVGNode = SVG.drawPoligon(d, this.GraphEdgeGroup, [ + [ "fill", this.getFormatter().getEdgeById(edgeId).getDefault().getStroke() ], + [ "stroke", this.getFormatter().getEdgeById(edgeId).getDefault().getStroke() ], [ "id", this.getSVGArrowEdgeId(edgeId) ] ]);//, ["transform", "rotate("+angle+"), translate(0,0)"]]); + flechaSVGNode.setAttribute("transform", " translate(" + coordenateTargetX + ", " + coordenateTargetY + "), rotate(" + angle + ")"); + } + ; + + if ((this.getFormatter().getEdgeById(edgeId) instanceof DotDirectedLineEdgeGraphFormatter) + || (this.getFormatter().getEdgeById(edgeId) instanceof OdotDirectedLineEdgeGraphFormatter)) { + var coordenateSourceX = svgNodeSource.getAttribute("dragx"); + var coordenateSourceY = svgNodeSource.getAttribute("dragy"); + var coordenateTargetX = svgNodeTarget.getAttribute("dragx"); + var coordenateTargetY = svgNodeTarget.getAttribute("dragy"); + var point = this._calculateEdgePointerPosition(coordenateSourceX, coordenateSourceY, coordenateTargetX, coordenateTargetY, offset); + coordenateTargetX = point.x; + coordenateTargetY = point.y; + var angle = Geometry.toDegree(point.angle) + 90; + // this.arrowDefaultSize = this.formatter.getEdgeById(edgeId).getArrowSize(); //getDefault().getArrowSize(); + var attributes = []; + if (this.getFormatter().getEdgeById(edgeId) instanceof OdotDirectedLineEdgeGraphFormatter) { + attributes = [ + [ "fill", "#FFFFFF" ], [ "stroke", this.getFormatter().getEdgeById(edgeId).getDefault().getStroke() ], + [ "id", this.getSVGArrowEdgeId(edgeId) ] ]; + } else { + attributes = [ + [ "fill", this.getFormatter().getEdgeById(edgeId).getDefault().getStroke() ], + [ "stroke", this.getFormatter().getEdgeById(edgeId).getDefault().getStroke() ], [ "id", this.getSVGArrowEdgeId(edgeId) ] ]; + } + var flechaSVGNode = SVG.drawCircle(0, 0, 4, this.GraphEdgeGroup, attributes); + flechaSVGNode.setAttribute("transform", " translate(" + coordenateTargetX + ", " + coordenateTargetY + "), rotate(" + angle + ")"); + } + ; + + var _this = this; + /** Events for the SVG edge **/ + if (svgEdge != null) { + if (this.getDataset().getEdgesCount() < this.args.maxNumberEdgesFiringEvents) { + svgEdge.addEventListener("mouseover", function() { + _this.overEdge(edgeId); + }, false); + svgEdge.addEventListener("mouseout", function() { + _this.outEdge(edgeId); + }, false); + } + } +}; + +GraphCanvas.prototype._calculateEdgePointerPosition = function(sourceX, sourceY, targetX, targetY, radius) { + var angle = Geometry.getAngleBetweenTwoPoints(sourceX, sourceY, targetX, targetY); + + /** Suponiendo el node source que este a la derecha **/ + if ((targetX - sourceX) < 0) { + var b = Geometry.getAdjacentSideOfRectangleRight(angle, radius); + targetX = parseFloat(targetX) + parseFloat(b); + arrowX = parseFloat(targetX) + parseFloat(b) + this.arrowDefaultSize / 2; + } else { + var b = Geometry.getAdjacentSideOfRectangleRight(angle, radius); + targetX = parseFloat(targetX) - parseFloat(b); + arrowX = parseFloat(targetX) - parseFloat(b) - this.arrowDefaultSize / 2; + } + + /** Suponiendo el node source que este a la arriba **/ + if ((targetY - sourceY) > 0) { + var a = Geometry.getOppositeSideOfRectangleRight(angle, radius); + targetY = parseFloat(targetY) - parseFloat(a); + arrowY = parseFloat(targetY) - parseFloat(a) - this.arrowDefaultSize / 2; + } else { + var a = Geometry.getOppositeSideOfRectangleRight(angle, radius); + targetY = parseFloat(targetY) + parseFloat(a); + arrowY = parseFloat(targetY) + parseFloat(a) - this.arrowDefaultSize / 2; + + } + + return { + "x" : arrowX, + "y" : arrowY, + "angle" : angle + }; +}; + +GraphCanvas.prototype.calculateCoordenatesBezier = function(nodeSize, x1, y1) { + var x11 = x1 - (nodeSize / 2); + var y11 = y1 - (nodeSize / 2); + + var x12 = parseFloat(x1) + parseFloat(nodeSize / 2); + var y12 = y1 - (nodeSize / 2); + + var curvePointX = (x12 - x11) / 2 + x11; + var curvePointY = y1 - (nodeSize * 2); + var d = "M" + x11 + "," + y11 + " T" + curvePointX + "," + curvePointY + " " + x12 + "," + y12; + return d; + +}; + +GraphCanvas.prototype.renderEdges = function() { + for ( var edge in this.getDataset().getEdges()) { + this.renderEdge(this.getDataset().getEdgeById(edge).getId()); + + } +}; + +GraphCanvas.prototype.getLastSelectedNode = function() { + var node = null; + if (this.getSelectedVertices().length > 0) { + var nodeId = this.getSelectedVertices()[this.getSelectedVertices().length - 1]; + node = this.getDataset().getVertexById(nodeId); + } + return node; +}; +/* + GraphCanvas.prototype.getNodeByNameAndIndex = function(node, index){ + var nodeId = this.getDataset().verticesIndex[node][index]; + var nodeItem = this.getDataset().getVertexById(nodeId); + return nodeItem; + }; + */ + +GraphCanvas.prototype.setDataset = function(dataset) { + this.dataset = dataset; +}; + +GraphCanvas.prototype.setFormatter = function(formatter) { + this.formatter = formatter; +}; + +GraphCanvas.prototype.setLayout = function(layout) { + this.layout = layout; +}; + +/** API **/ +GraphCanvas.prototype.getDataset = function() { + return this.dataset; +}; + +GraphCanvas.prototype.getFormatter = function() { + return this.formatter; +}; + +GraphCanvas.prototype.getLayout = function() { + return this.layout; +}; + +/** API DATASET **/ +GraphCanvas.prototype.addVertex = function(name, args) { + this.getDataset().addNode(name, args); +}; + +GraphCanvas.prototype.removeVertex = function(vertexId) { + this.getDataset().getVertexById(vertexId).remove(); +}; + +GraphCanvas.prototype.addEdge = function(edgeName, nodeSourceId, nodeTargetId, args) { + this.getDataset().addEdge(edgeName, nodeSourceId, nodeTargetId, args); +}; +/* + GraphCanvas.prototype.removeEdge = function(edgeId){ + this.getDataset().getEdgeById(edgeId).remove(); + }; + */ + +/** API FORMATTER **/ +GraphCanvas.prototype.getWidth = function() { + return this.getFormatter().getWidth(); +}; + +GraphCanvas.prototype.getHeight = function() { + return this.getFormatter().getHeight(); +}; + +GraphCanvas.prototype.getBackgroundImage = function() { + return this.getFormatter().getBackgroundImage(); +}; + +//GraphCanvas.prototype.setBackgroundImage = function(value){ +// this.getFormatter().setBackgroundImage(value); +//}; + +GraphCanvas.prototype.getBackgroundColor = function() { + return this.getFormatter().getBackgroundColor(); +}; + +GraphCanvas.prototype.setBackgroundColor = function() { + this.getFormatter().setBackgroundColor(value); +}; + +//GraphCanvas.prototype.setEdgeFill = function(edgeId, value){ +// this.getFormatter().getEdgeById(edgeId).getDefault().setFill(value); +//}; +// +//GraphCanvas.prototype.getEdgeFill = function(edgeId){ +// return this.getFormatter().getEdgeById(edgeId).getDefault().getFill(); +//}; + +/** VERTICES FORMATTER **/ +GraphCanvas.prototype.setVertexSize = function(vertexId, value) { + this.getFormatter().getVertexById(vertexId).getDefault().setSize(value); +}; + +GraphCanvas.prototype.getVertexSize = function(vertexId) { + return this.getFormatter().getVertexById(vertexId).getDefault().getSize(); +}; + +GraphCanvas.prototype.setVertexStroke = function(vertexId, value) { + this.getFormatter().getVertexById(vertexId).getDefault().setStroke(value); +}; + +GraphCanvas.prototype.getVertexStroke = function(vertexId) { + return this.getFormatter().getVertexById(vertexId).getDefault().getStroke(); +}; + +GraphCanvas.prototype.setVertexStrokeOpacity = function(vertexId, value) { + this.getFormatter().getVertexById(vertexId).getDefault().setStrokeOpacity(value); +}; + +GraphCanvas.prototype.getVertexStrokeOpacity = function(vertexId) { + return this.getFormatter().getVertexById(vertexId).getDefault().getStrokeOpacity(); +}; + +GraphCanvas.prototype.setVertexOpacity = function(vertexId, value) { + this.getFormatter().getVertexById(vertexId).getDefault().setOpacity(value); +}; + +GraphCanvas.prototype.getVertexOpacity = function(vertexId) { + return this.getFormatter().getVertexById(vertexId).getDefault().getOpacity(); +}; + +GraphCanvas.prototype.setVertexFill = function(vertexId, color) { + this.getFormatter().getVertexById(vertexId).getDefault().setFill(color); +}; + +GraphCanvas.prototype.getVertexFill = function(vertexId) { + return this.getFormatter().getVertexById(vertexId).getDefault().getFill(); +}; + +/** EDGES FORMATTER **/ +GraphCanvas.prototype.setEdgeSize = function(edgeId, value) { + this.getFormatter().getEdgeById(edgeId).getDefault().setSize(value); +}; + +GraphCanvas.prototype.getEdgeSize = function(edgeId) { + return this.getFormatter().getEdgeById(edgeId).getDefault().getSize(); +}; + +GraphCanvas.prototype.setEdgeStroke = function(edgeId, value) { + this.getFormatter().getEdgeById(edgeId).getDefault().setStroke(value); +}; + +GraphCanvas.prototype.getEdgeStroke = function(edgeId) { + return this.getFormatter().getEdgeById(edgeId).getDefault().getStroke(); +}; + +GraphCanvas.prototype.setEdgeStrokeOpacity = function(edgeId, value) { + this.getFormatter().getEdgeById(edgeId).getDefault().setStrokeOpacity(value); +}; + +GraphCanvas.prototype.getEdgeStrokeOpacity = function(edgeId) { + return this.getFormatter().getEdgeById(edgeId).getDefault().getStrokeOpacity(); +}; + +GraphCanvas.prototype.setEdgeFill = function(edgeId, color) { + this.getFormatter().getEdgeById(edgeId).getDefault().setFill(color); +}; + +GraphCanvas.prototype.getEdgeFill = function(edgeId) { + return this.getFormatter().getEdgeById(edgeId).getDefault().getFill(); +}; + +/** API LAYOUT **/ +GraphCanvas.prototype.setCoordinates = function(vertexId, x, y) { + return this.getLayout().getEdgeById(vertexId).setCoordinates(x, y); +}; + + + +function HPLCGraph(args) { + this.width = 600; + this.height = 600; + this.title = ''; + this.bbar = false; + this.plotInnerPanelPadding = 10; + this.plotPanelPadding = 5; + this.id = BUI.id(); - if (record.raw.measurementId != null) { - /** For testing * */ - grid.setLoading("ISPyB: Removing measurement"); - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, data) { - grid.setLoading(false); - /** - * We get and refresh experiment - * because specimens has changed * - */ - var adapter2 = new BiosaxsDataAdapter(); - adapter2.onSuccess.attach(function(sender, experiment) { - _this.onRemoved.notify(experiment); - _this._showStatusBarReady('Ready'); - }); - if (_this.experiments.experiments[0].experimentId != null) { - adapter2.getExperimentById(_this.experiments.experiments[0].experimentId, "MEDIUM"); - _this._showStatusBarBusy("ISPyB: Removing Unused Specimens"); - } - }); + this.hidePlots = null; + this.xlabel = ""; + this.scaled = false; + this.xParam = null; + this.showRangeSelector = true; + this.interactionModel = null; + + /** for each stat the max and minimum value when it is scaled in order to show correctly in the legend **/ + this.ranges = {}; + if (args != null) { + if (args.interactionModel != null) { + this.interactionModel = args.interactionModel; + } + if (args.width != null) { + this.width = args.width; + } + if (args.height != null) { + this.height = args.height; + } + if (args.bbar != null) { + this.bbar = args.bbar; + } + if (args.title != null) { + this.title = args.title; + } + if (args.plots != null) { + this.plots = args.plots; + } + + if (args.scaled != null) { + this.scaled = args.scaled; + } + if (args.xlabel != null) { + this.xlabel = args.xlabel; + } + if (args.xParam != null) { + this.xParam = args.xParam; + } + if (args.showRangeSelector != null) { + this.showRangeSelector = args.showRangeSelector; + } + } + + this.onZoomX = new Event(this); + this.onResetZoom = new Event(this); + this.dblclick = new Event(this); +} + +HPLCGraph.prototype.getMenu = function () { + var _this = this; + /** Actions buttons **/ + var actions = []; + + function toggle(item, pressed) { + if (pressed) { + _this.plots[item.param] = true; + } else { + delete _this.plots[item.param]; + } + _this.reloadData(this.hplcData); + } + + for (var i = 0; i < this.hplcData.length; i++) { + if (this.hplcData[i].showOnMenu != false) { + var param = this.hplcData[i].param; + var style = "style='padding:0 0px 0 5px;'"; + actions.push({ + text : "
" + BUI.getRectangleColorDIV(this.hplcData[i].color, 10, 10) + " " + this.hplcData[i].label + "
", + id : _this.id + param, + param : param, + enableToggle : true, + scope : this, + toggleHandler : toggle, + pressed : (_this.plots[param] != null) + }); + } + } + actions.push("-"); + + actions.push({ + text : "Scale", + enableToggle : true, + scope : this, + pressed : this.scaled, + icon : '../images/icon_graph.png', + toggleHandler : function (item, pressed) { + _this.scaled = pressed; + _this.reloadData(this.hplcData); + } + }); + + actions.push("->"); + actions.push({ + text : "Save", + scope : this, + icon : '../images/save.gif', + handler : function (item, pressed) { + var largeImage = document.createElement("img"); + largeImage.style.display = 'block'; + largeImage.style.width = 200 + "px"; + largeImage.style.height = 200 + "px"; + largeImage.setAttribute('src', Dygraph.Export.asCanvas(this.dygraphObject.dygraph).toDataURL()); + window.open(Dygraph.Export.asCanvas(this.dygraphObject.dygraph).toDataURL(), 'Image', ''); + } + }); + + return actions; +}; - adapter.onError.attach(function(sender, data) { - alert("Error: " + data); - grid.setLoading(false); - }); +/** Looks for the maximum value and then divide everything but that value **/ +HPLCGraph.prototype.scaledData = function (data) { + for (var i = 0; i < data.length; i++) { + var values = this.getMaxAndMinValue(data[i]); + data[i] = this.divideValuesByMax(data[i], values.max); + this.ranges[data[i].label] = values; + } + return data; +}; - adapter.removeMeasurement(record.raw); - } - } - } +/** Given a stat float[] and a max number it will divide each value by max **/ +HPLCGraph.prototype.divideValuesByMax = function (stat, max) { + for (var j = 0; j < stat.data.length; j++) { + if (max != 0) { + stat.data[j] = Number(stat.data[j]) / max; + stat.std[j] = Number(stat.std[j]) / max; + } + } + return stat; +}; - } - } - }); +/** returns max value of a stat **/ +HPLCGraph.prototype.getMaxAndMinValue = function (stat) { + var max = 0; + var min = stat.data[0]; + for (var j = 0; j < stat.data.length; j++) { + if (Number(stat.data[j]) > max) { + max = Number(stat.data[j]); + } + if (Number(stat.std[j]) > max) { + max = Number(stat.std[j]); + } + if (Number(stat.data[j]) < min) { + min = Number(stat.data[j]); + } + } + return { + max : Number(max), + min : Number(min) + }; +}; - this.grid.on("afterrender", function() { +HPLCGraph.prototype.getPoint = function (data, i) { + var point = [ 10, 10, 10 ]; + var y = parseFloat(data.data[i]); + var error = parseFloat(data.std[i]); + if (data.fdata == null) { + return [ y - error, y, y + error ]; + } else { + if (data.fstd != null) { + return [ data.fstd(y - error), data.fdata(y), data.fstd(y + error) ]; + } + return [ data.fdata(y) - error, data.fdata(y), data.fdata(y) + error ]; + } + return point; +}; - function updateTime() { - try { - _this.estimatedTime = _this.estimatedTime - 1; +HPLCGraph.prototype.reloadData = function(hplcData) { + this.panel.setLoading(false); + this.hplcData = hplcData; - _this.onUpdateTime.notify({ - hours : (Number(_this.estimatedTime / 3600).toFixed()), - minutes : (Number((_this.estimatedTime / 60) % 60).toFixed()), - seconds : (Number(_this.estimatedTime % 60).toFixed()) + var data = hplcData; + - }); + /** In case of having peaks **/ + if (this.peaks != null) { + for (var peak in this.peaks) { + var values = []; + var std = []; + for (var i = 0; i < this.peaks[peak].length; i++) { + values.push(this.peaks[peak][i][1]); + std.push(this.peaks[peak][i][2]); + } + data.push({ + param : peak, + data : values, + showOnMenu : false, + fdata : function (a) { + var value = (Math.log(parseFloat(a))); + if (isNumber(value)) { + return value; + } + }, + fstd : function (a) { + var value = Math.log(Math.abs(parseFloat(a))); + if (isNumber(value)) + return value; + }, + std : std, + color : this.colorPeak[peak], + label : peak + }); + + } + } + + + if (this.scaled) { + data = this.scaledData(JSON.parse(JSON.stringify(hplcData))); + } - if (Number(_this.estimatedTime) < 0) { - _this.timer = null; - grid.setTitle(_this.title); + var paramIndex = {}; + var parsed = []; + var j = 0; + for (var i = 0; i < data[0].data.length - 1; i++) { + var aux = []; + for (j = 0; j < data.length; j++) { + if (this.plots != null) { + if (this.plots[data[j].param] != null) { + aux.push(this.getPoint(data[j], i)); + paramIndex[data[j].param] = aux.length - 1; } + } else { + aux.push([ data[j].data[i] - data[j].std[i], data[j].data[i], data[j].data[i] + data[j].std[i] ]); + } + } + parsed.push([]); - } catch (e) { - console.log(e); - _this.timer = null; + var index = i; + if (this.xParam != null) { + index = parseFloat(data[this.xParam].data[i]); + } + + parsed[parsed.length - 1].push(index); + + for (j = 0; j < data.length; j++) { + if (this.plots != null) { + if (this.plots[data[j].param] != null) { + parsed[parsed.length - 1].push(aux[paramIndex[data[j].param]]); + } + } else { + parsed[parsed.length - 1].push(aux[j]); } } + } - if (_this.estimateTime) { - var experimentList = _this.experiments; - var collected = experimentList.getMeasurementsCollected(); - if (collected.length > 0) { - if (collected[0].run3VO != null) { - try { - var end = collected[0].run3VO.timeEnd; - var start = collected[0].run3VO.timeStart; - var dstart = moment(start); - var dend = moment(end); - var seconds = Number(dend.diff(dstart) / 1000).toFixed(); + var colors = []; + var labels = [ "" ]; + for (j = 0; j < data.length; j++) { + if (this.plots != null) { + if (this.plots[data[j].param] != null) { + colors.push(data[j].color); + labels.push(data[j].label); + } + } else { + parsed[parsed.length - 1].push(aux[j]); + } + } - _this.estimatedTime = (seconds * experimentList.getMeasurementsNotCollected().length); + this._renderDygraph(parsed, colors, labels); +}; - if (_this.estimatedTime > 0) { - updateTime(); - _this.timer = setInterval(function() { - updateTime(); - }, 1000); +HPLCGraph.prototype._renderDygraph = function (parsed, colors, labels) { + this.dygraphObject = new StdDevDyGraph(this.id, { + width : this.width, + height : this.height - 10, + xlabel : this.xlabel, + showRangeSelector : this.showRangeSelector, + interactionModel : this.interactionModel, + scaled : this.scaled, + ranges : this.ranges + }); + this.dygraphObject.draw(parsed, colors, labels); + + var _this = this; + this.dygraphObject.onZoomX.attach(function (sender, args) { + try { + _this.onZoomX.notify(args); + } catch (e) { + } + }); + + this.dygraphObject.onResetZoom.attach(function (sender, args) { + try { + _this.onResetZoom.notify(args); + } catch (e) { + } + }); + + this.dygraphObject.dblclick.attach(function (sender, args) { + try { + _this.dblclick.notify(args); + } catch (e) { + } + }); + +}; + +HPLCGraph.prototype.loadData = function (data) { + var _this = this; + this.reloadData(data); + this.panel.addDocked({ + xtype : 'toolbar', + items : this.getMenu() + }); + + + if (this.bbar == true){ + this.panel.addDocked({ + xtype : 'toolbar', + dock: 'bottom', + items : [ + { + xtype: 'numberfield', + id: 'main_field_start', + fieldLabel: 'Range from', + width: 170, + labelWidth : 70, + value: 0, + minValue: 0 + }, + { + xtype: 'numberfield', + id: 'main_field_end', + fieldLabel: 'to', + width: 130, + labelWidth : 30, + value: 0, + minValue: 0 + }, + { + xtype: 'button', + text: 'Go', + handler: function () { + var start = parseFloat(Ext.getCmp("main_field_start").getValue()); + var end = parseFloat(Ext.getCmp("main_field_end").getValue()); + + if (start < 0) { + start = 0; + } + if (end < 0) { + end = 0; + } + if (start > end) { + var aux = end; + end = start; + start = aux; + } + + _this.dygraphObject.dygraph.updateOptions({ isZoomedIgnoreProgrammaticZoom: true, dateWindow: [start, end] }); + } } + ] + }); + } +}; - } catch (e) { - } - } - } - } +HPLCGraph.prototype.getPanel = function () { + this.panel = Ext.create('Ext.panel.Panel', { + padding : this.plotPanelPadding, + width : this.width + 4 * this.plotInnerPanelPadding, + height : this.height + 4 * this.plotInnerPanelPadding, + items : [ { + html : "", + id : this.id, + width : this.width, + height : this.height + } ] }); - return this.grid; + return this.panel; }; -/** Method for testing * */ -MeasurementGrid.prototype.input = function() { - var experiment = DATADOC.getExperiment_10(); - var measurements = DATADOC.getMeasurements_10(); - var proposal = DATADOC.getProposal_10(); - return { - experiment : experiment, - measurements : measurements, - proposal : proposal - }; +HPLCGraph.prototype.input = function () { + return DATADOC.getHPLCData(); }; -MeasurementGrid.prototype.test = function(targetId) { - var measurementGrid = new MeasurementGrid({ - tbar : true - - }); - BIOSAXS.proposal = new Proposal(measurementGrid.input().proposal); - var panel = measurementGrid.getPanel(measurementGrid.input().measurements, new ExperimentList([ new Experiment(measurementGrid.input().experiment) ])); - panel.render(targetId); +HPLCGraph.prototype.getDataByFrameNumber = function (frameNumber) { + var data = {}; + data.frameNumber = frameNumber; + for (var key in this.hplcData){ + data[this.hplcData[key].label] = this.hplcData[key].data[frameNumber]; + } + console.log(data); + return data; }; - -/** - * A shipment may contains one or more cases where stock solutions and sample - * plates are stored - * - * @height - * @btnEditVisible - * @btnRemoveVisible - * - * #onEditButtonClicked - */ -function MolarityGrid(args) { - this.height = 100; - this.width = 100; - if (args != null) { - if (args.height != null) { - this.height = args.height; - } - if (args.width != null) { - this.width = args.width; + +HPLCGraph.prototype.test = function (targetId) { + var mainPlotPanel = new HPLCGraph({ + title : 'I0', + width : 800, + height : 400, + plots : { + "I0" : true, + "Rg" : true, + "Mass" : true + }, + xlabel : "HPLC Frames", + scaled : this.scaled, + interactionModel : { + 'dblclick' : function (event, g, context) { + } } - } + }); + mainPlotPanel.getPanel().render(targetId); + mainPlotPanel.loadData(mainPlotPanel.input()); - var _this = this; - - this.molarityForm = new MolarityForm({height : 180, width : 455}); +}; - this.molarityForm.onSave.attach(function(sender){ - _this.molarityWindow.destroy(); - _this.updateProposal(); - - }); - - this.molarityForm.onClose.attach(function(sender){ - _this.molarityWindow.destroy(); - - }); + +function MergesHPLCGraph(args) { + HPLCGraph.prototype.constructor.call(this, args); - /** Events * */ - this.onEditButtonClicked = new Event(this); +// this.peakColors = ["#00FB42", "#00BA31", "#007C21", "#003E10"]; + this.peakColors = ["#DEBD00", "#6D9100", "#872900", "#0092CC"]; } -MolarityGrid.prototype._getColumns = function() { - return [ { - text : 'Subunit', - columns : [ { - text : "Acronym", - width : 100, - hidden : false, - dataIndex : 'acronym', - sortable : true - }, { - text : "Name", - width : 125, - hidden : false, - dataIndex : 'name', - sortable : true - }, { - text : "MM Est.", - width : 100, - dataIndex : 'molecularMass', - sortable : true, - renderer : function(grid, cls, record){ - return BUI.formatValuesUnits(record.data.molecularMass , "Da", 10, 2); - - } - } ] - }, { -// text : "Number
in assymmetric units", - text : "Ratio", - width : 100, - dataIndex : 'ratio', - tooltip : 'Number of times the subunit is present in the macromolecule', - sortable : true - }, { - id : this.id + 'MOLARITY_REMOVE', - flex : 0.1, - sortable : false, - renderer : function(value, metaData, record, rowIndex, colIndex, store) { - return BUI.getRedButton('REMOVE'); - } - } ]; -}; -MolarityGrid.prototype._openMolarityWindow = function() { - this.molarityWindow = Ext.create('Ext.window.Window', { - title : 'Molarity', - height : 220, - width : 500, - modal : true, - items : [this.molarityForm.getPanel() ] - }).show(); -}; +MergesHPLCGraph.prototype.scaledData = HPLCGraph.prototype.scaledData; +MergesHPLCGraph.prototype.divideValuesByMax = HPLCGraph.prototype.divideValuesByMax; +MergesHPLCGraph.prototype.getMaxAndMinValue = HPLCGraph.prototype.getMaxAndMinValue; +MergesHPLCGraph.prototype.getPoint = HPLCGraph.prototype.getPoint; +MergesHPLCGraph.prototype.reloadData = HPLCGraph.prototype.reloadData; +MergesHPLCGraph.prototype._renderDygraph = HPLCGraph.prototype._renderDygraph; +MergesHPLCGraph.prototype.loadData = HPLCGraph.prototype.loadData; +MergesHPLCGraph.prototype.getPanel = HPLCGraph.prototype.getPanel; +MergesHPLCGraph.prototype.getDataByFrameNumber = HPLCGraph.prototype.getDataByFrameNumber; -MolarityGrid.prototype._getButtons = function() { - var _this = this; - return [ { - text : 'Add subunit', - icon : '../images/add.png', - handler : function() { - _this._openMolarityWindow(); + +MergesHPLCGraph.prototype.setPeaks = function (data) { + this.peaks = data; + /** get size of peaks **/ + this.peakKeys = []; + this.colorPeak = {}; + var colorCount = 1; + for (var key in this.peaks) { + if (this.peaks.hasOwnProperty(key)) { + var color = this.peakColors[colorCount % this.peakColors.length]; + colorCount = colorCount + 1; + this.peakKeys.push(key); + this.colorPeak[key] = color; } - }]; + } + this.peakKeys.sort(); }; -MolarityGrid.prototype.updateProposal = function() { +MergesHPLCGraph.prototype.getMenu = function () { var _this = this; - this.panel.setLoading(); - BIOSAXS.proposal.onInitialized.attach(function() { - if (BIOSAXS.proposal != null) { - var macromolecules = BIOSAXS.proposal.macromolecules; - for (var i = 0; i < macromolecules.length; i++) { - - if (macromolecules[i].macromoleculeId == _this.macromolecule.macromoleculeId) { - _this.refresh(macromolecules[i]); - _this.panel.setLoading(false); + /** Actions buttons **/ + var actions = []; + + function toggle(item, pressed) { + if (pressed) { + _this.plots[item.param] = true; + } else { + delete _this.plots[item.param]; + } + _this.reloadData(_this.hplcData); + } + + + /** Toolbar for peaks Average **/ + if (this.peaks != null) { + var items = []; + for (var i = 0; i < this.peakKeys.length; i++) { + var color = this.colorPeak[this.peakKeys[i]]; + items.push({ + text: "Peak #" + i + " " + this.peakKeys[i].replace("- ", " to #").replace(".0", "").replace(".0", "") + "", + peakid : this.peakKeys[i], + checked: false, + checkHandler: function (sender, pressed) { + var item = new Object(); + item.param = sender.peakid; + toggle(item, pressed); } - } + }); } - }); - BIOSAXS.proposal.init(); -}; - + + var menu = Ext.create('Ext.menu.Menu', { + id: 'mainMenu', + style: { + overflow: 'visible' + }, + items: items + }); + var tb = Ext.create('Ext.toolbar.Toolbar'); + tb.add({ + text: 'Peaks Avg.', + menu: menu + }); + actions.push(tb); + } -MolarityGrid.prototype.getPanel = function() { - var _this = this; + - this.molarityStore = Ext.create('Ext.data.Store', { - fields : [ 'acronym', 'ratio', 'comments', 'stoichiometryId', 'name', 'molecularMass' ], - sorters : { - property : 'ratio', - direction : 'DESC' + for (var i = 0; i < this.hplcData.length; i++) { + if (this.hplcData[i].showOnMenu != false) { + var param = this.hplcData[i].param; + var style = "style='padding:0 0px 0 5px;'"; + actions.push({ + text : "
" + BUI.getRectangleColorDIV(this.hplcData[i].color, 10, 10) + " " + this.hplcData[i].label + "
", + id : _this.id + param, + param : param, + enableToggle : true, + scope : this, + margin : 5, + toggleHandler : toggle, + pressed : (_this.plots[param] != null) + }); } - }); - - this.panel = Ext.create('Ext.grid.Panel', { - store : this.molarityStore, - height : this.height, - padding : 5, - columns : this._getColumns(), - listeners : { - cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { - - /** Remove entry * */ - if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'MOLARITY_REMOVE') { - var dataAdapter = new BiosaxsDataAdapter(); - dataAdapter.onSuccess.attach(function(sender) { - _this.updateProposal(); - - }); - dataAdapter.removeStoichiometry(record.data.stoichiometryId); - _this.panel.setLoading("Removing Structure"); - } - } - }, - tbar : this._getButtons() - }); - return this.panel; -}; + } -MolarityGrid.prototype._prepareData = function(macromolecule) { - /** Return an array of [{ratio,acronym, stoichiometryId, name}] **/ - var data = []; - if (macromolecule.stoichiometry != null) { - for (var i = 0; i < macromolecule.stoichiometry.length; i++) { - var hostMacromolecule = BIOSAXS.proposal.getMacromoleculeById(macromolecule.stoichiometry[i].macromoleculeId); - data.push({ - ratio : macromolecule.stoichiometry[i].ratio, - acronym : hostMacromolecule.acronym, - comments : hostMacromolecule.comments, - molecularMass : hostMacromolecule.molecularMass, - stoichiometryId : macromolecule.stoichiometry[i].stoichiometryId, - name : hostMacromolecule.name - }); - } + actions.push("->"); + actions.push({ + text : "Save", + scope : this, + icon : '../images/save.gif', + handler : function (item, pressed) { + var largeImage = document.createElement("img"); + largeImage.style.display = 'block'; + largeImage.style.width = 200 + "px"; + largeImage.style.height = 200 + "px"; + largeImage.setAttribute('src', Dygraph.Export.asCanvas(this.dygraphObject.dygraph).toDataURL()); + window.open(Dygraph.Export.asCanvas(this.dygraphObject.dygraph).toDataURL(), 'Image', ''); } - return data; -}; + }); -MolarityGrid.prototype.refresh = function(macromolecule) { - if (macromolecule != null){ - this.molarityStore.loadData(this._prepareData(macromolecule)); - this.molarityForm.refresh(macromolecule); - this.macromolecule = macromolecule; - } + return actions; }; -MolarityGrid.prototype.input = function() { - return { - proposal : DATADOC.getProposal_10(), - dewars : DATADOC.getDewars_10() - }; +MergesHPLCGraph.prototype.input = function () { + return DATADOC.getScatteringHPLCFrameData(); }; -MolarityGrid.prototype.test = function(targetId) { - var MolarityGrid = new MolarityGrid({ - height : 150 +MergesHPLCGraph.prototype.test = function (targetId) { + var mainPlotPanel = new MergesHPLCGraph({ + title : 'Scattering', + width : this.plotWidth, + height : 500, + showRangeSelector : false, + xParam : 0, + xlabel : "scattering_I", + plots : { + "scattering_I" : true, + "subtracted_I" : true, + "buffer_I" : true + } }); - BIOSAXS.proposal = new Proposal(MolarityGrid.input().proposal); - var panel = MolarityGrid.getPanel(MolarityGrid.input().dewars); - panel.render(targetId); - + mainPlotPanel.getPanel().render(targetId); + mainPlotPanel.loadData(mainPlotPanel.input()); }; + + +function NetworkWidget(args) { + this.id = "NetworkViewer_" + Math.random().toString().replace(".", "_"); + + this.label = true; + if (args != null) { + if (args.targetId != null) { + this.targetId = args.targetId; + } + if (args.label != null) { + this.label = args.label; + } + } + + this.onVertexOver = new Event(this); + this.onVertexOut = new Event(this); +} + +NetworkWidget.prototype.draw = function(dataset, formatter, layout) { + + this.graphCanvas = new GraphCanvas(this.id, document.getElementById(this.targetId), { + "labeled" : this.label, + "multipleSelectionEnabled" : false, + "draggingCanvasEnabled" : false + }); + this.graphCanvas.draw(dataset, formatter, layout); + + var _this = this; + this.graphCanvas.onVertexOver.attach(function(sender, nodeId) { + _this.onVertexOver.notify(nodeId); + }); + + this.graphCanvas.onVertexOut.attach(function(sender, nodeId) { + _this.onVertexOut.notify(nodeId); + }); +}; + +/** SELECT VERTICES BY NAME * */ +NetworkWidget.prototype.selectVertexByName = function(vertexName) { + var vertices = this.getDataset().getVertexByName(vertexName); + if (vertices != null) { + for ( var nodeId in vertices) { + if (vertices.hasOwnProperty(nodeId)) { + var vertexId = vertices[nodeId].getId(); + this.selectVertexById(vertexId); + } + } + } +}; + +NetworkWidget.prototype.selectVerticesByName = function(verticesName) { + for ( var i = 0; i < verticesName.length; i++) { + this.selectVertexByName(verticesName[i]); + } +}; + +/** SELECT VERTICES BY ID * */ +NetworkWidget.prototype.selectVertexById = function(vertexId) { + this.graphCanvas.selectNode(vertexId); + this.blinkVertexById(vertexId); +}; + +NetworkWidget.prototype.selectVerticesById = function(verticesId) { + for ( var i = 0; i < verticesId.length; i++) { + this.selectVertexById(verticesId[i]); + } +}; + +/** VECINDARIO * */ +NetworkWidget.prototype.selectNeighbourhood = function() { + this.selectEdgesFromVertices(); + this.selectAdjacent(); +}; +/** DESELECT * */ +NetworkWidget.prototype.deselectNodes = function() { + this.graphCanvas.deselectNodes(); +}; + +/** SELECT ALL NODES * */ +NetworkWidget.prototype.selectAllNodes = function() { + this.getGraphCanvas().selectAllNodes(); +}; + +/** SELECT EVERYTHING * */ +NetworkWidget.prototype.selectAll = function() { + this.getGraphCanvas().selectAll(); +}; + +/** SELECT ALL EDGES * */ +NetworkWidget.prototype.selectAllEdges = function() { + this.getGraphCanvas().selectAllEdges(); +}; + +/** ZOOM * */ +NetworkWidget.prototype.setScale = function(value) { + this.graphCanvas.setScale(value); +}; + +NetworkWidget.prototype.getScale = function() { + return this.graphCanvas.getScale(value); +}; + +/** SELECT ADJACENT VERTICES FROM SELECTED VERTICES * */ +NetworkWidget.prototype.selectAdjacent = function() { + var selectedVertices = this.getGraphCanvas().getSelectedVertices(); + var edges = []; + for ( var i = 0; i < selectedVertices.length; i++) { + edges = edges.concat(this.getGraphCanvas().getDataset().getVertexById(selectedVertices[i]).getEdges()); + } + var vertices = []; + for ( i = 0; i < edges.length; i++) { + vertices.push(edges[i].getNodeSource().getId()); + vertices.push(edges[i].getNodeTarget().getId()); + } + + this.selectVerticesById(vertices); +}; + +/** SELECT EDGES FROM SELECTED VERTICES * */ +NetworkWidget.prototype.selectEdgesFromVertices = function() { + var selectedVertices = this.getGraphCanvas().getSelectedVertices(); + var edges = []; + for ( var i = 0; i < selectedVertices.length; i++) { + edges = edges.concat(this.getGraphCanvas().getDataset().getVertexById(selectedVertices[i]).getEdges()); + } + var edgesId = []; + for ( i = 0; i < edges.length; i++) { + edgesId.push(edges[i].getId()); + } + this.getGraphCanvas().selectEdges(edgesId); +}; + +/** BLINKING * */ +NetworkWidget.prototype.blinkVertexById = function(vertexId) { + this.graphCanvas.blinkVertexById(vertexId); +}; + +/** COMPONENTE CONEXA DE LOS NODOS SELECCIONADOS * */ +NetworkWidget.prototype.selectConnectedComponent = function() { + var elements = this.getConnectedComponent(); + this.selectVerticesById(elements.nodes); + this.graphCanvas.selectEdges(elements.edges); +}; + +NetworkWidget.prototype.getConnectedComponent = function() { + var nodosVisitados = {}; + var aristasVisitadas = {}; + var arrNodos = []; + var arrAristas = []; + var selectedGraphNodesId = this.getGraphCanvas().getSelectedVertices(); + for ( var i = 0; i < selectedGraphNodesId.length; i++) { + this.visitNode(selectedGraphNodesId[i], nodosVisitados, aristasVisitadas, arrNodos, arrAristas); + } + return { + nodes : arrNodos, + edges : arrAristas + }; +}; + +NetworkWidget.prototype.visitNode = function(nodeId, nodosVisitados, aristasVisitadas, arrNodos, arrAristas) { + nodosVisitados[nodeId] = true; + arrNodos.push(nodeId); + var nodeEdges = this.getDataset().getVertexById(nodeId).getEdges(); + for ( var j = 0; j < nodeEdges.length; j++) { + var edge = nodeEdges[j]; + var edgeId = edge.getId(); + if (aristasVisitadas[edgeId] == null) { + aristasVisitadas[edgeId] = true; + arrAristas.push(edgeId); + var nodeTargetId = edge.getNodeTarget().getId(); + if (nodosVisitados[nodeTargetId] == null) { + this.visitNode(nodeTargetId, nodosVisitados, aristasVisitadas, arrNodos, arrAristas); + } + var nodeSourceId = edge.getNodeSource().getId(); + if (nodosVisitados[nodeSourceId] == null) { + this.visitNode(nodeSourceId, nodosVisitados, aristasVisitadas, arrNodos, arrAristas); + } + } + } +}; + +/** COLLAPSE SELECTED VERTICES * */ +NetworkWidget.prototype.collapse = function() { + var selectedVertices = this.getGraphCanvas().getSelectedVertices(); + var xMin = -Infinity; + var xMax = Infinity; + var yMin = -Infinity; + var yMax = Infinity; + + for ( var i = 0; i < selectedVertices.length; i++) { + var vertex = this.getGraphCanvas().getLayout().getNodeById(selectedVertices[i]); + if (xMin < vertex.x) { + xMin = vertex.x; + } + if (xMax > vertex.x) { + xMax = vertex.x; + } + if (yMin < vertex.y) { + yMin = vertex.y; + } + if (yMax > vertex.y) { + yMax = vertex.y; + } + } + + var centerX = xMin - xMax; + var centerY = yMin - yMax; + var radius = (xMax - xMin) / 4; + + var count = selectedVertices.length; + var verticesCoordinates = []; + + for ( i = 0; i < selectedVertices.length; i++) { + x = centerX + radius * Math.sin(i * 2 * Math.PI / count); + y = centerY + radius * Math.cos(i * 2 * Math.PI / count); + verticesCoordinates.push({ + 'x' : x, + 'y' : y + }); + } + + for ( i = 0; i < selectedVertices.length; i++) { + this.getGraphCanvas().getLayout().getNodeById(selectedVertices[i]).setCoordinates(verticesCoordinates[i].x, verticesCoordinates[i].y); + } +}; +/** SETTER FONT SIZE * */ +NetworkWidget.prototype.setVerticesFontSize = function(value) { + for ( var nodeId in this.getDataset().getVertices()) { + if (this.getDataset().getVertices().hasOwnProperty(nodeId)) { + this.getFormatter().getVertexById(nodeId).getDefault().setFontSize(value); + } + } +}; + +/** GETTERS * */ +NetworkWidget.prototype.getFormatter = function() { + return this.getGraphCanvas().getFormatter(); +}; +NetworkWidget.prototype.getLayout = function() { + return this.getGraphCanvas().getLayout(); +}; + +NetworkWidget.prototype.getDataset = function() { + return this.getGraphCanvas().getDataset(); +}; + +NetworkWidget.prototype.getGraphCanvas = function() { + return this.graphCanvas; +}; + +RangeWhiskerGraph.prototype.cleanArray = GenericGraph.prototype.cleanArray; +RangeWhiskerGraph.prototype.plotAxes = GenericGraph.prototype.plotAxes; +RangeWhiskerGraph.prototype.plotRuler = GenericGraph.prototype.plotRuler; +RangeWhiskerGraph.prototype.drawSVGVerticalText = GenericGraph.prototype.drawSVGVerticalText; +RangeWhiskerGraph.prototype.getClassColor = GenericGraph.prototype.getClassColor; +RangeWhiskerGraph.prototype.getDimensions = GenericGraph.prototype.getDimensions; +RangeWhiskerGraph.prototype.calculate = GenericGraph.prototype.calculate; +RangeWhiskerGraph.prototype.getMedian = GenericGraph.prototype.getMedian; +RangeWhiskerGraph.prototype.pointToPixel = GenericGraph.prototype.pointToPixel; +RangeWhiskerGraph.prototype.maxValueWhisker = GenericGraph.prototype.maxValueWhisker; +RangeWhiskerGraph.prototype.refresh = GenericGraph.prototype.refresh; + /** - * Given data analysis parsed with ResultsAssemblyWidget makes a selector grid with buffer/protein - **/ -function SpecimenSelectorResultGrid() { - this.id = BUI.id(); -} + * Subclass of GenericGraph + * + * @plotHorizontalByCluster + */ +function RangeWhiskerGraph(args) { + this.maxBoxWidth = 25; -SpecimenSelectorResultGrid.prototype._prepareData = function(data) { - var parsed = []; - for ( var i = 0; i < data.length; i++) { - var row = data[i]; - for ( var j = 0; j < row.conditions.length; j++) { - parsed.push({ - bufferId : row.conditions[j].bufferId, - macromoleculeId : row.macromoleculeId, - macromoleculeAcronym : BIOSAXS.proposal.getMacromoleculeById(row.macromoleculeId).acronym, - bufferAcronym : BIOSAXS.proposal.getBufferById(row.conditions[j].bufferId).acronym - }); - } + if (args == null) { + args = {}; } - return parsed; -}; - -SpecimenSelectorResultGrid.prototype.refresh = function(data) { - this.store.loadData(this._prepareData(data)); -}; - -SpecimenSelectorResultGrid.prototype.getPanel = function(data) { - var _this = this; - this.store = Ext.create('Ext.data.Store', { - fields : [ 'macromoleculeId', 'bufferId', 'macromoleculeAcronym', 'bufferAcronym', 'concentration' ], - data : this._prepareData(data), - groupField : 'macromoleculeAcronym' - }); + args.plotHorizontalByCluster = false; - this.store.sort('concentration'); - this.grid = Ext.create('Ext.grid.Panel', { - id : this.id, - store : this.store, - width : this.width, - height : this.height, - maxHeight : this.maxHeight, - border : 1, - features : [ { - ftype : 'grouping', - groupHeaderTpl : '{name}', - hideGroupedHeader : true, - startCollapsed : false - } ], - selModel : Ext.create('Ext.selection.CheckboxModel', { - allowDeselect : true, - mode : 'MULTI', - listeners : { - selectionchange : function(sm, selections) { - _this.selected = []; - for ( var i = 0; i < selections.length; i++) { - _this.selected.push(selections[i].raw); - } - } - } - }), - margin : 10, - sortableColumns : true, - columns : [ { - text : 'Macromolecule', - dataIndex : 'macromoleculeAcronym', - flex : 1 - }, { - text : '', - dataIndex : 'bufferId', - width : 20, - hidden : false, - renderer : function(val, y, sample) { - return BUI.getRectangleColorDIV(BIOSAXS.proposal.bufferColors[val], 10, 10); - } - }, { - text : 'Buffer', - dataIndex : 'bufferAcronym', - flex : 1 - }, { - text : 'Concentration', - dataIndex : 'concentration', - flex : 1, - renderer : function(val, y, sample) { - return val + " mg/ml"; - } - } ] - }); + GenericGraph.call(this, args); - /** Adding the tbar **/ - if (this.tbar) { - this.grid.addDocked({ - xtype : 'toolbar', - items : this.getTbar() - }); + if (args.maxBoxWidth != null) { + this.maxBoxWidth = args.maxBoxWidth; } - return this.grid; -}; +} -SpecimenSelectorResultGrid.prototype.input = function() { - return { - data : new ResultsAssemblyGrid()._prepareData(new ResultsAssemblyGrid().input().data), - proposal : new ResultsAssemblyGrid().input().proposal - }; +RangeWhiskerGraph.prototype.refresh = function(data) { + document.getElementById(this.targetId).innerHTML = ""; + this.draw(this.targetId, data); }; -SpecimenSelectorResultGrid.prototype.test = function(targetId) { - var specimenSelectorResultGrid = new SpecimenSelectorResultGrid(); - BIOSAXS.proposal = new Proposal(specimenSelectorResultGrid.input().proposal); - var panel = specimenSelectorResultGrid.getPanel(specimenSelectorResultGrid.input().data); - panel.render(targetId); -}; +RangeWhiskerGraph.prototype.isNumber = function(value) { + if (value == "") + return false; -/** - * Show all buffer conditions for each macromolecules pointing out the measurements quality - * - * @height - * @maxHeight - * @width - * @searchBar - * @tbar - * @btnResultVisible - * - * #onClick - */ -function ResultsAssemblyGrid(args) { - this.height = 500; - this.id = BUI.id(); - this.maxHeight = this.height; + var d = parseInt(value); + if (!isNaN(d)) + return true; + else + return false; - this.width = 900; - this.searchBar = false; - this.tbar = false; - this.btnResultVisible = false; +}; - /** For processing **/ - this.processed = {}; - this.renderedPlotIndex = 0; +/** + There are several different methods for calculating quartiles.[1] This calculator uses a method described by Moore and McCabe to find quartile values. + The same method also used by The TI-83 to calculate quartile values. + With this method, the first quartile is the median of the numbers below the median, the third quartile is the median of the numbers above the median. - this.plotWidth = 210; - this.plotHeight = 80; + http://www.miniwebtool.com/quartile-calculator/ + http://www.alcula.com/calculators/statistics/box-plot/ - /** Colors **/ - this.validColor = BUI.getValidColor(); - this.warningColor = BUI.getWarningColor(); - this.notValidColor = BUI.getErrorColor(); + **/ +RangeWhiskerGraph.prototype.getQ1 = function(array) { + array = array.slice(0, array.length / 2); + return this.getMedian(array); +}; - /** Show warning if guinier quality less than **/ - this.guinierQuality = BUI.getQualityThreshold(); - this.framePercentageThreshold = BUI.getRadiationDamageThreshold(); +RangeWhiskerGraph.prototype.getQ3 = function(array) { + array = array.slice((array.length + 1) / 2); + return this.getMedian(array); - if (args != null) { - if (args.height != null) { - this.height = args.height; - this.maxHeight = this.height; - } - if (args.maxHeight != null) { - this.maxHeight = args.maxHeight; - } - if (args.width != null) { - this.width = args.width; - } - if (args.searchBar != null) { - this.searchBar = args.searchBar; +}; + +RangeWhiskerGraph.prototype.getQ2 = function(array) { + return this.getMedian(array); +}; + +RangeWhiskerGraph.prototype.getBelowOutliers = function(belowLimit, array) { + var points = []; + for ( var i = 0; i < array.length; i++) { + if (array[i] <= belowLimit) { + points.push(array[i]); } + } + return points; +}; - if (args.tbar != null) { - this.tbar = args.tbar; +RangeWhiskerGraph.prototype.getAboveOutliers = function(aboveLimit, array) { + var points = []; + for ( var i = 0; i < array.length; i++) { + if (array[i] >= aboveLimit) { + points.push(array[i]); } - if (args.btnResultVisible != null) { - this.btnResultVisible = args.btnResultVisible; + } + return points; +}; + +RangeWhiskerGraph.prototype.minValueWhisker = function(belowLimit, q1, array) { + var points = []; + + for ( var i = 0; i < array.length; i++) { + if ((array[i] < q1) && (array[i]) > belowLimit) { + points.push(array[i]); } + } + if (points.length > 0) { + points.sort(function(a, b) { + return a - b; + }); + return points[0]; } + return null; +}; - this.onClick = new Event(); -} +//RangeWhiskerGraph.prototype.maxValueWhisker = function(aboveLimit, q3, array){ +// var points = []; +// for (var i = 0; i < array.length; i++){ +// if ((array[i] > q3) && (array[i]) <= aboveLimit){ +// points.push(array[i]); +// } +// } +// if (points.length > 0){ +// points.sort(function(a, b){return a - b;}); +// return points[points.length - 1]; +// } +// return null; +//}; -ResultsAssemblyGrid.prototype.edit = function(macromoleculeId) { - var _this = this; - var window = new MacromoleculeWindow(); - window.onSuccess.attach(function(sender, proposal) { - _this.store.loadData(_this._prepareData(BIOSAXS.proposal.getMacromolecules())); - }); - window.draw(BIOSAXS.proposal.getMacromoleculeById(macromoleculeId)); +RangeWhiskerGraph.prototype.drawPoints = function(boxProperties) { + if (this.plotPoints) { + for ( var i = 0; i < boxProperties.values.length; i++) { + var value = boxProperties.values[i]; + var toPixel = this.pointToPixel(value, boxProperties); + SVG.drawCircle(boxProperties.x, toPixel, this.pointRadius, this.svg, + [ + [ "fill", "green" ], [ "fill-opacity", this.fillOpacityPoint ], [ 'stroke-opacity', this.strokeOpacityPoint ], + [ "stroke", "black" ] ]); + } + } }; -ResultsAssemblyGrid.prototype.getTbar = function() { - var _this = this; - var actions = []; +RangeWhiskerGraph.prototype.plotRangeQ1Q3 = function(data, properties) { + var posX = this.left + this.rulerWidth; - actions.push(Ext.create('Ext.Action', { - icon : '../images/add.png', - text : 'Add Macromolecule', - disabled : false, - handler : function(widget, event) { - var window = new MacromoleculeWindow(); - window.onSuccess.attach(function(sender) { - _this.refresh(); - }); - window.draw({}); - } - })); + var boxBordersPointsQ1 = []; + var boxBordersPointsQ3 = []; + var Q2 = []; - actions.push(Ext.create('Ext.Action', { - icon : '../images/add.png', - text : 'Define an Assembly', - disabled : false, - handler : function(widget, event) { - var createAssemblywindow = new CreateAssemblyWindow(); - createAssemblywindow.onSaved.attach(function(evt, args) { - if (args.macromoleculeIds.length > 0) { - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, proposal) { - _this.refresh(); - }); - adapter.saveAssembly(args.assemblyId, args.macromoleculeIds); + for ( var i = 0; i < data.clusters.length; i++) { + var cluster = data.clusters[i]; + /** inter cluster space **/ + posX = posX + this.interClustersSpace; - } + for ( var j = 0; j < cluster.classes.length; j++) { + var ratio = properties.width / (properties.xValues.range); + var coorX = (cluster.x - properties.xValues.min) * ratio + this.rulerWidth; + var boxProperties = { + name : cluster.classes[j].name, + values : cluster.classes[j].values, + minPoint : properties.minPoint, + maxPoint : properties.maxPoint, + x : coorX + this.left, + y : this.top, + width : properties.classWidth, + height : this.height - this.top - this.bottom - this.clusterTitleHeight - this.rulerHeight + }; - }); - createAssemblywindow.draw(_this.experiment); - } - })); + var result = this.calculate(boxProperties.values); + var boxColor = this.getClassColor(boxProperties.name); - return actions; -}; + if (this.isNumber(result.Q1) && this.isNumber(result.Q3)) { + var x = boxProperties.x; + var y = this.pointToPixel(result.Q1, boxProperties); + if (boxBordersPointsQ1[cluster.classes[j].name] == null) { + boxBordersPointsQ1[cluster.classes[j].name] = []; + } + boxBordersPointsQ1[cluster.classes[j].name].push({ + x : x, + y : y + }); -ResultsAssemblyGrid.prototype.refresh = function() { - this.store.loadData(this._prepareData()); -}; + x = boxProperties.x; + y = this.pointToPixel(result.Q3, boxProperties); + if (boxBordersPointsQ3[cluster.classes[j].name] == null) { + boxBordersPointsQ3[cluster.classes[j].name] = []; + } + boxBordersPointsQ3[cluster.classes[j].name].push({ + x : x, + y : y + }); + } else { -ResultsAssemblyGrid.prototype.addCondition = function(record, data_parsed, i) { - function getCondition(record) { - return { - concentration : record.conc, - quality : record.quality, - bufferBeforeFramesMerged : record.bufferBeforeFramesMerged, - bufferAfterFramesMerged : record.bufferAfterFramesMerged, - framesCount : record.framesCount, - framesMerge : record.framesMerge - }; - } + if (this.isNumber(result.Q2)) { - if (data_parsed[i].conditions != null) { - var bufferFound = false; - for ( var index in data_parsed[i].conditions) { - condition = data_parsed[i].conditions[index]; + if (boxBordersPointsQ1[cluster.classes[j].name] == null) { + boxBordersPointsQ1[cluster.classes[j].name] = []; + } - if ((condition.macromoleculeId == record.macromoleculeId) && (condition.bufferId == record.bufferId)) { - data_parsed[i].conditions[index].concentrations.push(getCondition(record)); - bufferFound = true; + if (boxBordersPointsQ3[cluster.classes[j].name] == null) { + boxBordersPointsQ3[cluster.classes[j].name] = []; + } + + boxBordersPointsQ1[cluster.classes[j].name].push({ + x : boxProperties.x, + y : this.pointToPixel(result.Q2, boxProperties) + }); + boxBordersPointsQ3[cluster.classes[j].name].push({ + x : boxProperties.x, + y : this.pointToPixel(result.Q2, boxProperties) + }); + } } } - if (!bufferFound) { - data_parsed[i].conditions.push({ - macromoleculeId : record.macromoleculeId, - bufferId : record.bufferId, - concentrations : [ getCondition(record) ] - }); + } + for ( var classe in boxBordersPointsQ1) { + var points = boxBordersPointsQ1[classe]; + var pointsSVG = ""; + for (var k = 0; k < points.length; k++) { + pointsSVG = Number(points[k].x).toFixed(1) + "," + Number(points[k].y).toFixed(1) + " " + pointsSVG; + } + + points = boxBordersPointsQ3[classe]; + for (var z = points.length - 1; z >= 0; z--) { + pointsSVG = Number(points[z].x).toFixed(1) + "," + Number(points[z].y).toFixed(1) + " " + pointsSVG; } + + SVG.drawPoligon(pointsSVG, this.svg, [ + [ "fill", this.getClassColor(classe) ], [ "opacity", "0.3" ], [ "stroke", "black" ], [ "stroke-width", 1 ] ]); } }; -ResultsAssemblyGrid.prototype.process = function(record, data_parsed) { - if (this.processed[record.macromoleculeId] == null) { - this.processed[record.macromoleculeId] = true; - record.measurementCount = 1; - record.subtractionCount = 1; - record.averageCount = 1; - record.conditions = []; - data_parsed.push(record); - } +RangeWhiskerGraph.prototype.plotWhisters = function(data, properties) { + var colors = [ "yellow", "orange", "green" ]; - for ( var i = 0; i < data_parsed.length; i++) { - if (data_parsed[i].macromoleculeId == record.macromoleculeId) { - data_parsed[i].measurementCount = data_parsed[i].measurementCount + 1; + this.plotRangeQ1Q3(data, properties); + var Q2 = {}; - if (record.subtractionId != null) { - data_parsed[i].subtractionCount = data_parsed[i].subtractionCount + 1; - this.addCondition(record, data_parsed, i); - } + for ( var i = 0; i < data.clusters.length; i++) { + var cluster = data.clusters[i]; + for ( var j = 0; j < cluster.classes.length; j++) { + var ratio = properties.width / (properties.xValues.range); + var coorX = (cluster.x - properties.xValues.min) * ratio + this.left + this.rulerWidth - this.rulerStroke; + var boxProperties = { + name : cluster.classes[j].name, + values : cluster.classes[j].values, + minPoint : properties.minPoint, + maxPoint : properties.maxPoint, + x : coorX, + y : this.top, + width : properties.classWidth, + height : this.height - this.top - this.bottom - this.clusterTitleHeight - this.rulerHeight + }; - if (record.framesMerge != null) { - data_parsed[i].averageCount = data_parsed[i].averageCount + 1; - } + this.drawPoints(boxProperties); - if (record.timeStart != null) { - if (data_parsed[i].timeStart != null) { - if (moment(data_parsed[i].timeStart).format("X") > moment(record.timeStart).format("X")) { - data_parsed[i].timeStart = record.timeStart; - } + /** PLOTTING Q2 **/ + var result = this.calculate(boxProperties.values); + var boxColor = this.getClassColor(boxProperties.name); + if (this.isNumber(result.Q2)) { + if (Q2[boxProperties.name] != null) { + SVG.drawLine(boxProperties.x, this.pointToPixel(result.Q2, boxProperties), Q2[boxProperties.name].x, Q2[boxProperties.name].y, + this.svg, [ [ "stroke", boxColor ], [ "stroke-width", "2" ] ]); + } + Q2[boxProperties.name] = { + x : boxProperties.x, + y : this.pointToPixel(result.Q2, boxProperties) } } } } - return data_parsed; - }; -ResultsAssemblyGrid.prototype._prepareData = function(data) { - var data_parsed = []; - for ( var i = 0; i < data.length; i++) { - data_parsed = this.process(data[i], data_parsed); - } - this.data = data_parsed; - return data_parsed; +RangeWhiskerGraph.prototype.draw = function(targetId, data) { + //this.calculate(data); + this.targetId = targetId; + var properties = (this.getDimensions(data)); + this.svg = SVG.createSVGCanvas(document.getElementById(this.targetId), [ [ "width", this.width ], [ "height", this.height ] ]); + this.plotAxes(properties); + this.plotWhisters(data, properties); }; -/** Given an array of conditions it returns distinct(concentrations) order by concentration and hash map with number of ocurrences**/ -ResultsAssemblyGrid.prototype.parseConcentrations = function(val, conditions, differentConcentration, quality) { - var conditions = []; - var differentConcentration = {}; - var quality = []; - for ( var i = 0; i < val.length; i++) { - var conc = Number(val[i].concentration).toFixed(1); - var quality = Number(val[i].quality).toFixed(2); - if (differentConcentration[conc] == null) { - differentConcentration[conc] = 0; - conditions.push({ - concentration : conc, - quality : [ quality ], - frames : [ { - bufferAfterFramesMerged : val[i].bufferAfterFramesMerged, - bufferBeforeFramesMerged : val[i].bufferBeforeFramesMerged, - framesMerge : val[i].framesMerge, - framesCount : val[i].framesCount - } ] - }); - } else { - /** Add quality **/ - for ( var j = 0; j < conditions.length; j++) { - if (conditions[j].concentration == conc) { - conditions[j].quality.push(quality); - conditions[j].frames.push({ - bufferAfterFramesMerged : val[i].bufferAfterFramesMerged, - bufferBeforeFramesMerged : val[i].bufferBeforeFramesMerged, - framesMerge : val[i].framesMerge, - framesCount : val[i].framesCount - }); - } - } - } +RangeWhiskerGraph.prototype.input = function() { + return DATADOC.getBoxWhikerData(); +}; - differentConcentration[conc] = differentConcentration[conc] + 1; - } - /** sorting concentrations **/ - conditions.sort(function(a, b) { - return a.concentration - b.concentration; +RangeWhiskerGraph.prototype.test = function(targetId) { + var plot = new RangeWhiskerGraph({ + targetId : targetId, + height : 350, + width : 450, + maxBoxWidth : 25 }); - return { - concentrations : conditions, - differentConcentration : differentConcentration - }; + plot.refresh(this.input()); }; + +StdDevDyGraph.prototype.dblclick = DygraphWidget.prototype.dblclick; +StdDevDyGraph.prototype._createHTLMWrapper = DygraphWidget.prototype._createHTLMWrapper; +StdDevDyGraph.prototype.draw = DygraphWidget.prototype.draw; -ResultsAssemblyGrid.prototype.getConditionWarnings = function(condition) { - - var withWarnings = 0; - - for ( var i = 0; i < condition.frames.length; i++) { - if (condition.quality[i] == null) { - withWarnings = withWarnings + 1; - continue; - } else { - if (Number(condition.quality[i]) < this.guinierQuality) { - withWarnings = withWarnings + 1; - continue; - } - } - - if (condition.frames[i].bufferBeforeFramesMerged / condition.frames[i].framesCount < this.framePercentageThreshold|| condition.frames[i].framesMerged / condition.frames[i].framesCount < this.framePercentageThreshold|| condition.frames[i].bufferAfterFramesMerged / condition.frames[i].framesCount < this.framePercentageThreshold) { - withWarnings = withWarnings + 1; - continue; - } +function StdDevDyGraph(targetId, args) { + this.scaled = false; + if (args == null) { + args = {}; } + args.customBars = true; + DygraphWidget.prototype.constructor.call(this, targetId, args); +} +StdDevDyGraph.prototype.input = function () { return { - withWarnings : withWarnings, - withoutWarnings : condition.frames.length - withWarnings + data : [ [ 1, [ 2, 3, 3.5 ], [ 4, 4.2, 5 ] ], [ 2, [ 5, 5.5, 5.7 ], [ 4, 4.2, 5 ] ] ], + colors : [ "blue", "red" ], + labels : [ "", 'data1', 'data2' ] }; }; -ResultsAssemblyGrid.prototype.getFrameHTMLTable = function(warnings) { - var html = ""; - if (warnings.withWarnings > 0) { - html = html + "
" + warnings.withWarnings + - "x
"; - } - - if (warnings.withoutWarnings > 0) { - html = html + "
" + warnings.withoutWarnings + - "x
"; - } - return html; -}; +StdDevDyGraph.prototype.test = function (targetId) { + var dygraphObject = new StdDevDyGraph(targetId, { + width : 500, + height : 400, + xlabel : "xLabel", + showRangeSelector : false + }); -ResultsAssemblyGrid.prototype.createConcentrationRow = function(numberOcu, condition, warnings){ - var html = ""; - if (numberOcu > 1){ - html = html + "" +numberOcu + "x " + BUI.formatConcentration(condition.concentration); - } - else{ - html = html + BUI.formatConcentration(condition.concentration); - } - html = html + ""; - html = html + this.getFrameHTMLTable(warnings); + ""; - html = html + ""; - return html; + dygraphObject.draw(dygraphObject.input().data, dygraphObject.input().colors, dygraphObject.input().labels); }; + + + +function AbinitioGrid(args) { + this.height = null; + this.width = null; + this.id = BUI.id(); + + if (args != null) { + if (args.height != null) { + this.height = args.height; + + if (args.width != null) { + this.width = args.width; + } + } + } + + this.onSelected = new Event(this); +}; + + +AbinitioGrid.prototype.refresh = function(subtractions){ + this.store.loadData(this._prepareData(subtractions)); +}; + +AbinitioGrid.prototype._prepareData = function(subtractions){ + /** Parsing data * */ + var models = []; + for (var l = 0; l < subtractions.length; l++) { + var subtraction = subtractions[l]; + for (var k = 0; k < subtraction.substractionToAbInitioModel3VOs.length; k++) { + var data = subtraction.substractionToAbInitioModel3VOs[k].abinitiomodel3VO; + if (data.averagedModel != null) { + models.push(data.averagedModel); + models[models.length - 1].type = "Reference"; + } + + if (data.shapeDeterminationModel != null) { + models.push(data.shapeDeterminationModel); + models[models.length - 1].type = "Refined"; + } + + if (data.modelList3VO != null) { + if (data.modelList3VO.modeltolist3VOs != null) { + for (var i = 0; i < data.modelList3VO.modeltolist3VOs.length; i++) { + models.push(data.modelList3VO.modeltolist3VOs[i].model3VO); + models[models.length - 1].type = "Model"; + } + } + } + } + } + return models; +}; + +AbinitioGrid.prototype.getPanel = function(){ + var _this = this; + + + var modelFields = [ "modelId", "type", "chiSqrt", "dmax", "firFile", "logFile", "fitFile", "pdbFile", "rfactor", "rg", "volume" ]; + Ext.define('AbinitioModel', { + extend : 'Ext.data.Model', + fields : modelFields + + }); + + /** + * Store in Memory + */ + this.store = Ext.create('Ext.data.Store', { + model : 'AbinitioModel', + autoload : true, + groupField : 'type' + }); + + + var groupingFeature = Ext.create('Ext.grid.feature.Grouping',{ + groupHeaderTpl: '{name} ({rows.length} model{[values.rows.length > 1 ? "s" : ""]})', + startCollapsed: true, + collapsible : true + }); + + this.grid = Ext.create('Ext.grid.Panel', { + collapsible : false, + resizable : true, + features: [groupingFeature], + autoscroll : true, + multiSelect : true, + store : this.store, + height : this.height, + width : this.width, + margin : 10, + columns : [ { + text : "Type", + dataindex : "type", + hidden : true, + renderer : function(a, b, record) { + return record.data.type; + }, + flex : 1 + }, + { + text : "ModelId", + dataindex : "modelId", + hidden : true, + renderer : function(a, b, record) { + return record.data.modelId; + + }, + flex : 1 + }, + + { + text : "chiSqrt", + dataindex : "chiSqrt", + renderer : function(a, b, record) { + if (record.data.dmax != null) { + return BUI.formatValuesUnits(record.data.chiSqrt, "", 12, this.decimals); + } + + }, + flex : 1 + }, + { + text : "Dmax", + dataindex : "dmax", + renderer : function(a, b, record) { + if (record.data.dmax != null) { + return BUI.formatValuesUnits(record.data.dmax, "nm", 12, this.decimals); + } + + }, + flex : 1 + }, { + text : "rFactor", + dataindex : "rfactor", + hidden : true, + renderer : function(a, b, record) { + if (record.data.rfactor != null) { + return record.data.rfactor; + } + }, + flex : 1 + }, { + text : "Rg", + dataindex : "rg", + renderer : function(a, b, record) { + if (record.data.rg != null) { + return BUI.formatValuesUnits(record.data.rg, "nm", 12, this.decimals); + } + + }, + flex : 1 + }, + { + text : "Volume", + dataindex : "volume", + renderer : function(a, b, record) { + if (record.raw.volume != null){ + return BUI.formatValuesUnits(record.raw.volume, '') + " nm3"; + } + }, + flex : 1 + }, + { + text : "PDB", + dataindex : "pdbFile", + renderer : function(a, b, record) { + if (record.data.pdbFile != null){ + return record.data.pdbFile.split("/")[record.data.pdbFile.split("/").length - 1]; + } + }, + flex : 1 + }, { + text : "Fir", + dataindex : "firFile", + renderer : function(a, b, record) { + if (record.data.firFile != null){ + return record.data.firFile.split("/")[record.data.firFile.split("/").length - 1]; + } + }, + flex : 1 + }, { + text : "LOG", + dataindex : "logFile", + hidden : true, + renderer : function(a, b, record) { + if (record.data.logFile != null){ + return record.data.logFile.split("/")[record.data.logFile.split("/").length - 1]; + } + }, + flex : 1 + } + ], + viewConfig : { + enableTextSelection : true, + preserveScrollOnRefresh : true, + stripeRows : true, + listeners : { + 'celldblclick' : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + }, + 'cellclick' : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + var models = []; + for (var i = 0; i < grid.getSelectionModel().selected.items.length; i++) { + models.push(grid.getSelectionModel().selected.items[i].raw); + } + _this.onSelected.notify(models); + } + } + } + }); + return this.grid; + +}; + +function AdditiveGrid(args) { + this.onRemoveButtonClicked = new Event(this); +} -ResultsAssemblyGrid.prototype.getConditionHTMLTable = function(val, style, record) { - var maxNumberColumns = 2; - var html = "
"; - var nColumns = 0; - for ( var r = 0; r < val.length; r++) { - if (nColumns == maxNumberColumns) { - nColumns = 0; - html = html + ""; - } - html = html + ""; - nColumns = nColumns + 1; - } - html = html + "
"; - var value = val[r]; - - var bufferAcronym = (BIOSAXS.proposal.getBufferById(value.bufferId).acronym); - - var parsed = (this.parseConcentrations(value.concentrations)); - var conditions = parsed.concentrations; - var differentConcentration = parsed.differentConcentration; - - /** Checking warnings **/ - var warnings = []; - var concentrationValidPerconcentration = 0; - var measurements = 0; - for ( var i = 0; i < conditions.length; i++) { - measurements = measurements + conditions[i].frames.length; - var warning = this.getConditionWarnings(conditions[i]); - warnings.push(warning); - if (warning.withoutWarnings > 0) { - concentrationValidPerconcentration = concentrationValidPerconcentration + 1; - } - } - - this.validColor = '#E0F8E0'; - this.warningColor = '#F5DA81'; - this.notValidColor = '#F6CED8'; - - var color = this.warningColor; - if (concentrationValidPerconcentration > 2) { - color = this.validColor; - } - /** More measurement need to be done **/ - if (measurements < 3) { - color = this.notValidColor; - } - - html = html + ""; - html = html + ""; - for ( var i = 0; i < conditions.length; i++) { - html = html + this.createConcentrationRow(differentConcentration[conditions[i].concentration], conditions[i], warnings[i]); - } - - html = html + ""; - html = html + "
" + bufferAcronym.toUpperCase() + - "
"; - - html = html + "
"; - return html; +AdditiveGrid.prototype.getBuffer = function() { + return this.buffer; }; -ResultsAssemblyGrid.prototype._getTbar = function() { - function goTo(url) { - window.location = url; - } - +AdditiveGrid.prototype._getActions = function() { var _this = this; - return [ { - icon : '../images/application_view_list.png', - text : 'Multiple Select', - handler : function() { - var specimenSelectorResultGrid = new SpecimenSelectorResultGrid(); - var window = Ext.create('Ext.window.Window', { - title : 'Multiple select', - height : 600, - width : 600, - layout : 'fit', - items : [ specimenSelectorResultGrid.getPanel(_this.data) ], - buttons : [ { - text : 'Go', - handler : function() { - var array = []; - for ( var i = 0; i < specimenSelectorResultGrid.selected.length; i++) { - var row = specimenSelectorResultGrid.selected[i]; - array.push({ - macromoleculeId : row.macromoleculeId, - bufferId : row.bufferId - }); - } - goTo(BUI.getMacromoleculeResultsURLByMultipleSearch(array)); + /** Actions buttons **/ + var actions = []; - } - }, { - text : 'Cancel', - handler : function() { - window.close(); - } - } ] + actions.push(Ext.create('Ext.Action', { + icon : '../images/add.png', + text : 'Add', + disabled : true, + alwaysEnabled : true, + handler : function(widget, event) { + _this.buffer.bufferhasadditive3VOs.push(BIOSAXS_BEANS.getBufferhasAdditive3VO()); + _this.refresh(_this.buffer, _this.experiment); + } + })); - }).show(); + return actions; +}; +AdditiveGrid.prototype.refresh = function(buffer, experiment) { + this.buffer = buffer; + this.experiment = experiment; + if (buffer) { + if (buffer.bufferhasadditive3VOs) { + this.features = buffer.bufferhasadditive3VOs; } - } ]; + } + this.experiment = experiment; + this.store.loadData(this._prepareData(), false); }; -ResultsAssemblyGrid.prototype.getLegendPanel = function() { - return { - html : '
' + BUI.getRectangleColorDIV(this.validColor, 10, 10) + - 'Good quality measurements' + - BUI.getRectangleColorDIV(this.warningColor, 10, 10) + - 'Probably valid with manual processing' + - BUI.getRectangleColorDIV(this.notValidColor, 10, 10) + - 'More measurements need to be done
' - }; -}; -/** Returns the grid **/ -ResultsAssemblyGrid.prototype.getPanel = function(macromolecules) { - var _this = this; +AdditiveGrid.prototype.getAdditives = function() { + var additives = []; + for ( var i = 0; i < this.store.getCount(); i++) { + var bufferHasAdditive = BIOSAXS_BEANS.getBufferhasAdditive3VO(); + var data = this.store.getAt(i).getData(); - this.store = Ext.create('Ext.data.Store', { - fields : [ - 'macromoleculeId', 'macromoleculeAcronym', 'measurementCount', 'subtractionCount', 'averageCount', 'timeStart', 'concentrationArray', - 'bufferConditions', 'conditions' ], - data : this._prepareData(macromolecules) - }); + bufferHasAdditive.additive3VO.name = data.name; + bufferHasAdditive.additive3VO.comments = data.comments; + bufferHasAdditive.additive3VO.additiveType = data.additiveType; - this.store.sort('macromoleculeAcronym'); + bufferHasAdditive.bufferId = this.buffer.bufferId; + bufferHasAdditive.bufferHasAdditiveId = data.bufferHasAdditiveId; + bufferHasAdditive.quantity = data.quantity; + additives.push(bufferHasAdditive); + } - this.grid = Ext.create('Ext.grid.Panel', { - id : this.id, - title : 'Macromolecules', - store : this.store, - tbar : this._getTbar(), - bbar : [ this.getLegendPanel() ], - width : this.width, - height : this.height, - maxHeight : this.maxHeight, - sortableColumns : true, - columns : [ - { - text : '', - dataIndex : 'macromoleculeId', - width : 20, - hidden : false, - renderer : function(val, y, sample) { - return BUI.getRectangleColorDIV(BIOSAXS.proposal.macromoleculeColors[val], 10, 10); - } - }, - { - text : 'Macromolecule', - dataIndex : 'macromoleculeAcronym', - width : 200 + return additives; +}; + +AdditiveGrid.prototype._getFields = function(buffers) { + var columns = [ + + { + header : 'Name', + dataIndex : 'name', + type : 'string', + editor : { + allowBlank : true }, - { - text : 'Buffer Conditions', - dataIndex : 'conditions', - flex : 1, - renderer : function(val, style, record) { - return _this.getConditionHTMLTable(val, style, record); - } + flex : 1 + }, + { + header : 'Type', + name : 'additiveType', + dataIndex : 'additiveType', + editor : { + xtype : 'combobox', + typeAhead : true, + triggerAction : 'all', + selectOnTab : true, + store : BIOSAXS.proposal.getAdditiveTypes(), + lazyRender : true, + listClass : 'x-combo-list-small' }, - { - header : 'Average', - dataIndex : 'percentageCollected', - name : 'percentageCollected', - type : 'string', - hidden : true, - renderer : function(val, y, sample) { - return "
" + - BUI.getProgessBar((sample.raw.averageCount / sample.raw.measurementCount) * 100, sample.raw.averageCount + "/" + - sample.raw.measurementCount) + "
"; - }, - width : 100, - sorter : false + flex : 0.6 + }, + { + header : 'Quantity', + dataIndex : 'quantity', + name : 'quantity', + editor : { + allowBlank : true }, - { - header : 'Subtractions', - dataIndex : 'percentageCollected', - name : 'percentageCollected', - type : 'string', - hidden : true, - renderer : function(val, y, sample) { - return "
"+ BUI.getProgessBar((sample.raw.subtractionCount / sample.raw.measurementCount) * 100, sample.raw.subtractionCount + "/" + - sample.raw.measurementCount) + "
"; - }, - width : 100 - }, { - header : 'Date', - dataIndex : 'timeStart', - name : 'timeStart', - type : 'string', - renderer : function(value, metaData, record, rowIndex, colIndex, store) { - if (record.raw.timeStart != null) { - return moment(record.raw.timeStart).format("MMM Do YY"); + type : 'string', + flex : 1 + }, + { + xtype : 'actioncolumn', + items : [ { + icon : '../images/cancel.png', + tooltip : 'Delete additive', + scope : this, + handler : function(grid, rowIndex, colIndex) { + if ((grid.getStore().getAt(rowIndex).data.bufferHasAdditiveId == null)|| (grid.getStore().getAt(rowIndex).data.bufferHasAdditiveId == "")) { + grid.getStore().removeAt(rowIndex); + } else { + this.onRemoveButtonClicked.notify({ + 'bufferId' : this.buffer.bufferId, + 'bufferHasAdditiveId' : this.store.getAt(rowIndex).data.bufferHasAdditiveId + }); } } - }, { - header : 'Download', - dataIndex : 'percentageCollected', - name : 'percentageCollected', - type : 'string', - renderer : function(val, y, sample) { - return BUI.getZipHTMLByMacromoleculeId(sample.raw.macromoleculeId); - }, - width : 100 - }, + } ] + } ]; + + return columns; +}; + +AdditiveGrid.prototype._prepareData = function() { + var data = []; + if (this.features == null) { + this.features = []; + } + for (var i = 0; i < this.features.length; i++) { + var object = this.features[i]; + object.name = this.features[i].additive3VO.name; + object.additiveType = this.features[i].additive3VO.additiveType; + object.comments = this.features[i].additive3VO.comments; + object.additiveId = this.features[i].additive3VO.additiveId; + data.push(object); + } + return data; +}; - { - id : 'btnResultVisible', - width : 85, - sortable : false, - renderer : function(value, metaData, record, rowIndex, colIndex, store) { - return BUI.getGreenButton('GO'); - } - } ], +AdditiveGrid.prototype.getPanel = function(buffer, experiment) { + this.buffer = buffer; + this.features = buffer.bufferhasadditive3VOs; + this.experiment = experiment; + return this._renderGrid(); +}; + +AdditiveGrid.prototype.getStore = function() { + var _this = this; + var store = Ext.create('Ext.data.Store', { + fields : [ "name", "additiveType", "comments", "additiveId", "bufferHasAdditiveId", "quantity" ], + autoload : false, + data : this._prepareData(), + listeners : { + update : function(store, record) { + record.index = _this.grid.getSelectionModel().getCurrentPosition().row; + _this.buffer.bufferhasadditive3VOs[record.index].additive3VO.name = record.data.name; + _this.buffer.bufferhasadditive3VOs[record.index].additive3VO.additiveType = record.data.additiveType; + _this.buffer.bufferhasadditive3VOs[record.index].additive3VO.comments = record.data.comments; + _this.buffer.bufferhasadditive3VOs[record.index].quantity = record.data.quantity; + } + } + }); + + // store.sort('bufferHasAdditiveId', 'ASC'); + store.loadData(this._prepareData(), false); + return store; +}; + +AdditiveGrid.prototype._renderGrid = function() { + var _this = this; + this.store = this.getStore(); + + var cellEditing = Ext.create('Ext.grid.plugin.CellEditing', { + clicksToEdit : 1 + }); + + this.grid = Ext.create('Ext.grid.Panel', { + dockedItems : [ { + xtype : 'toolbar', + items : this._getActions() + } ], + store : this.store, + height : 230, + title : "Additives", + width : "100%", + columns : _this._getFields(), + plugins : [ cellEditing ], viewConfig : { stripeRows : true, listeners : { - afterrender : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { - }, - celldblclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { - _this.edit(record.data.macromoleculeId); - }, - cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { - if (grid.getGridColumns()[cellIndex].getId() == 'buttonEditMacromolecule') { - _this.edit(record.data.macromoleculeId); - } - if (grid.getGridColumns()[cellIndex].getId() == 'buttonRemoveMacromolecule') { - BUI.showBetaWarning(); - } - if (grid.getGridColumns()[cellIndex].getId() == 'btnResultVisible') { - window.location = BUI.getMacromoleculeResultsURL(record.data.macromoleculeId); - } + itemcontextmenu : function(view, rec, node, index, e) { + e.stopEvent(); + contextMenu.showAt(e.getXY()); + return false; } } + }, + selModel : { + mode : 'SINGLE' } }); - /** Adding the tbar **/ - if (this.tbar) { - this.grid.addDocked({ - xtype : 'toolbar', - items : this.getTbar() - }); - } return this.grid; }; -ResultsAssemblyGrid.prototype.input = function(targetId) { - return { - data : DATADOC.getData_3367(), - proposal : DATADOC.getProposal_3367() - }; +AdditiveGrid.prototype.input = function() { }; -ResultsAssemblyGrid.prototype.test = function(targetId) { - var grid = new ResultsAssemblyGrid({ - height : 600, - searchBar : false, - tbar : false, - btnResultVisible : true, - width : 1000 - }); - BIOSAXS.proposal = new Proposal(grid.input().proposal); - var panel = grid.getPanel(grid.input().data); +AdditiveGrid.prototype.test = function(targetId) { + var grid = new AdditiveGrid(); + var panel = grid.getPanel([]); panel.render(targetId); }; +/** AnalysisGrid **/ +function AnalysisGrid(args) { + var _this = this; -function RigidModelGrid(args) { - this.height = null; - this.width = null; this.id = BUI.id(); + if (Ext.get("mainPanel")){ + this.width = Ext.get("mainPanel").getWidth(); + } + else{ + this.width = this.maxWidth; + } + this.maxWidth = 1500; + + /** Visibles **/ + this.isGuinierTabVisible = true; + this.isGnomTabVisible = true; + this.isPorodTabVisible = true; + this.isScatteringPlotVisible = true; + this.isAbinitioTabVisible = true; + this.showButtonsVisible = true; + this.isI0Visible = true; + + this.margin = null; + this.grouped = true; + + this.hideNulls = false; + this.decimals = 4; + this.height = null; + this.sorters = [ { + property : 'conc', + direction : 'ASC' + } ]; if (args != null) { + if (args.grouped != null) { + this.grouped = args.grouped; + } + + if (args.showButtonsVisible != null) { + this.showButtonsVisible = args.showButtonsVisible; + } + if (args.isI0Visible != null) { + this.isI0Visible = args.isI0Visible; + } + if (args.sorters != null) { + this.sorters = args.sorters; + } + if (args.isScatteringPlotVisible != null) { + this.isScatteringPlotVisible = args.isScatteringPlotVisible; + } + if (args.isGuinierTabVisible != null) { + this.isGuinierTabVisible = args.isGuinierTabVisible; + } + if (args.isGnomTabVisible != null) { + this.isGnomTabVisible = args.isGnomTabVisible; + } + if (args.isPorodTabVisible != null) { + this.isPorodTabVisible = args.isPorodTabVisible; + } + if (args.hideNulls != null) { + this.hideNulls = args.hideNulls; + } if (args.height != null) { this.height = args.height; + } + if (args.width != null) { + this.width = args.width; + } + if (args.maxWidth != null) { + this.maxWidth = args.maxWidth; + } + } +} - if (args.width != null) { - this.width = args.width; +AnalysisGrid.prototype.refresh = function(data, args) { + this.store.loadData(this._prepareData(data), false); + if (args != null){ + if (args.experiment != null){ + this.experiment = args.experiment; + } + } +}; + +AnalysisGrid.prototype._getPorod = function() { + return { + text : 'Porod', + name : 'Porod', + columns : [ + { + text : 'Volume', + dataIndex : 'volumeEdna', + width : 75, + sortable : true, + hidden : !this.isPorodTabVisible, + renderer : function(val, y, sample) { + if (sample.raw.volume != null) + return BUI.formatValuesUnits(sample.raw.volume, '') + " nm3"; + } + }, + { + text : 'MM Vol. est.', + dataIndex : 'volumeEdna', + tooltip : '[Volume/2 - Volume/1.5] (Guinier)', + sortable : true, + width : 95, + hidden : !this.isPorodTabVisible, + renderer : function(val, y, sample) { + if (sample.raw.volume != null) + return Number(sample.raw.volume / 2).toFixed(1) + " - " + Number(sample.raw.volume / 1.5).toFixed(1)+ "kD"; + } + } ] + }; +}; + + +AnalysisGrid.prototype._getFramesColumn = function() { + var _this = this; + return { + text : 'Frames (Averaged/Total)', + dataIndex : 'datacollection', + name : 'datacollection', + sortable : true, + width : 150, + renderer : function(dataCollections, y, data) { + /** Bug of Webservices: frames count were swapped with frames averages **/ + if (data.raw.bufferAfterFramesCount){ + if (data.raw.bufferAfterFramesMerged){ + if (parseInt(data.raw.bufferAfterFramesMerged) > parseInt(data.raw.bufferAfterFramesCount)){ + var aux = parseInt(data.raw.bufferAfterFramesCount); + data.raw.bufferAfterFramesCount= data.raw.bufferAfterFramesMerged; + data.raw.bufferAfterFramesMerged = aux; + } + } + } + + if (data.raw.bufferBeforeFramesCount){ + if (data.raw.bufferBeforeFramesMerged){ + if (parseInt(data.raw.bufferBeforeFramesMerged) > parseInt(data.raw.bufferBeforeFramesCount)){ + var aux = parseInt(data.raw.bufferBeforeFramesCount); + data.raw.bufferBeforeFramesCount= data.raw.bufferBeforeFramesMerged; + data.raw.bufferBeforeFramesMerged = aux; + } + } + + } + + var bufferAcronym = data.raw.bufferAcronym; + var macromoleculeAcronym = data.raw.macromoleculeAcronym; + var bbmerges = data.raw.bufferBeforeFramesMerged; + var molmerges = data.raw.framesMerge; + var bamerges = data.raw.bufferAfterFramesMerged; + var totalframes = data.raw.framesCount; + var bufferId = data.raw.bufferId; + var macromoleculeId = data.raw.macromoleculeId; + var macromoleculeColor = null; + if (_this.experiment != null){ + macromoleculeColor = _this.experiment.macromoleculeColors[data.raw.macromoleculeId]; } - } - } - - this.onSelected = new Event(this); -} - - -RigidModelGrid.prototype.refresh = function(subtractions) { - var data = []; - if (subtractions != null){ - if (subtractions.length > 0){ - for (j in subtractions){ - var subtraction = subtractions[j]; - if (subtraction.rigidBodyModeling3VOs != null){ - for (i in subtraction.rigidBodyModeling3VOs){ - data.push(subtraction.rigidBodyModeling3VOs[i]); + + /** BUG in the database to be fixed **/ + try{ + if (totalframes != null){ + if(molmerges != null){ + if (parseFloat(totalframes) < parseFloat(molmerges)){ + var aux = totalframes; + totalframes = molmerges; + molmerges = aux; + } } } } + catch(e){ + + } + + return BUI.getHTMLTableForFrameAveraged(bufferAcronym, + macromoleculeAcronym, + bbmerges, + molmerges, + bamerges, + totalframes, + bufferId, + macromoleculeId, + macromoleculeColor); } - } - this.store.loadData(data); + }; }; -RigidModelGrid.prototype.getPanel = function() { +AnalysisGrid.prototype._getColumns = function() { var _this = this; - this.store = Ext.create('Ext.data.Store', { - fields : [ 'symmetry', 'subtractionId', 'subUnitConfigFilePath', 'rigidBodyModelingId', 'rigidBodyModelFilePath', 'logFilePath', 'fitFilePath', 'curveConfigFilePath', - 'crossCorrConfigFilePath', 'contactDescriptionFilePath' ], - data : [] - }); - - this.store.sort([ { - property : 'name', - direction : 'ASC' - } ]); + return [ + { + name : 'groupeField', + dataIndex : 'groupeField', + hidden : true - this.panel = Ext.create('Ext.grid.Panel', { - store : this.store, - width : this.width, - height : this.height, - margin : 10, - deferredRender : false, - columns : [ { - text : 'RBM', - dataIndex : 'rigidBodyModelFilePath', - hidden : false, - flex : 1, - renderer : function(val){ - return BUI.getFileName(val); - } - }, { - text : 'Sub Unit Conf.', - dataIndex : 'subUnitConfigFilePath', + }, + { + "header" : "subtractionId", hidden : true, - flex : 1, - renderer : function(val){ - return BUI.getFileName(val); + "dataIndex" : "subtractionId", + "name" : "subtractionId" + }, + { + header : "Macromolecule", + dataIndex : "macromoleculeAcronym", + name : "macromoleculeAcronym", + renderer : function(val, y, sample) { + return '
' + val + '
' + + BUI.formatValuesUnits(sample.raw.conc, "mg/ml", 10, this.decimals) + '
' + + BUI.formatValuesUnits(sample.raw.exposureTemperature, "C", 10, this.decimals) + '
' } - }, { - text : 'Log', - dataIndex : 'logFilePath', + }, + { + header : "Order", + dataIndex : "priorityLevelId", + name : "priorityLevelId", + hidden : true + }, + { + header : "Code", + dataIndex : "code", + name : "code", + hidden : true + }, + { + header : "Conc.", + dataIndex : "conc", + width : 80, + name : "conc", + type : "number", hidden : true, - flex : 1, - renderer : function(val){ - return BUI.getFileName(val); - } - }, { - text : 'Fit', - dataIndex : 'fitFilePath', - hidden : false, - flex : 1, - renderer : function(val){ - return BUI.getFileName(val); - } - }, { - text : 'Curve Conf.', - dataIndex : 'curveConfigFilePath', - hidden : false, - flex : 1, - renderer : function(val){ - return BUI.getFileName(val); + renderer : function(val, y, sample) { + return BUI.formatValuesUnits(val, "mg/ml", 10, this.decimals); } - }, { - text : 'Cross Corr.', - dataIndex : 'crossCorrConfigFilePath', - hidden : false, - flex : 1, - renderer : function(val){ - return BUI.getFileName(val); + }, + { + text : 'Scattering', + width : 100, + dataIndex : 'subtractionId', + name : 'subtractionId', + hidden : !this.isScatteringPlotVisible, + renderer : function(val, y, sample) { + var url = BUI.getURL() + '&type=scattering&subtractionId=' + sample.raw.subtractionId; + var event = "OnClick= window.open('" + url + "')"; + return ''; } - }, { - text : 'Contact Desc.', - dataIndex : 'contactDescriptionFilePath', + }, + { + text : 'Kratky', + width : 100, + dataIndex : 'subtractionId', hidden : true, - flex : 1, - renderer : function(val){ - return BUI.getFileName(val); + name : 'subtractionId', + renderer : function(val, y, sample) { + var url = BUI.getURL() + '&type=kratky&subtractionId=' + sample.raw.subtractionId; + var event = "OnClick= window.open('" + url + "')"; + return ''; } - } ], - - viewConfig : { - enableTextSelection : true, - preserveScrollOnRefresh : true }, - listeners : { - cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { - - }, - afterrender : function() { + this._getFramesColumn(), + { + text : 'File Name', + dataIndex : 'averageFilePath', + name : 'averageFilePath', + sortable : true, + width : 150, + hidden : true, + renderer : function(dataCollections, y, data) { + return BUI.getHTMLTableForPrefixes(data.raw.bufferBeforeAverageFilePath, data.raw.averageFilePath, + data.raw.bufferAfterAverageFilePath); } - } - }); - return this.panel; -}; + }, - -/** - * shows shipments - * - * @height - * @width - * @minHeight - * @btnEditVisible - */ -function ShipmentGrid(args) { - this.id = BUI.id(); - this.height = 100; - this.width = null; - this.minHeight = null; - this.btnEditVisible = true; + { + text : 'Guinier', + name : 'Guinier', + columns : [ + { + text : 'Rg', + dataIndex : 'rgGuinier', + name : 'rgGuinier', + hidden : !this.isGuinierTabVisible, + width : 75, + tooltip : 'In polymer physics, the radius of gyration is used to describe the dimensions of a polymer chain.', + sortable : true, + renderer : function(val, y, sample) { + if (sample.raw.rgGuinier != null) { + /** Show warning if rgGuinier and rgGnom differ more than 10% **/ + if (Math.abs(sample.raw.rgGuinier - sample.raw.rgGnom) > (sample.raw.rgGuinier * 0.1)) { + return "" + BUI.formatValuesUnits(sample.raw.rgGuinier, "") + ""; - if (args != null) { - if (args.height != null) { - this.height = args.height; - } - if (args.width != null) { - this.width = args.width; - } - if (args.minHeight != null) { - this.minHeight = args.minHeight; - } - if (args.btnEditVisible != null) { - this.btnEditVisible = args.btnEditVisible; - } - } -} + } + return BUI.formatValuesUnits(sample.raw.rgGuinier, "nm", 12, this.decimals); + } + } + }, + { + text : 'Points', + dataIndex : 'points', + sortable : true, + width : 100, + type : 'string', + hidden : !this.isGuinierTabVisible, + renderer : function(val, y, sample) { + if ((sample.raw.firstPointUsed == "") || (sample.raw.firstPointUsed == null)) + return; + return "" + sample.raw.firstPointUsed + " - " + sample.raw.lastPointUsed + " (" + + (sample.raw.lastPointUsed - sample.raw.firstPointUsed) + " )"; + } + }, + { + text : 'Quality', + dataIndex : 'quality', + hidden : !this.isGuinierTabVisible, + tooltip : 'Estimated data quality. 1.0 - means ideal quality, 0.0 - unusable data. In table format it is given in percent (100% - ideal quality, 0% - unusable data). Please note that this estimation is based only on the Guinier interval (very low angles).', + width : 60, + sortable : true, + renderer : function(val, y, sample) { + if (sample.raw.quality != null) { + val = sample.raw.quality; + if ((val != null) && (val != "")) { + return "" + (Number(val) * 100).toFixed(2) + " %"; + } + } + } + }, { + text : 'I(0)', + dataIndex : 'I0', + sortable : true, + hidden : !this.isI0Visible, + tooltip : 'Extrapolated scattering intensity at zero angle I(0) (forward scattering)', + width : 75, + type : 'string', + renderer : function(val, y, sample) { + if (sample.raw.I0 != null) { + return BUI.formatValuesErrorUnitsScientificFormat(sample.raw.I0, sample.raw.i0stdev, ""); + } + } + }, { + text : 'Aggregated', + tooltip : "If aggregation was detected from the slope of the data curve at low angles the value is '1', otherwise '0'.", + dataIndex : 'isagregated', + hidden : true, + width : 75, + renderer : function(val, y, sample) { + if ((sample.raw.isagregated != null)) { + if (val == true) { + return "Yes"; + } else { + return "No"; + } + } + } + }, { + text : 'Guinier', + sortable : true, + dataIndex : 'subtractionId', + type : 'string', + width : 100, + hidden : true, + renderer : function(val, y, sample) { -ShipmentGrid.prototype._getColumns = function() { - var _this = this; - var columns = [ - { - text : 'Name', - dataIndex : 'shippingName', - flex : 1 + if (sample.raw.subtractionId != null) { + var url = BUI.getURL() + '&type=guinier&subtractionId=' + sample.raw.subtractionId; + var event = "OnClick= window.open('" + url + "')"; + return ''; + } + } + } ] }, { - header : 'Type', - dataIndex : 'shippingType', - flex : 1, - hidden : true, - renderer : function(val, comp, record) { - if (val != null) { - return val.toUpperCase(); - } + text : 'Gnom', + name : 'Gnom', - } - }, - { - header : 'Status', - type : 'string', - flex : 1, - hidden : false, - renderer : function(comp, val, record) { - if (record.raw.shippingStatus != null) { - if (new String(record.raw.shippingStatus).toUpperCase() == 'OPENED') { - return "" + new String(record.raw.shippingStatus).toUpperCase() + ""; + columns : [ { + text : 'Rg', + dataIndex : 'rgGnom', + type : 'string', + width : 65, + hidden : !this.isGnomTabVisible, + sortable : true, + renderer : function(val, y, sample) { + /** Show warning if rgGuinier and rgGnom differ more than 10% **/ + if (sample.raw.rgGnom != null) { + if (Math.abs(sample.raw.rgGuinier - sample.raw.rgGnom) > (sample.raw.rgGuinier * 0.1)) { + return "" + BUI.formatValuesUnits(sample.raw.rgGnom, "") + ""; + + } + return BUI.formatValuesUnits(sample.raw.rgGnom, "nm"); } - return "" + new String(record.raw.shippingStatus).toUpperCase() + ""; } - } + }, { + text : 'Total', + dataIndex : 'total', + width : 65, + hidden : !this.isGnomTabVisible, + sortable : true, + renderer : function(val, y, sample) { + if (sample.raw.total != null) + return BUI.formatValuesUnits(sample.raw.total, ''); + } + }, { + text : 'Dmax', + dataIndex : 'dmax', + sortable : true, + width : 75, + hidden : !this.isGnomTabVisible, + renderer : function(val, y, sample) { + if (sample.raw.dmax != null) + return BUI.formatValuesUnits(sample.raw.dmax, "") + " nm"; + } + }, { + text : 'P(r)', + sortable : true, + hidden : true, + width : 100, + dataIndex : 'subtractionId', + type : 'string', + renderer : function(val, y, sample) { + var url = BUI.getURL() + '&type=gnom&subtractionId=' + sample.raw.subtractionId; + var event = "OnClick= window.open('" + url + "')"; + return ''; + } + } ] }, + this._getPorod(), { - text : 'Cases', - flex : 1, - hidden : false, - renderer : function(comp, val, record) { - var shipment = BIOSAXS.proposal.getShipmentById(record.data.shippingId); - var container = ""; - if (shipment.dewarVOs.length > 0) { - container = container + ""; - } else { - return "Empty"; - } - return container + "
" + shipment.dewarVOs.length + "x
"; - } - }, { - header : 'Comments', - dataIndex : 'comments', - flex : 1, - hidden : false - }, { - header : 'Creation Date', - dataIndex : 'creationDate', - hidden : true - } ]; - - if (this.btnEditVisible) { - columns.push({ - id : _this.id + 'buttonEdit', - text : '', - hidden : !_this.btnEditVisible, - width : 100, - sortable : false, - renderer : function(value, metaData, record, rowIndex, colIndex, store) { - return BUI.getGreenButton('EDIT'); - } - }); - } - - columns.push({ - id : _this.id + 'buttonRemove', - text : '', - width : 100, - sortable : false, - renderer : function(value, metaData, record, rowIndex, colIndex, store) { - var shipment = BIOSAXS.proposal.getShipmentById(record.data.shippingId); - if (shipment.dewarVOs.length == 0) { - return BUI.getRedButton('REMOVE'); - } - - } - }); - - return columns; -}; - -ShipmentGrid.prototype._getTopButtons = function() { - var _this = this; - /** Actions buttons **/ - var actions = []; - actions.push(Ext.create('Ext.Action', { - icon : '../images/add.png', - text : 'Add Shipment', - handler : function(widget, event) { - //window.location = BUI.getCreateShipmentURL(); - var _this = this; - - var shipmentForm = new ShipmentForm({ - creationMode : true, - showTitle : false - }); - shipmentForm.onSaved.attach(function(sender, shipment) { - _this.showShipmentTabs(shipment, targetId); - }); - - var window = Ext.create('Ext.window.Window', { - title : 'New Shipment', - height : 600, - width : 800, - layout : 'fit', - items : [ shipmentForm.getPanel() ] - }).show(); - - } - })); - return actions; -}; - -ShipmentGrid.prototype.refresh = function(shippings) { - this.features = shippings; - this.store.loadData(this._prepareData(), false); -}; - -ShipmentGrid.prototype._prepareData = function() { - var data = []; - for ( var i = 0; i < this.features.length; i++) { - data.push(this.features[i]); - } - return data; -}; - -ShipmentGrid.prototype.getPanel = function(shipments) { - this.features = shipments; - return this._renderGrid(); -}; - -ShipmentGrid.prototype.edit = function(shippingId) { - window.location = BUI.getShippingURL(shippingId); -}; - -ShipmentGrid.prototype._getStoreFields = function(data) { - var _this = this; - return [ { - name : 'shippingId' - }, { - name : 'shippingName' - }, { - name : 'shippingStatus' - }, { - name : 'shippingType' - }, { - name : 'creationDate' - }, { - name : 'comments' - } ]; -}; - -ShipmentGrid.prototype._renderGrid = function() { - var _this = this; - - /** Store **/ - var data = this._prepareData(); - this.store = Ext.create('Ext.data.Store', { - fields : this._getStoreFields(data), - autoload : true, - data : data - }); - - this.store.loadData(data, false); + text : 'AbInitio Modeling', + name : 'AbInitio_modeling', - this.grid = Ext.create('Ext.grid.Panel', { - title : "Shipping", - icon : '/ispyb/images/plane.gif', - width : this.width, - minWidth : this.minWidth, - height : this.height, - store : this.store, - columns : this._getColumns(), - viewConfig : { - stripeRows : true, - listeners : { - itemdblclick : function(dataview, record, item, e) { - _this.edit(record.raw.shippingId); + columns : [ + { + text : 'NSD', + dataIndex : 'volumeEdna', + width : 100, + sortable : true, + hidden : true, + renderer : function(val, y, sample) { + var url = BUI.getNSDImageURL(sample.raw.modelListId); + var event = "OnClick= window.open('" + url + "')"; + return ''; + } }, - cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { - if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonEdit') { - _this.edit(record.data.shippingId); + { + text : 'Chi2', + dataIndex : 'volumeEdna', + sortable : true, + hidden : true, + width : 100, + renderer : function(val, y, sample) { + var url = BUI.getCHI2ImageURL(sample.raw.modelListId); + var event = "OnClick= window.open('" + url + "')"; + return ''; } + }, + { + text : 'Pdb', + dataIndex : 'volumeEdna', + tooltip : 'Damaver: The program suite DAMAVER is a set of programs to align ab initio models, select the most typical one and build an averaged model. Dammif : Rapid ab initio shape determination by simulated annealing using a single phase dummy atom model. Dammin :Ab initio shape determination by simulated annealing using a single phase dummy atom model', + sortable : true, + width : 125, + hidden : !this.isAbinitioTabVisible, + renderer : function(val, y, sample) { + var html = new String(); + var split = null; + var url = null; + var file = sample.raw.averagedModel; + if (file != null) { + split = file.split("/"); + if (split.length > 0) { + url = BUI.getPdbURL() + '&type=AVERAGED&abInitioModelId=' + sample.raw.abInitioModelId; + html = html + ''+ split[split.length - 1] + '

'; - if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonRemove') { - var shipment = BIOSAXS.proposal.getShipmentById(record.data.shippingId); - if (shipment.dewarVOs.length == 0) { + } + } - var adapter = new BiosaxsDataAdapter(); - _this.grid.setLoading("ISPyB: Removing shipment"); - adapter.onSuccess.attach(function(sender) { - BIOSAXS.proposal.onInitialized.attach(function(sender) { - _this.refresh(BIOSAXS.proposal.getShipments()); - _this.grid.setLoading(false); - }); - BIOSAXS.proposal.init(); - }); + file = sample.raw.rapidShapeDeterminationModel; + if (file != null) { + split = file.split("/"); + if (split.length > 0) { + url = BUI.getPdbURL() + '&type=RAPIDSHAPEDETERMINATIONMODEL&abInitioModelId=' + sample.raw.abInitioModelId; + html = html + '' + split[split.length - 1] + '

'; + } + } - adapter.onError.attach(function(sender) { - alert("Error"); - _this.grid.setLoading(false); - }); - adapter.removeShipment(record.data.shippingId); + file = sample.raw.shapeDeterminationModel; + if (file != null) { + split = file.split("/"); + if (split.length > 0) { + url = BUI.getPdbURL() + '&type=SHAPEDETERMINATIONMODEL&abInitioModelId=' + sample.raw.abInitioModelId; + html = html + ''+ split[split.length - 1] + ''; + } } + return html; } - } - } - }, - selModel : { - mode : 'SINGLE' - } - }); - - var actions = _this._getTopButtons(); - this.grid.addDocked({ - xtype : 'toolbar', - items : actions - }); - - this.grid.getSelectionModel().on({ - selectionchange : function(sm, selections) { - if (selections.length) { - for ( var i = 0; i < actions.length; i++) { - if (actions[i].enable) { - actions[i].enable(); + }, + { + text : 'Damaver', + dataIndex : 'volumeEdna', + hidden : true, + tooltip : 'Damaver: The program suite DAMAVER is a set of programs to align ab initio models, select the most typical one and build an averaged model. ', + sortable : true, + width : 125, + renderer : function(val, y, sample) { + var file = sample.raw.averagedModel; + if (file != null) { + var split = file.split("/"); + if (split.length > 0) { + var url = BUI.getPdbURL() + '&type=AVERAGED&abInitioModelId=' + sample.raw.abInitioModelId; + return '' + split[split.length - 1]+ ''; + } + } + return sample.raw.averagedModel; } - } - } else { - for ( var i = 0; i < actions.length; i++) { - if (actions[i].alwaysEnabled == false) { - if (actions[i].disable) { - actions[i].disable(); + }, + { + text : 'Dammif', + dataIndex : 'volumeEdna', + hidden : true, + tooltip : 'Dammif : Rapid ab initio shape determination by simulated annealing using a single phase dummy atom model', + sortable : true, + width : 125, + renderer : function(val, y, sample) { + var file = sample.raw.rapidShapeDeterminationModel; + if (file != null) { + var split = file.split("/"); + if (split.length > 0) { + var url = BUI.getPdbURL() + '&type=RAPIDSHAPEDETERMINATIONMODEL&abInitioModelId=' + sample.raw.abInitioModelId; + return '' + split[split.length - 1] + ''; + } } + return sample.raw.averagedModel; } - } - } - } - }); - return this.grid; -}; - -ShipmentGrid.prototype.input = function() { - return { - proposal : new MeasurementGrid().input().proposal, - shippings : DATADOC.getShippings_10() - }; -}; - -ShipmentGrid.prototype.test = function(targetId) { - var shipmentGrid = new ShipmentGrid({ - minHeight : 300, - height : 440 - }); - BIOSAXS.proposal = new Proposal(shipmentGrid.input().proposal); - var panel = shipmentGrid.getPanel(targetId); - panel.render(targetId); - shipmentGrid.refresh(shipmentGrid.input().shippings); -}; - -function SpecimenGrid(args) { - this.id = BUI.id(); - this.height = 500; - this.unitsFontSize = 9; - this.editEnabled = false; - this.isPositionColumnHidden = false; - this.removeBtnEnabled = false; - - this.selectionMode = "MULTI"; - this.updateRowEnabled = false; - this.grouped = true; - this.width = 900; - this.title = 'Specimens'; - - this.margin = "0 0 0 0"; -// this.experimentColorBased = false; - - if (args != null) { - if (args.height != null) { - this.height = args.height; - } - - if (args.showTitle == false) { - this.title = null; - } - - if (args.margin == false) { - this.margin = args.margin; - } - - if (args.grouped == false) { - this.grouped = null; - } - - if (args.width != null) { - this.width = args.width; - } - - - if (args.editEnabled != null) { - this.editEnabled = args.editEnabled; - } - if (args.removeBtnEnabled != null) { - this.removeBtnEnabled = args.removeBtnEnabled; - } - if (args.isPositionColumnHidden != null) { - this.isPositionColumnHidden = args.isPositionColumnHidden; - } - if (args.selectionMode != null) { - this.selectionMode = args.selectionMode; - } - if (args.updateRowEnabled != null) { - this.updateRowEnabled = args.updateRowEnabled; - } - - } - this.onClick = new Event(this); - this.onSelected = new Event(this); - this.onRemoved = new Event(this); - this.onSpecimenChanged = new Event(); -} - -SpecimenGrid.prototype._prepareData = function(experiment) { - var data = []; - - var samples = experiment.getSamples(); - for ( var i = 0; i < samples.length; i++) { - var sample = samples[i]; - if (sample.macromolecule3VO != null) { - sample.macromolecule = sample.macromolecule3VO.acronym; - sample.exposureTemperature = []; - sample.macromoleculeId = sample.macromolecule3VO.macromoleculeId; - } + }, + { + text : 'Dammin', + dataIndex : 'volumeEdna', + hidden : true, + tooltip : 'Dammin :Ab initio shape determination by simulated annealing using a single phase dummy atom model', + sortable : true, + width : 125, + renderer : function(val, y, sample) { + var file = sample.raw.shapeDeterminationModel; + if (file != null) { + var split = file.split("/"); + if (split.length > 0) { + var url = BUI.getPdbURL() + '&type=SHAPEDETERMINATIONMODEL&abInitioModelId=' + sample.raw.abInitioModelId; + return '' + split[split.length - 1]+ ''; + } + } + return sample.raw.averagedModel; + } + } ] + }, { + text : 'Time', + dataIndex : 'substractionCreationTime', + name : 'substractionCreationTime', + hidden : true, + width : 80, + sortable : true, + renderer : function(value, metaData, record, rowIndex, colIndex, store) { + try { + if (record.raw.substractionCreationTime != null) { + return moment(record.raw.substractionCreationTime).format('h:mm:ss a'); + } + } catch (e) { + return "NA"; + } + } + }, { + text : 'Date', + dataIndex : 'substractionCreationTime', + name : 'substractionCreationTime', + hidden : true, + width : 80, + sortable : true, + renderer : function(value, metaData, record, rowIndex, colIndex, store) { + try { + if (record.raw.substractionCreationTime != null) { + return moment(record.raw.substractionCreationTime).format("LL"); + } + } catch (e) { + return "NA"; + } + } + }, + this._getButtons()]; +}; - if (sample.sampleplateposition3VO != null) { - if (sample.sampleplateposition3VO.samplePlateId != null) { - sample.samplePlateId = sample.sampleplateposition3VO.samplePlateId; - sample.rowNumber = sample.sampleplateposition3VO.rowNumber; - sample.columnNumber = sample.sampleplateposition3VO.columnNumber; - if (experiment.getSamplePlateById(sample.sampleplateposition3VO.samplePlateId).plategroup3VO != null) { - sample.plateGroupName = experiment.getSamplePlateById(sample.sampleplateposition3VO.samplePlateId).plategroup3VO.name; - sample.samplePlateName = experiment.getSamplePlateById(sample.sampleplateposition3VO.samplePlateId).name + " [" + sample.plateGroupName + "]"; - sample.slotPositionColumn = experiment.getSamplePlateById(sample.sampleplateposition3VO.samplePlateId).slotPositionColumn; +AnalysisGrid.prototype._getButtons = function() { + return { + id : this.id + 'buttonPlot', + name : 'buttonPlot', + hidden : !this.showButtonsVisible, + width : 110, + sortable : false, + renderer : function(value, metaData, sample, rowIndex, colIndex, store) { + var html = ""; + + if (sample.raw.subtractionId) { + html = html + ""; + if (sample.raw.rapidShapeDeterminationModelId != null) { + html = html + ""; } } - } else { - sample.samplePlateName = "Unallocated Specimens"; + return html + "
" + BUI.getGreenButton('Calibration', { + height : 20, + width : 100 + }) + "
" + BUI.getGreenButton('Primary Data Proc.', { + height : 20, + width : 100 + }) + "
" + BUI.getGreenButton('AbInitio Modeling', { + height : 20, + width : 100 + }) + "
"; } - - /** For grouping, because sencha has not option for multiple grouping I add a field to your store with a convert function that concatenates these two fields and then group by that field.**/ - sample.groupIndex = sample.bufferId + sample.macromoleculeId; - var macromolecule = BIOSAXS.proposal.getMacromoleculeById(sample.macromoleculeId); - - sample.acronym = "Buffers"; - if (macromolecule != null) { - sample.acronym = BIOSAXS.proposal.getMacromoleculeById(sample.macromoleculeId).acronym; + }; +}; +AnalysisGrid.prototype._prepareData = function(data) { + if (this.hideNulls) { + var result = []; + for ( var i = 0; i < data.length; i++) { + if (data[i].subtractionId != null) { + data[i].groupeField = data[i].macromoleculeAcronym + " " + data[i].bufferAcronym; + result.push(data[i]); + } } - - sample.buffer = experiment.getBufferById(sample.bufferId); - - sample.volumeToLoad = experiment.getVolumeToLoadBySampleId(sample.sampleId); - data.push(sample); + return result; + } else { + return data; } - return data; }; -SpecimenGrid.prototype.deselectAll = function() { - this.grid.getSelectionModel().deselectAll(); -}; +AnalysisGrid.prototype.getPanel = function(data) { + var _this = this; + var columns = this._getColumns(); -SpecimenGrid.prototype.selectById = function(specimenId) { - this.grid.getSelectionModel().deselectAll(); - for ( var i = 0; i < this.grid.getStore().data.items.length; i++) { - var item = this.grid.getStore().data.items[i].raw; - if (item.specimenId == specimenId) { - this.grid.getSelectionModel().select(i); - } - } -}; + var fields = JSON.parse(JSON.stringify(columns)); + fields.push({ + name : 'experimentId', + dataIndex : 'experimentId' -SpecimenGrid.prototype.getStore = function() { - return this.store; -}; + }); + this.store = Ext.create('Ext.data.Store', { + fields : fields, + autoload : true, + data : this._prepareData(data), + groupField : 'groupeField' + }); -SpecimenGrid.prototype.getPlugins = function() { - var _this = this; + this.store.sort(this.sorters); - var plugins = []; + var features = []; - if (this.updateRowEnabled) { - plugins.push(Ext.create('Ext.grid.plugin.RowEditing', { - clicksToEdit : 1, + if (this.grouped) { + features.push({ + ftype : 'grouping', + groupHeaderTpl : '{name}', + hideGroupedHeader : false, + startCollapsed : false + }); + } + + this.grid = Ext.create('Ext.grid.Panel', { + style : { + padding : 5 + }, + maxWidth : this.maxWidth, + width : this.width, + height : this.height, + store : this.store, + columns : columns, + resizable : true, + features : features, + viewConfig : { + preserveScrollOnRefresh : true, + stripeRows : true, listeners : { - validateedit : function(grid, e) { - var measurements = []; + cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { - if (e.newValues.bufferId != e.record.raw.bufferId) { - /** If buffer has changed we have to change all the specimens sharing same datacollection **/ - var dataCollections = []; - if (e.record.raw.macromoleculeId == null) { - dataCollections = dataCollections.concat(_this.experiment.getDataCollectionsBySpecimenId(e.record.raw.specimenId)); - } else { - var sampleDataCollections = _this.experiment.getDataCollectionsBySpecimenId(e.record.raw.specimenId); - for ( var i = 0; i < sampleDataCollections.length; i++) { - var sampleDataCollection = sampleDataCollections[i]; - if (sampleDataCollection != null) { - for ( var j = 0; j < sampleDataCollection.measurementtodatacollection3VOs.length; j++) { - var measurementTODc = sampleDataCollection.measurementtodatacollection3VOs[j]; - if (measurementTODc.dataCollectionOrder == 1) { - dataCollections = dataCollections.concat(_this.experiment.getDataCollectionsBySpecimenId(_this.experiment - .getMeasurementById(measurementTODc.measurementId).specimenId)); - } - } - } - } - } - var i = null; - for ( i = 0; i < dataCollections.length; i++) { - var dataCollection = dataCollections[i]; - var specimens = _this.experiment.getSpecimenByDataCollectionId(dataCollection.dataCollectionId); - measurements = measurements.concat(specimens); + if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonPlot') { + if (e.target.defaultValue == 'AbInitio Modeling') { + var url = BUI.getPDBVisualizerURL(record.raw.averagedModelId, record.raw.subtractionId, record.raw.experimentId); + window.open(url, "_blank"); } - for ( i = 0; i < measurements.length; i++) { - var measurement = measurements[i]; - var specimen = _this.experiment.getSpecimenById(measurement.specimenId); - specimen.bufferId = e.newValues.bufferId; - new BiosaxsDataAdapter().saveSpecimen(specimen, _this.experiment); + if (e.target.defaultValue == 'Primary Data Proc.') { + _this._edit(record.raw); } - } - - /** Setting values **/ - e.record.raw.concentration = e.newValues.concentration; - e.record.raw.volume = e.newValues.volume; - /** Position **/ - if (e.record.raw.sampleplateposition3VO != null) { - var samplePlate = _this.experiment.getSamplePlateBySlotPositionColumn(e.newValues.slotPositionColumn); - if (samplePlate != null) { - e.record.raw.sampleplateposition3VO = { - columnNumber : e.newValues.columnNumber, - rowNumber : e.newValues.rowNumber, - samplePlateId : samplePlate.samplePlateId - }; - } - } else { - if (e.newValues.slotPositionColumn != null) { - var samplePlate = _this.experiment.getSamplePlateBySlotPositionColumn(e.newValues.slotPositionColumn); - if (samplePlate != null) { - e.record.raw.sampleplateposition3VO = { - columnNumber : e.newValues.columnNumber, - rowNumber : e.newValues.rowNumber, - samplePlateId : samplePlate.samplePlateId - }; - } + if (e.target.defaultValue == 'Calibration') { + _this._showCalibration(record.raw); } } - - var macromoleculeId = e.record.data.macromoleculeId; - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, specimen) { - /** Because macromolecule3VO is fecthed LAZY **/ - if (macromoleculeId != null) { - specimen.macromolecule3VO = BIOSAXS.proposal.getMacromoleculeById(macromoleculeId); - } - _this.onSpecimenChanged.notify(specimen); - _this.grid.setLoading(false); - }); - adapter.onError.attach(function() { - alert("Error"); - _this.grid.setLoading(false); - }); - _this.grid.setLoading(); - adapter.saveSpecimen(e.record.raw, _this.experiment); } } - })); - } - return plugins; + }, + selModel : { + mode : 'SINGLE' + } + }); + + return this.grid; + +}; + +AnalysisGrid.prototype._openVisualizarBySubstractionId = function(record) { + //experimentId, subtractionId) { + var experimentId = record.experimentId; + var subtractionId = record.subtractionId; + + var _this = this; + var adapter = new BiosaxsDataAdapter(); + this.subtractionId = subtractionId; + + adapter.onSuccess.attach(function(sender, data) { + var experiment = new Experiment(data); + var experimentList = new ExperimentList([ experiment ]); + var subtraction = experiment.getSubtractionById(_this.subtractionId); + _this.grid.setLoading(false); + _this._openVisualizer(experimentList, subtraction, record); + }); + this.grid.setLoading("ISPyB: Fetching experiment"); + adapter.getExperimentById(experimentId, "MEDIUM"); }; -SpecimenGrid.prototype._getRowCombo = function() { - var data = []; - for ( var i = 1; i <= 8; i++) { - data.push({ - rowNumber : i, - name : BUI.getSamplePlateLetters()[i - 1] +AnalysisGrid.prototype._edit = function(record) { + var experimentId = record.experimentId; + var _this = this; + + if (BIOSAXS.proposal.macromolecules == null) { + this.grid.setLoading("ISPyB: Fetching proposal"); + BIOSAXS.proposal.onInitialized.attach(function(sender, data) { + _this._openVisualizarBySubstractionId(record); }); + BIOSAXS.proposal.init(); + } else { + this._openVisualizarBySubstractionId(record); } +}; - var positionsStore = Ext.create('Ext.data.Store', { - fields : [ 'rowNumber', 'name' ], - data : data - }); - return Ext.create('Ext.form.ComboBox', { - store : positionsStore, - queryMode : 'local', - displayField : 'name', - valueField : 'rowNumber' +AnalysisGrid.prototype._showCalibration = function(row) { + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, data) { + Ext.create('Ext.window.Window', { + title : 'Calibration', + width : 900, + resizable : true, + height : 400, + modal : true, + frame : false, + draggable : true, + closable : true, + autoscroll : true, + paddin : 5, + layout : { + type : 'vbox', + align : 'stretch' + }, + items : [ new AnalysisGrid({ + isGuinierTabVisible : false, + isGnomTabVisible : false, + isPorodTabVisible : false, + isAbinitioTabVisible : false, + isI0Visible : true, + showButtonsVisible : false, + height : 350, + sorters : [ { + property : 'experimentId', + direction : 'DESC' + } ] + }).getPanel(data) ] + }).show(); }); + adapter.getAnalysisCalibrationByProposalId(); }; -SpecimenGrid.prototype._getColumnCombo = function() { - var data = []; - for ( var i = 1; i <= 12; i++) { - data.push({ - columnNumber : i - }); +AnalysisGrid.prototype._openVisualizer = function(experimentList, subtraction, record) { + var merges = experimentList.getMergesByDataCollectionId(subtraction.dataCollectionId); + var mergeIdList = []; + for ( var i = 0; i < merges.length; i++) { + mergeIdList.push(merges[i].mergeId); } - var positionsStore = Ext.create('Ext.data.Store', { - fields : [ 'columnNumber' ], - data : data + var dataAdapter = new BiosaxsDataAdapter(); + dataAdapter.onSuccess.attach(function(sender, merges) { + var dataCollectionCurveVisualizer = new DataCollectionCurveVisualizer(); + dataCollectionCurveVisualizer.draw(); + dataCollectionCurveVisualizer.refresh(merges, experimentList, subtraction.dataCollectionId, record); }); - return Ext.create('Ext.form.ComboBox', { - store : positionsStore, - queryMode : 'local', - displayField : 'columnNumber', - valueField : 'columnNumber' + dataAdapter.onError.attach(function(sender, error) { }); + + dataAdapter.getMergesByIdsList(mergeIdList); }; -SpecimenGrid.prototype._getSlotColumBombo = function() { - if (this.experiment){ - var length = this.experiment.getSamplePlates().length; - - var data = []; - for ( var i = 1; i <= length; i++) { - data.push({ - slotPositionColumn : i - }); - } +AnalysisGrid.prototype.input = function() { + return { + data : DATADOC.getData_3367().slice(20, 30),//[{"total":"0.447505552082","bufferBeforeMeasurementId":22150,"sampleMergeId":12373,"averagedModelId":43636,"I0":"32.50776","proposalCode":"OPD","averagedModel":"/data/pyarch/bm29/opd29/1137/1d/damaver.pdb","bufferAfterMergeId":12374,"framesCount":"10","bufferBeforeMergeId":12372,"averageFilePath":" /data/pyarch/bm29/opd29/1137/1d/MG386_016_ave.dat","bufferAfterMeasurementId":22152,"rgGuinier":"2.96883","subtractedFilePath":" /data/pyarch/bm29/opd29/1137/1d/MG386_016_sub.dat","firstPointUsed":"30","chi2RgFilePath":"/data/pyarch/bm29/opd29/1137/1d/chi2_R.png","abInitioModelId":272,"macromoleculeId":112,"code":"_20.0_1.25","transmission":"100.0","timeStart":"2013-07-17 17:10:25.743734","bufferAcronym":"MES","quality":"0.87091","shapeDeterminationModelId":43638,"expermientComments":"[BsxCube] Generated from BsxCube","bufferId":707,"isagregated":"False","dmax":"10.390905","bufferBeforeAverageFilePath":" /data/pyarch/bm29/opd29/1137/1d/MG386_015_ave.dat","exposureTemperature":"20.0","subtractionId":5165,"conc":"1.25","rapidShapeDeterminationModel":"/data/pyarch/bm29/opd29/1137/1d/damfilt.pdb","experimentCreationDate":"Jul 17, 2013 5:08:37 PM","lastPointUsed":"92","modelListId":272,"framesMerge":"10","rapidShapeDeterminationModelId":43637,"bufferAfterAverageFilePath":" /data/pyarch/bm29/opd29/1137/1d/MG386_017_ave.dat","nsdFilePath":"/data/pyarch/bm29/opd29/1137/1d/nsd.png","bufferAfterFramesMerged":"10","experimentId":1137,"macromoleculeAcronym":"MG386","i0stdev":"0.05726128","sampleMeasurementId":22151,"measurementComments":"[1] MES","priorityLevelId":2,"bufferBeforeFramesMerged":"10","substractionCreationTime":"Jul 17, 2013 5:12:32 PM","proposalNumber":"29","rgGnom":"3.05242714339","volume":"44.1406","shapeDeterminationModel":"/data/pyarch/bm29/opd29/1137/1d/dammin.pdb","comments":null},{"total":"0.582641941147","bufferBeforeMeasurementId":22152,"sampleMergeId":12375,"averagedModelId":43809,"I0":"31.4366153846","proposalCode":"OPD","averagedModel":"/data/pyarch/bm29/opd29/1137/1d/damaver.pdb","bufferAfterMergeId":12376,"framesCount":"10","bufferBeforeMergeId":12374,"averageFilePath":" /data/pyarch/bm29/opd29/1137/1d/MG386_018_ave.dat","bufferAfterMeasurementId":22155,"rgGuinier":"2.95541","subtractedFilePath":" /data/pyarch/bm29/opd29/1137/1d/MG386_018_sub.dat","firstPointUsed":"21","chi2RgFilePath":"/data/pyarch/bm29/opd29/1137/1d/chi2_R.png","abInitioModelId":273,"macromoleculeId":112,"code":"_20.0_0.65000000000000002","transmission":"100.0","timeStart":"2013-07-17 17:12:55.837462","bufferAcronym":"MES","quality":"0.870164","shapeDeterminationModelId":43811,"expermientComments":"[BsxCube] Generated from BsxCube","bufferId":707,"isagregated":"False","dmax":"10.343935","bufferBeforeAverageFilePath":" /data/pyarch/bm29/opd29/1137/1d/MG386_017_ave.dat","exposureTemperature":"20.0","subtractionId":5166,"conc":"0.65000000000000002","rapidShapeDeterminationModel":"/data/pyarch/bm29/opd29/1137/1d/damfilt.pdb","experimentCreationDate":"Jul 17, 2013 5:08:37 PM","lastPointUsed":"80","modelListId":273,"framesMerge":"10","rapidShapeDeterminationModelId":43810,"bufferAfterAverageFilePath":" /data/pyarch/bm29/opd29/1137/1d/MG386_019_ave.dat","nsdFilePath":"/data/pyarch/bm29/opd29/1137/1d/nsd.png","bufferAfterFramesMerged":"10","experimentId":1137,"macromoleculeAcronym":"MG386","i0stdev":"0.100250461538","sampleMeasurementId":22154,"measurementComments":"[2] MES","priorityLevelId":4,"bufferBeforeFramesMerged":"10","substractionCreationTime":"Jul 17, 2013 5:15:02 PM","proposalNumber":"29","rgGnom":"2.9963404542","volume":"42.2255","shapeDeterminationModel":"/data/pyarch/bm29/opd29/1137/1d/dammin.pdb","comments":null}], + proposal : new ResultsAssemblyGrid().input().proposal + }; +}; + +AnalysisGrid.prototype.test = function(targetId) { + var analysisGrid = new AnalysisGrid(); + BIOSAXS.proposal = new Proposal(analysisGrid.input().proposal); + var panel = analysisGrid.getPanel(analysisGrid.input().data); + panel.render(targetId); +}; + + +function HPLCAnalysisGrid(args) { + AnalysisGrid.prototype.constructor.call(this, args); +} - var positionsStore = Ext.create('Ext.data.Store', { - fields : [ 'slotPositionColumn' ], - data : data - }); +HPLCAnalysisGrid.prototype._edit = AnalysisGrid.prototype._edit; +HPLCAnalysisGrid.prototype._getColumns = AnalysisGrid.prototype._getColumns; +HPLCAnalysisGrid.prototype._openVisualizarBySubstractionId = AnalysisGrid.prototype._openVisualizarBySubstractionId; +HPLCAnalysisGrid.prototype._openVisualizer = AnalysisGrid.prototype._openVisualizer; +HPLCAnalysisGrid.prototype._prepareData = AnalysisGrid.prototype._prepareData; +HPLCAnalysisGrid.prototype._showCalibration = AnalysisGrid.prototype._showCalibration; +HPLCAnalysisGrid.prototype.getPanel = AnalysisGrid.prototype.getPanel; +HPLCAnalysisGrid.prototype.input = AnalysisGrid.prototype.input; +HPLCAnalysisGrid.prototype.test = AnalysisGrid.prototype.test; +HPLCAnalysisGrid.prototype.refresh = AnalysisGrid.prototype.refresh; +HPLCAnalysisGrid.prototype._getPorod = AnalysisGrid.prototype._getPorod; + +HPLCAnalysisGrid.prototype._getButtons = function() { + return { + id : this.id + 'buttonPlot', + name : 'buttonPlot', + hidden : !this.showButtonsVisible, + width : 110, + sortable : false, + renderer : function(value, metaData, sample, rowIndex, colIndex, store) { + var html = ""; - return Ext.create('Ext.form.ComboBox', { - store : positionsStore, - queryMode : 'local', - displayField : 'slotPositionColumn', - valueField : 'slotPositionColumn' - }); + if (sample.raw.subtractionId) { + if (sample.raw.rapidShapeDeterminationModelId != null) { + html = html + ""; + } + } + return html + "
" + BUI.getGreenButton('AbInitio Modeling', { + height : 20, + width : 100 + }) + "
"; + } + }; +}; + +HPLCAnalysisGrid.prototype._getFramesColumn = function() { + return { + text : 'Frames', + dataIndex : 'datacollection', + name : 'datacollection', + sortable : true, + width : 150, + renderer : function(dataCollections, y, data) { + molmerges = data.raw.framesMerge; + totalframes = data.raw.framesCount; + return molmerges +" - "+ totalframes; + } + }; +}; + + + + +/** + * Rigid body grid to show PDB, symmetry and multiplicity + * + * + * #onUploadFile click on upload file + */ +function AprioriRigidBodyGrid(args) { + + this.height = 250; + this.btnEditVisible = true; + this.btnRemoveVisible = true; + + if (args != null) { + if (args.height != null) { + this.height = args.height; + } + if (args.btnEditVisible != null) { + this.btnEditVisible = args.btnEditVisible; + } + if (args.btnRemoveVisible != null) { + this.btnRemoveVisible = args.btnRemoveVisible; + } + } + + /** Events **/ + this.onUploadFile = new Event(this); + this.onRemove = new Event(this); +} + +AprioriRigidBodyGrid.prototype._getColumns = function() { +}; + +AprioriRigidBodyGrid.prototype._getTopButtons = function() { + var _this = this; + /** Actions buttons **/ + var actions = []; + + /** ADD BUTTON **/ + actions.push(Ext.create('Ext.Action', { + icon : '../images/add.png', + text : 'Add', + disabled : false, + handler : function(widget, event) { + _this.onAddButtonClicked.notify(); + } + })); + + return actions; +}; + +AprioriRigidBodyGrid.prototype.refresh = function(macromolecule) { + this.macromolecule = macromolecule; + if (macromolecule != null){ + this.pdbStore.loadData(macromolecule.structure3VOs); + } +}; + +AprioriRigidBodyGrid.prototype._prepareData = function() { + var data = []; + for ( var i = 0; i < this.features.length; i++) { + data.push(this.features[i]); + } + return data; +}; + +AprioriRigidBodyGrid.prototype._getPlugins = function() { + var _this = this; + var plugins = []; + plugins.push(Ext.create('Ext.grid.plugin.RowEditing', { + clicksToEdit : 1, + listeners : { + validateedit : function(grid, e) { + /** Comments are always updatable* */ + e.record.raw.symmetry = e.newValues.symmetry; + e.record.raw.multiplicity = e.newValues.multiplicity; + + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, proposal) { + BIOSAXS.proposal.setItems(proposal); + _this.refresh(BIOSAXS.proposal.getMacromoleculeById(_this.macromolecule.macromoleculeId)); + _this.panel.setLoading(false); + }); + adapter.onError.attach(function() { + alert("Error"); + }); + + _this.panel.setLoading(); + adapter.saveStructure(e.record.raw); + } + } + })); + return plugins; +}; + +AprioriRigidBodyGrid.prototype.getPanel = function() { + var _this = this; + + this.pdbStore = Ext.create('Ext.data.Store', { + fields : [ 'filePath', 'structureId', 'structureType', 'symmetry', 'structureId', 'name', 'multiplicity' ], + groupField : 'structureType', + sorters : { + property : 'structureId', + direction : 'DESC' + } + }); + + var groupingFeature = Ext.create('Ext.grid.feature.Grouping', { + groupHeaderTpl : Ext.create('Ext.XTemplate', + "
{name:this.formatName}
", { + formatName : function(name) { + return name; + } + }), + hideGroupedHeader : true, + startCollapsed : false + }); + + this.panel = Ext.create('Ext.grid.Panel', { + margin : "15 10 0 10", + height : this.height, + store : this.pdbStore, + plugins : _this._getPlugins(), + tbar : [ { + text : 'Add Modeling Option (PDB)', + icon : '../images/add.png', + handler : function() { + _this.onUploadFile.notify('PDB', 'Upload PDB File'); + } + } + + ], + columns : [ + { + text : "structureId", + flex : 0.2, + hidden : true, + dataIndex : 'structureId', + sortable : true + }, + { + text : "File", + flex : 0.5, + dataIndex : 'filePath', + sortable : true, + hidden : true + }, + { + text : "PDB", + flex : 0.4, + dataIndex : 'name', + sortable : true + }, + { + text : "Symmetry", + flex : 0.2, + dataIndex : 'symmetry', + sortable : true, + editor : { + xtype : 'combobox', + typeAhead : true, + triggerAction : 'all', + selectOnTab : true, + store : [ [ "P1", "P1" ], [ "P2", "P2" ], [ "P3", "P3" ], [ "P4", "P4" ], [ "P5", "P5" ], [ "P6", "P6" ], [ "P32", "P32" ], [ "P42", "P42" ], + [ "P52", "P52" ], [ "P62", "P62" ], [ "P222", "P222" ] ], + } + }, { + text : "Multiplicity", + flex : 0.2, + dataIndex : 'multiplicity', + sortable : true, + editor : { + xtype : 'textfield' + } + + }, { + text : "Subunit", + flex : 0.2, + dataIndex : 'isSubunit', + sortable : true, + hidden : true + }, { + text : "Type", + flex : 0.2, + dataIndex : 'structureType', + sortable : true, + hidden : true + }, + + { + id : this.id + 'REMOVE', + flex : 0.2, + sortable : false, + renderer : function(value, metaData, record, rowIndex, colIndex, store) { + return BUI.getRedButton('REMOVE'); + } + }, ], + + listeners : { + itemdblclick : function(dataview, record, item, e) { + _this._editExperiment(record.raw.experimentId); + }, + cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + + if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'REMOVE') { + var dataAdapter = new BiosaxsDataAdapter(); + dataAdapter.onSuccess.attach(function() { + _this.panel.setLoading(false); + _this.onRemove.notify(); + }); + _this.panel.setLoading("Removing PDB file"); + dataAdapter.removeStructure(record.data.structureId); + } + + } + }, + viewConfig : { + getRowClass : function(record, rowIdx, params, store) { + if (record.raw.isSubunit != null) { + return "blue-row"; + } + } + } + }); + + return this.panel; +}; + + + + +AprioriRigidBodyGrid.prototype.input = function() { + return { + proposal : DATADOC.getProposal_10(), + dewars : DATADOC.getDewars_10() + + }; +}; + +AprioriRigidBodyGrid.prototype.test = function(targetId) { + var AprioriRigidBodyGrid = new AprioriRigidBodyGrid({ + height : 150 + }); + BIOSAXS.proposal = new Proposal(AprioriRigidBodyGrid.input().proposal); + var panel = AprioriRigidBodyGrid.getPanel(AprioriRigidBodyGrid.input().dewars); + panel.render(targetId); + +}; + +/** + * It shows buffer grid with a top bar with "Add" button + * + * @height + * @searchBar + * @collapsed + * @width + */ +function BufferGrid(args) { + this.height = 500; + this.searchBar = false; + this.tbar = false; + this.collapsed = false; + this.id = BUI.id(); + + if (args != null) { + if (args.height != null) { + this.height = args.height; + } + if (args.searchBar != null) { + this.searchBar = args.searchBar; + } + + if (args.tbar != null) { + this.tbar = args.tbar; + } + + if (args.collapsed != null) { + this.collapsed = args.collapsed; + } + + if (args.width != null) { + this.width = args.width; + } } +} + +BufferGrid.prototype._edit = function(bufferId) { + var _this = this; + var window = new BufferWindow(); + window.onSuccess.attach(function(sender, proposal) { + _this.store.loadData(BIOSAXS.proposal.getBuffers()); + }); + window.draw(BIOSAXS.proposal.getBufferById(bufferId)); }; -SpecimenGrid.prototype.getPanelByExperiment = function(experiment) { - this.experiment = experiment; - var data = this._prepareData(experiment); - return this.getPanel(data); +BufferGrid.prototype.refresh = function(buffers) { + this.store.loadData(this._prepareData(buffers), false); }; -SpecimenGrid.prototype.refresh = function(experiment) { - this.experiment = experiment; - var data = this._prepareData(experiment); - this.store.loadData(data); +BufferGrid.prototype._prepareData = function(buffers) { + return buffers; }; -SpecimenGrid.prototype.getPanel = function() { +BufferGrid.prototype._getTbar = function() { var _this = this; - this.store = Ext.create('Ext.data.Store', { - fields : [ - 'buffer', 'bufferId', 'code', 'macromolecule', 'acronym', 'macromoleculeId', 'concentration', 'volume', 'samplePlateId', - 'slotPositionColumn', 'rowNumber', 'columnNumber', 'groupIndex' ], - data : [], - groupField : 'acronym' - }); - this.store.sort([ { - property : 'concentration', - direction : 'ASC' - }, { - property : 'buffer', - direction : 'ASC' - } ]); + var actions = []; - var selModel = Ext.create('Ext.selection.RowModel', { - allowDeselect : true, - mode : this.selectionMode, - listeners : { - selectionchange : function(sm, selections) { - var selected = []; - for ( var i = 0; i < selections.length; i++) { - selected.push(selections[i].raw); - } - _this.onSelected.notify(selected); - } + actions.push(Ext.create('Ext.Action', { + icon : '../images/add.png', + text : 'Add buffer', + disabled : false, + handler : function(widget, event) { + var window = new BufferWindow(); + window.onSuccess.attach(function(sender) { + _this.store.loadData(BIOSAXS.proposal.getBuffers()); + }); + window.draw({}); } - }); - - var features = []; - - if (this.grouped) { - features.push({ - ftype : 'grouping', - groupHeaderTpl : '{name}', - hideGroupedHeader : false, - startCollapsed : false, - id : 'myGroupedStore' - }); - } + })); + return actions; +}; - this.grid = Ext.create( - 'Ext.grid.Panel', - { - title : this.title, - height : this.height, - width : this.width, - selModel : selModel, - store : this.store, - features : features, - margin : this.margin, - plugins : this.getPlugins(), - columns : [ - { - text : '', - dataIndex : 'macromolecule', - width : 20, - renderer : function(val, y, sample) { - var macromoleculeId = null; - if (sample.raw.macromolecule3VO != null) { - macromoleculeId = sample.raw.macromolecule3VO.macromoleculeId; - } - else{ - macromoleculeId = sample.raw.macromoleculeId; - } - - if (macromoleculeId == null) return; - return BUI.getRectangleColorDIV(_this.experiment.macromoleculeColors[macromoleculeId], 10, 10); - } - }, - { - text : 'Macromolecule', - dataIndex : 'macromolecule', - width : 100 - }, - { - text : '', - dataIndex : 'buffer', - width : 20, - renderer : function(val, y, sample) { - var color = "black"; - if (sample.raw.bufferId != null) { - if (_this.experiment.getDataCollectionsBySpecimenId(sample.raw.specimenId)[0] != null){ - color = _this.experiment.getSpecimenColorByBufferId(_this.experiment.getMeasurementById(_this.experiment.getDataCollectionsBySpecimenId(sample.raw.specimenId)[0].measurementtodatacollection3VOs[0].measurementId).specimenId); - } - return BUI.getRectangleColorDIV(color, 10, 10); - } - } - }, { - text : 'Buffer', - dataIndex : 'bufferId', - width : 140, - renderer : function(val, y, sample) { - if (sample.raw.bufferId != null) { - return BIOSAXS.proposal.getBufferById(val).acronym; - } - }, - editor : BIOSAXS_COMBOMANAGER.getComboBuffers(BIOSAXS.proposal.getBuffers(), { - noLabel : true, - width : 300 - }) - }, { - text : 'Conc.', - dataIndex : 'concentration', - width : 100, - editor : { - allowBlank : false - }, - renderer : function(val, meta, sample) { - if (isNaN(val)) { - meta.tdCls = 'yellow-cell'; - return val; - } else { - if (val != 0) { - return BUI.formatValuesUnits(val, 'mg/ml', { - fontSize : 16, - decimals : 3, - unitsFontSize : this.unitsFontSize - }); - } else { - return; - } - } - } - }, { - text : 'Vol. Well', - dataIndex : 'volume', - width : 70, - editor : { - allowBlank : true - }, - renderer : function(val, y, sample) { - return BUI.formatValuesUnits(sample.data.volume, 'µl', { - fontSize : 12, - decimals : 2, - unitsFontSize : this.unitsFontSize - }); - } - }, { - text : 'Position', - hidden : true, - flex : 1, - renderer : function(val, y, sample) { - return BUI.getSamplePositionHTML(sample.raw, _this.experiment); - } - }, { - text : 'samplePlateId', - dataIndex : 'samplePlateId', - hidden : true - }, { - text : 'Plate', - hidden : this.isPositionColumnHidden, - dataIndex : 'slotPositionColumn', - editor : _this._getSlotColumBombo(), - flex : 1, - renderer : function(val, meta, sample) { - if ((val != null) & (val != "")) { - return val; - } else { - meta.tdCls = 'yellow-cell'; - } - } - }, { - text : 'Row', - hidden : this.isPositionColumnHidden, - dataIndex : 'rowNumber', - editor : this._getRowCombo(), - flex : 1, - renderer : function(val, meta, sample) { - if ((val != null) && (val != "")) { - return BUI.getSamplePlateLetters()[val - 1]; - } else { - meta.tdCls = 'yellow-cell'; - } - } - }, { - text : 'Well', - hidden : this.isPositionColumnHidden, - dataIndex : 'columnNumber', - editor : this._getColumnCombo(), - flex : 1, - renderer : function(val, meta, sample) { - if ((val != null) && (val != "")) { - return val; - } else { - meta.tdCls = 'yellow-cell'; - } - } - }, { - id : _this.id + 'buttonEditSample', - text : 'Edit', - width : 80, - sortable : false, - hidden : !_this.editEnabled, - renderer : function(value, metaData, record, rowIndex, colIndex, store) { - if (_this.editEnabled) { - return BUI.getGreenButton('EDIT'); - } - } - }, { - id : _this.id + 'buttonRemoveSample', - text : '', - hidden : !_this.removeBtnEnabled, - width : 100, - sortable : false, - renderer : function(value, metaData, record, rowIndex, colIndex, store) { - if (_this.removeBtnEnabled) { - return BUI.getRedButton('REMOVE'); - } - } - } +BufferGrid.prototype.getPanel = function(buffers) { + var _this = this; - ], - viewConfig : { - preserveScrollOnRefresh : true, - stripeRows : true, - getRowClass : function(record) { - var specimens = _this.experiment.getSampleByPosition(record.data.samplePlateId, record.data.rowNumber, - record.data.columnNumber); - if (specimens.length > 1) { - return 'red-row'; + this.store = Ext.create('Ext.data.Store', { + fields : [ 'bufferId', 'acronym', 'name', 'composition' ], + data : buffers + }); - } - }, - listeners : { - selectionchange : function(grid, selected) { - _this.onClick.notify(record.raw); - }, - cellclick : function(grid, td, cellIndex, record, tr) { - if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonEditSample') { - } - if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonRemoveSample') { - grid.getStore().removeAt(rowIndex); - _this.onRemoved.notify(); - } + this.store.sort('acronym'); - } + var type = 'Ext.grid.Panel'; + if (this.searchBar == true) { + type = 'Ext.ux.LiveSearchGridPanel'; + } - } - } - }); + this.grid = Ext.create(type, { + title : 'Buffers', + collapsible : true, + collapsed : this.collapsed, + store : this.store, + height : this.height, + width : this.width, + columns : [ + /*{ + text : '', + dataIndex : 'bufferId', + width : 20, + renderer : function(val, y, sample) { + return BUI.getRectangleColorDIV(BIOSAXS.proposal.bufferColors[val], 10, 10); + } + },*/ + { + text : 'Acronym', + dataIndex : 'acronym', + flex : 1 + }, { + text : 'Name', + dataIndex : 'name', + flex : 1, + hidden : true + }, { + text : 'Composition', + dataIndex : 'composition', + flex : 1, + hidden : true + }, { + id : _this.id + 'buttonEditBuffer', + width : 80, + sortable : false, + renderer : function(value, metaData, record, rowIndex, colIndex, store) { + return BUI.getGreenButton('EDIT'); + } + }, { + id : _this.id + 'buttonRemoveBuffer', + width : 85, + hidden : true, + sortable : false, + renderer : function(value, metaData, record, rowIndex, colIndex, store) { + return BUI.getRedButton('REMOVE'); + } + } ], + flex : 1, + viewConfig : { + stripeRows : true, + listeners : { + 'celldblclick' : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + _this._edit(record.data.bufferId); + }, + 'cellclick' : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonEditBuffer') { + _this._edit(record.data.bufferId); + } + + if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonRemoveBuffer') { + BUI.showBetaWarning(); + } + } + + } + } + }); + + /** Adding the tbar **/ + if (this.tbar) { + this.grid.addDocked({ + xtype : 'toolbar', + items : this._getTbar() + }); + } return this.grid; }; -SpecimenGrid.prototype.input = function() { - return { - experiment : DATADOC.getExperiment_10(), - proposal : DATADOC.getProposal_10() - }; +BufferGrid.prototype.input = function() { + return new MacromoleculeGrid().input(); }; -SpecimenGrid.prototype.test = function(targetId) { - var specimenGrid = new SpecimenGrid({ - height : 400, - maxHeight : 400, - width : 1000 +BufferGrid.prototype.test = function(targetId) { + var bufferGrid = new BufferGrid({ + width : 800, + height : 350, + collapsed : false, + tbar : true }); - BIOSAXS.proposal = new Proposal(specimenGrid.input().proposal); - var experiment = new Experiment(specimenGrid.input().experiment); - var panel = specimenGrid.getPanelByExperiment(experiment); + BIOSAXS.proposal = new Proposal(bufferGrid.input().proposal); + var panel = bufferGrid.getPanel(BIOSAXS.proposal.macromolecules); panel.render(targetId); - }; /** - * Shows a list of stock solutions with macromolecule, buffer, storage temperature, concentration, shipment and comments + * A shipment may contains one or more cases where stock solutions and sample plates are stored * - * @multiselect allows multiple selection - * @height - * @minHeight - * @width - * @tbar - * @showTitle - * @isPackedVisible shows is stock solution is in a box - * @btnEditVisible shows edit button - * @btnAddVisible - * @btnAddExisting - * @btnUnpackVisible allows to unpack a stock solution - * @btnRemoveVisible allow to remove a stock solution + * @height + * @btnEditVisible + * @btnRemoveVisible + * + * #onEditButtonClicked + * #onAddButtonClicked + * #onRemoveButtonClicked + * #onDuplicateButtonClicked */ +function CaseGrid(args) { -function StockSolutionGrid(args) { - this.id = BUI.id(); this.height = 100; - this.width = null; - this.minHeight = null; - this.tbar = true; - - this.title = "Stock Solutions"; - - /** Visible buttons and actions **/ this.btnEditVisible = true; this.btnRemoveVisible = true; - this.btnAddVisible = true; - this.btnAddExisting = false; - this.isPackedVisible = true; - this.btnUnpackVisible = false; - - /** Selectors **/ - this.multiselect = false; - this.selectedStockSolutions = []; if (args != null) { - if (args.btnUnpackVisible != null) { - this.btnUnpackVisible = args.btnUnpackVisible; - } - if (args.multiselect != null) { - this.multiselect = args.multiselect; - } if (args.height != null) { this.height = args.height; } if (args.btnEditVisible != null) { this.btnEditVisible = args.btnEditVisible; } - if (args.btnAddVisible != null) { - this.btnAddVisible = args.btnAddVisible; - } - if (args.btnAddExisting != null) { - this.btnAddExisting = args.btnAddExisting; - } - if (args.width != null) { - this.width = args.width; - } - if (args.minHeight != null) { - this.minHeight = args.minHeight; - } - if (args.tbar != null) { - this.tbar = args.tbar; - } if (args.btnRemoveVisible != null) { this.btnRemoveVisible = args.btnRemoveVisible; } - if (args.isPackedVisible != null) { - this.isPackedVisible = args.isPackedVisible; - } - if (args.showTitle != null) { - this.showTitle = args.showTitle; - if (this.showTitle == false) { - this.title = null; + } + + /** Events **/ + this.onEditButtonClicked = new Event(this); + this.onAddButtonClicked = new Event(this); + this.onRemoveButtonClicked = new Event(this); + this.onDuplicateButtonClicked = new Event(this); +} + +CaseGrid.prototype._getColumns = function() { + var _this = this; + + function actionItemRenderer(value, meta, record, rowIx, ColIx, store) { + /*if (record.raw.specimen3VOs.length > 0){ + return 'x-hide-display'; + }*/ + } + + var columns = [ + { + header : 'Code', + dataIndex : 'code', + name : 'code', + type : 'string', + flex : 1 + }, + { + header : 'Bar Code', + dataIndex : 'barCode', + name : 'barCode', + type : 'string', + flex : 1, + hidden : true + }, + { + header : 'BeamLine', + dataIndex : 'barCode', + type : 'string', + flex : 1, + renderer : function(comp, val, record) { + if (record.raw.sessionVO != null) { + return "" + record.raw.sessionVO.beamlineName + ""; + } + } + }, + { + header : 'Session', + dataIndex : 'barCode', + type : 'string', + flex : 1, + renderer : function(comp, val, record) { + if (record.raw.sessionVO != null) { + return "" + moment(record.raw.sessionVO.startDate).format("MMM Do YY") + ""; + } } - } - - } - - /** Events **/ - this.onProposalChanged = new Event(this); - this.onStockSolutionSelected = new Event(this); -} - -StockSolutionGrid.prototype._getColumns = function() { - var _this = this; - var columns = [ - - { - header : 'Macromolecule', - dataIndex : 'macromolecule', - id : _this.id + 'macromolecule', - type : 'string', - renderer : function(val, y, specimen) { - return '' + val + ''; }, - hidden : false, - flex : 1 - }, { - header : 'Buffer', - dataIndex : 'buffer', - name : 'buffer', - hidden : false, - renderer : function(val, y, specimen) { - return '' + val + ''; + { + header : 'Status', + dataIndex : 'dewarStatus', + name : 'dewarStatus', + type : 'string', + flex : 1, + renderer : function(comp, val, record) { + if (new String(record.raw.dewarStatus).toUpperCase() == 'OPENED') { + return "" + new String(record.raw.dewarStatus).toUpperCase() + ""; + } + return "" + new String(record.raw.dewarStatus).toUpperCase() + ""; + } }, - type : 'string', - flex : 1 - }, { - header : 'Acronym', - dataIndex : 'name', - name : 'name', - type : 'string', - flex : 1, - hidden : true - }, { - header : 'Storage Temp.', - dataIndex : 'storageTemperature', - name : 'storageTemperature', - type : 'string', - flex : 1, - hidden : false, - renderer : function(val) { - return BUI.formatValuesUnits(val, 'C', { - fontSize : 12, - decimals : 2, - unitsFontSize : 10 - }); - } - }, { - header : 'Volume', - dataIndex : 'volume', - type : 'string', - flex : 1, - hidden : false, - renderer : function(val) { - return BUI.formatValuesUnits(val, 'µl', { - fontSize : 12, - decimals : 2, - unitsFontSize : 10 - }); - } - }, { - header : 'Concentration', - dataIndex : 'concentration', - name : 'concentration', - type : 'string', - flex : 1, - renderer : function(val) { - return BUI.formatValuesUnits(val, 'mg/ml', { - fontSize : 12, - decimals : 2, - unitsFontSize : 10 - }); - } - }, { - header : 'Packed', - dataIndex : 'comments', - id : _this.id + "box", - type : 'string', - width : 50, - hidden : !this.isPackedVisible, - renderer : function(val, cmp, a) { - if (a.raw.boxId != null) { - return "
"; + { + header : 'Type', + dataIndex : 'type', + name : 'type', + type : 'string', + flex : 1, + hidden : true + }, + { + header : 'Sample Plates', + dataIndex : 'plates', + name : 'plates', + type : 'string', + flex : 1, + hidden : true + }, + { + header : 'Stock Solutions', + dataIndex : 'plates', + name : 'plates', + type : 'string', + width : 100, + renderer : function(comp, val, record) { + var html = "
"; + var stockSolutions = BIOSAXS.proposal.getStockSolutionsByDewarId(record.raw.dewarId); + html = html + "" + stockSolutions.length + " x"; + return html + "
"; } - + }, { + header : 'isStorageDewar', + dataIndex : 'isStorageDewar', + name : 'isStorageDewar', + type : 'string', + flex : 1, + hidden : true + }, { + header : 'Tracking Number From Synchrotron', + dataIndex : 'trackingNumberFromSynchrotron', + name : 'trackingNumberFromSynchrotron', + type : 'string', + flex : 1, + hidden : true + }, { + header : 'Tracking Number To Synchrotron', + dataIndex : 'trackingNumberToSynchrotron', + name : 'trackingNumberToSynchrotron', + type : 'string', + flex : 1, + hidden : true + }, { + header : 'Storage Location', + dataIndex : 'storageLocation', + name : 'storageLocation', + type : 'string', + flex : 1, + hidden : false + }, { + header : 'Comments', + dataIndex : 'comments', + name : 'comments', + type : 'string', + flex : 1, + hidden : false } - }, { - header : 'Comments', - dataIndex : 'comments', - type : 'string', - flex : 1 - } ]; + ]; if (this.btnEditVisible) { columns.push({ id : _this.id + 'buttonEdit', + text : '', + hidden : !_this.btnEditVisible, width : 85, sortable : false, renderer : function(value, metaData, record, rowIndex, colIndex, store) { - if (_this.btnEditVisible) { - return BUI.getGreenButton('EDIT'); - } + return BUI.getGreenButton('EDIT'); } }); } @@ -22014,4646 +16857,9803 @@ StockSolutionGrid.prototype._getColumns = function() { }); } - if (this.btnUnpackVisible) { - columns.push({ - id : _this.id + 'buttonUnpack', - width : 85, - sortable : false, - renderer : function(value, metaData, record, rowIndex, colIndex, store) { - if (_this.btnUnpackVisible) { - return BUI.getBlueButton('UNPACK'); - } - } - }); - } + columns.push({ + dataIndex : 'comments', + type : 'string', + width : 85, + hidden : false, + renderer : function(comp, val, record) { + return BUI.getBlueButton("LABELS", { + href : BUI.getPrintcomponentURL(record.raw.dewarId) + }); + } + }); + return columns; }; -StockSolutionGrid.prototype._getTopButtons = function() { - var _this = this; - /** Actions buttons **/ - var actions = []; - - /** ADD BUTTON **/ - if (this.btnAddVisible) { - actions.push(Ext.create('Ext.Action', { - icon : '../images/add.png', - text : 'Add Stock Solution', - tooltip : 'Will create a new stock solution', - disabled : false, - alwaysEnabled : true, - handler : function(widget, event) { - _this.edit(); - } - })); - } - - if (this.btnAddExisting) { - actions.push(Ext.create('Ext.Action', { - icon : '../images/add.png', - text : 'Add Existing', - tooltip : 'Allows to select upacked stock solutions', - disabled : false, - alwaysEnabled : true, - handler : function(widget, event) { - var stockSolutionGrid = new StockSolutionGrid({ - btnAddVisible : false, - btnEditVisible : false, - btnRemoveVisible : false, - btnAddExisting : false, - isPackedVisible : true, - multiselect : true - }); - - var window = Ext.create('Ext.window.Window', { - title : 'Select', - height : 400, - width : 800, - layout : 'fit', - items : [ stockSolutionGrid.getPanel() ], - buttons : [ { - text : 'Pack', - handler : function() { - _this.onStockSolutionSelected.notify(stockSolutionGrid.selectedStockSolutions); - window.close(); - } - }, { - text : 'Cancel', - handler : function() { - window.close(); - } - } ] - - }).show(); +CaseGrid.prototype._getTopButtons = function() { + var _this = this; + /** Actions buttons **/ + var actions = []; - stockSolutionGrid.refresh(BIOSAXS.proposal.getUnpackedStockSolutions()); - } - })); - } + /** ADD BUTTON **/ + actions.push(Ext.create('Ext.Action', { + icon : '../images/add.png', + text : 'Add', + disabled : false, + handler : function(widget, event) { + _this.onAddButtonClicked.notify(); + } + })); return actions; }; -StockSolutionGrid.prototype.refresh = function(stockSolutions) { - this.features = stockSolutions; - this.store.loadData(this._prepareData(), false); +CaseGrid.prototype.refresh = function(dewars) { + this.features = dewars; + this.store.loadData(this._prepareData(dewars), false); }; -StockSolutionGrid.prototype._prepareData = function() { +CaseGrid.prototype._sort = function(store) { + store.sort('dewarId', 'DESC'); +}; + +CaseGrid.prototype._prepareData = function() { var data = []; for ( var i = 0; i < this.features.length; i++) { - var stockSolution = this.features[i]; - stockSolution.buffer = BIOSAXS.proposal.getBufferById(stockSolution.bufferId).acronym; - if (stockSolution.macromoleculeId != null) { - stockSolution.macromolecule = BIOSAXS.proposal.getMacromoleculeById(stockSolution.macromoleculeId).acronym; - } - data.push(stockSolution); + data.push(this.features[i]); } return data; }; -StockSolutionGrid.prototype.getPanel = function() { +CaseGrid.prototype.getPanel = function(dewars, plates) { + this.features = dewars; + this.plates = plates; return this._renderGrid(); }; -StockSolutionGrid.prototype.edit = function(stockSolutionId) { +CaseGrid.prototype._edit = function(dewar) { var _this = this; - var stockSolutionWindow = new StockSolutionWindow(); - /** On stock solution SAVED **/ - stockSolutionWindow.onSaved.attach(function(sender, stockSolution) { - _this.onProposalChanged.notify(stockSolution); + var caseWindow = new CaseWindow(); + /**SAVED **/ + caseWindow.onSaved.attach(function(sender, dewar) { + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, shipment) { + _this.refresh(shipment.dewarVOs); + }); + adapter.saveCase(BIOSAXS.proposal.getShipmentByDewarId(dewar.dewarId).shippingId, dewar); }); - stockSolutionWindow.draw(BIOSAXS.proposal.getStockSolutionById(stockSolutionId)); + caseWindow.draw(dewar); }; -StockSolutionGrid.prototype._getStoreFields = function() { +CaseGrid.prototype._getStoreFields = function(data) { return [ { - name : 'name', + name : 'dewarId', type : 'string' }, { - name : 'stockSolutionId', + name : 'barCode', type : 'string' }, { - name : 'macromolecule', + name : 'code', type : 'string' }, { - name : 'buffer', + name : 'comments', type : 'string' }, { - name : 'storageTemperature', - type : 'numeric' + name : 'dewarStatus', + type : 'string' }, { - name : 'volume', + name : 'isStorageDewar', type : 'string' }, { - name : 'concentration', + name : 'plates', type : 'string' }, { - name : 'buffer', + name : 'transportValue', type : 'string' }, { - name : 'comments', + name : 'trackingNumberFromSynchrotron', type : 'string' - } ]; -}; + }, { + name : 'trackingNumberToSynchrotron', + type : 'string' + }, { + name : 'timeStamp', + type : 'string' + }, { + name : 'storageLocation', + type : 'string' + }, { + name : 'type', + type : 'string' + } -//StockSolutionGrid.prototype.refresh = function() { -// this.proposal.onInitialized.attach(function(sender){ -// -// }); -// this.proposal.init(Ext.urlDecode(window.location.href).sessionId); -//}; + ]; +}; -StockSolutionGrid.prototype._renderGrid = function() { +CaseGrid.prototype._renderGrid = function() { var _this = this; /** Store **/ + var data = this._prepareData(); this.store = Ext.create('Ext.data.Store', { - fields : this._getStoreFields(), //columns, - autoload : true + fields : this._getStoreFields(data), + autoload : true, + data : data }); + this._sort(this.store); - var filters = { - ftype : 'filters', - local : true, - filters : this.filters - }; - - var selModel = null; - - if (this.multiselect) { - selModel = Ext.create('Ext.selection.CheckboxModel', { - //multiSelect : false,//this.multiselect, - mode : 'SINGLE', - listeners : { - selectionchange : function(sm, selections) { - _this.selectedStockSolutions = []; - for ( var i = 0; i < selections.length; i++) { - _this.selectedStockSolutions.push(selections[i].raw); - } - } - } - }); - } else { - selModel = { - mode : 'SINGLE' - }; - } - - this.store.sort("stockSolutionId", "desc"); + this.store.loadData(data, false); this.grid = Ext.create('Ext.grid.Panel', { style : { padding : 5 }, - icon : '/ispyb/images/SampleHolder_24x24_01.png', - title : this.title, height : this.height, - width : this.width, - minWidth : this.minWidth, - selModel : selModel, store : this.store, columns : this._getColumns(), viewConfig : { stripeRows : true, listeners : { itemdblclick : function(dataview, record, item, e) { - _this.edit(record.raw.stockSolutionId); - }, - cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { - var adapter = null; - if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonUnpack') { - _this.grid.setLoading("ISPyB: Unpacking stock solution"); - adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender) { - _this.onProposalChanged.notify(); - _this.grid.setLoading(false); - }); - adapter.onError.attach(function(sender) { - _this.onProposalChanged.notify(); - _this.grid.setLoading(false); - }); - record.raw.boxId = null; - adapter.saveStockSolution(record.raw); - } - - if (grid.getGridColumns()[cellIndex].getId() == _this.id + "box") { - window.location = BUI.getShippingURL(BIOSAXS.proposal.getShipmentByDewarId(record.raw.boxId).shippingId); - } - if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonEdit') { - _this.edit(record.data.stockSolutionId); - } - - if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonRemove') { - _this.grid.setLoading("ISPyB: Removing stock solution"); - adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender) { - _this.onProposalChanged.notify(); - _this.grid.setLoading(false); - }); - adapter.onError.attach(function(sender) { - _this.onProposalChanged.notify(); - _this.grid.setLoading(false); - }); - adapter.removeStockSolution(record.data.stockSolutionId); - } - } - } - } - - }); - - var actions = _this._getTopButtons(); - this.grid.addDocked({ - xtype : 'toolbar', - items : actions - }); - - var i = null; - this.grid.getSelectionModel().on({ - selectionchange : function(sm, selections) { - if (selections.length) { - for ( i = 0; i < actions.length; i++) { - if (actions[i].enable) { - actions[i].enable(); - } - } - } else { - for ( i = 0; i < actions.length; i++) { - if (actions[i].alwaysEnabled == false) { - if (actions[i].disable) { - actions[i].disable(); - } - } - } - } - } - }); - return this.grid; -}; - -StockSolutionGrid.prototype.input = function() { - return { - proposal : DATADOC.getProposal_10() - }; -}; - -StockSolutionGrid.prototype.test = function(targetId) { - var stockSolutionGrid = new StockSolutionGrid({ - height : 300, - width : 900 - }); - BIOSAXS.proposal = new Proposal(stockSolutionGrid.input().proposal); - var panel = stockSolutionGrid.getPanel(); - stockSolutionGrid.refresh(BIOSAXS.proposal.getStockSolutions()); - panel.render(targetId); -}; - - -function SuperpositionGrid(args) { - this.height = null; - this.width = null; - this.id = BUI.id(); - - if (args != null) { - if (args.height != null) { - this.height = args.height; - - if (args.width != null) { - this.width = args.width; - } - } - } - - this.onSelected = new Event(this); -} - -SuperpositionGrid.prototype._prepareData = function(data) { - return data; -}; + _this._edit(record.raw); + }, + cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonEdit') { + _this._edit(record.raw); + } -SuperpositionGrid.prototype.refresh = function(subtractions) { - var data = []; - if (subtractions != null){ - if (subtractions.length > 0){ - for (j in subtractions){ - var subtraction = subtractions[j]; - if (subtraction.superposition3VOs != null){ - for (i in subtraction.superposition3VOs){ - data.push(subtraction.superposition3VOs[i]); + if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonRemove') { + _this.onRemoveButtonClicked.notify(_this.store.getAt(rowIndex).raw.dewarId); } } } + }, + selModel : { + mode : 'SINGLE' } - } - this.store.loadData(data); -}; + }); -SuperpositionGrid.prototype.getPanel = function() { - var _this = this; - this.store = Ext.create('Ext.data.Store', { - fields : [ 'abinitioModelPdbFilePath', 'aprioriPdbFilePath', 'alignedPdbFilePath' ], - data : [] + var actions = _this._getTopButtons(); + this.grid.addDocked({ + xtype : 'toolbar', + items : actions }); - this.selModel = Ext.create('Ext.selection.RowModel', { - allowDeselect : true, - mode : 'MULTI', - listeners : { - selectionchange : function(sm, selections) { - var selected = []; - for (var i = 0; i < selections.length; i++) { - selected.push(selections[i].raw); + this.grid.getSelectionModel().on({ + selectionchange : function(sm, selections) { + if (selections.length) { + for ( var i = 0; i < actions.length; i++) { + if (actions[i].enable) { + actions[i].enable(); + } + } + } else { + for ( var i = 0; i < actions.length; i++) { + if (actions[i].alwaysEnabled == false) { + if (actions[i].disable) { + actions[i].disable(); + } + } } - _this.onSelected.notify(selected); } } }); - - this.store.sort([ { - property : 'name', - direction : 'ASC' - } ]); + return this.grid; +}; - this.panel = Ext.create('Ext.grid.Panel', { - store : this.store, - selModel : this.selModel, - width : this.width, - height : this.height, - margin : 10, - deferredRender : false, - columns : [ { - text : 'Abinitio', - dataIndex : 'abinitioModelPdbFilePath', - hidden : false, - flex : 1, - renderer : function(val){ - return BUI.getFileName(val); - } - }, { - text : 'Apriori', - dataIndex : 'aprioriPdbFilePath', - hidden : false, - flex : 1, - renderer : function(val){ - return BUI.getFileName(val); - } - }, { - text : 'Aligned', - dataIndex : 'alignedPdbFilePath', - hidden : false, - flex : 1, - renderer : function(val){ - return BUI.getFileName(val); - } - } ], +CaseGrid.prototype.input = function() { + return { + proposal : DATADOC.getProposal_10(), + dewars : DATADOC.getDewars_10() - viewConfig : { - enableTextSelection : true, - preserveScrollOnRefresh : true - }, - listeners : { - cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + }; +}; - }, - afterrender : function() { - } - } +CaseGrid.prototype.test = function(targetId) { + var CaseGrid = new CaseGrid({ + height : 150 }); - return this.panel; + BIOSAXS.proposal = new Proposal(CaseGrid.input().proposal); + var panel = CaseGrid.getPanel(CaseGrid.input().dewars); + panel.render(targetId); + }; +/** + * A shipment may contains one or more cases where stock solutions and sample plates are stored + * + * @height + * @btnEditVisible + * @btnRemoveVisible + * + * #onEditButtonClicked + * #onAddButtonClicked + * #onRemoveButtonClicked + * #onDuplicateButtonClicked + */ +function ExampleGrid(args) { + + this.height = 100; + this.btnEditVisible = true; + this.btnRemoveVisible = true; + + if (args != null) { + if (args.height != null) { + this.height = args.height; + } + if (args.btnEditVisible != null) { + this.btnEditVisible = args.btnEditVisible; + } + if (args.btnRemoveVisible != null) { + this.btnRemoveVisible = args.btnRemoveVisible; + } + } + + /** Events **/ + this.onEditButtonClicked = new Event(this); + this.onAddButtonClicked = new Event(this); + this.onRemoveButtonClicked = new Event(this); + this.onDuplicateButtonClicked = new Event(this); +} + +ExampleGrid.prototype._getColumns = function() { + var _this = this; + + function actionItemRenderer(value, meta, record, rowIx, ColIx, store) { + /*if (record.raw.specimen3VOs.length > 0){ + return 'x-hide-display'; + }*/ + } + + var columns = [ + { + header : 'Code', + dataIndex : 'code', + name : 'code', + type : 'string', + flex : 1 + }, + { + header : 'Bar Code', + dataIndex : 'barCode', + name : 'barCode', + type : 'string', + flex : 1, + hidden : true + }, + { + header : 'BeamLine', + dataIndex : 'barCode', + type : 'string', + flex : 1, + renderer : function(comp, val, record) { + if (record.raw.sessionVO != null) { + return "" + record.raw.sessionVO.beamlineName + ""; + } + } + }, + { + header : 'Session', + dataIndex : 'barCode', + type : 'string', + flex : 1, + renderer : function(comp, val, record) { + if (record.raw.sessionVO != null) { + return "" + moment(record.raw.sessionVO.startDate).format("MMM Do YY") + ""; + } + } + }, + { + header : 'Status', + dataIndex : 'dewarStatus', + name : 'dewarStatus', + type : 'string', + flex : 1, + renderer : function(comp, val, record) { + if (new String(record.raw.dewarStatus).toUpperCase() == 'OPENED') { + return "" + new String(record.raw.dewarStatus).toUpperCase() + ""; + } + return "" + new String(record.raw.dewarStatus).toUpperCase() + ""; + } + }, + { + header : 'Type', + dataIndex : 'type', + name : 'type', + type : 'string', + flex : 1, + hidden : true + }, + { + header : 'Sample Plates', + dataIndex : 'plates', + name : 'plates', + type : 'string', + flex : 1, + hidden : true + }, + { + header : 'Stock Solutions', + dataIndex : 'plates', + name : 'plates', + type : 'string', + width : 100, + renderer : function(comp, val, record) { + var html = "
"; + var stockSolutions = BIOSAXS.proposal.getStockSolutionsByDewarId(record.raw.dewarId); + html = html + "" + stockSolutions.length + " x"; + return html + "
"; + } + }, { + header : 'isStorageDewar', + dataIndex : 'isStorageDewar', + name : 'isStorageDewar', + type : 'string', + flex : 1, + hidden : true + }, { + header : 'Tracking Number From Synchrotron', + dataIndex : 'trackingNumberFromSynchrotron', + name : 'trackingNumberFromSynchrotron', + type : 'string', + flex : 1, + hidden : true + }, { + header : 'Tracking Number To Synchrotron', + dataIndex : 'trackingNumberToSynchrotron', + name : 'trackingNumberToSynchrotron', + type : 'string', + flex : 1, + hidden : true + }, { + header : 'Storage Location', + dataIndex : 'storageLocation', + name : 'storageLocation', + type : 'string', + flex : 1, + hidden : false + }, { + header : 'Comments', + dataIndex : 'comments', + name : 'comments', + type : 'string', + flex : 1, + hidden : false + } + ]; + + if (this.btnEditVisible) { + columns.push({ + id : _this.id + 'buttonEdit', + text : '', + hidden : !_this.btnEditVisible, + width : 85, + sortable : false, + renderer : function(value, metaData, record, rowIndex, colIndex, store) { + return BUI.getGreenButton('EDIT'); + } + }); + } + + if (this.btnRemoveVisible) { + columns.push({ + id : _this.id + 'buttonRemove', + width : 85, + sortable : false, + renderer : function(value, metaData, record, rowIndex, colIndex, store) { + if (_this.btnRemoveVisible) { + return BUI.getRedButton('REMOVE'); + } + } + }); + } + + columns.push({ + dataIndex : 'comments', + type : 'string', + width : 85, + hidden : false, + renderer : function(comp, val, record) { + return BUI.getBlueButton("LABELS", { + href : BUI.getPrintcomponentURL(record.raw.dewarId) + }); + } + }); + + return columns; +}; + +ExampleGrid.prototype._getTopButtons = function() { + var _this = this; + /** Actions buttons **/ + var actions = []; + + /** ADD BUTTON **/ + actions.push(Ext.create('Ext.Action', { + icon : '../images/add.png', + text : 'Add', + disabled : false, + handler : function(widget, event) { + _this.onAddButtonClicked.notify(); + } + })); + + return actions; +}; + +ExampleGrid.prototype.refresh = function(dewars) { + this.features = dewars; + this.store.loadData(this._prepareData(dewars), false); +}; + +ExampleGrid.prototype._sort = function(store) { + store.sort('dewarId', 'DESC'); +}; + +ExampleGrid.prototype._prepareData = function() { + var data = []; + for ( var i = 0; i < this.features.length; i++) { + data.push(this.features[i]); + } + return data; +}; + +ExampleGrid.prototype.getPanel = function() { + this.grid = Ext.create('Ext.grid.Panel', { + style : { + padding : 5 + }, + height : this.height, + store : this.store, + columns : this._getColumns(), + viewConfig : { + stripeRows : true, + listeners : { + itemdblclick : function(dataview, record, item, e) { + _this._edit(record.raw); + }, + cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonEdit') { + _this._edit(record.raw); + } + + if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonRemove') { + _this.onRemoveButtonClicked.notify(_this.store.getAt(rowIndex).raw.dewarId); + } + } + } + }, + selModel : { + mode : 'SINGLE' + } + }); + + var actions = _this._getTopButtons(); + this.grid.addDocked({ + xtype : 'toolbar', + items : actions + }); + + this.grid.getSelectionModel().on({ + selectionchange : function(sm, selections) { + if (selections.length) { + for ( var i = 0; i < actions.length; i++) { + if (actions[i].enable) { + actions[i].enable(); + } + } + } else { + for ( var i = 0; i < actions.length; i++) { + if (actions[i].alwaysEnabled == false) { + if (actions[i].disable) { + actions[i].disable(); + } + } + } + } + } + }); + return this.grid; +}; + + +ExampleGrid.prototype._getStoreFields = function(data) { + return [ { + name : 'dewarId', + type : 'string' + }, { + name : 'barCode', + type : 'string' + }, { + name : 'code', + type : 'string' + }, { + name : 'comments', + type : 'string' + }, { + name : 'dewarStatus', + type : 'string' + }, { + name : 'isStorageDewar', + type : 'string' + }, { + name : 'plates', + type : 'string' + }, { + name : 'transportValue', + type : 'string' + }, { + name : 'trackingNumberFromSynchrotron', + type : 'string' + }, { + name : 'trackingNumberToSynchrotron', + type : 'string' + }, { + name : 'timeStamp', + type : 'string' + }, { + name : 'storageLocation', + type : 'string' + }, { + name : 'type', + type : 'string' + } + + ]; +}; + +ExampleGrid.prototype._renderGrid = function() { + var _this = this; + + /** Store **/ + var data = this._prepareData(); + this.store = Ext.create('Ext.data.Store', { + fields : this._getStoreFields(data), + autoload : true, + data : data + }); + this._sort(this.store); + + this.store.loadData(data, false); + + this.grid = Ext.create('Ext.grid.Panel', { + style : { + padding : 5 + }, + height : this.height, + store : this.store, + columns : this._getColumns(), + viewConfig : { + stripeRows : true, + listeners : { + itemdblclick : function(dataview, record, item, e) { + _this._edit(record.raw); + }, + cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonEdit') { + _this._edit(record.raw); + } + + if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonRemove') { + _this.onRemoveButtonClicked.notify(_this.store.getAt(rowIndex).raw.dewarId); + } + } + } + }, + selModel : { + mode : 'SINGLE' + } + }); + + var actions = _this._getTopButtons(); + this.grid.addDocked({ + xtype : 'toolbar', + items : actions + }); + + this.grid.getSelectionModel().on({ + selectionchange : function(sm, selections) { + if (selections.length) { + for ( var i = 0; i < actions.length; i++) { + if (actions[i].enable) { + actions[i].enable(); + } + } + } else { + for ( var i = 0; i < actions.length; i++) { + if (actions[i].alwaysEnabled == false) { + if (actions[i].disable) { + actions[i].disable(); + } + } + } + } + } + }); + return this.grid; +}; + +ExampleGrid.prototype.input = function() { + return { + proposal : DATADOC.getProposal_10(), + dewars : DATADOC.getDewars_10() + + }; +}; + +ExampleGrid.prototype.test = function(targetId) { + var ExampleGrid = new ExampleGrid({ + height : 150 + }); + BIOSAXS.proposal = new Proposal(ExampleGrid.input().proposal); + var panel = ExampleGrid.getPanel(ExampleGrid.input().dewars); + panel.render(targetId); + +}; + + /** - * See ExperimentGrid - * + * Shows a list of experiment AKA data acquisitions + * @height + * @sorters + * @minHeight + * @gridType: Ext.ux.LiveSearchGridPanel or Ext.grid.Panel + * @tbar true or false + * @grouping true or false + * @width + * @title + * #onEditButtonClicked */ -function TemplateGrid(args) { - - if (args == null) { - args = {}; - } - args.sorters = [ { - property : 'experimentId', - direction : 'DESC' - } ]; - - ExperimentGrid.prototype.constructor.call(this, args); -} - -TemplateGrid.prototype._getFilterTypes = ExperimentGrid.prototype._getFilterTypes; -TemplateGrid.prototype._prettyPrintMacromolecules = ExperimentGrid.prototype._prettyPrintMacromolecules; -TemplateGrid.prototype._getPercentage = ExperimentGrid.prototype._getPercentage; -TemplateGrid.prototype._getPercentageCollected = ExperimentGrid.prototype._getPercentageCollected; -TemplateGrid.prototype.getPercentageMerged = ExperimentGrid.prototype.getPercentageMerged; -TemplateGrid.prototype._prepareData = ExperimentGrid.prototype._prepareData; -TemplateGrid.prototype.getPanel = ExperimentGrid.prototype.getPanel; -TemplateGrid.prototype._renderGrid = ExperimentGrid.prototype._renderGrid; -TemplateGrid.prototype._editExperiment = ExperimentGrid.prototype._editExperiment; -TemplateGrid.prototype._removeExperimentById = ExperimentGrid.prototype._removeExperimentById; - -TemplateGrid.prototype._getTopButtons = function() { - /** Actions buttons * */ - var actions = []; - - actions.push(Ext.create('Ext.Action', { - icon : '../images/add.png', - text : 'Add experiment', - handler : function(widget, event) { - var wizardWidget = new WizardWidget({ - windowMode : true - }); - - wizardWidget.onFinished.attach(function(sender, result) { - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, data) { - var experiment = new Experiment(data); - BIOSAXS.openExperimentByExperiment(experiment); - wizardWidget.window.close(); - }); - wizardWidget.current.setLoading("ISPyB: Creating experiment"); - adapter.createTemplate(result.name, "comments", result.data); - }); - -// wizardWidget.draw(this.targetId, new ExperimentTypeWizardForm()); - wizardWidget.draw(this.targetId, new MeasurementCreatorStepWizardForm(BIOSAXS.proposal.getMacromolecules())); - } - })); - return actions; -}; - -TemplateGrid.prototype.refresh = function(data) { - var filtered = []; - for ( var i = 0; i < data.length; i++) { - if (data[i].experimentType == "TEMPLATE") { - filtered.push(data[i]); - } - } - this.store.loadData(this._prepareData(filtered), false); -}; - -TemplateGrid.prototype._getColumns = function() { - var _this = this; - function actionItemRenderer(value, meta, record, rowIx, ColIx, store) { - if (record.raw.buffer3VOs != null) { - if (record.raw.buffer3VOs.length > 0) { - return 'x-hide-display'; - } - } +function ExperimentGrid(args) { + this.width = "100%"; + this.height = 700; + this.minHeight = 500; - if (record.data.platesCount > 0) { - return 'x-hide-display'; - } - } + this.id = BUI.id(); + this.gridType = 'Ext.grid.Panel'; //'Ext.ux.LiveSearchGridPanel'; + this.tbar = false; + this.hideHeaders = false; + this.grouping = true; + this.title = null; - return [ { - xtype : 'rownumberer', - width : 40 - }, { - text : 'experimentId', - dataIndex : 'experimentId', - name : 'experimentId', - type : 'string', - hidden : true + this.filtered = null; + + /** maximum row count **/ + this.limit = 100; + + this.sessionIdFilter = null; + + /** if not null filtered by date **/ + this.date = null; + + this.sorters = [ { + property : 'date', + direction : 'DESC' }, { - text : 'Name', - dataIndex : 'name', - name : 'name', - type : 'string', - flex : 1 - }, + property : 'time', + direction : 'DESC' + } ]; - { - text : 'Type', - dataIndex : 'type', - name : 'type', - type : 'string', - hidden : true, - flex : 1, - renderer : function(val) { - return val; + this.dates = {}; + if (args != null) { + if (args.height != null) { + this.height = args.height; } - }, { - text : 'Macromolecules', - name : 'macromolecules_names', - dataIndex : 'macromolecules_names', - flex : 1, - renderer : function(val) { - return " " + val + ""; + if (args.limit != null) { + this.limit = args.limit; } - }, { - id : _this.id + 'GO', - width : 80, - sortable : false, - renderer : function(value, metaData, record, rowIndex, colIndex, store) { - return BUI.getGreenButton('EDIT'); + if (args.sorters != null) { + this.sorters = args.sorters; } - }, { - id : _this.id + 'REMOVE', - width : 100, - sortable : false, - renderer : function(value, metaData, record, rowIndex, colIndex, store) { - return BUI.getRedButton('REMOVE'); + if (args.sessionId != null){ + this.sessionIdFilter = args.sessionId; + } + if (args.filtered != null) { + this.filtered = args.filtered; + } + if (args.minHeight != null) { + this.minHeight = args.minHeight; + } + if (args.gridType != null) { + this.gridType = args.gridType; + } + if (args.tbar != null) { + this.tbar = args.tbar; + } + if (args.grouping != null) { + this.grouping = args.grouping; + } + if (args.width != null) { + this.width = args.width; + } + if (args.title != null) { + this.title = args.title; } - } ]; -}; - -TemplateGrid.prototype.input = function() { - var experiments = DATADOC.getExperimentList_10(); - return { - experiments : experiments, - proposal : new MeasurementGrid().input().proposal - - }; -}; - -TemplateGrid.prototype.test = function(targetId) { - var experimentGrid = new TemplateGrid({ - height : 350, - minHeight : 350, - width : 1000, - gridType : 'Ext.grid.Panel', - title : 'Experiments', - grouping : false, - tbar : true - }); - BIOSAXS.proposal = new Proposal(experimentGrid.input().proposal); - var panel = experimentGrid.getPanel(experimentGrid.input().experiments); - experimentGrid.refresh(experimentGrid.input().experiments); - panel.render(targetId); -}; - -function VolumeGrid() { - this.id = BUI.id(); + } + /** Events **/ + this.onEditButtonClicked = new Event(this); } -VolumeGrid.prototype.getPanel = function(experiment) { - this.experiment = experiment; - return this.render(); +ExperimentGrid.prototype._getFilterTypes = function() { + return []; }; -VolumeGrid.prototype.getVolumesPanel = function(data, title) { - var _this = this; - var store = Ext.create('Ext.data.Store', { - fields : [ 'name', 'volume', 'macromoleculeId', 'bufferId' ], - data : data - }); - store.sort([ { - property : 'name', - direction : 'ASC' - } ]); - - var grid = Ext.create('Ext.grid.Panel', { - title : title, - height : 400, - maxHeight : 400, - width : 900, - store : store, - margin : '10 0 50 10', - tbar : [ { - text : 'Go to Shipment', - icon : '../images/plane-small.gif', - handler : function() { - window.location = BUI.getCreateShipmentList(); - } - } ], - viewConfig : { - stripeRows : true, - listeners : { - cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { - if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonCreate') { - var stockSolutionWindow = new StockSolutionWindow(); - /** On stock solution SAVED **/ - stockSolutionWindow.onSaved.attach(function(sender, stockSolution) { - BIOSAXS.proposal.onInitialized.attach(function(sender, data) { - _this.refresh(_this.experiment); - }); - BIOSAXS.proposal.init(); - }); - var acronym = "ST"; - if (record.raw.macromoleculeId != null) { - acronym = acronym + "_" + BIOSAXS.proposal.getMacromoleculeById(record.raw.macromoleculeId).acronym; - } - if (record.raw.bufferId != null) { - acronym = acronym + "_" + BIOSAXS.proposal.getBufferById(record.raw.bufferId).acronym; - } - stockSolutionWindow.draw({ - concentration : record.raw.concentration, - macromoleculeId : record.raw.macromoleculeId, - bufferId : record.raw.bufferId, - name : acronym, - volume : record.raw.volume - }); - } - - if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonStockSolutions') { - var stockSolutionGrid = new StockSolutionGrid({ - btnAddVisible : false, - btnEditVisible : false, - btnRemoveVisible : false, - btnAddExisting : false, - isPackedVisible : true, - multiselect : false - }); - - var window = Ext.create('Ext.window.Window', { - title : 'Stock solutions by specimen', - height : 400, - width : 800, - layout : 'fit', - items : [ stockSolutionGrid.getPanel() ], - buttons : [ { - text : 'Close', - handler : function() { - window.close(); - } - } ] - - }).show(); - stockSolutionGrid.refresh(BIOSAXS.proposal.getStockSolutionsBySpecimen(record.raw.macromoleculeId, record.raw.bufferId)); - } - } - } - }, - columns : [ -// { -// text : '', -// dataIndex : 'macromoleculeId', -// width : 20, -// renderer : function(val, y, sample) { -// if (val != null) { -//// return BUI.getRectangleColorDIV(BIOSAXS.proposal.macromoleculeColors[val], 10, 10); -// return BUI.getRectangleColorDIV(_this.experiment.macromoleculeColors[val], 10, 10); -// } -// } -// }, -// { -// text : '', -// dataIndex : 'bufferId', -// width : 20, -// renderer : function(val, y, sample) { -// if (val != null) { -// return BUI.getRectangleColorDIV(_this.experiment.getSpecimenColorByBufferId(val), 10, 10); -// } -// } -// }, - { - text : 'Specimen', - dataIndex : 'name', - flex : 0.5 - }, - { - text : 'Estimated Volume', - dataIndex : 'volume', - tooltip : 'Estimation of the maximum volume needed for making this experiment', - flex : 0.5, - editor : { - allowBlank : true - }, - renderer : function(val, y, sample) { - return BUI.formatValuesUnits(sample.data.volume, 'µl', { - fontSize : 16, - decimals : 2, - unitsFontSize : this.unitsFontSize - }); - } - }, - { - text : 'Stock Solution', - id : _this.id + 'buttonStockSolutions', - dataIndex : 'name', - flex : 0.5, - tooltip : 'Stock Solutions containing this specimen in this proposal', - renderer : function(value, metaData, record, rowIndex, colIndex, store) { - var macromoleculeId = record.raw.macromoleculeId; - var bufferId = record.raw.bufferId; - var stockSolutions = BIOSAXS.proposal.getStockSolutionsBySpecimen(macromoleculeId, bufferId); - if (stockSolutions.length > 0) { - return "
" + stockSolutions.length + " x
"; - } +ExperimentGrid.prototype._prepareData = function(rows) { + var data = []; + var count = 0; + + rows.sort(function(a,b){return b.experimentId - a.experimentId;}); + for ( var i = 0; i < rows.length; i++) { + var row = rows[i]; + this.dates[moment(row.creationDate).format("YYYYMMDD")] = moment(row.creationDate).format("MMM Do YY"); + if ( + ( this.filtered == null || row.experimentType == this.filtered ) && (count < this.limit || this.limit == null ) && (this.sessionIdFilter == null || this.date != null || (this.date == null && this.sessionIdFilter == row.sessionId)) + ){ - } - }, { - id : _this.id + 'buttonCreate', - text : '', - tooltip : 'Create a new stock solution for shipping', - width : 170, - sortable : false, - renderer : function(value, metaData, record, rowIndex, colIndex, store) { - return BUI.getGreenButton('NEW STOCK SOLUTION', { - width : 160 + data.push({ + experimentId : row.experimentId, + status : row.status, + dataAcquisitionFilePath : row.dataAcquisitionFilePath, + type : row.experimentType, + name : row.name, + macromolecules_names : row.macromolecules, + percentageAnalysed : { + value : (row.dataCollectionDoneCount / row.dataCollectionCount) * 100, + text : row.dataCollectionDoneCount + " of " + row.dataCollectionCount + }, + percentageCollected : { + value : (row.measurementDoneCount / row.measurementCount) * 100, + text : row.measurementDoneCount + " of " + row.measurementCount + }, + percentageMerged : { + value : (row.measurementAveragedCount / row.measurementCount) * 100, + text : row.measurementAveragedCount + " of " + row.measurementCount + }, + date : moment(row.creationDate).format("YYYYMMDD"), + time : moment(row.creationDate).format("YYYYMMDDHHmmss"), + creationDate : row.creationDate }); - } - } ] - }); - return grid; + count ++; + } + } + return data; +}; +ExperimentGrid.prototype.getPanel = function(experiments) { + this.features = experiments; + return this._renderGrid(experiments); }; -VolumeGrid.prototype._prepareData = function(experiment) { - var keys = {}; - for ( var i = 0; i < experiment.getSamples().length; i++) { - var sample = experiment.getSamples()[i]; - var key = ""; - if (sample.macromoleculeId == null) { - key = experiment.getBufferById(sample.bufferId).acronym; - if (keys[key] == null) { - keys[key] = { - macromoleculeId : sample.macromoleculeId, - name : key, - bufferId : sample.bufferId, - volume : 0 - }; - } - keys[key].volume = Number(sample.volume) + Number(keys[key].volume); +ExperimentGrid.prototype.refresh = function(experiments) { + this.experiments = experiments; + var filtered = []; + for ( var i = 0; i < experiments.length; i++) { + if (experiments[i].experimentType != "TEMPLATE") { + filtered.push(experiments[i]); } + } - if ((sample.macromolecule3VO != null) || (sample.macromoleculeId != null)) { - macromoleculeId = sample.macromoleculeId; - if (sample.macromoleculeId == null) { - sample.macromoleculeId = sample.macromolecule3VO.macromoleculeId; - } - key = BIOSAXS.proposal.getMacromoleculeById(sample.macromoleculeId).acronym + " + " + experiment.getBufferById(sample.bufferId).acronym; - if (keys[key] == null) { - keys[key] = { - macromoleculeId : sample.macromoleculeId, - name : key, - bufferId : sample.bufferId, - volume : 0 - }; - } - keys[key].volume = Number(sample.volume) + Number(keys[key].volume); + this.parsedData = this._prepareData(filtered); + + var day = {}; + var dates = []; + for ( var i = 0; i < this.experiments.length; i++) { + var date = moment(this.experiments[i].creationDate).format("MMM Do YYYY"); + if (day[date] == null){ + dates.push({ + date : date, + value : moment(this.experiments[i].creationDate) + }); + day[date] = true; } } - var data = []; - for (var keyId in keys) { - data.push(keys[keyId]); + + this.storeDate.loadData(dates, false); + this.store.loadData(this.parsedData, false); + + /** If it has already been filtered by date we keep the filter **/ + if (this.date != null){ + this._filterByDate(this.date); } - - return data; }; -VolumeGrid.prototype.refresh = function(experiment) { - this.experiment = experiment; - this.macromoleculeGrid.getStore().loadData(this._prepareData(this.experiment), false); -}; +ExperimentGrid.prototype._getTopButtons = function() { + var _this = this; + /** Actions buttons **/ + var actions = []; -VolumeGrid.prototype.render = function() { - this.macromoleculeGrid = this.getVolumesPanel(this._prepareData(this.experiment), "Estimation of required Volume"); + actions.push(Ext.create('Ext.Action', { + icon : '../images/calendar_icon.png', + text : 'Show Calendar', + disabled : false, + handler : function(widget, event) { + var window = Ext.create('Ext.window.Window', { + title : 'Calendar', + width : 600, + height : 600, + modal : true, + closable : true, + layout : { + type : 'vbox', + align : 'stretch' + }, + items : [ { + xtype : 'label', + html : 'Click on a data acquisition to select:', + margin : '5 5 5 5' + }, { + html : '
', + margin : '5 5 5 5' + } - return { - xtype : 'container', - layout : 'vbox', - margin : "0, 0, 0, 5", - items : [ { - xtype : 'container', - layout : 'hbox', - margin : "0, 0, 0, 0", - items : [ this.macromoleculeGrid ] - } ] - }; + ] + }).show(); + + var calendarWidget = new CalendarWidget({ + height : 450 + }); + var aux = _this.limit; + /** we remove the limit temporarily **/ + _this.limit = null; + _this.sessionIdFilter = null; + calendarWidget.loadData(_this._prepareData(_this.experiments)); + _this.limit = aux; + calendarWidget.draw('calendar'); + calendarWidget.onClick.attach(function(sender, date) { + date = moment(date, "YYYY-MM-DD"); + _this._filterByDate(date); + window.close(); + + }); + + } + })); + this.storeDate = Ext.create('Ext.data.ArrayStore', { + fields: ['date', 'value'], + data : [] + }); + + this.dateMenu = Ext.create('Ext.form.field.ComboBox', { + hideLabel: true, + store: this.storeDate, + displayField: 'date', + typeAhead: true, + queryMode: 'local', + margin : '0 0 0 30', + triggerAction: 'all', + emptyText:'Select a date...', + selectOnFocus:true, + width:135, + listeners:{ + scope: this, + 'select': function (a,b,c){ + _this.limit = null; + _this._filterByDate(moment(b[0].raw.value, "YYYY-MM-DD")); + } + } + }); + + actions.push(this.dateMenu); + + actions.push("->"); + if (_this.filtered != null){ + actions.push({ + html : "Experiment Type: " + _this.filtered +"" + }); + } + else{ + actions.push({ + html : "Experiment Type: ALL" + }); + } + return actions; }; -VolumeGrid.prototype.input = function(experiment) { - return { - experiment : DATADOC.getExperiment_10() - }; +/** + * Date format: "YYYY-MM-DD" + */ +ExperimentGrid.prototype._filterByDate = function(date) { + var experimentsFiltered = []; + /** Getting all the experiments of date**/ + for ( var i = 0; i < this.experiments.length; i++) { + var experiment = this.experiments[i]; + if (experiment.creationDate != null) { + var experimentDate = moment(experiment.creationDate); + if (experimentDate.year() == date.year()) { + if (experimentDate.month() == date.month()) { + if (experimentDate.date() == date.date()) { + experimentsFiltered.push(experiment); + } + } + } + } + } + var parsedData = this._prepareData(experimentsFiltered); + this.store.loadData(parsedData); + this.date = date; }; -VolumeGrid.prototype.test = function(targetId) { - var volumeGrid = new VolumeGrid(); - BIOSAXS.proposal = new Proposal(new MeasurementGrid().input().proposal); - var panel = volumeGrid.getPanel(new Experiment(new VolumeGrid().input().experiment)); - Ext.create('Ext.panel.Panel', { - height : 500, - width : 1000, - renderTo : targetId, - items : [ panel ] +/** Only for templates **/ +ExperimentGrid.prototype._removeExperimentById = function(experimentId) { + var _this = this; + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(evt, args) { + _this.grid.setLoading(false); + document.getElementById(BIOSAXS.targetId).innerHTML = ""; + BIOSAXS.start(BIOSAXS.targetId); }); + this.grid.setLoading("Removing experiment "); + adapter.removeExperimentById(experimentId); }; - -/** - * Example of a tab panel to be populated with widgets - * - * @width width in pixels - * @height height in pixels - * - * #myEvent event that this class is supposed to throw - * - **/ -function ExampleTabs(args) { - this.width = 500; - this.height = 500; + +ExperimentGrid.prototype._renderGrid = function() { + var _this = this; + + /** Store **/ + this.store = Ext.create('Ext.data.Store', { + fields : this._getColumns(), + groupField : 'date', + autoload : true, + data : [], + remoteSort: false, + sorters : this.sorters + }); + - if (args != null){ - if (args.width != null){ - this.width= args.width; - } - if (args.height != null){ - this.height= args.height; - } - } - /** Events **/ - this.myEvent = new Event(); -} - -/** Populate the widget **/ -ExampleTabs.prototype.refresh = function(macromolecule) { -}; + var groupingFeature = Ext.create('Ext.grid.feature.Grouping', { + groupHeaderTpl : Ext.create('Ext.XTemplate', + "
{name:this.formatName}
", { + formatName : function(name) { + return _this.dates[name]; + } + }), + hideGroupedHeader : true, + startCollapsed : false + }); -/** It creates a tab panel with the specified with and height **/ -ExampleTabs.prototype.getPanel = function() { - this.panel = Ext.createWidget('tabpanel', { - height : this.height, - width : this.width, - style : { - padding : 2 + this.features = []; + if (this.grouping) { + this.features.push(groupingFeature); + } + + /** Grid **/ + this.grid = Ext.create(this.gridType, { + hideHeaders : this.hideHeaders, + resizable : true, + title : this.title, + width : this.width, + minHeight : this.minHeight, + height : this.height, + features : this.features, + store : this.store, + columns : this._getColumns(), + selModel : { + mode : 'SINGLE' }, - items : [ { - tabConfig : { - title : 'Test', - icon : '/ispyb/images/plane-small.gif' + viewConfig : { + stripeRows : true, + getRowClass : function(record, rowIdx, params, store) { + if (record.raw.type == "TEMPLATE") { + return "template-color-row"; + } + if ((record.raw.type == "CALIBRATION") && (record.raw.status == "FINISHED")) { + return "blue-row"; + } }, - items : [ { - xtype : 'container', - margin : '5 5 5 5', - items : [ ] - } ] - } ] + listeners : { + itemdblclick : function(dataview, record, item, e) { + _this._editExperiment(record.raw.experimentId); + }, + cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'GO') { + _this._editExperiment(record.raw.experimentId); + } + + if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'REMOVE') { + _this._removeExperimentById(record.raw.experimentId); + } + + } + } + } }); - return this.panel; -}; - -/** - * Shows an experiments with the specimens, measurements, analysis tabs where - * results are shown and the frames widget - * - * @targetId - */ -function ExperimentTabs(targetId) { - this.height = 900; - this.targetId = targetId; + var actions = _this._getTopButtons(); - this.id = BUI.id(); - var _this = this; + if (this.tbar) { + this.grid.addDocked({ + xtype : 'toolbar', + items : actions + }); + this.grid.getSelectionModel().on({ + selectionchange : function(sm, selections) { + if (selections.length) { + for ( var i = 0; i < actions.length; i++) { + if (actions[i].enable) { + actions[i].enable(); + } + } + } else { + for ( var i = 0; i < actions.length; i++) { + if (actions[i].alwaysEnabled == false) { + if (actions[i].disable) { + actions[i].disable(); + } + } + } + } + } + }); + } - this.INTERVAL_UPDATE = BUI.getUpdateInterval(); + return this.grid; +}; - this.gridHeight = 1000; - /** data * */ - this.experiment = null; +ExperimentGrid.prototype._getColumns = function() { + var _this = this; + function actionItemRenderer(value, meta, record, rowIx, ColIx, store) { + if (record.raw.buffer3VOs != null) { + if (record.raw.buffer3VOs.length > 0) { + return 'x-hide-display'; + } + } - - /** Specimen Widget contains a specimenGrid and a sampleChangerWidget than can be displayed with are vertical or horizontal layout **/ - this.specimenWidget = new SpecimenWidget({ - height : 600 - }); - - - /** For Overview * */ - /*this.samplePlateGroupWidget = new SamplePlateGroupWidget({ - showTitle : false, - height : 250, - margin : 5, - border : 0 + if (record.data.platesCount > 0) { + return 'x-hide-display'; + } + } - });*/ + return [ + { + text : 'experimentId', + dataIndex : 'experimentId', + name : 'experimentId', + type : 'string', + hidden : true + }, + { + xtype : 'rownumberer', + width : 40 + }, + { + text : 'Name', + dataIndex : 'name', + name : 'name', + type : 'string', + flex : 1 + }, - /** For Measurements * */ - /*this.measurementSamplePlateGroupWidget = new SamplePlateGroupWidget({ - showTitle : false, - height : 250, - margin : '5 0 0 0', - border : 0 - });*/ + { + text : 'Type', + dataIndex : 'type', + name : 'type', + type : 'string', + flex : 1, + renderer : function(val) { + if (val == "CALIBRATION") { + return "" + val + ""; + } + + return val; + } + }, + { + text : 'Macromolecules', + name : 'macromolecules_names', + dataIndex : 'macromolecules_names', + flex : 1, + renderer : function(val) { + if (val != null) { + return " " + val + ""; + } + return " Information not available"; + } + }, + { + text : 'Buffers', + dataIndex : 'buffer_names', + name : 'buffer_names', + flex : 1, + hidden : true, + renderer : function(val) { + return "Buffer/s: " + val + ""; + } + }, + { + text : 'Status', + dataIndex : 'status', + name : 'status', + type : 'string', + flex : 1, + renderer : function(val, x, sample) { + if (sample.raw.type == "TEMPLATE") { + return "READY"; + } + if (sample.raw.status == "ABORTED") { + return "" + val + ""; + } + return "" + val + ""; + } + }, + { + text : 'Download', + dataIndex : 'creationDate', + name : 'creationDate', + renderer : function(val, x, sample) { + if (sample != null) { + if (sample.raw.type == "HPLC") { + return; + } + return BUI.getZipHTMLByExperimentId(sample.raw.experimentId, sample.raw.name); + } + }, + width : 100 - this.measurementGridDone = new MeasurementGrid({ - height : 600, - minHeight : 400, - maxHeight : 800, - positionColumnsHidden : true, - showTitle : false, - estimateTime : true, - width : 900, - maxWidth : 1500, - addBtnEnable : false, - markDone : true, - removeBtnEnabled : false - }); - this.measurementGridDone.onSelected.attach(function(sender, measurements) { - var specimens = []; - for ( var i = 0; i < measurements.length; i++) { - specimens.push(_this.experiment.getSampleById(measurements[i].specimenId)); - } - //_this.measurementSamplePlateGroupWidget.selectSpecimens(specimens); - }); + }, + { + header : 'Measurements', + dataIndex : 'percentageCollected', + name : 'percentageCollected', + type : 'string', + renderer : function(val, y, sample) { + if ((sample.raw.type == "TEMPLATE") || (sample.raw.type == "HPLC")) { + return; + } + return "
" + BUI.getProgessBar(sample.raw.percentageCollected.value, sample.raw.percentageCollected.text) + "
"; + }, + width : 100 + }, + { + header : 'Averaged', + dataIndex : 'percentageMerged', + name : 'percentageMerged', + type : 'string', + renderer : function(val, y, sample) { + if ((sample.raw.type == "TEMPLATE") || (sample.raw.type == "HPLC")) { + return; + } + return "
" + BUI.getProgessBar(sample.raw.percentageMerged.value, sample.raw.percentageMerged.text) + "
"; + }, + width : 100 + }, + { + header : 'Subtractions', + dataIndex : 'percentageAnalysed', + name : 'percentageAnalysed', + type : 'string', + renderer : function(val, y, sample) { + if ((sample.raw.type == "TEMPLATE") || (sample.raw.type == "HPLC")) { + return; + } + return "
" + BUI.getProgessBar(sample.raw.percentageAnalysed.value, sample.raw.percentageAnalysed.text) + "
"; + }, + width : 100 + }, - /** AnalysisGrid * */ - this.analysisGrid = new AnalysisGrid({ - height : Ext.getBody().getViewSize().height * 0.9 - 300, - positionColumnsHidden : true, - sorters : [ { - property : 'priorityLevelId', - direction : 'ASC' - } ] - }); - - - /** Queue * */ - this.queueGrid = new QueueGrid({ - url : BUI.getQueueUrlByExperiment(), - height : Ext.getBody().getViewSize().height * 0.9 - 300, - width : Ext.getBody().getViewSize().width * 0.9 - 300, - isGrouped : true, - tbar : false, - bbar : false, - sorter : [{ - property: 'measurementId', - direction: 'DESC' - }] - }); + { + text : 'time', + dataIndex : 'time', + name : 'time', + hidden : true, + renderer : function(val) { + return val; + }, + width : 100 -} + }, { + text : 'Date', + dataIndex : 'date', + name : 'date', + renderer : function(val) { + return val; + }, + width : 100 -ExperimentTabs.prototype.error = function(error) { - var e = JSON.parse(error); - showError(e); - this.panel.setLoading(false); -}; + }, -ExperimentTabs.prototype.draw = function(experiment) { - this.renderDataAcquisition(experiment); -}; + { + text : 'Time', + dataIndex : 'creationDate', + name : 'creationDate', + renderer : function(val) { + return moment(val).format(" HH:mm:ss"); + }, + width : 100 -ExperimentTabs.prototype.refreshAnalysisData = function() { - var _this = this; - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, data) { - _this.analysisGrid.refresh(data, {experiment : _this.experiment}); - }); - adapter.getAnalysisInformationByExperimentId(this.experiment.experimentId); - - /** Auto load **/ - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, data) { - _this.queueGrid.refresh(data); - }); - adapter.getCompactAnalysisByExperimentId(this.experiment.experimentId); - this.queueGrid.store.proxy.url = BUI.getQueueUrlByExperiment(this.experiment.experimentId); - this.queueGrid.refresh(); + }, { + id : _this.id + 'GO', + width : 80, + sortable : false, + renderer : function(value, metaData, record, rowIndex, colIndex, store) { + return BUI.getGreenButton('GO'); + } + } ]; }; -ExperimentTabs.prototype.getSpecimenContainerHeight = function(experiment) { - var maxItems = 0; - if (maxItems < experiment.getSamples().length + 1) { - maxItems = experiment.getSamples().length + 1; - } - - var height = (maxItems + 1) * 40 + 40; - if (height > 400) { - height = 400; +/** Changes location.href in order to edit the experiment **/ +ExperimentGrid.prototype._editExperiment = function(experimentId) { + if (Ext.urlDecode(window.location.href).sessionId != null) { + location.href = 'viewProjectList.do?reqCode=display&experimentId=' + experimentId + '&sessionId='+ Ext.urlDecode(window.location.href).sessionId; + } else { + location.href = 'viewProjectList.do?reqCode=display&experimentId=' + experimentId; } - return height; -}; - -ExperimentTabs.prototype.getExperimentTitle = function() { - var experimentHeaderForm = new ExperimentHeaderForm(); - return experimentHeaderForm.getPanel(this.experiment); }; -ExperimentTabs.prototype.renderDataAcquisition = function(experiment) { - var _this = this; - this.experiment = experiment; +ExperimentGrid.prototype.input = function() { + var experiments = DATADOC.getExperimentList_10(); + return { + experiments : experiments, + proposal : new MeasurementGrid().input().proposal - var specimenGrid = new SpecimenGrid({ - height : 400, - maxHeight : 500, - width : 890 - }); + }; +}; - specimenGrid.onClick.attach(function(sender, args) { - }); +ExperimentGrid.prototype.test = function(targetId) { + var experimentGrid = new ExperimentGrid({ + height : 350, + minHeight : 350, + width : 1000 - specimenGrid.onSelected.attach(function(sender, specimens) { - //_this.samplePlateGroupWidget.selectSpecimens(specimens); - }); - var specimenContainer = this.specimenWidget.getPanel(); - this.specimenWidget.refresh(experiment); - /* - var specimenContainer = Ext.create('Ext.container.Container', { - layout : 'hbox', - width : 900, - padding : '10 0 0 2', - items : [ specimenGrid.getPanel() ] }); + BIOSAXS.proposal = new Proposal(experimentGrid.input().proposal); + var panel = experimentGrid.getPanel(experimentGrid.input().experiments); + experimentGrid.refresh(experimentGrid.input().experiments); + panel.render(targetId); +}; + + +function FitStructureToExperimentDataGrid(args) { + this.height = null; + this.width = null; + this.id = BUI.id(); + + if (args != null) { + if (args.height != null) { + this.height = args.height; + + if (args.width != null) { + this.width = args.width; + } + } + } + + this.onSelected = new Event(this); +} + +FitStructureToExperimentDataGrid.prototype.getStructuresByMacromolecule = function(macromolecule) { + var structures = []; + if (macromolecule.structure3VOs != null) { + if (macromolecule.structure3VOs.length > 0) { + for (var i = 0; i < macromolecule.structure3VOs.length; i++) { + structures.push(macromolecule.structure3VOs[i]); + } + } + } + return structures; +}; + +FitStructureToExperimentDataGrid.prototype._prepareData = function(subtractions) { + var data = []; + for (var i = 0; i < subtractions.length; i++) { + + for (var j = 0; j < subtractions[i].fitStructureToExperimentalData3VOs.length; j++) { + var fit = subtractions[i].fitStructureToExperimentalData3VOs[j]; + data.push({ + fit : fit.fitFilePath, + fitStructureToExperimentalDataId : fit.fitStructureToExperimentalDataId, + mixtureToStructure3VOs : fit.mixtureToStructure3VOs, + subtractedFile : subtractions[i].substractedFilePath + }); + } + } + return data; + +}; + +FitStructureToExperimentDataGrid.prototype.refresh = function(subtractions) { + this.store.loadData(this._prepareData(subtractions), false); +}; + +FitStructureToExperimentDataGrid.prototype.getPanel = function() { + var _this = this; + this.store = Ext.create('Ext.data.Store', { + fields : [ 'fit', 'subtractedFile', 'structureId', 'fitFilePath', 'mixtureToStructure3VOs' ], + data : [] + }); + + this.store.sort([ { + property : 'name', + direction : 'ASC' + } ]); + + this.selModel = Ext.create('Ext.selection.RowModel', { + allowDeselect : true, + mode : 'MULTI', + listeners : { + selectionchange : function(sm, selections) { + var selected = []; + for (var i = 0; i < selections.length; i++) { + selected.push(selections[i].raw); + } + _this.onSelected.notify(selected); + } + } + }); + + this.panel = Ext.create('Ext.grid.Panel', { + store : this.store, + deferredRender : false, + width : this.width, + height : this.height, + margin : 10, + selModel : this.selModel, + columns : [ { + text : 'Name', + dataIndex : 'fit', + flex : 1, + renderer : function(val, b, record) { + return BUI.getFileName(val); + } + }, { + text : 'PDB', + dataIndex : 'mixtureToStructure3VOs', + flex : 1, + renderer : function(values, b, record) { + var html = ""; + for (var i = 0; i < values.length; i++) { + var structure = BIOSAXS.proposal.getStructuresById(values[i].structureId); + html = html + ""; + if (structure != null){ + html = html + ""; + } + html = html + ""; + } + return html + "
" + structure.name + "
"; + } + }, { + text : 'Volume Fraction', + dataIndex : 'mixtureToStructure3VOs', + flex : 1, + renderer : function(values, b, record) { + var html = ""; + for (var i = 0; i < values.length; i++) { + html = html + ""; + html = html + ""; + html = html + ""; + } + + return html + "
" + values[i].volumeFraction + "
"; + } + },{ + text : 'Subtraction', + dataIndex : 'subtractedFile', + flex : 1, + renderer : function(values, b, record) { + return values.split("/")[values.split("/").length - 1]; + } + },], + + viewConfig : { + enableTextSelection : true, + preserveScrollOnRefresh : true + }, + listeners : { + cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + }, + afterrender : function() { + } + } + }); + return this.panel; +}; + +/** Static method **/ +FitStructureToExperimentDataGrid.prototype.RUNFitScattering = function(subtractionId, structureId) { + var _this = this; + /** Add to Workflow **/ + var adapter = new BiosaxsDataAdapter(); + var workflow = { + 'workflowTitle' : 'FitExperimentalDatatoStructure', + 'comments' : 'FitExperimentalDatatoStructure run from ISPyB for subtractionId: ' + subtractionId + ' and structureId ' + structureId + } + + var inputs = [ { + name : 'subtractionId', + value : subtractionId + }, { + name : 'structureId', + value : structureId + } ]; + + adapter.onSuccess.attach(function(sender, data) { + /** Add to Fit **/ + var adapter2 = new BiosaxsDataAdapter(); + var fit = { + 'workflowId' : data.workflowId, + 'subtractionId' : subtractionId, + 'structureId' : structureId, + 'comments' : 'Sent to workflow engine' + } + adapter2.onSuccess.attach(function(sender, fit) { + _this.refresh(_this.subtractionId, _this.macromolecule); + }); + adapter2.addFitStructureData(fit); + + }); + adapter.addWorkflow(workflow, inputs); + +}; + +/** + * It shows buffer grid with a top bar with "Add" button + * + * @height + * @searchBar + * @collapsed + * @width + */ +function FrameGrid(args) { + this.height = 500; + this.width = 500; + this.searchBar = false; + this.tbar = false; + this.collapsed = false; - specimenGrid.refresh(experiment);*/ - var experimentList = new ExperimentList([ _this.experiment ]); - var measurementContainer = Ext.create('Ext.container.Container', { - layout : 'vbox', - padding : '5px 0px 0px 10px', - items : [] - }); - measurementContainer.insert(0, _this.measurementGridDone.getPanel(this.experiment.getMeasurements(), experimentList)); -// measurementContainer.insert(1, _this.measurementSamplePlateGroupWidget.getPanel(experiment)); + if (args != null) { + if (args.height != null) { + this.height = args.height; + } + + if (args.searchBar != null) { + this.searchBar = args.searchBar; + } - // this.dataCollectionCurveVisualizer = new DataCollectionCurveVisualizer(); - // this.dataCollectionCurveVisualizer.experiments = [experiment]; - // this.dataCollectionCurveVisualizer.dataCollectionFrameTree.experiments = - // [experiment]; - // this.dataCollectionCurveVisualizer.dataCollections = - // experiment.getDataCollections(); + if (args.tbar != null) { + this.tbar = args.tbar; + } - this.panel = Ext.createWidget('tabpanel', { - plain : true, - style : { - padding : 2 - }, - items : [ { - tabConfig : { - id : 'genralTabl', - title : "Overview" - }, - items : [ specimenContainer ] - }, { - tabConfig : { - title : 'Measurements' - }, - items : [ measurementContainer] - }, { - tabConfig : { - id : 'SpecimenTab', - title : 'Analysis', - hidden : this.isTemplate() - }, - items : [ { - xtype : 'container', - layout : 'vbox', - padding : '5px 0px 10px 10px', - items : [ _this.analysisGrid.getPanel([]) ] - } ] - }, - { - tabConfig : { - id : 'newAnalysisTab', - title : 'Analysis BETA', - hidden : this.isTemplate() - }, - items : [ { - xtype : 'container', - layout : 'vbox', - padding : '5px 0px 10px 10px', - items : [ _this.queueGrid.getPanel([]) ] - } ] + if (args.collapsed != null) { + this.collapsed = args.collapsed; } - ] + + if (args.width != null) { + this.width = args.width; + } + } +} + +FrameGrid.prototype._edit = function(bufferId) { + var _this = this; + var window = new BufferWindow(); + window.onSuccess.attach(function(sender, proposal) { + _this.store.loadData(BIOSAXS.proposal.getBuffers()); }); + window.draw(BIOSAXS.proposal.getBufferById(bufferId)); +}; - return this.getPanel(this.panel); +FrameGrid.prototype.refresh = function(buffers, experimentId) { + this.experimentId = experimentId; + this.store.loadData(this._prepareData(buffers), false); }; -ExperimentTabs.prototype.isTemplate = function() { - if (this.experiment.json.type == "TEMPLATE") { - return true; - } - return false; +FrameGrid.prototype._prepareData = function(buffers) { + return buffers; }; -ExperimentTabs.prototype.update = function() { +FrameGrid.prototype._getTbar = function() { var _this = this; - var inter; - if (!_this.isTemplate()) { - function updateExperiments() { - _this.refreshAnalysisData(); - window.clearInterval(inter); - inter = setInterval(function() { - updateExperiments(); - }, _this.INTERVAL_UPDATE); - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, data) { - var experiment = new Experiment(data); - _this.measurementGridDone.refresh(experiment.getMeasurements(), new ExperimentList([ experiment ])); + var actions = []; + + actions.push(Ext.create('Ext.Action', { + icon : '../images/add.png', + text : 'Add Buffer', + disabled : false, + handler : function(widget, event) { + var window = new BufferWindow(); + window.onSuccess.attach(function(sender) { + _this.store.loadData(BIOSAXS.proposal.getBuffers()); }); - adapter.getExperimentById(_this.experiment.json.experimentId, "MEDIUM"); + window.draw({}); } - inter = setInterval(function() { - updateExperiments(); - }, _this.INTERVAL_UPDATE); - } + })); + return actions; }; -ExperimentTabs.prototype.getPanel = function(panel) { +FrameGrid.prototype.getPanel = function(buffers) { var _this = this; - if (this.experimentPanel == null) { - this.experimentPanel = Ext.create('Ext.container.Container', { - bodyPadding : 2, - width : Ext.getBody().getViewSize().width * 0.9, - renderTo : this.targetId, - style : { - padding : 2 - }, - items : [ this.getExperimentTitle(), this.panel ], - listeners : { - afterrender : function() { - _this.refreshAnalysisData(); - _this.update(); - } - } - }); - } - - return this.experimentPanel; -}; - -ExperimentTabs.prototype.input = function() { - return { - experiment : DATADOC.getExperiment_10(), - proposal : DATADOC.getProposal_10() - }; -}; - -ExperimentTabs.prototype.test = function(targetId) { - var experimentTabs = new ExperimentTabs(targetId); - BIOSAXS.proposal = new Proposal(experimentTabs.input().proposal); - experimentTabs.draw(new Experiment(experimentTabs.input().experiment)); -}; + this.store = Ext.create('Ext.data.Store', { + fields : [ 'frameNumber', 'I0', 'Rg', 'Mass', 'Vc', 'Qr', 'quality'], + data : buffers + }); - -/** - * Shows an experiments with the specimens, measurements, analysis tabs where results are shown and the frames widget - * - * @targetId - */ -function HPLCTabs(targetId) { - this.height = 1400; - this.targetId = targetId; + this.store.sort('frameNumber'); - this.id = BUI.id(); + var type = 'Ext.grid.Panel'; + if (this.searchBar == true) { + type = 'Ext.ux.LiveSearchGridPanel'; + } - this.gridHeight = 600; - this.pointsCount = 1036; - this.plotHeight = 350; - this.plotWidth = 800; - this.plotPanelPadding = 5; + this.grid = Ext.create(type, { + store : this.store, + height : this.height, + width : this.width, + margin : 5, + columns : [{ + text : 'Frame', + dataIndex : 'frameNumber', + flex : 1 + },{ + text : 'I0', + dataIndex : 'I0', + flex : 1, + renderer : function(val, y, sample) { + return BUI.formatValuesUnits(sample.data.I0, "", 12, 3); + } + },{ + text : 'Rg', + dataIndex : 'Rg', + flex : 1, + renderer : function(val, y, sample) { + return BUI.formatValuesUnits(sample.data.Rg, "nm", 12, 3); + } + },{ + text : 'Mass', + dataIndex : 'Mass', + flex : 1, + renderer : function(val, y, sample) { + return BUI.formatValuesUnits(sample.data.Mass, "", 12, 3); + } + },{ + text : 'Vc', + dataIndex : 'Vc', + flex : 1, + renderer : function(val, y, sample) { + return BUI.formatValuesUnits(sample.data.Vc, "", 12, 3); + } + },{ + text : 'Qr', + dataIndex : 'Qr', + flex : 1, + renderer : function(val, y, sample) { + return BUI.formatValuesUnits(sample.data.Qr, "", 12, 3); + } + },{ + text : 'Quality', + dataIndex : 'quality', + flex : 1, + renderer : function(val, y, sample) { + return (Number(sample.data.quality) * 100).toFixed(2) + "%"; + } + }, + { + text : '', + renderer : function(val, x, sample) { + if (sample != null) { + return BUI.getZipHTMLByFrameRangeId(_this.experimentId, sample.raw.frameNumber, sample.raw.frameNumber); + } + }, + width : 100 + }], + flex : 1, + viewConfig : { + stripeRows : true + } + }); - var _this = this; + /** Adding the tbar **/ + if (this.tbar) { + this.grid.addDocked({ + xtype : 'toolbar', + items : this._getTbar() + }); + } + return this.grid; +}; - this.analysisGrid = new HPLCAnalysisGrid({ - height : Ext.getBody().getViewSize().height * 0.9 - 300, - positionColumnsHidden : true, - sorters : [ { - property : 'priorityLevelId', - direction : 'ASC' - } ] - }); +FrameGrid.prototype.input = function() { + return []; +}; - this.frameGrid = new FrameGrid({ - width : 600, - height : 60, - collapsed : false, - tbar : false - }); - - this.peakGrid = new PeakGrid({ - width : 550, - height : 500, - collapsed : false, - tbar : false - }); - - this.peakGrid2 = new PeakGrid({ - width : 102, - height : this.plotHeight, +FrameGrid.prototype.test = function(targetId) { + var frameGrid = new FrameGrid({ + width : 800, + height : 350, collapsed : false, - tbar : false, - showExtendedColumns : true + tbar : true }); - - this.mainPlotPanel = new HPLCGraph({ - title : 'I0', - width : this.plotWidth - 110, - height : this.plotHeight, - bbar : true, - plots : { - "I0" : true, - "Rg" : true - }, - xlabel : "HPLC Frames", - scaled : true, - interactionModel : { - 'dblclick' : function(event, g, context) { - _this._selectFrame(g.lastx_); - var annotations = []; - annotations.push({ - series : g.selPoints_[0].name, - x : g.lastx_, - width : 100, - height : 23, - tickHeight : 4, - shortText : g.lastx_, - text : g.lastx_, - attachAtBottom : true - }); - g.setAnnotations(annotations); - } + var panel = frameGrid.getPanel([]); + panel.render(targetId); +}; + + +function PeakGrid(args) { + this.height = 500; + this.searchBar = false; + this.tbar = false; + this.collapsed = false; + this.width = 500; + this.showExtendedColumns = false; + + if (args != null) { + if (args.showExtendedColumns != null) { + this.showExtendedColumns = args.showExtendedColumns; + } + if (args.height != null) { + this.height = args.height; + } + if (args.searchBar != null) { + this.searchBar = args.searchBar; } - }); - this.intensityPlotPanel = new MergesHPLCGraph({ - title : 'Scattering', - width : this.plotWidth, - height : 500, - showRangeSelector : false, - xParam : 0, - xlabel : "scattering_I", - plots : { - "scattering_I" : true, - "subtracted_I" : true, - "buffer_I" : true + if (args.tbar != null) { + this.tbar = args.tbar; } - }); -} + if (args.collapsed != null) { + this.collapsed = args.collapsed; + } -HPLCTabs.prototype._selectFrame = function(frameNumber) { - try{ - this._renderScatteringCurve(frameNumber); - this.frameGrid.refresh([this.mainPlotPanel.getDataByFrameNumber(frameNumber)], this.experiment.experimentId); - } - catch(e){ - console.log(e); + if (args.width != null) { + this.width = args.width; + } } -}; +} -HPLCTabs.prototype.error = function(error) { - var e = JSON.parse(error); - showError(e); - this.panel.setLoading(false); -}; -HPLCTabs.prototype.loadDataMainPlot = function(data) { - var zeroArray = []; - for ( var i = 0; i < data.I0.length; i++) { - zeroArray.push(0); - } - data = [ { - param : "I0", - data : data.I0, - std : data.I0_Stdev, - color : '#0066CC', - label : "I0" - }, - { - param : "sum_I", - label : "sum_I", - color : "#00FF00", - data : data.sum_I, - std : zeroArray - }, - { - param : "Rg", - label : "Rg", - color : "#21610B", - data : data.Rg, - std : data.Rg_Stdev - }, { - param : "Mass", - data : data.mass, - std : data.mass_Stdev, - color : '#FF9900', - label : "Mass" - }, { - param : "Vc", - data : data.Vc, - std : data.Vc_Stdev, - color : '#990099', - label : "Vc" - }, { - param : "Qr", - data : data.Qr, - std : data.Qr_Stdev, - color : '#FF0066', - label : "Qr" - }, { - param : "quality", - label : "quality", - color : "#FF00FF", - data : data.quality, - std : zeroArray - } ]; - this.data = data; - this.mainPlotPanel.loadData(data); +PeakGrid.prototype.refresh = function(buffers) { + this.store.loadData(this._prepareData(buffers), false); }; -HPLCTabs.prototype._loadIntensityPlotByFrameNumber = function(frameNumber) { - var _this = this; +PeakGrid.prototype._prepareData = function(buffers) { - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, data) { - var array = []; - data = [ { - param : "q", - data : data[frameNumber].q, - std : data[frameNumber].q, - fstd : function(a) { - return parseFloat(a); - }, - color : '#green', - label : "q", - showOnMenu : false - }, - { - param : "scattering_I", - data : data[frameNumber].scattering_I, - fdata : function(a) { - return Math.log(parseFloat(a)); - }, - std : data[frameNumber].scattering_Stdev, - fstd : function(a) { - return Math.log(Math.abs(parseFloat(a))); - }, - color : 'green', - label : "Log(I) Sample" - }, - { - param : "buffer_I", - data : data[frameNumber].buffer_I, - fdata : function(a) { - return Math.log(parseFloat(a)); - }, - fstd : function(a) { - return Math.log(Math.abs(parseFloat(a))); - }, - std : data[frameNumber].subtracted_Stdev, - color : '#0000FF', - label : "Log(I) Avg Buf." - }, - { - param : "subtracted_I", - data : data[frameNumber].subtracted_I, - fdata : function(a) { - var value = (Math.log(parseFloat(a))); - if (isNumber(value)) - return value; - }, - fstd : function(a) { - var value = Math.log(Math.abs(parseFloat(a))); - if (isNumber(value)) - return value; - }, - std : data[frameNumber].subtracted_Stdev, - color : 'red', - label : "Log(I) Sub." - } - ]; +// for ( var i = 0; i < buffers.length; i++) { +// buffers[i].name = "Peak #" + (i+1); +// } + /** Adding information of buffer **/ + if (buffers.length > 0){ + buffers.unshift({ + name : "BUFFER", + start : 0, + experimentId : buffers[0].experimentId, + end : buffers[0].start - 1 + }); + } + + + return buffers; +}; - _this.intensityPlotPanel.xlabel = "Frame " + frameNumber + " (q, nm-1)"; +PeakGrid.prototype._getTbar = function() { + var _this = this; + var actions = []; - if (_this.intensityPlotPanel.hplcData == null) { - /** It creates also top bar **/ - _this.intensityPlotPanel.loadData(data); - } else { - _this.intensityPlotPanel.reloadData(data); - + actions.push(Ext.create('Ext.Action', { + icon : '../images/add.png', + text : 'Add Buffer', + disabled : false, + handler : function(widget, event) { + var window = new BufferWindow(); + window.onSuccess.attach(function(sender) { + _this.store.loadData(BIOSAXS.proposal.getBuffers()); + }); + window.draw({}); } - _this.intensityPlotPanel.panel.setLoading(false); - }); - adapter.onError.attach(function(sender, data) { - _this.intensityPlotPanel.panel.setLoading(false); - }); - this.intensityPlotPanel.panel.setLoading("Retrieving Frame " + frameNumber); - adapter.getH5FrameScattering(this.experiment.json.experimentId, frameNumber); + })); + return actions; }; -HPLCTabs.prototype._renderScatteringCurve = function(frameNumber) { +PeakGrid.prototype.getPanel = function(buffers) { var _this = this; + this.peakCount = 0; + this.store = Ext.create('Ext.data.Store', { + fields : ['name', {name:'start', type : 'int'}, {name:'end', type : 'int'}], + data : buffers + + }); - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, data) { - _this.intensityPlotPanel.setPeaks(data); - _this._loadIntensityPlotByFrameNumber(frameNumber); - try{ - var peaks = []; - for(key in data){ - peaks.push({ - start : key.split("-")[0], - end : key.split("-")[1], - experimentId : _this.experiment.json.experimentId - }); + this.store.sort('start'); + + var type = 'Ext.grid.Panel'; + if (this.searchBar == true) { + type = 'Ext.ux.LiveSearchGridPanel'; + } + this.grid = Ext.create(type, { + store : this.store, + height : this.height, + width : this.width, + margin : 5, + columns : [ + { + text : '', + dataIndex : 'name', + width : 100, + hidden : _this.showExtendedColumns, + renderer : function(val, y, sample) { + if (sample.data.name == "BUFFER") + return "BUFFER"; + _this.peakCount++; + return "Peak #" + _this.peakCount; + } + + + }, + { + text : 'Peaks', + dataIndex : 'start', + sortable : true, + width : 100, + hidden: !_this.showExtendedColumns, + renderer : function(val, y, sample) { + return "From " + Number(sample.raw.start) + " to " + Number(sample.raw.end); + } + }, + { + text : 'Frames', + hidden : _this.showExtendedColumns, + columns : [ + { + text : 'Start', + dataIndex : 'start', + sortable : true, + hidden : _this.showExtendedColumns, + width : 100, + renderer : function(val, y, sample) { + return Number(val); + } + },{ + text : 'End', + dataIndex : 'end', + width : 100, + hidden : _this.showExtendedColumns, + renderer : function(val, y, sample) { + return Number(val); + } + },{ + text : 'Total', + dataIndex : 'end', + width : 100, + hidden : _this.showExtendedColumns, + renderer : function(val, y, sample) { + return "" + (sample.raw.end - sample.raw.start) + " frames"; + } + } + ] + } +// , +// { +// text : '', +// hidden : _this.showExtendedColumns, +// renderer : function(val, x, sample) { +// if (sample != null) { +// return BUI.getZipHTMLByFrameRangeId(sample.raw.experimentId, sample.raw.start, sample.raw.end); +// } +// }, +// width : 100 +// } + ], + flex : 1, + viewConfig : { + stripeRows : true, + getRowClass : function(record, rowIdx, params, store) { + if (record.raw.name == "BUFFER") { + return "blue-row"; + } } - _this.peakGrid2.refresh(JSON.parse(JSON.stringify(peaks))); - _this.peakGrid.refresh(peaks); -// console.log(peaks); - } - catch(e){ - showError(e); } }); - adapter.onError.attach(function(sender, data) { - _this.intensityPlotPanel.setLoading(false); + + /** Adding the tbar **/ + if (this.tbar) { + this.grid.addDocked({ + xtype : 'toolbar', + items : this._getTbar() + }); + } + return this.grid; +}; + +PeakGrid.prototype.input = function() { + return []; +}; + +PeakGrid.prototype.test = function(targetId) { + var PeakGrid = new PeakGrid({ + width : 800, + height : 350, + collapsed : false, + tbar : true }); - this.intensityPlotPanel.panel.setLoading("Reading HDF5 File "); - adapter.getH5FramesMerge(this.experiment.json.experimentId); + + var panel = PeakGrid.getPanel([]); + panel.render(targetId); }; + +/** + * Macromolecule Grid showing macromolecules and adding anb updating buttons + * + * @height + * @maxHeight + * @width + * @cssFontStyle + * @searchBar makes this grid as Ext.ux.LiveSearchGridPanel + * @tbar top bar containing "Add" and "Update From SMIS" button + * @collapsed + * @collapsible + * @btnEditVisible + * @btnRemoveVisible + * @multiselect makes it multiselect using Ext.selection.CheckboxModel + * + * #onSelected + * #onMacromoleculesChanged + */ +function MacromoleculeGrid(args) { + this.height = 500; + this.width = 500; + this.id = BUI.id(); + this.maxHeight = this.height; -HPLCTabs.prototype._postRenderOverviewPanel = function() { - var _this = this; - var adapter = new BiosaxsDataAdapter(); - adapter.onError.attach(function(sender, data) { - data = data.replace(/NaN/g, '0'); - _this.loadDataMainPlot(JSON.parse(data)); - + this.searchBar = false; + this.tbar = false; + + this.collapsible = true; + this.collapsed = true; + + this.btnEditVisible = true; + this.btnRemoveVisible = false; + this.multiselect = false; + + /** Font style applied to the acronym column **/ + this.cssFontStyle = null; + + if (args != null) { + if (args.height != null) { + this.height = args.height; + this.maxHeight = this.height; + } + if (args.maxHeight != null) { + this.maxHeight = args.maxHeight; + } + if (args.width != null) { + this.width = args.width; + } + if (args.cssFontStyle != null) { + this.cssFontStyle = args.cssFontStyle; + } + + if (args.searchBar != null) { + this.searchBar = args.searchBar; + } + if (args.tbar != null) { + this.tbar = args.tbar; + } + if (args.collapsible != null) { + this.collapsible = args.collapsible; + } + if (args.collapsed != null) { + this.collapsed = args.collapsed; + } + if (args.btnEditVisible != null) { + this.btnEditVisible = args.btnEditVisible; + } + if (args.btnRemoveVisible != null) { + this.btnRemoveVisible = args.btnRemoveVisible; + } + if (args.multiselect != null) { + this.multiselect = args.multiselect; + } + } - }); - adapter.onSuccess.attach(function(sender, data) { - _this.loadDataMainPlot(data); - _this._renderScatteringCurve(0); - }); - adapter.getH5fileParameters(this.experiment.json.experimentId, [ - "I0", "I0_Stdev", "sum_I","Rg", "Rg_Stdev", "Vc", "Vc_Stdev", "Qr", "Qr_Stdev", "mass", "mass_Stdev", "quality" ]); + this.onSelected = new Event(); -}; + this.onMacromoleculesChanged = new Event(); +} -HPLCTabs.prototype._postRenderDetailsPanel = function() { - if (this.data != null) { - this.I0PlotPanel.loadData(this.data); - this.RgPlotPanel.loadData(this.data); - this.MassPlotPanel.loadData(this.data); - this.VcPlotPanel.loadData(this.data); - } -}; -HPLCTabs.prototype.draw = function(experiment) { - var _this = this; - this.renderDataAcquisition(experiment); -}; -HPLCTabs.prototype.getExperimentTitle = function() { - var experimentHeaderForm = new ExperimentHeaderForm(); - return experimentHeaderForm.getPanel(this.experiment); -}; -HPLCTabs.prototype.renderDataAcquisition = function(experiment) { +MacromoleculeGrid.prototype.edit = function(macromolecule) { var _this = this; - this.experiment = experiment; - if (this.experimentPanel == null) { - this.experimentPanel = Ext.create('Ext.container.Container', { - bodyPadding : 2, - height : this.height, - width : Ext.getBody().getViewSize().width * 0.9, - renderTo : this.targetId, - style : { - padding : 2 - }, - items : [ this.getExperimentTitle(), this.getPanel() ], - listeners : { - afterrender : function() { - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, data) { - _this.analysisGrid.refresh(data); - }); - adapter.getAnalysisInformationByExperimentId(_this.experiment.experimentId); - } - } - }); - } - return this.experimentPanel; + var window = new MacromoleculeWindow(); + window.onSave.attach(function(sender) { + _this.store.loadData(BIOSAXS.proposal.getMacromolecules()); + _this.onMacromoleculesChanged.notify(); + }); + window.draw(macromolecule); }; -HPLCTabs.prototype.getPanel = function() { +MacromoleculeGrid.prototype.getTbar = function() { var _this = this; - this.panel = Ext.createWidget('tabpanel', - { - plain : true, - height : this.height, -// width : 900, - style : { - padding : 2 - }, - items : [ - { - tabConfig : { - title : "Overview" + var actions = []; - }, - items : [ { - xtype : 'container', - layout : 'hbox', - border : 1, - flex : 1, - items : [ { - xtype : 'container', - layout : 'vbox', - border : 1, - items : [ { - xtype : 'container', - margin : '10px', - border : 1, - items : [ - { - xtype : 'container', - margin : '0px', - layout : 'hbox', - border : 1, - items : [ - this.peakGrid2.getPanel([]), - this.mainPlotPanel.getPanel() - ] - }, - { - xtype : 'container', - layout : 'vbox', - border : 1, - items : [ - { - html: '
Select a frame by double-clicking on the HPLC Frames plot
', - margin: 5, - border : 0 - }, - this.frameGrid.getPanel([]), - this.intensityPlotPanel.getPanel() - ] - } - - ] - }], - listeners : { - afterrender : function() { - _this._postRenderOverviewPanel(); - } - } - } ] - } ] - }, -// { -// tabConfig : { -// title : "Details" -// }, -// items : [ { -// xtype : 'container', -// layout : 'hbox', -// border : 1, -// flex : 1, -// items : [ { -// xtype : 'container', -// layout : 'vbox', -// border : 1, -// items : [ { -// xtype : 'container', -// padding : '1px', -// items : [ -// this.I0PlotPanel.getPanel(), this.RgPlotPanel.getPanel(), this.MassPlotPanel.getPanel(), -// this.VcPlotPanel.getPanel() ] -// } ], -// listeners : { -// afterrender : function() { -// _this._postRenderDetailsPanel(); -// } -// } -// } ] -// } ] -// }, - { - tabConfig : { - title : "Analysis" - }, - items : [ { - xtype : 'container', - layout : 'vbox', - padding : '5px 0px 10px 10px', - items : [ _this.analysisGrid.getPanel([]) ] - } ] - }, - { - tabConfig : { - title : "File Manager" - }, - items : [ { - xtype : 'container', - layout : 'vbox', - padding : '5px 0px 10px 10px', - items : [ - { - html: '
Custom Download
', - margin : '10 0 10 5', - border : 0 - }, - { - html: '
Download has been temporary disabled. Contact your labcontact if you want to extract the frames from the HDF5 file
', - margin : '10 0 10 5', - hidden : _this.experiment.experimentId == 2602, - border : 0 - }, - { - xtype : 'container', - layout : 'hbox', - margin : '0 0 0 20', - flex : 1, - items :[ - { - xtype: 'numberfield', - id: 'field_start', - fieldLabel: 'Frames from', - value: 0, - minValue: 0 - }, - { - xtype: 'numberfield', - id: 'field_end', - fieldLabel: 'to', - labelWidth : 20, - margin : '0 0 0 10', - minValue: 0 - }, - { - xtype: 'button', - /** allowing test account to download frames **/ - disabled : _this.experiment.experimentId != 2602, - text : 'Download', - margin : '0 0 0 10', - handler : function() { - var experimentId = _this.experiment.experimentId; - var start = Ext.getCmp("field_start").getValue(); - var end = Ext.getCmp("field_end").getValue(); - /** Checking if start is bigger than end **/ - if (start > end){ - var aux = end; - end = start; - start = aux; - } - var url = BUI.getZipFrameHPLCUrl(experimentId, start, end); - window.open(url) - } - } - ] - }, - _this.peakGrid.getPanel([]) - - ] - } - ] - } - ] + actions.push(Ext.create('Ext.Action', { + icon : '../images/add.png', + text : 'Add macromolecule', + disabled : false, + handler : function(widget, event) { + _this.edit(); + } + })); + actions.push("->"); + actions.push(Ext.create('Ext.Action', { + icon : '../images/folder_go.png', + text : 'Update From SMIS', + tooltip : "Retrieve all the macromolecules of your proposal from SMIS database", + disabled : false, + handler : function(widget, event) { + _this.grid.setLoading("Connecting to SMIS"); + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, data) { + BIOSAXS.proposal.setMacromolecules(data.macromolecules); + _this.refresh(BIOSAXS.proposal.macromolecules); + _this.grid.setLoading(false); + }); + adapter.onError.attach(function(sender, data) { + _this.grid.setLoading(false); }); + adapter.updateDataBaseFromSMIS(); + } + })); + return actions; +}; - return this.panel; +MacromoleculeGrid.prototype.deselectAll = function() { + this.grid.getSelectionModel().deselectAll(); +}; + +MacromoleculeGrid.prototype.selectById = function(macromoleculeId) { + this.grid.getSelectionModel().deselectAll(); + for ( var i = 0; i < this.grid.getStore().data.items.length; i++) { + var item = this.grid.getStore().data.items[i].raw; + if (item.macromoleculeId == macromoleculeId) { + this.grid.getSelectionModel().select(i); + } + } +}; + +MacromoleculeGrid.prototype.refresh = function(macromolecules) { + this.store.loadData(macromolecules, false); +}; + +MacromoleculeGrid.prototype.getColumns = function() { + var _this = this; + var columns = [ + { + text : 'Acronym', + dataIndex : 'acronym', + id : this.id + "acronym", + flex : 1, + renderer : function(value, metaData, record, rowIndex, colIndex, store) { + if (_this.cssFontStyle != null) { + return "" + value + ""; + } + return value; + } + }, { + text : 'Name', + dataIndex : 'name', + id : this.id + "name", + flex : 1, + hidden : true + } ]; + + if (this.btnEditVisible) { + columns.push({ + id : _this.id + 'buttonEditMacromolecule', + width : 85, + sortable : false, + renderer : function(value, metaData, record, rowIndex, colIndex, store) { + if (_this.btnEditVisible) { + return BUI.getGreenButton('EDIT'); + } + return null; + } + }); + } + if (this.btnRemoveVisible) { + columns.push({ + id : _this.id + 'buttonRemoveMacromolecule', + width : 85, + sortable : false, + renderer : function(value, metaData, record, rowIndex, colIndex, store) { + if (_this.btnRemoveVisible) { + return BUI.getRedButton('REMOVE'); + } + return null; + } + }); + } + + return columns; +}; + + +/** Returns the grid **/ +MacromoleculeGrid.prototype.getPanel = function() { + var _this = this; + + this.store = Ext.create('Ext.data.Store', { + fields : [ 'macromoleculeId', 'name', 'acronym' ] + }); + + var type = 'Ext.grid.Panel'; + if (this.searchBar == true) { + type = 'Ext.ux.LiveSearchGridPanel'; + } + + this.selModel = Ext.create('Ext.selection.RowModel', { +// allowDeselect : true, +// mode : 'MULTI', + listeners : { + selectionchange : function(sm, selections) { + var selected = []; + for (var i = 0; i < selections.length; i++) { + selected.push(selections[i].raw); + } + _this.onSelected.notify(selected); + } + } + }); + + this.grid = Ext.create(type, { + id : this.id, + title : 'Macromolecules', + collapsible : this.collapsible, + collapsed : this.collapsed, + store : this.store, + height : this.height, + maxHeight : this.maxHeight, + selModel : this.selModel, + columns : this.getColumns(), + width : this.width, + viewConfig : { + stripeRows : true, + listeners : { + 'celldblclick' : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + _this.edit(BIOSAXS.proposal.getMacromoleculeById(record.data.macromoleculeId)); + }, + 'cellclick' : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonEditMacromolecule') { + _this.edit(BIOSAXS.proposal.getMacromoleculeById(record.data.macromoleculeId)); + } + if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonRemoveMacromolecule') { + BUI.showBetaWarning(); + } + } + + } + } + }); + + /** Adding the tbar **/ + if (this.tbar) { + this.grid.addDocked({ + xtype : 'toolbar', + items : this.getTbar() + }); + } + return this.grid; +}; + +MacromoleculeGrid.prototype.input = function() { + return { + proposal : DATADOC.getProposal_10() + }; +}; + +MacromoleculeGrid.prototype.test = function(targetId) { + var macromoleculeGrid = new MacromoleculeGrid({ + width : 800, + height : 350, + collapsed : false, + tbar : true + }); + + BIOSAXS.proposal = new Proposal(macromoleculeGrid.input().proposal); + var panel = macromoleculeGrid.getPanel(BIOSAXS.proposal.macromolecules); + panel.render(targetId); }; /** - * Main form tab for macromolecule + * Shows measurements with their attributes as specimen, exposure temperature, + * volume to load, transmission etc... * - * @width + * @addBtnMultipleEdit: if true add a button for changing measurements' + * parameters by choosing multiple measurements. It opens + * MultipleEditMeasurementGridWindow + * @collapsed: if true it doesn't show buffer's measurements + * @editor: hashmap containing columns to be edited. It is an array of a json + * like editor of Ext + * @selModel + * @collapseBtnEnable + * @removeBtnEnabled Ext.selection.RowModel + * @addBtnEnable + * @updateRowEnabled true/false if update plugin is set to the grid + * @showTitle + * @title + * @isStatusColumnHidden + * @isPriorityColumnHidden + * @isTimeColumnHidden + * @estimateTime + * @margin + * @tbar * @height + * @maxHeight + * @minHeight + * @width + * @maxWidth + * @resizable + * @experimentColorBased when colors for buffers and macromolecules are not + * selected by the proposal but by experiment, so the + * number of colors is smaller * - * #onClose when user clicks on close button in the MacromoleculeForm - * #onSave when macromole is saved by macromoleculeForm + * #onClick #onSelected #onRemoved #onUpdateTime #onMeasurementChanged + * #onExperimentChanged */ -function MacromoleculeTabs(args) { - this.width = 500; +function MeasurementGrid(args) { + this.id = BUI.id(); + this.height = 500; + this.width = 900; - if (args != null) { - if (args.width != null) { - this.width = args.width; + this.maxWidth = 1200; + this.maxHeight = 600; + this.minHeight = 500; + + this.unitsFontSize = 9; + this.title = "Measurements"; + this.estimateTime = false; + this.collapsed = true; + this.tbar = true; + + this.showTitle = true; + this.resizable = true; + this.updateRowEnabled = true; + + /** + * Hash map containing the keys of the editable columns. Ex: + * 'exposureTemperature' * + */ + this.editor = { + comments : { + xtype : 'textfield', + allowBlank : true } - if (args.height != null) { - this.height = args.height; + }; + + this.isTimeColumnHidden = false; + this.isStatusColumnHidden = false; + this.isPriorityColumnHidden = true; + this.margin = "0 0 0 0"; + + this.addBtnEnable = true; + this.sorter = [ { + property : 'priority', + direction : 'ASC' + } ]; + + this.removeBtnEnabled = false; + this.collapseBtnEnable = true; + this.addBtnMultipleEdit = false; + this.sortingBtnEnable = false; + + var _this = this; + this.selModel = Ext.create('Ext.selection.RowModel', { + allowDeselect : true, + mode : 'MULTI', + listeners : { + selectionchange : function(sm, selections) { + var selected = []; + for (var i = 0; i < selections.length; i++) { + selected.push(selections[i].raw); + } + _this.onSelected.notify(selected); + } } - } + }); - var _this = this; + if (args != null) { - /** Widgets **/ + if (args.selModel != null) { + this.selModel = args.selModel; + } - /** Macromolecule Form **/ - this.macromoleculeForm = new MacromoleculeForm({ - width : this.width - 30, - height : this.height - 50, - }); + if (args.removeBtnEnabled != null) { + this.removeBtnEnabled = args.removeBtnEnabled; + } - this.macromoleculeForm.onClose.attach(function(sender) { - _this.onClose.notify(); - }); + if (args.addBtnMultipleEdit != null) { + this.addBtnMultipleEdit = args.addBtnMultipleEdit; + } - this.macromoleculeForm.onSave.attach(function(sender, macromolecule) { - _this.onSave.notify(macromolecule); - }); + // if (args.experimentColorBased != null) { + // this.experimentColorBased = args.experimentColorBased; + // } - this.assemblyForm = new AssemblyForm({ - width : this.width - 30, - height : this.height - 50, - }); - - this.rigibBodyModelingForm = new RigibBodyModelingForm({ - width : this.width - 30, - height : this.height - 50, - }); - - this.rigibBodyModelingForm.onSave.attach(function(sender, macromolecule) { - _this.onSave.notify(macromolecule); - }); - + if (args.collapsed != null) { + this.collapsed = args.collapsed; + } + if (args.resizable != null) { + this.resizable = args.resizable; + } - /** Events **/ - this.onClose = new Event(this); - this.onSave = new Event(this); -} + if (args.editor != null) { + this.editor = args.editor; + } -/** Populate the widget **/ -MacromoleculeTabs.prototype.refresh = function(macromolecule) { - this.macromoleculeForm.refresh(macromolecule); - this.assemblyForm.refresh(macromolecule); - this.rigibBodyModelingForm.refresh(macromolecule); + if (args.collapseBtnEnable != null) { + this.collapseBtnEnable = args.collapseBtnEnable; + } - if (macromolecule != null){ - if (macromolecule.macromoleculeId == null){ - Ext.getCmp(this.id + "_advancedTab").disable(); - Ext.getCmp(this.id + "_assemblyTab").disable(); + if (args.addBtnEnable != null) { + this.addBtnEnable = args.addBtnEnable; } - else{ - Ext.getCmp(this.id + "_advancedTab").enable(); - Ext.getCmp(this.id + "_assemblyTab").enable(); + if (args.sortingBtnEnable != null) { + this.sortingBtnEnable = args.sortingBtnEnable; } - } - else{ - Ext.getCmp(this.id + "_advancedTab").disable(); - Ext.getCmp(this.id + "_assemblyTab").disable(); - } -}; -MacromoleculeTabs.prototype.getItems = function() { - return [ { - tabConfig : { - title : 'General' - }, - items : [ { - xtype : 'container', - items : [ this.macromoleculeForm.getPanel() ] - } ] - }, { - id : this.id + "_assemblyTab", - tabConfig : { - title : 'Assembly' - }, - items : [ { - xtype : 'container', - items : [ this.assemblyForm.getPanel() ] - } ] - },{ - id : this.id + "_advancedTab", - tabConfig : { - title : 'Advanced' - }, - items : [ this.rigibBodyModelingForm.getPanel() ] - } ]; -}; + if (args.isPriorityColumnHidden != null) { + this.isPriorityColumnHidden = args.isPriorityColumnHidden; + } -MacromoleculeTabs.prototype.getPanel = function() { - this.panel = Ext.createWidget('tabpanel', { - height : this.height, - width : this.width, - plain : true, - margin : 5, - border : 0, - items : this.getItems() - }); - return this.panel; -}; - -function ResultTabs() { -} + if (args.width != null) { + this.width = args.width; + } -ResultTabs.prototype.draw = function(targetId, data, macromoleculeId) { - var panel = this.getPanel(targetId, data, macromoleculeId); - return Ext.create('Ext.container.Container', { - renderTo : targetId, - items : [ BUI.getMacromoleculeHeader(macromoleculeId), panel ] - }); + if (args.updateRowEnabled != null) { + this.updateRowEnabled = args.updateRowEnabled; + } -}; + if (args.showTitle != null) { + this.showTitle = args.showTitle; + if (this.showTitle == false) { + this.title = null; + } + } -ResultTabs.prototype._splitBySpecimen = function(data) { - var splitted = {}; + if (args.height != null) { + this.height = args.height; + } - for ( var i = 0; i < data.length; i++) { - var row = data[i]; - if (splitted[row.macromoleculeId + "_" + row.bufferId] == null) { - splitted[row.macromoleculeId + "_" + row.bufferId] = []; + if (args.maxHeight != null) { + this.maxHeight = args.maxHeight; } - splitted[row.macromoleculeId + "_" + row.bufferId].push(row); - } - return splitted; -}; + if (args.minHeight != null) { + this.minHeight = args.minHeight; + } -ResultTabs.prototype._getTabTitle = function(key, data) { - var macromoleculeId = key.split("_")[0]; - var bufferId = key.split("_")[1]; + if (args.maxWidth != null) { + this.maxWidth = args.maxWidth; + } - return BIOSAXS.proposal.getMacromoleculeById(macromoleculeId).acronym + " + " + BIOSAXS.proposal.getBufferById(bufferId).acronym + "(" + data.length + ")"; -}; + if (args.isStatusColumnHidden != null) { + this.isStatusColumnHidden = args.isStatusColumnHidden; + } + if (args.isTimeColumnHidden != null) { + this.isTimeColumnHidden = args.isTimeColumnHidden; + } -ResultTabs.prototype.getPanel = function(targetId, data, macromoleculeId) { + if (args.title != null) { + this.title = args.title; + } + if (args.estimateTime != null) { + this.estimateTime = args.estimateTime; + } - var dataFiltered = new AnalysisGrid({ - hideNulls : true, - grouped : true, - sorters : [ { - property : 'quality', - direction : 'DESC' - } ] - })._prepareData(data); + if (args.margin != null) { + this.margin = args.margin; + } - var items = [ { - tabConfig : { - title : 'Analysis (' + dataFiltered.length + ')' - }, - items : [ { - xtype : 'container', - layout : 'vbox', - padding : 10, - items : [ new AnalysisGrid({ - isScatteringPlotVisible : false - }).getPanel(dataFiltered) ] - } ] - }, { - tabConfig : { - title : 'Concentration Effects' - }, - items : [ new ResultSummaryForm().getPanel(data) ] - } ]; + if (args.tbar != null) { + this.tbar = args.tbar; + } + + if (args.sorter != null) { + this.sorter = args.sorter; + } - var splitted = this._splitBySpecimen(dataFiltered); - for ( var key in splitted) { - items.push({ - tabConfig : { - title : this._getTabTitle(key, splitted[key]) - }, - items : [ new ResultSummaryForm().getPanel(splitted[key]) ] - }); } + this.onClick = new Event(this); + this.onSelected = new Event(this); + this.onRemoved = new Event(this); + this.onUpdateTime = new Event(this); + this.onMeasurementChanged = new Event(this); + this.onExperimentChanged = new Event(this); +} - this.panel = Ext.createWidget('tabpanel', { - style : { - padding : 2 - }, - items : items +MeasurementGrid.prototype._sortBy = function(sort) { + var _this = this; + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, data) { + _this.onExperimentChanged.notify(data); + _this.grid.setLoading(false); + // wizardWidget.window.close(); }); - return this.panel; - + adapter.onError.attach(function(sender, data) { + _this.grid.setLoading(false); + alert("Oops, there was a problem"); + }); + _this.grid.setLoading("Sorting"); + adapter.sortMeasurements(this.experiments.experiments[0].experimentId, sort); }; - - -function ShipmentTabs(targetId) { - this.targetId = targetId; +MeasurementGrid.prototype._getMenu = function() { var _this = this; - this.gridHeight = 500; - /** data **/ - this.shipment = null; - - /** Shipment Form **/ - this.shipmentForm = new ShipmentForm({ - creationMode : false, - showTitle : false - }); - this.shipmentForm.onSaved.attach(function(sender, data) { - _this.refresh(data); + if (this.tbar) { - }); + var items = []; + if (_this.addBtnEnable) { + items.push({ + icon : '../images/add.png', + text : 'Add measurements', + handler : function() { + _this._openAddMeasurementWindow(); + } + }); + } + if (_this.addBtnMultipleEdit) { + items.push({ + icon : '../images/Edit_16x16_01.png', + text : 'Multiple Edit', + handler : function() { + var multipleEditMeasurementGridWindow = new MultipleEditMeasurementGridWindow(); + multipleEditMeasurementGridWindow.onExperimentChanged.attach(function(sender, data) { + _this.onExperimentChanged.notify(data); + }); - /** Cases grid **/ - this.caseGrid = new CaseGrid({ - height : this.gridHeight - }); + multipleEditMeasurementGridWindow.draw(_this.measurements, _this.experiments); - this.caseGrid.onAddButtonClicked.attach(function(sender, dewar) { - _this.caseGrid.grid.setLoading("ISPyB: Creating a new case"); - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, shipment) { - /** updateing shipment on proposal **/ - for ( var i = 0; i < BIOSAXS.proposal.shippings.length; i++) { - if (BIOSAXS.proposal.shippings[i].shippingId == shipment.shippingId) { - BIOSAXS.proposal.shippings[i] = shipment; } - } - _this.refresh(shipment); - }); - adapter.onError.attach(function(sender, shipment) { - _this.caseGrid.grid.setLoading(false); - }); - adapter.addCase(_this.shipment.json.shippingId); - }); + }); + } - this.caseGrid.onRemoveButtonClicked.attach(function(sender, dewarId) { - _this.panel.setLoading("ISPyB: removing case"); - _this.shipment.onSaved.attach(function(sender, shipment) { - _this.refresh(shipment); + items.push("->"); - }); - _this.shipment.removeCase(dewarId); - }); -} + if (_this.sortingBtnEnable) { + var split = Ext.create('Ext.button.Split', { + text : 'Sort by', + // handle a click on the button itself + handler : function() { + // alert("The button was clicked"); + }, + menu : new Ext.menu.Menu({ + items : [ + { + text : 'First Created First Measured', + handler : function() { + _this._sortBy("FIFO"); + } + }, "-", { + text : 'Default', + handler : function() { + _this._sortBy("DEFAULT"); + } + } ] + }) + }); + items.push(split); + } -ShipmentTabs.prototype.refresh = function(shipment) { + if (_this.collapseBtnEnable) { + items.push({ + text : 'Collapse buffers', + enableToggle : true, + scope : this, + toggleHandler : function(item, pressed) { + this.collapsed = pressed; + this.grid.getStore().loadData(this._prepareData(this.measurements, this.experiments), false); + }, + pressed : this.collapsed + }); + } + var tb = Ext.create('Ext.toolbar.Toolbar', { + items : items + }); + return tb; + } + return null; +}; +/** Opens WizardWidget for adding new measurements * */ +MeasurementGrid.prototype._openAddMeasurementWindow = function(measurements, experiments) { var _this = this; - this.shipment = shipment; - var proposal = new Proposal(); - proposal.onDataRetrieved.attach(function(sender, plates) { - - _this.refreshWithPlates(shipment, plates); - _this.caseGrid.grid.setLoading(false); + var wizardWidget = new WizardWidget(); + wizardWidget.onFinished.attach(function(sender, result) { + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, data) { + _this.onExperimentChanged.notify(data); + _this.grid.setLoading(false); + wizardWidget.window.close(); + }); + wizardWidget.current.setLoading("ISPyB: Adding measurements"); + adapter.addMeasurements(result.name, "comments", result.data, _this.experiments.experiments[0].experimentId); }); - proposal.getPlatesByProposal(); + + wizardWidget.draw(null, new MeasurementCreatorStepWizardForm(BIOSAXS.proposal.getMacromolecules(), { + noNext : true + })); }; -ShipmentTabs.prototype.refreshWithPlates = function(shipment, plates) { - this.shipment = new Shipment(shipment); +/******************************************************************************* + * Opens WizardWidget for adding new measurements + * + * @Measurements + * @Experiments experimentList Object + ******************************************************************************/ +MeasurementGrid.prototype._prepareData = function(measurements, experiments) { + var data = []; + for (var i = 0; i < measurements.length; i++) { + var measurement = measurements[i]; + var specimen = experiments.getSampleById(measurement.specimenId); + var buffer = experiments.getBufferById(specimen.bufferId); + measurement.buffer_acronym = buffer.acronym; + measurement.bufferId = buffer.bufferId; + measurement.volume = specimen.volume; + if (specimen.macromolecule3VO != null) { + measurement.acronym = specimen.macromolecule3VO.acronym; + measurement.macromoleculeId = specimen.macromolecule3VO.macromoleculeId; + } + measurement.concentration = specimen.concentration; + if (measurement.run3VO != null) { + measurement.energy = measurement.run3VO.energy; + measurement.expExposureTemperature = measurement.run3VO.exposureTemperature; + measurement.storageTemperature = measurement.run3VO.storageTemperature; + measurement.timePerFrame = measurement.run3VO.timePerFrame; + measurement.radiationAbsolute = measurement.run3VO.radiationAbsolute; + measurement.radiationRelative = measurement.run3VO.radiationRelative; + measurement.status = "DONE"; - this.caseGrid.refresh(this.shipment.getDewars(), plates); - this.panel.setLoading(false); + try { + if (measurement.run3VO.timeStart != null) { + if (measurement.run3VO.timeStart != "") { + measurement.miliseconds = moment(measurement.run3VO.timeStart).format("X"); + } + } + } catch (E) { + console.log(E); + } + } + + if (experiments.getDataCollectionByMeasurementId(measurement.measurementId).length > 0) { + var measurementtodatacollection3VOs = experiments.getDataCollectionByMeasurementId(measurement.measurementId)[0].measurementtodatacollection3VOs; + for (var k = 0; k < measurementtodatacollection3VOs.length; k++) { + if (measurementtodatacollection3VOs[k].dataCollectionOrder == 1) { + var specimenBuffer = experiments.getSampleById(experiments.getMeasurementById(measurementtodatacollection3VOs[k].measurementId).specimenId); + if (specimenBuffer.sampleplateposition3VO != null) { + measurement.bufferSampleplateposition3VO = specimenBuffer.sampleplateposition3VO; + measurement.bufferSampleplate = (experiments.getSamplePlateById(specimenBuffer.sampleplateposition3VO.samplePlateId)); + } + } + } + } + + if (this.collapsed) { + /** If collapsed only the samples * */ + if (specimen.macromolecule3VO != null) { + data.push(measurement); + } + } else { + data.push(measurement); + } + + } + return data; }; -ShipmentTabs.prototype.error = function(error) { - var e = JSON.parse(error); - showError(e); - this.panel.setLoading(false); +/** + * Refresh data grid with the measurements and the experiments + * + * @measurements array with measurement3VO objects + * @experiments array with experiments objects + */ +MeasurementGrid.prototype.refresh = function(measurements, experiments) { + this.experiments = experiments; + this.measurements = measurements; + this.store.loadData(this._prepareData(measurements, experiments), false); }; -//ShipmentTabs.prototype.refreshTabTitles = function() { -/*Ext.getCmp("MacromoleculeTab").setText(this.getMacromoleculeTitle()); -Ext.getCmp("BufferTab").setText(this.getBuffersTitle()); -Ext.getCmp("SpecimenTab").setText(this.getSpecimenTitle()); -Ext.getCmp("PlatesTab").setText(this.getPlatesTitle()); -Ext.getCmp("AssembliesTab").setText(this.getShipmentTitle()); -Ext.getCmp("PlateGroupsTab").setText(this.getPlateGroupsTitle());*/ -//}; -ShipmentTabs.prototype.draw = function(shipment) { - var _this = this; - _this.plates = []; - _this.shipment = shipment; - _this.render(shipment); +/** + * Set status bar to busy (refreshing icon) + * + * @msg message to be displayed on the bar + */ +MeasurementGrid.prototype._showStatusBarBusy = function(msg) { + var statusBar = Ext.getCmp(this.id + 'basic-statusbar'); + statusBar.setStatus({ + text : msg, + iconCls : 'x-status-busy', + clear : false + }); +}; - // var proposal = new Proposal(); - // proposal.onDataRetrieved.attach(function(sender, plates){ - // _this.plates = plates; - // _this.shipment = shipment; - // _this.render(shipment); - // - // }); - // proposal.getPlatesByProposal(); +/** + * Set status bar to ready (ok icon) + * + * @msg message to be displayed on the bar + */ +MeasurementGrid.prototype._showStatusBarReady = function(msg) { + var statusBar = Ext.getCmp(this.id + 'basic-statusbar'); + statusBar.setStatus({ + text : msg, + iconCls : 'x-status-valid', + clear : false + }); }; -//ShipmentTabs.prototype.getShipmentTitle = function() { -// return 'Shipment'; -//}; +/** + * If updateRowEnabled returns an array with Ext.grid.plugin.RowEditing + */ +MeasurementGrid.prototype._getPlugins = function() { + var _this = this; + var plugins = []; + if (this.updateRowEnabled) { + plugins.push(Ext.create('Ext.grid.plugin.RowEditing', { + clicksToEdit : 1, + listeners : { + validateedit : function(grid, e) { + /** Setting values * */ + for ( var key in _this.editor) { + e.record.raw[key] = e.newValues[key]; + } + /** Comments are always updatable* */ + e.record.raw.comments = e.newValues.comments; -//ShipmentTabs.prototype.getMacromoleculeTitle = function() { -// return 'Macromolecules (' + this.experiment.getMacromolecules().length + ')'; -//}; + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, measurement) { + _this.onMeasurementChanged.notify(measurement); + _this.grid.setLoading(false); + }); + adapter.onError.attach(function() { + alert("Error"); + _this.grid.setLoading(false); + }); -//ShipmentTabs.prototype.getBuffersTitle = function() { -// return 'Buffers (' + this.experiment.getBuffers().length + ')'; -//}; + _this.grid.setLoading(); + adapter.saveMeasurement(e.record.raw, _this.experiments.experiments[0]); + } + } + })); + } + return plugins; +}; -//ShipmentTabs.prototype.getPlateGroupsTitle = function() { -// return 'Plate Groups (' + this.experiment.getPlateGroups().length + ')'; -//}; +/** + * @key name of the columns mathing the this.editor[key] + */ +MeasurementGrid.prototype._getEditor = function(key) { + if (this.editor[key] != null) { + return this.editor[key]; + } + return null; +}; -//ShipmentTabs.prototype.getSampleChangerTitle = function() { -// return 'Sample Changer'; -//}; +MeasurementGrid.prototype.getPanel = function(measurements, experiments) { + this.experiments = experiments; + this.measurements = measurements; + var _this = this; + this.store = Ext.create('Ext.data.Store', { + fields : [ { + name : 'miliseconds', + type : 'int' + }, 'priority', 'bufferId', { + name : 'exposureTemperature', + type : 'numeric' + }, 'volumeToLoad','code', 'transmission', 'viscosity', 'waitTime', 'flow', 'buffer_acronym', 'macromoleculeId', 'acronym', 'concentration', 'extraFlowTime', 'volume', 'energy', + 'expExposureTemperature', 'storageTemperature', 'timePerFrame', 'radiationAbsolute', 'radiationRelative', 'status', 'comments' ], + data : this._prepareData(measurements, experiments) + }); + this.store.sort(this.sorter); + var bbar = {}; + try { + bbar = Ext.create('Ext.ux.StatusBar', { + id : _this.id + 'basic-statusbar', + defaultText : 'Ready', + text : 'Ready', + iconCls : 'x-status-valid', + items : [] + }); + } catch (exp) { + console.log("bbar error"); + } -//ShipmentTabs.prototype.getSpecimenTitle = function() { -// return 'Specimens(' + this.experiment.getSpecimenCount() + ')'; -//}; + this.grid = Ext + .create( + 'Ext.grid.Panel', + { + id : this.id, + title : this.title, + store : this.store, + selModel : this.selModel, + plugins : this._getPlugins(), + resizable : this.resizable, + margin : this.margin, + maxHeight : this.maxHeight, + minHeight : this.minHeight, + maxWidth : this.maxWidth, + width : this.width, + tbar : this._getMenu(), + columns : [ + { + text : 'Order', + dataIndex : 'priority', + width : 50, + hidden : _this.isPriorityColumnHidden, + sortable : true -//ShipmentTabs.prototype.getPlatesTitle = function() { -// return 'Plates(' + this.experiment.getSamplePlates().length + ')'; -//}; + }, + { + text : 'Run Number', + dataIndex : 'code', + width : 50, + hidden : true, + sortable : true -//ShipmentTabs.prototype.getBuffersTip = function() { -/*if (this.experiment.getBuffers().length == 0){ - return BUI.getWarningHTML("There are no buffers. Click on add to create new ones. Click on edit button or double click to edit them"); - -} -else{ - return BUI.getTipHTML("Click on edit button or double click to edit them. Click on duplicate to create an identical buffer including its additives") -}*/ -//}; + }, -//ShipmentTabs.prototype.refreshTips = function() { -/*Ext.getCmp("BufferTabTip").update(this.getBuffersTip()); -Ext.getCmp("SpecimenTabTip").update(this.getSpecimensTip());*/ -//}; + { + text : 'Specimen', + columns : [ -ShipmentTabs.prototype.render = function(shipment) { - return this.getPanel(shipment); -}; + { + text : '', + dataIndex : 'macromoleculeId', + width : 30, + renderer : function(val, y, sample) { + if (val != null) { + if (_this.experiments == null) { + return BUI.getRectangleColorDIV(BIOSAXS.proposal.macromoleculeColors[val], 10, 10); + } else { + return BUI.getRectangleColorDIV(_this.experiments.macromoleculeColors[val], 10, 10); + } + } + }, + sortable : true + }, + { + text : 'Macromo.', + dataIndex : 'acronym', + width : 80, + renderer : function(val, y, sample) { + return val; + }, + sortable : true + }, + { + text : 'Conc. ', + dataIndex : 'concentration', + width : 80, + renderer : function(val, y, sample) { + if (sample.raw.macromoleculeId == null) { + return ""; + } + if (isNaN(val)) + return val; -ShipmentTabs.prototype.getShipmentHeader = function(shipment) { - var _this = this; - function getHTMLSource() { - var name = shipment.json.shippingName; - var status = shipment.json.shippingStatus; - var creationDate = shipment.json.creationDate; - var html = BUI.createFormLabel("Name :", name, 75, 400); - html = html + BUI.createFormLabel("Status :", status, 75, 400); - html = html + BUI.createFormLabel("Date :", creationDate, 75, 400); - return html; - } + if (val != 0) { + return BUI.formatValuesUnits(val, '', { + fontSize : 16, + decimals : 3, + unitsFontSize : this.unitsFontSize + }); + } else { + return; + } - return Ext.create('Ext.container.Container', { - frame : false, - layout : 'hbox', - title : 'General', - padding : 5, - bodyPadding : '5 5 0 0', - width : 890, - margin : '0 0 10 0', - height : 100, - style : { - borderColor : '#BDBDBD', - borderStyle : 'solid', - borderWidth : '1px' - }, - fieldDefaults : { - msgTarget : 'side', - labelWidth : 100 - }, - items : [ { - margin : "0 0 0 0", - width : 475, - border : 0, - html : getHTMLSource() - } - ] - }); -}; + }, + sortable : true + }, + { + text : '', + dataIndex : 'bufferId', + width : 30, + hidden : false, + renderer : function(val, y, sample) { + if (val != null) { + var color = '#FFCCFF'; + if (_this.experiments != null) { + var dc = _this.experiments.getDataCollectionByMeasurementId(sample.raw.measurementId); + if (dc != null) { + if (dc.length > 0) { + color = _this.experiments.getSpecimenColorByBufferId(_this.experiments + .getMeasurementById(dc[0].measurementtodatacollection3VOs[0].measurementId).specimenId); + } + } + } else { + color = BIOSAXS.proposal.bufferColors[val]; + } + return BUI.getRectangleColorDIV(color, 10, 10); + } + }, + sortable : true + }, + { + text : 'Buffer', + dataIndex : 'buffer_acronym', + width : 120, + renderer : function(val, y, sample) { + if (sample.raw.bufferSampleplateposition3VO != null) { + return BIOSAXS.proposal.getBufferById(sample.raw.bufferId).acronym + " Plate: [" + + sample.raw.bufferSampleplate.slotPositionColumn + ", " + + BUI.getSamplePlateLetters()[sample.raw.bufferSampleplateposition3VO.rowNumber - 1] + "-" + + sample.raw.bufferSampleplateposition3VO.columnNumber + "]"; + } + return val; + }, + sortable : true + }, { + text : 'Position', + width : 100, + hidden : true, + renderer : function(val, y, sample) { + if (_this.experiments != null) { + return BUI.getSamplePositionHTML(_this.experiments.getSampleById(sample.raw.specimenId), _this.experiments.experiments[0]); + } + } + } ] + }, + { + text : 'Parameters', + columns : [ + { + text : 'Ex. Flow. time (s)', + dataIndex : 'extraFlowTime', + width : 100, + hidden : true, + renderer : function(val, y, sample) { + return BUI.formatValuesUnits(val, 's', { + fontSize : 12, + decimals : 0, + unitsFontSize : this.unitsFontSize + }); + } + }, + { + text : 'Exp. Temp.', + dataIndex : 'exposureTemperature', + width : 70, + renderer : function(val, y, sample) { + if (Number(val)) { + return BUI.formatValuesUnits(val, 'C', { + fontSize : 12, + decimals : 2, + unitsFontSize : this.unitsFontSize + }); + } + return null; + }, + sortable : true, + editor : this._getEditor("exposureTemperature") + }, + { + text : 'Vol. Load', + dataIndex : 'volumeToLoad', + width : 60, + hidden : false, + editor : this._getEditor("volumeToLoad"), + renderer : function(val, y, sample) { + // return val+ " µl"; + return BUI.formatValuesUnits(val, 'µl', { + fontSize : 12, + decimals : 2, + unitsFontSize : this.unitsFontSize + }); + } + }, + { + text : 'Volume in Well', + dataIndex : 'volume', + hidden : true, + editor : this._getEditor("volume"), + width : 80, + renderer : function(val, y, sample) { + // return val + "(µl)"; + return BUI.formatValuesUnits(val, 'µl', { + fontSize : 12, + decimals : 2, + unitsFontSize : this.unitsFontSize + }); + } + }, + { + text : 'Trans.', + dataIndex : 'transmission', + width : 60, + editor : this._getEditor("transmission"), + renderer : function(val, y, sample) { + return BUI.formatValuesUnits(val, '%', { + fontSize : 12, + decimals : 0, + unitsFontSize : this.unitsFontSize + }); + } + }, + { + text : 'Wait T.', + dataIndex : 'waitTime', + editor : this._getEditor("waitTime"), + width : 50, + renderer : function(val, y, sample) { + // if (val != 0) { + return BUI.formatValuesUnits(val, 's', { + fontSize : 12, + decimals : 0, + unitsFontSize : this.unitsFontSize + }); + // } + } + }, + { + text : 'Flow', + dataIndex : 'flow', + editor : this._getEditor("flow"), + width : 50, + renderer : function(val, y, sample) { + if (val == true) { + return "yes"; + } + return null; + } + }, + { + text : 'Viscosity', + dataIndex : 'viscosity', + tooltip : 'The viscosity of a fluid is a measure of its resistance to gradual deformation by shear stress or tensile stress. For liquids, it corresponds to the informal notion of "thickness"', + editor : this._getEditor("viscosity"), + width : 50, + renderer : function(val, y, sample) { + return val; + } + } ] + }, { + text : 'Status', + dataIndex : 'status', + width : 50, + hidden : _this.isStatusColumnHidden, + renderer : function(val, y, sample) { + if (val != null) { + if (val == 'DONE') { + return "" + val + " "; + } + } + } + }, { + text : 'Time', + dataIndex : 'time', + width : 80, + hidden : _this.isTimeColumnHidden, + renderer : function(val, y, sample) { + if (sample.raw.run3VO != null) { + if (sample.raw.run3VO.timeStart != null) { + if (sample.raw.run3VO.timeStart != "") { + var m = moment(sample.raw.run3VO.timeStart); + return m.format("hh:mm:ss a"); + } + } + } + } + }, { + text : 'Energy', + dataIndex : 'energy', + width : 100, + hidden : true + }, { + text : 'Real Exp. Temp.(C)', + width : 100, + dataIndex : 'expExposureTemperature', + hidden : true + }, { + text : 'Storage Temp.(C)', + width : 100, + dataIndex : 'storageTemperature', + hidden : true + }, { + text : 'Time/Frame (s)', + width : 100, + dataIndex : 'timePerFrame', + hidden : true + }, { + text : 'Radiation Relative', + dataIndex : 'radiationRelative', + width : 100, + hidden : true + }, { + text : 'Radiation Absolute', + dataIndex : 'radiationAbsolute', + width : 100, + hidden : true + }, { + text : 'Comments', + dataIndex : 'comments', + flex : 1, + hidden : false, + editor : this._getEditor("comments") -ShipmentTabs.prototype.getTabPanel = function(shipment) { - this.panel = Ext.createWidget('tabpanel', { - height : 600, - style : { - padding : 2 - }, - items : [ { - tabConfig : { - id : 'Shipment', - title : 'Shipment', - icon : '/ispyb/images/plane-small.gif' - }, - items : [ { - xtype : 'container', - margin : '5 5 5 5', - items : [ this.shipmentForm.getPanel(shipment) ] - } ] - }, { - tabConfig : { - id : 'Cases', - title : 'Cases', - icon : '../images/box-icon-very-small.png' - }, - items : [ { - xtype : 'container', - margin : '5 5 5 5', - items : [ this.caseGrid.getPanel(shipment.getDewars(), this.plates) ] - } ] - } ] - }); - return this.panel; -}; + }, { + id : _this.id + 'buttonRemoveSample', + text : '', + hidden : !_this.removeBtnEnabled, + width : 100, + sortable : false, + renderer : function(value, metaData, record, rowIndex, colIndex, store) { + if (record.raw.macromoleculeId != null) { + if (_this.removeBtnEnabled) { + return BUI.getRedButton('REMOVE'); + } + } + } + } ], + bbar : bbar, + viewConfig : { + stripeRows : true, + getRowClass : function(record, index, rowParams, store) { + if (record.data.status == "DONE") { + return 'green-row'; + } -ShipmentTabs.prototype.getPanel = function(shipment) { - var _this = this; - this.shipment = shipment; - if (this.plates == null) { - this.plates = []; - } + }, + listeners : { + 'itemclick' : function(grid, record, item, index, e, eOpts) { + _this.onClick.notify({ + specimen : record.raw + }); + }, + 'cellclick' : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonRemoveSample') { + grid.getStore().removeAt(rowIndex); - if (this.shipPanel == null) { - this.shipPanel = Ext.create('Ext.container.Container', { - bodyPadding : 2, - width : Ext.getBody().getViewSize().width * 0.9, - renderTo : this.targetId, - style : { - padding : 2 - }, - items : [ this.getShipmentHeader(shipment), this.getTabPanel(shipment) ] - }); - } + if (record.raw.measurementId != null) { + /** For testing * */ + grid.setLoading("ISPyB: Removing measurement"); + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, data) { + grid.setLoading(false); + /** + * We get and refresh experiment + * because specimens has changed * + */ + var adapter2 = new BiosaxsDataAdapter(); + adapter2.onSuccess.attach(function(sender, experiment) { + _this.onRemoved.notify(experiment); + _this._showStatusBarReady('Ready'); + }); + if (_this.experiments.experiments[0].experimentId != null) { + adapter2.getExperimentById(_this.experiments.experiments[0].experimentId, "MEDIUM"); + _this._showStatusBarBusy("ISPyB: Removing Unused Specimens"); + } + }); - return this.shipPanel; -}; - -/** - * Shows an template with the specimens, measurements and the experiment's - * requirement - * - * @targetId - */ -function TemplateTabs(targetId) { - this.height = 600; - this.targetId = targetId; + adapter.onError.attach(function(sender, data) { + alert("Error: " + data); + grid.setLoading(false); + }); - this.id = BUI.id(); - var _this = this; + adapter.removeMeasurement(record.raw); + } + } - this.gridHeight = 1000; - /** data * */ - this.experiment = null; + } - this.specimenSelected = null; + } + } + }); - /** Specimen Widget contains a specimenGrid and a sampleChangerWidget than can be displayed with are vertical or horizontal layout **/ - this.specimenWidget = new SpecimenWidget({ - height : this.height - }); - - this.samplePlateGroupWidget = new SamplePlateGroupWidget({ - showTitle : false, - height : 250, - margin : 5, - bbar : true - }); - - this.samplePlateGroupWidget.onExperimentChanged.attach(function(sender, experiment) { - _this.refresh(experiment); - }); + this.grid.on("afterrender", function() { - this.samplePlateGroupWidget.onClick.attach(function(sender, args) { - }); + function updateTime() { + try { + _this.estimatedTime = _this.estimatedTime - 1; - this.volumePlanificator = new VolumeGrid(); + _this.onUpdateTime.notify({ + hours : (Number(_this.estimatedTime / 3600).toFixed()), + minutes : (Number((_this.estimatedTime / 60) % 60).toFixed()), + seconds : (Number(_this.estimatedTime % 60).toFixed()) - /** For Measurements * */ - var storeViscosity = Ext.create('Ext.data.Store', { - fields : [ 'name' ], - data : [ { - "name" : "low" - }, { - "name" : "medium" - }, { - "name" : "high" - } ] - }); + }); - // Create the combo box, attached to the states data store - var viscosityEditor = Ext.create('Ext.form.ComboBox', { - fieldLabel : '', - store : storeViscosity, - queryMode : 'local', - displayField : 'name', - valueField : 'name' - }); + if (Number(_this.estimatedTime) < 0) { + _this.timer = null; + grid.setTitle(_this.title); + } - this.measurementGrid = new MeasurementGrid({ - maxWidth : 1500, - estimateTime : false, - positionColumnsHidden : true, - isPriorityColumnHidden : true, - isStatusColumnHidden : true, - isTimeColumnHidden : true, - updateRowEnabled : true, - collapsed : true, - removeBtnEnabled : true, - showTitle : false, - collapseBtnEnable : false, - addBtnMultipleEdit : true, - sortingBtnEnable : true, - editor : { - exposureTemperature : { - xtype : 'textfield', - allowBlank : true - }, - comments : { - xtype : 'textfield', - allowBlank : true - }, - volumeToLoad : { - xtype : 'numberfield', - allowBlank : true - }, - transmission : { - xtype : 'numberfield', - allowBlank : true - }, - viscosity : viscosityEditor, - waitTime : { - xtype : 'numberfield', - allowBlank : true - }, - flow : { - xtype : 'checkbox', - allowBlank : true + } catch (e) { + console.log(e); + _this.timer = null; } } - }); - - this.measurementGrid.onSelected.attach(function(sender, measurements) { - var specimens = []; - for ( var i = 0; i < measurements.length; i++) { - specimens.push(_this.experiment.getSampleById(measurements[i].specimenId)); - } - }); - this.measurementGrid.onMeasurementChanged.attach(function(sender, measurement) { - _this.experiment.setMeasurement(measurement); - _this.refresh(_this.experiment); - }); + if (_this.estimateTime) { + var experimentList = _this.experiments; + var collected = experimentList.getMeasurementsCollected(); + if (collected.length > 0) { + if (collected[0].run3VO != null) { + try { + var end = collected[0].run3VO.timeEnd; + var start = collected[0].run3VO.timeStart; + var dstart = moment(start); + var dend = moment(end); + var seconds = Number(dend.diff(dstart) / 1000).toFixed(); - this.measurementGrid.onExperimentChanged.attach(function(sender, json) { - _this.refresh(new Experiment(json)); - }); + _this.estimatedTime = (seconds * experimentList.getMeasurementsNotCollected().length); - this.measurementGrid.onRemoved.attach(function(sender, experiment) { - _this.refreshSpecimen(new Experiment(experiment)); - }); + if (_this.estimatedTime > 0) { + updateTime(); + _this.timer = setInterval(function() { + updateTime(); + }, 1000); + } - this.measurementGrid.onUpdateTime.attach(function(sender, args) { - document.getElementById(_this.id + "_counter").innerHTML = args.hours + 'h, ' + args.minutes + 'min and ' + args.seconds + ' seconds'; + } catch (e) { + } + } + } + } }); - -} - -TemplateTabs.prototype.refreshMeasurement = function(experiment) { - this.experiment = experiment; - this.experiment.onSaved = new Event(this); - this.experiment.onSpecimenSaved = new Event(this); - var experimentList = new ExperimentList([ this.experiment ]); - this.measurementGrid.refresh(experimentList.getMeasurementsNotCollected(), experimentList); -}; -TemplateTabs.prototype.refreshSpecimen = function(experiment) { - this.experiment = experiment; - this.experiment.onSaved = new Event(this); - this.experiment.onSpecimenSaved = new Event(this); - this.samplePlateGroupWidget.refresh(this.experiment); - this.specimenWidget.refresh(this.experiment); - this.volumePlanificator.refresh(this.experiment); + return this.grid; }; -TemplateTabs.prototype.refresh = function(experiment) { - // var start = new Date().getTime(); - this.experiment = experiment; - this.experiment.onSaved = new Event(this); - this.experiment.onSpecimenSaved = new Event(this); - - // var experimentList = new ExperimentList([this.experiment]); - this.refreshMeasurement(experiment); - this.refreshSpecimen(experiment); - /** Refreshing grids * */ - this.panel.setLoading(false); - +/** Method for testing * */ +MeasurementGrid.prototype.input = function() { + var experiment = DATADOC.getExperiment_10(); + var measurements = DATADOC.getMeasurements_10(); + var proposal = DATADOC.getProposal_10(); + return { + experiment : experiment, + measurements : measurements, + proposal : proposal + }; }; -TemplateTabs.prototype.error = function(error) { - var e = JSON.parse(error); - showError(e); - this.panel.setLoading(false); +MeasurementGrid.prototype.test = function(targetId) { + var measurementGrid = new MeasurementGrid({ + tbar : true + }); + BIOSAXS.proposal = new Proposal(measurementGrid.input().proposal); + var panel = measurementGrid.getPanel(measurementGrid.input().measurements, new ExperimentList([ new Experiment(measurementGrid.input().experiment) ])); + panel.render(targetId); }; + +/** + * A shipment may contains one or more cases where stock solutions and sample + * plates are stored + * + * @height + * @btnEditVisible + * @btnRemoveVisible + * + * #onEditButtonClicked + */ +function MolarityGrid(args) { + this.height = 100; + this.width = 100; + + if (args != null) { + if (args.height != null) { + this.height = args.height; + } + if (args.width != null) { + this.width = args.width; + } + } + + var _this = this; + + this.molarityForm = new MolarityForm({height : 180, width : 455}); + + this.molarityForm.onSave.attach(function(sender){ + _this.molarityWindow.destroy(); + _this.updateProposal(); + + }); + + this.molarityForm.onClose.attach(function(sender){ + _this.molarityWindow.destroy(); + + }); + + /** Events * */ + this.onEditButtonClicked = new Event(this); +} + +MolarityGrid.prototype._getColumns = function() { + return [ { + text : 'Subunit', + columns : [ { + text : "Acronym", + width : 100, + hidden : false, + dataIndex : 'acronym', + sortable : true + }, { + text : "Name", + width : 125, + hidden : false, + dataIndex : 'name', + sortable : true + }, { + text : "MM Est.", + width : 100, + dataIndex : 'molecularMass', + sortable : true, + renderer : function(grid, cls, record){ + return BUI.formatValuesUnits(record.data.molecularMass , "Da", 10, 2); + + } + } ] + }, { +// text : "Number
in assymmetric units", + text : "Ratio", + width : 100, + dataIndex : 'ratio', + tooltip : 'Number of times the subunit is present in the macromolecule', + sortable : true + }, { + id : this.id + 'MOLARITY_REMOVE', + flex : 0.1, + sortable : false, + renderer : function(value, metaData, record, rowIndex, colIndex, store) { + return BUI.getRedButton('REMOVE'); + } + } ]; +}; + +MolarityGrid.prototype._openMolarityWindow = function() { + this.molarityWindow = Ext.create('Ext.window.Window', { + title : 'Molarity', + height : 220, + width : 500, + modal : true, + items : [this.molarityForm.getPanel() ] + }).show(); +}; + +MolarityGrid.prototype._getButtons = function() { + var _this = this; + return [ { + text : 'Add subunit', + icon : '../images/add.png', + handler : function() { + _this._openMolarityWindow(); + } + }]; +}; + + +MolarityGrid.prototype.updateProposal = function() { + var _this = this; + this.panel.setLoading(); + BIOSAXS.proposal.onInitialized.attach(function() { + if (BIOSAXS.proposal != null) { + var macromolecules = BIOSAXS.proposal.macromolecules; + for (var i = 0; i < macromolecules.length; i++) { + + if (macromolecules[i].macromoleculeId == _this.macromolecule.macromoleculeId) { + _this.refresh(macromolecules[i]); + _this.panel.setLoading(false); + } + } + } + }); + BIOSAXS.proposal.init(); +}; + + +MolarityGrid.prototype.getPanel = function() { + var _this = this; + + this.molarityStore = Ext.create('Ext.data.Store', { + fields : [ 'acronym', 'ratio', 'comments', 'stoichiometryId', 'name', 'molecularMass' ], + sorters : { + property : 'ratio', + direction : 'DESC' + } + }); + + this.panel = Ext.create('Ext.grid.Panel', { + store : this.molarityStore, + height : this.height, + padding : 5, + columns : this._getColumns(), + listeners : { + cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + + /** Remove entry * */ + if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'MOLARITY_REMOVE') { + var dataAdapter = new BiosaxsDataAdapter(); + dataAdapter.onSuccess.attach(function(sender) { + _this.updateProposal(); + + }); + dataAdapter.removeStoichiometry(record.data.stoichiometryId); + _this.panel.setLoading("Removing Structure"); + } + } + }, + tbar : this._getButtons() + }); + return this.panel; +}; + +MolarityGrid.prototype._prepareData = function(macromolecule) { + /** Return an array of [{ratio,acronym, stoichiometryId, name}] **/ + var data = []; + if (macromolecule.stoichiometry != null) { + for (var i = 0; i < macromolecule.stoichiometry.length; i++) { + var hostMacromolecule = BIOSAXS.proposal.getMacromoleculeById(macromolecule.stoichiometry[i].macromoleculeId); + data.push({ + ratio : macromolecule.stoichiometry[i].ratio, + acronym : hostMacromolecule.acronym, + comments : hostMacromolecule.comments, + molecularMass : hostMacromolecule.molecularMass, + stoichiometryId : macromolecule.stoichiometry[i].stoichiometryId, + name : hostMacromolecule.name + }); + } + } + return data; +}; + +MolarityGrid.prototype.refresh = function(macromolecule) { + if (macromolecule != null){ + this.molarityStore.loadData(this._prepareData(macromolecule)); + this.molarityForm.refresh(macromolecule); + this.macromolecule = macromolecule; + } +}; + +MolarityGrid.prototype.input = function() { + return { + proposal : DATADOC.getProposal_10(), + dewars : DATADOC.getDewars_10() + + }; +}; + +MolarityGrid.prototype.test = function(targetId) { + var MolarityGrid = new MolarityGrid({ + height : 150 + }); + BIOSAXS.proposal = new Proposal(MolarityGrid.input().proposal); + var panel = MolarityGrid.getPanel(MolarityGrid.input().dewars); + panel.render(targetId); + +}; + + +/** + * Given data analysis parsed with ResultsAssemblyWidget makes a selector grid with buffer/protein + **/ +function SpecimenSelectorResultGrid() { + this.id = BUI.id(); +} -TemplateTabs.prototype.draw = function(experiment) { - this.render(experiment); +SpecimenSelectorResultGrid.prototype._prepareData = function(data) { + var parsed = []; + for ( var i = 0; i < data.length; i++) { + var row = data[i]; + for ( var j = 0; j < row.conditions.length; j++) { + parsed.push({ + bufferId : row.conditions[j].bufferId, + macromoleculeId : row.macromoleculeId, + macromoleculeAcronym : BIOSAXS.proposal.getMacromoleculeById(row.macromoleculeId).acronym, + bufferAcronym : BIOSAXS.proposal.getBufferById(row.conditions[j].bufferId).acronym + }); + } + } + return parsed; }; - -TemplateTabs.prototype.getExperimentTitle = function() { - var _this = this; - var experimentHeaderForm = new ExperimentHeaderForm(); - return experimentHeaderForm.getPanel(this.experiment); +SpecimenSelectorResultGrid.prototype.refresh = function(data) { + this.store.loadData(this._prepareData(data)); }; -TemplateTabs.prototype.render = function(experiment) { +SpecimenSelectorResultGrid.prototype.getPanel = function(data) { var _this = this; - this.experiment = experiment; + this.store = Ext.create('Ext.data.Store', { + fields : [ 'macromoleculeId', 'bufferId', 'macromoleculeAcronym', 'bufferAcronym', 'concentration' ], + data : this._prepareData(data), + groupField : 'macromoleculeAcronym' + }); - /** - * - * Depending on the sample Changer configuration we want to display the plates vertically or horizontally - * Default is vertical - * - * */ - - var specimenContainer = this.specimenWidget.getPanel(); - this.specimenWidget.refresh(experiment); - - var experimentList = new ExperimentList([ _this.experiment ]); - var measurementContainer = Ext.create('Ext.container.Container', { - layout : { - type : 'vbox' - }, - defaults : { - style : { - padding : '5px 0px 0px 10px' + this.store.sort('concentration'); + this.grid = Ext.create('Ext.grid.Panel', { + id : this.id, + store : this.store, + width : this.width, + height : this.height, + maxHeight : this.maxHeight, + border : 1, + features : [ { + ftype : 'grouping', + groupHeaderTpl : '{name}', + hideGroupedHeader : true, + startCollapsed : false + } ], + selModel : Ext.create('Ext.selection.CheckboxModel', { + allowDeselect : true, + mode : 'MULTI', + listeners : { + selectionchange : function(sm, selections) { + _this.selected = []; + for ( var i = 0; i < selections.length; i++) { + _this.selected.push(selections[i].raw); + } + } } - }, - items : [ _this.measurementGrid.getPanel(experimentList.getMeasurementsNotCollected(), experimentList) ] + }), + margin : 10, + sortableColumns : true, + columns : [ { + text : 'Macromolecule', + dataIndex : 'macromoleculeAcronym', + flex : 1 + }, { + text : '', + dataIndex : 'bufferId', + width : 20, + hidden : false, + renderer : function(val, y, sample) { + return BUI.getRectangleColorDIV(BIOSAXS.proposal.bufferColors[val], 10, 10); + } + }, { + text : 'Buffer', + dataIndex : 'bufferAcronym', + flex : 1 + }, { + text : 'Concentration', + dataIndex : 'concentration', + flex : 1, + renderer : function(val, y, sample) { + return val + " mg/ml"; + } + } ] }); - this.panel = Ext - .createWidget( - 'tabpanel', - { - plain : true, - items : [ - { - tabConfig : { - title : 'Measurements' - }, - items : [ { - xtype : 'container', - layout : 'vbox', - border : 1, -// height : _this.gridHeight, - margin : "0 0 10 0", - items : [ measurementContainer ] - } - - ] - }, - { - tabConfig : { - id : 'genralTabl', - title : "Specimens" - // width : 900, - // border : 3 - - }, - items : [ specimenContainer ] - }, - { - tabConfig : { - title : "Requirements" + /** Adding the tbar **/ + if (this.tbar) { + this.grid.addDocked({ + xtype : 'toolbar', + items : this.getTbar() + }); + } + return this.grid; +}; - }, - items : [ - { - html : BUI.getTipHTML("Estimated volume is the maximum volume required. Depending on the order of your measurements you may use less. Click on create stock solutions if you plan to ship these stock solutions"), - margin : "10 10 10 10", - border : 0 - }, this.volumePlanificator.getPanel(experiment) ] - } ] - }); - // ); - return this.getPanel(this.panel); +SpecimenSelectorResultGrid.prototype.input = function() { + return { + data : new ResultsAssemblyGrid()._prepareData(new ResultsAssemblyGrid().input().data), + proposal : new ResultsAssemblyGrid().input().proposal + }; }; -TemplateTabs.prototype.isTemplate = function() { - if (this.experiment.json.type == "TEMPLATE") { - return true; - } - return false; +SpecimenSelectorResultGrid.prototype.test = function(targetId) { + var specimenSelectorResultGrid = new SpecimenSelectorResultGrid(); + BIOSAXS.proposal = new Proposal(specimenSelectorResultGrid.input().proposal); + var panel = specimenSelectorResultGrid.getPanel(specimenSelectorResultGrid.input().data); + panel.render(targetId); }; +/** + * Show all buffer conditions for each macromolecules pointing out the measurements quality + * + * @height + * @maxHeight + * @width + * @searchBar + * @tbar + * @btnResultVisible + * + * #onClick + */ +function ResultsAssemblyGrid(args) { + this.height = 500; + this.id = BUI.id(); + this.maxHeight = this.height; -TemplateTabs.prototype.getPanel = function(panel) { - var _this = this; - if (this.experimentPanel == null) { - this.experimentPanel = Ext.create('Ext.container.Container', { - bodyPadding : 2, - width : 1000,//Ext.getBody().getViewSize().width * 0.9, - renderTo : this.targetId, - height : 500, -// style : { -// padding : 2 -// }, - items : [ - this.getExperimentTitle(), - this.panel - ], - listeners : { - afterrender : function(thisCmp) { - $("#SchemeReport" + _this.experiment.experimentId).click(function() { - $(this).target = "_blank"; - window.open('viewProjectList.do?reqCode=display&menu=platescheme&experimentId=' + _this.experiment.experimentId); - return false; - }); + this.width = 900; + this.searchBar = false; + this.tbar = false; + this.btnResultVisible = false; - } - } - }); - } - return this.experimentPanel; -}; + /** For processing **/ + this.processed = {}; + this.renderedPlotIndex = 0; -TemplateTabs.prototype.input = function(targetId) { - return new ExperimentTabs().input(); -}; + this.plotWidth = 210; + this.plotHeight = 80; -TemplateTabs.prototype.test = function(targetId) { - var experimentTabs = new TemplateTabs(targetId); - BIOSAXS.proposal = new Proposal(experimentTabs.input().proposal); - experimentTabs.draw(new Experiment(experimentTabs.input().experiment)); -}; - -var BUI = { - //interval : 60000, - interval : 40000, - rainbow : function(numOfSteps, step) { - // This function generates vibrant, "evenly spaced" colours (i.e. no clustering). This is ideal for creating easily distinguishable vibrant markers in Google Maps and other apps. - // Adam Cole, 2011-Sept-14 - // HSV to RBG adapted from: http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript - var r, g, b; - var h = step / numOfSteps; - var i = ~~(h * 6); - var f = h * 6 - i; - var q = 1 - f; - switch (i % 6) { - case 0: - r = 1, g = f, b = 0; - break; - case 1: - r = q, g = 1, b = 0; - break; - case 2: - r = 0, g = 1, b = f; - break; - case 3: - r = 0, g = q, b = 1; - break; - case 4: - r = f, g = 0, b = 1; - break; - case 5: - r = 1, g = 0, b = q; - break; + /** Colors **/ + this.validColor = BUI.getValidColor(); + this.warningColor = BUI.getWarningColor(); + this.notValidColor = BUI.getErrorColor(); + + /** Show warning if guinier quality less than **/ + this.guinierQuality = BUI.getQualityThreshold(); + this.framePercentageThreshold = BUI.getRadiationDamageThreshold(); + + if (args != null) { + if (args.height != null) { + this.height = args.height; + this.maxHeight = this.height; } - var c = "#" + ("00" + (~~(r * 255)).toString(16)).slice(-2) + ("00" + (~~(g * 255)).toString(16)).slice(-2) - + ("00" + (~~(b * 255)).toString(16)).slice(-2); - return (c); - }, - getFileNameByPath : function(filePath) { - if (filePath != null){ - var split = filePath.split("/"); - if (split.length > 0){ - return split[split.length - 1]; - } + if (args.maxHeight != null) { + this.maxHeight = args.maxHeight; + } + if (args.width != null) { + this.width = args.width; + } + if (args.searchBar != null) { + this.searchBar = args.searchBar; } - return "Not file"; - }, - getUpdateInterval : function() { - this.interval = this.interval + 2000; - return this.interval; - }, - getRadiationDamageThreshold : function() { - return 0.7; - }, - getQualityThreshold : function() { - return 0.7; - }, - getCreateShipmentURL : function() { - return '/ispyb/user/viewProjectList.do?reqCode=display&menu=create_shipment'; - }, - getCreateShipmentList : function() { - return '/ispyb/user/viewProjectList.do?reqCode=display&menu=list_shipment'; - }, - getShippingURL : function(shippingId) { - return '/ispyb/user/viewProjectList.do?reqCode=display&menu=shipment&shippingId=' + shippingId; - }, - getMacromoleculeResultsURLByMultipleSearch : function(array) { - return '/ispyb/user/viewProjectList.do?reqCode=display&menu=macromolecule&search=' - + JSON.stringify(array).replace(new RegExp("\"", 'g'), "'"); - }, - getMacromoleculeResultsURL : function(macromoleculeId) { - return '/ispyb/user/viewProjectList.do?reqCode=display&menu=macromolecule¯omoleculeId=' + macromoleculeId; - }, - getMacromoleculeBufferResultsURL : function(macromoleculeId, bufferId) { - return '/ispyb/user/viewProjectList.do?reqCode=display&menu=macromolecule&bufferId=' + bufferId + '¯omoleculeId=' + macromoleculeId; - }, + if (args.tbar != null) { + this.tbar = args.tbar; + } + if (args.btnResultVisible != null) { + this.btnResultVisible = args.btnResultVisible; + } - getMacromoleculeHeader : function(macromoleculeId) { + } - function getHTMLSource(macromoleculeId) { - if (macromoleculeId != null) { - var html = BUI.createFormLabel("Name :", BIOSAXS.proposal.getMacromoleculeById(macromoleculeId).name, 75, 400); - html = html + BUI.createFormLabel("Acronym :", BIOSAXS.proposal.getMacromoleculeById(macromoleculeId).acronym, 75, 400); - if (BIOSAXS.proposal.getMacromoleculeById(macromoleculeId).comments != null) { - html = html + BUI.createFormLabel("Comments :", BIOSAXS.proposal.getMacromoleculeById(macromoleculeId).comments, 75, 400); - } - return html; - } - } + this.onClick = new Event(); +} - return Ext.create('Ext.container.Container', { - frame : false, - layout : 'hbox', - title : 'Macromolecule', - bodyPadding : '5', - width : 890, - margin : '0 0 10 0', - height : 100, - style : { - borderColor : '#BDBDBD', - borderStyle : 'solid', - borderWidth : '1px' - }, - fieldDefaults : { - msgTarget : 'side', - labelWidth : 100 - }, - items : [ { - margin : "10 0 0 10", - width : 475, - border : 0, - html : getHTMLSource(macromoleculeId) - }, { - margin : "10 0 0 10", - width : 475, - border : 0, - html : BUI.getZipHTMLByMacromoleculeId(macromoleculeId) - } - ] - }); - }, +ResultsAssemblyGrid.prototype.edit = function(macromoleculeId) { + var _this = this; + var window = new MacromoleculeWindow(); + window.onSuccess.attach(function(sender, proposal) { + _this.store.loadData(_this._prepareData(BIOSAXS.proposal.getMacromolecules())); + }); + window.draw(BIOSAXS.proposal.getMacromoleculeById(macromoleculeId)); +}; - getZipHTMLByMacromoleculeId : function(macromoleculeId) { - if (macromoleculeId != null) { - var fileName = BIOSAXS.proposal.getMacromoleculeById(macromoleculeId).acronym; - return ""; - } - }, - getZipHTMLByExperimentId : function(experimentId, filename) { - if (filename == null){ - filename = "experiment"; - } - return ""; - }, - - getZipURLByAverageId : function(averageId, filename) { - if (filename == null){ - filename = "experiment"; - } - return "/ispyb/user/dataadapter.do?reqCode=getZipFileByAverageListId&f&mergeIdsList=" + averageId + "&fileName=" + filename; - }, - getZipURLBySubtractionId : function(subtractionId, filename) { - if (filename == null){ - filename = "experiment"; +ResultsAssemblyGrid.prototype.getTbar = function() { + var _this = this; + var actions = []; + + actions.push(Ext.create('Ext.Action', { + icon : '../images/add.png', + text : 'Add Macromolecule', + disabled : false, + handler : function(widget, event) { + var window = new MacromoleculeWindow(); + window.onSuccess.attach(function(sender) { + _this.refresh(); + }); + window.draw({}); } - return "/ispyb/user/dataadapter.do?reqCode=getZipFileByAverageListId&f&subtractionIdList=" + subtractionId + "&fileName=" + filename; - }, - - - getZipHTMLByFrameRangeId : function(experimentId, start, end) { - var fileName = "experiment"; - return ""; - }, - getZipFrameHPLCUrl : function(experimentId, start, end) { - return "/ispyb/user/dataadapter.do?reqCode=getZipFileH5ByFramesRange&f&experimentId=" + experimentId + "&start=" + Number(start) +"&end="+ Number(end); - }, - - getQueueUrl : function() { - return "/ispyb/user/dataadapter.do?reqCode=getPagingCompactAnalysisByProposalId"; - }, - - getQueueUrlByExperiment: function(experimentId) { - return "/ispyb/user/dataadapter.do?reqCode=getPagingCompactAnalysisByExperimentId&f&experimentId=" + experimentId; - }, - getStandardDeviation : function(values) { - var sum = 0; - var count = 0; - var avg = null; + })); + + actions.push(Ext.create('Ext.Action', { + icon : '../images/add.png', + text : 'Define an Assembly', + disabled : false, + handler : function(widget, event) { + var createAssemblywindow = new CreateAssemblyWindow(); + createAssemblywindow.onSaved.attach(function(evt, args) { + if (args.macromoleculeIds.length > 0) { + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, proposal) { + _this.refresh(); + }); + adapter.saveAssembly(args.assemblyId, args.macromoleculeIds); - var curatedValues = new Array(); - for ( var i = 0; i < values.length; i++) { - var value = values[i]; - if (value != null) { - if (!isNaN(value)) { - count = count + 1; - sum = sum + Number(value); - curatedValues.push(Number(value)); } - } - } - if (count > 0) { - avg = sum / count; - } else { - avg = sum; - } - var aux = 0; - for ( var i = 0; i < curatedValues.length; i++) { - var value = curatedValues[i]; - aux = aux + Math.pow(value - avg, 2); + }); + createAssemblywindow.draw(_this.experiment); } - /** std **/ - var std = Math.sqrt(aux / count); + })); + + return actions; +}; + +ResultsAssemblyGrid.prototype.refresh = function() { + this.store.loadData(this._prepareData()); +}; + +ResultsAssemblyGrid.prototype.addCondition = function(record, data_parsed, i) { + function getCondition(record) { return { - std : (std), - sum : (sum), - avg : (avg), - validNumber : count, - totalNumber : values.length, - values : values + concentration : record.conc, + quality : record.quality, + bufferBeforeFramesMerged : record.bufferBeforeFramesMerged, + bufferAfterFramesMerged : record.bufferAfterFramesMerged, + framesCount : record.framesCount, + framesMerge : record.framesMerge }; - }, - - getHTMLTableForFrameAveraged : function(bufferAcronym, macromoleculeAcronym, bbmerges, molmerges, bamerges, totalframes, bufferId,macromoleculeId, macromoleculeColor) { - - function getFrameSpan(framesMerged, total) { - return "(" + framesMerged + " of " + total + ")"; - } + } - function getColorFrame(framesMerged, total) { - if (framesMerged / total < 0.5) { - return "#FA5858"; - } - if ((framesMerged / total >= 0.5) && (framesMerged / total < 0.8)) { - return "#FF9900"; + if (data_parsed[i].conditions != null) { + var bufferFound = false; + for ( var index in data_parsed[i].conditions) { + condition = data_parsed[i].conditions[index]; + + if ((condition.macromoleculeId == record.macromoleculeId) && (condition.bufferId == record.bufferId)) { + data_parsed[i].conditions[index].concentrations.push(getCondition(record)); + bufferFound = true; } - return "white"; } - - function getRow(color, acroynm, framesMerged, totalframes) { - return " " - + BUI.getRectangleColorDIV(color, 10, 10) - + " " + acroynm + "" - + getFrameSpan(framesMerged, totalframes) + ""; + if (!bufferFound) { + data_parsed[i].conditions.push({ + macromoleculeId : record.macromoleculeId, + bufferId : record.bufferId, + concentrations : [ getCondition(record) ] + }); } + } +}; - var html = ""; +ResultsAssemblyGrid.prototype.process = function(record, data_parsed) { + if (this.processed[record.macromoleculeId] == null) { + this.processed[record.macromoleculeId] = true; + record.measurementCount = 1; + record.subtractionCount = 1; + record.averageCount = 1; + record.conditions = []; + data_parsed.push(record); + } - /** Buffer Before **/ - if (bufferAcronym != null) { - if (bbmerges != null) { - color = BIOSAXS.proposal.bufferColors[bufferId]; - html = html + getRow(color, bufferAcronym, bbmerges, totalframes); + for ( var i = 0; i < data_parsed.length; i++) { + if (data_parsed[i].macromoleculeId == record.macromoleculeId) { + data_parsed[i].measurementCount = data_parsed[i].measurementCount + 1; + + if (record.subtractionId != null) { + data_parsed[i].subtractionCount = data_parsed[i].subtractionCount + 1; + this.addCondition(record, data_parsed, i); } - } - /** Molecule **/ - if (macromoleculeAcronym != null) { - if (molmerges != null) { - if (macromoleculeColor == null){ - color = BIOSAXS.proposal.macromoleculeColors[macromoleculeId]; - } - else{ - color = macromoleculeColor; //BIOSAXS.proposal.macromoleculeColors[macromoleculeId]; + if (record.framesMerge != null) { + data_parsed[i].averageCount = data_parsed[i].averageCount + 1; + } + + if (record.timeStart != null) { + if (data_parsed[i].timeStart != null) { + if (moment(data_parsed[i].timeStart).format("X") > moment(record.timeStart).format("X")) { + data_parsed[i].timeStart = record.timeStart; + } } - html = html + getRow(color, macromoleculeAcronym, molmerges, totalframes); } } + } - /** Buffer After **/ - if (bufferAcronym != null) { - if (bamerges != null) { - color = BIOSAXS.proposal.bufferColors[bufferId]; - html = html + getRow(color, bufferAcronym, bamerges, totalframes); + return data_parsed; + +}; + +ResultsAssemblyGrid.prototype._prepareData = function(data) { + var data_parsed = []; + for ( var i = 0; i < data.length; i++) { + data_parsed = this.process(data[i], data_parsed); + } + this.data = data_parsed; + return data_parsed; +}; + +/** Given an array of conditions it returns distinct(concentrations) order by concentration and hash map with number of ocurrences**/ +ResultsAssemblyGrid.prototype.parseConcentrations = function(val, conditions, differentConcentration, quality) { + var conditions = []; + var differentConcentration = {}; + var quality = []; + for ( var i = 0; i < val.length; i++) { + var conc = Number(val[i].concentration).toFixed(1); + var quality = Number(val[i].quality).toFixed(2); + if (differentConcentration[conc] == null) { + differentConcentration[conc] = 0; + conditions.push({ + concentration : conc, + quality : [ quality ], + frames : [ { + bufferAfterFramesMerged : val[i].bufferAfterFramesMerged, + bufferBeforeFramesMerged : val[i].bufferBeforeFramesMerged, + framesMerge : val[i].framesMerge, + framesCount : val[i].framesCount + } ] + }); + } else { + /** Add quality **/ + for ( var j = 0; j < conditions.length; j++) { + if (conditions[j].concentration == conc) { + conditions[j].quality.push(quality); + conditions[j].frames.push({ + bufferAfterFramesMerged : val[i].bufferAfterFramesMerged, + bufferBeforeFramesMerged : val[i].bufferBeforeFramesMerged, + framesMerge : val[i].framesMerge, + framesCount : val[i].framesCount + }); + } } } - return html + "
"; - }, - isWebGLEnabled : function(return_context) { - if (!!window.WebGLRenderingContext) { - var canvas = document.createElement("canvas"); - names = [ "webgl", "experimental-webgl", "moz-webgl", "webkit-3d" ]; - context = false; - for ( var i = 0; i < 4; i++) { - try { - context = canvas.getContext(names[i]); - if (context && typeof context.getParameter == "function") { - // WebGL is enabled - if (return_context) { - // return WebGL object if the function's argument is present - return { - name : names[i], - gl : context - }; - } - // else, return just true - return true; - } - } catch (e) { - } + + differentConcentration[conc] = differentConcentration[conc] + 1; + } + /** sorting concentrations **/ + conditions.sort(function(a, b) { + return a.concentration - b.concentration; + }); + return { + concentrations : conditions, + differentConcentration : differentConcentration + }; +}; + +ResultsAssemblyGrid.prototype.getConditionWarnings = function(condition) { + + var withWarnings = 0; + + for ( var i = 0; i < condition.frames.length; i++) { + if (condition.quality[i] == null) { + withWarnings = withWarnings + 1; + continue; + } else { + if (Number(condition.quality[i]) < this.guinierQuality) { + withWarnings = withWarnings + 1; + continue; } - // WebGL is supported, but disabled - return false; } - // WebGL not supported28. - return false; - }, - getHTMLTableForPrefixes : function(bufferBeforeaverageFilePath, averageFilePath, bufferAfterAverageFilePath) { - function getRow(bufferBeforeaverageFilePath) { - file = bufferBeforeaverageFilePath; - try { - file = bufferBeforeaverageFilePath.split("/")[bufferBeforeaverageFilePath.split("/").length - 1]; - } catch (e) { - file = "NA"; + if (condition.frames[i].bufferBeforeFramesMerged / condition.frames[i].framesCount < this.framePercentageThreshold|| condition.frames[i].framesMerged / condition.frames[i].framesCount < this.framePercentageThreshold|| condition.frames[i].bufferAfterFramesMerged / condition.frames[i].framesCount < this.framePercentageThreshold) { + withWarnings = withWarnings + 1; + continue; + } + } + + return { + withWarnings : withWarnings, + withoutWarnings : condition.frames.length - withWarnings + }; +}; + +ResultsAssemblyGrid.prototype.getFrameHTMLTable = function(warnings) { + var html = ""; + if (warnings.withWarnings > 0) { + html = html + "
" + warnings.withWarnings + + "x
"; + } + + if (warnings.withoutWarnings > 0) { + html = html + "
" + warnings.withoutWarnings + + "x
"; + } + return html; +}; + +ResultsAssemblyGrid.prototype.createConcentrationRow = function(numberOcu, condition, warnings){ + var html = ""; + if (numberOcu > 1){ + html = html + "" +numberOcu + "x " + BUI.formatConcentration(condition.concentration); + } + else{ + html = html + BUI.formatConcentration(condition.concentration); + } + html = html + ""; + html = html + this.getFrameHTMLTable(warnings); + ""; + html = html + ""; + return html; +}; + + +ResultsAssemblyGrid.prototype.getConditionHTMLTable = function(val, style, record) { + var maxNumberColumns = 2; + var html = "
"; + var nColumns = 0; + for ( var r = 0; r < val.length; r++) { + if (nColumns == maxNumberColumns) { + nColumns = 0; + html = html + ""; + } + html = html + ""; } - var html = "
"; + var value = val[r]; + + var bufferAcronym = (BIOSAXS.proposal.getBufferById(value.bufferId).acronym); + + var parsed = (this.parseConcentrations(value.concentrations)); + var conditions = parsed.concentrations; + var differentConcentration = parsed.differentConcentration; + + /** Checking warnings **/ + var warnings = []; + var concentrationValidPerconcentration = 0; + var measurements = 0; + for ( var i = 0; i < conditions.length; i++) { + measurements = measurements + conditions[i].frames.length; + var warning = this.getConditionWarnings(conditions[i]); + warnings.push(warning); + if (warning.withoutWarnings > 0) { + concentrationValidPerconcentration = concentrationValidPerconcentration + 1; } - return "
" + file + "
"; - /** Buffer Before **/ - if (bufferBeforeaverageFilePath != null) { - html = html + getRow(bufferBeforeaverageFilePath); - } + this.validColor = '#E0F8E0'; + this.warningColor = '#F5DA81'; + this.notValidColor = '#F6CED8'; - /** Molecule **/ - if (averageFilePath != null) { - html = html + getRow(averageFilePath); + var color = this.warningColor; + if (concentrationValidPerconcentration > 2) { + color = this.validColor; + } + /** More measurement need to be done **/ + if (measurements < 3) { + color = this.notValidColor; } - /** Buffer After **/ - if (bufferAfterAverageFilePath != null) { - html = html + getRow(bufferAfterAverageFilePath); + html = html + "
"; + html = html + ""; + for ( var i = 0; i < conditions.length; i++) { + html = html + this.createConcentrationRow(differentConcentration[conditions[i].concentration], conditions[i], warnings[i]); } - return html + "
" + bufferAcronym.toUpperCase() + + "
"; - }, - getBaseURL : function() { - return '/ispyb/user/dataadapter.do'; - }, + html = html + ""; + html = html + ""; - getPrintcomponentURL : function(dewarId) { - return '/ispyb/user/viewDewarAction.do?reqCode=generateLabels&dewarId=' + dewarId; + html = html + ""; + nColumns = nColumns + 1; + } + html = html + "
"; + return html; +}; - }, - getPDBVisualizerURL : function(modelId, subtractionId, experimentId) { - return '/ispyb/user/viewProjectList.do?reqCode=display&menu=PDBVisualizer&modelId=' + modelId + '&experimentId=' + experimentId - + '&subtractionId=' + subtractionId; - }, +ResultsAssemblyGrid.prototype._getTbar = function() { + function goTo(url) { + window.location = url; + } - getURL : function() { - return this.getBaseURL() + '?reqCode=getImage'; + var _this = this; + return [ { + icon : '../images/application_view_list.png', + text : 'Multiple Select', + handler : function() { + var specimenSelectorResultGrid = new SpecimenSelectorResultGrid(); + var window = Ext.create('Ext.window.Window', { + title : 'Multiple select', + height : 600, + width : 600, + layout : 'fit', + items : [ specimenSelectorResultGrid.getPanel(_this.data) ], + buttons : [ { + text : 'Go', + handler : function() { + var array = []; + for ( var i = 0; i < specimenSelectorResultGrid.selected.length; i++) { + var row = specimenSelectorResultGrid.selected[i]; + array.push({ + macromoleculeId : row.macromoleculeId, + bufferId : row.bufferId + }); + } + goTo(BUI.getMacromoleculeResultsURLByMultipleSearch(array)); + + } + }, { + text : 'Cancel', + handler : function() { + window.close(); + } + } ] + + }).show(); - }, - getAbinitioImageURL : function() { - return this.getBaseURL() + '?reqCode=getAbinitioImage'; - }, - getNSDImageURL : function(modelListId) { - return BUI.getAbinitioImageURL() + '&type=NSD&modelListId=' + modelListId; - }, - getCHI2ImageURL : function(modelListId) { - return BUI.getAbinitioImageURL() + '&type=CHI2&modelListId=' + modelListId; - }, - getModelFile : function(type, modelId, format) { - return this.getBaseURL() + '?reqCode=getModelFile' + "&type=" + type + "&modelId=" + modelId + "&format=" + format; - }, - getPdbURL : function() { - return this.getBaseURL() + '?reqCode=getPdbFiles'; - }, - getStvArray : function(value, error) { - value = Number(value); - error = Number(error); - return [ value - error, value, value + error ]; - }, - getPointArrayForDygraph : function(x, y, error) { - return [ x, BUI.getStvArray(y, error) ]; - }, - createDIV : function(text, width, className, backgroundColor) { - var nameContainer = document.createElement("div"); - var nameSpan = document.createElement("span"); - if (className != null) { - nameSpan.setAttribute("class", className); - } - if (backgroundColor != null) { - nameSpan.setAttribute("style", "float:left;width:" + width + "px;height:18px;background-color:" + backgroundColor); - } else { - nameSpan.setAttribute("style", "float:left;width:" + width + "px;height:18px;"); } - nameSpan.appendChild(document.createTextNode(text)); - nameContainer.appendChild(nameSpan); - return nameContainer; - }, + } ]; +}; - createFormLabel : function(labelText, text, labelWidth, textWidth, backgroundColor) { - var div = document.createElement("div"); +ResultsAssemblyGrid.prototype.getLegendPanel = function() { + return { + html : '
' + BUI.getRectangleColorDIV(this.validColor, 10, 10) + + 'Good quality measurements' + + BUI.getRectangleColorDIV(this.warningColor, 10, 10) + + 'Probably valid with manual processing' + + BUI.getRectangleColorDIV(this.notValidColor, 10, 10) + + 'More measurements need to be done
' + }; +}; +/** Returns the grid **/ +ResultsAssemblyGrid.prototype.getPanel = function(macromolecules) { + var _this = this; - div.appendChild(BUI.createDIV(labelText, labelWidth, "bLabel", backgroundColor)); - div.appendChild(BUI.createDIV(text, textWidth, "btext", backgroundColor)); - return div.innerHTML; - }, + this.store = Ext.create('Ext.data.Store', { + fields : [ + 'macromoleculeId', 'macromoleculeAcronym', 'measurementCount', 'subtractionCount', 'averageCount', 'timeStart', 'concentrationArray', + 'bufferConditions', 'conditions' ], + data : this._prepareData(macromolecules) + }); - createTextArea : function(labelText, text, labelWidth, rows, cols) { - var div = document.createElement("div"); - div.appendChild(BUI.createDIV(labelText, labelWidth, "bLabel")); - var textArea = document.createElement("textarea"); - textArea.setAttribute("rows", rows); - textArea.setAttribute("cols", cols); - textArea.appendChild(document.createTextNode(text)); - div.appendChild(textArea); - return div.innerHTML; - }, + this.store.sort('macromoleculeAcronym'); - showBetaWarning : function() { - alert("ISPyB for Biosaxs version Beta has not this functionality enabled"); - }, + this.grid = Ext.create('Ext.grid.Panel', { + id : this.id, + title : 'Macromolecules', + store : this.store, + tbar : this._getTbar(), + bbar : [ this.getLegendPanel() ], + width : this.width, + height : this.height, + maxHeight : this.maxHeight, + sortableColumns : true, + columns : [ + { + text : '', + dataIndex : 'macromoleculeId', + width : 20, + hidden : false, + renderer : function(val, y, sample) { + return BUI.getRectangleColorDIV(BIOSAXS.proposal.macromoleculeColors[val], 10, 10); + } + }, + { + text : 'Macromolecule', + dataIndex : 'macromoleculeAcronym', + width : 200 + }, + { + text : 'Buffer Conditions', + dataIndex : 'conditions', + flex : 1, + renderer : function(val, style, record) { + return _this.getConditionHTMLTable(val, style, record); + } + }, + { + header : 'Average', + dataIndex : 'percentageCollected', + name : 'percentageCollected', + type : 'string', + hidden : true, + renderer : function(val, y, sample) { + return "
" + + BUI.getProgessBar((sample.raw.averageCount / sample.raw.measurementCount) * 100, sample.raw.averageCount + "/" + + sample.raw.measurementCount) + "
"; + }, + width : 100, + sorter : false + }, + { + header : 'Subtractions', + dataIndex : 'percentageCollected', + name : 'percentageCollected', + type : 'string', + hidden : true, + renderer : function(val, y, sample) { + return "
"+ BUI.getProgessBar((sample.raw.subtractionCount / sample.raw.measurementCount) * 100, sample.raw.subtractionCount + "/" + + sample.raw.measurementCount) + "
"; + }, + width : 100 + }, { + header : 'Date', + dataIndex : 'timeStart', + name : 'timeStart', + type : 'string', + renderer : function(value, metaData, record, rowIndex, colIndex, store) { + if (record.raw.timeStart != null) { + return moment(record.raw.timeStart).format("MMM Do YY"); + } + } + }, { + header : 'Download', + dataIndex : 'percentageCollected', + name : 'percentageCollected', + type : 'string', + renderer : function(val, y, sample) { + return BUI.getZipHTMLByMacromoleculeId(sample.raw.macromoleculeId); + }, + width : 100 + }, - formatValuesErrorUnitsScientificFormat : function(val, error, unit, args) { - var fontSize = 14; - var decimals = 2; - var errorFontSize = 10; - /** line break **/ - var lineBreak = true; - if (args != null) { - if (args.fontSize != null) { - fontSize = args.fontSize; - } - if (args.decimals != null) { - decimals = args.decimals; - } - if (args.errorFontSize != null) { - errorFontSize = args.errorFontSize; - } - if (args.lineBreak != null) { - lineBreak = args.lineBreak; + { + id : 'btnResultVisible', + width : 85, + sortable : false, + renderer : function(value, metaData, record, rowIndex, colIndex, store) { + return BUI.getGreenButton('GO'); + } + } ], + viewConfig : { + stripeRows : true, + listeners : { + afterrender : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + }, + celldblclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + _this.edit(record.data.macromoleculeId); + }, + cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + if (grid.getGridColumns()[cellIndex].getId() == 'buttonEditMacromolecule') { + _this.edit(record.data.macromoleculeId); + } + if (grid.getGridColumns()[cellIndex].getId() == 'buttonRemoveMacromolecule') { + BUI.showBetaWarning(); + } + if (grid.getGridColumns()[cellIndex].getId() == 'btnResultVisible') { + window.location = BUI.getMacromoleculeResultsURL(record.data.macromoleculeId); + } + } } - } + }); - if (val == "") { - return ""; - } - if (error == null) { - return "" + Number(val).toFixed(decimals) + ""; - } - var html = "" + Number(val).toFixed(decimals) + " " + unit + ""; - if (lineBreak) { - html = html + "
"; - } - return html + " ± " + Number(Number(error).toFixed(3)).toExponential() - + ""; - }, + /** Adding the tbar **/ + if (this.tbar) { + this.grid.addDocked({ + xtype : 'toolbar', + items : this.getTbar() + }); + } + return this.grid; +}; - formatValuesErrorUnits : function(val, error, unit, args) { - var fontSize = 16; - var decimals = 2; - var errorFontSize = 10; - /** line break **/ - var lineBreak = true; - if (args != null) { - if (args.fontSize != null) { - fontSize = args.fontSize; - } - if (args.decimals != null) { - decimals = args.decimals; - } - if (args.errorFontSize != null) { - errorFontSize = args.errorFontSize; - } - if (args.lineBreak != null) { - lineBreak = args.lineBreak; - } +ResultsAssemblyGrid.prototype.input = function(targetId) { + return { + data : DATADOC.getData_3367(), + proposal : DATADOC.getProposal_3367() + }; +}; - } +ResultsAssemblyGrid.prototype.test = function(targetId) { + var grid = new ResultsAssemblyGrid({ + height : 600, + searchBar : false, + tbar : false, + btnResultVisible : true, + width : 1000 + }); + BIOSAXS.proposal = new Proposal(grid.input().proposal); + var panel = grid.getPanel(grid.input().data); + panel.render(targetId); - if (val == "") { - return ""; +}; + + +function RigidModelGrid(args) { + this.height = null; + this.width = null; + this.id = BUI.id(); + + if (args != null) { + if (args.height != null) { + this.height = args.height; + + if (args.width != null) { + this.width = args.width; + } + } + } + + this.onSelected = new Event(this); +} + + +RigidModelGrid.prototype.refresh = function(subtractions) { + var data = []; + if (subtractions != null){ + if (subtractions.length > 0){ + for (j in subtractions){ + var subtraction = subtractions[j]; + if (subtraction.rigidBodyModeling3VOs != null){ + for (i in subtraction.rigidBodyModeling3VOs){ + data.push(subtraction.rigidBodyModeling3VOs[i]); + } + } + } + } + } + this.store.loadData(data); +}; + +RigidModelGrid.prototype.getPanel = function() { + var _this = this; + this.store = Ext.create('Ext.data.Store', { + fields : [ 'symmetry', 'subtractionId', 'subUnitConfigFilePath', 'rigidBodyModelingId', 'rigidBodyModelFilePath', 'logFilePath', 'fitFilePath', 'curveConfigFilePath', + 'crossCorrConfigFilePath', 'contactDescriptionFilePath' ], + data : [] + }); + + this.store.sort([ { + property : 'name', + direction : 'ASC' + } ]); + + this.panel = Ext.create('Ext.grid.Panel', { + store : this.store, + width : this.width, + height : this.height, + margin : 10, + deferredRender : false, + columns : [ { + text : 'RBM', + dataIndex : 'rigidBodyModelFilePath', + hidden : false, + flex : 1, + renderer : function(val){ + return BUI.getFileName(val); + } + }, { + text : 'Sub Unit Conf.', + dataIndex : 'subUnitConfigFilePath', + hidden : true, + flex : 1, + renderer : function(val){ + return BUI.getFileName(val); + } + }, { + text : 'Log', + dataIndex : 'logFilePath', + hidden : true, + flex : 1, + renderer : function(val){ + return BUI.getFileName(val); + } + }, { + text : 'Fit', + dataIndex : 'fitFilePath', + hidden : false, + flex : 1, + renderer : function(val){ + return BUI.getFileName(val); + } + }, { + text : 'Curve Conf.', + dataIndex : 'curveConfigFilePath', + hidden : false, + flex : 1, + renderer : function(val){ + return BUI.getFileName(val); + } + }, { + text : 'Cross Corr.', + dataIndex : 'crossCorrConfigFilePath', + hidden : false, + flex : 1, + renderer : function(val){ + return BUI.getFileName(val); + } + }, { + text : 'Contact Desc.', + dataIndex : 'contactDescriptionFilePath', + hidden : true, + flex : 1, + renderer : function(val){ + return BUI.getFileName(val); + } + } ], + + viewConfig : { + enableTextSelection : true, + preserveScrollOnRefresh : true + }, + listeners : { + cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + + }, + afterrender : function() { + } + } + }); + return this.panel; +}; + + +/** + * shows shipments + * + * @height + * @width + * @minHeight + * @btnEditVisible + */ +function ShipmentGrid(args) { + this.id = BUI.id(); + this.height = 100; + this.width = null; + this.minHeight = null; + this.btnEditVisible = true; + + if (args != null) { + if (args.height != null) { + this.height = args.height; } - if (error == null) { - return "" + Number(val).toFixed(decimals) + ""; + if (args.width != null) { + this.width = args.width; } - var html = "" + Number(val).toFixed(decimals) + " " + unit + ""; - if (lineBreak) { - html = html + "
"; + if (args.minHeight != null) { + this.minHeight = args.minHeight; } - return html + " ± " + Number(Number(error).toFixed(8)).toExponential() - + ""; - }, - - formatValuesUnits : function(val, unit, args) { - var fontSize = 12; - var decimals = 2; - var unitsFontSize = 10; - if (args != null) { - if (args.fontSize != null) { - fontSize = args.fontSize; - } - if (args.decimals != null) { - decimals = args.decimals; - } - if (args.unitsFontSize != null) { - unitsFontSize = args.unitsFontSize; - } - + if (args.btnEditVisible != null) { + this.btnEditVisible = args.btnEditVisible; } + } +} - if (val === "") { - return ""; - } - if (unit == null) { - return "" + Number(val).toFixed(decimals) + ""; - } - return "" + Number(val).toFixed(decimals) + " " + unit + ""; - }, +ShipmentGrid.prototype._getColumns = function() { + var _this = this; + var columns = [ + { + text : 'Name', + dataIndex : 'shippingName', + flex : 1 + }, + { + header : 'Type', + dataIndex : 'shippingType', + flex : 1, + hidden : true, + renderer : function(val, comp, record) { + if (val != null) { + return val.toUpperCase(); + } - getGreenButton : function(text, args) { - var width = 70; - var height = 20; - if (args != null) { - if (args.width != null) { - width = args.width; } - if (args.height != null) { - height = args.height; + }, + { + header : 'Status', + type : 'string', + flex : 1, + hidden : false, + renderer : function(comp, val, record) { + if (record.raw.shippingStatus != null) { + if (new String(record.raw.shippingStatus).toUpperCase() == 'OPENED') { + return "" + new String(record.raw.shippingStatus).toUpperCase() + ""; + } + return "" + new String(record.raw.shippingStatus).toUpperCase() + ""; + } + } + }, + { + text : 'Cases', + flex : 1, + hidden : false, + renderer : function(comp, val, record) { + var shipment = BIOSAXS.proposal.getShipmentById(record.data.shippingId); + var container = ""; + if (shipment.dewarVOs.length > 0) { + container = container + ""; + } else { + return "Empty"; + } + return container + "
" + shipment.dewarVOs.length + "x
"; } - } - - return ''; - }, -// getBlueButton : function(text, args) { -// var width = 70; -// var height = 20; -// if (args != null) { -// if (args.width != null) { -// width = args.width; -// } -// if (args.height != null) { -// height = args.height; -// } -// } -// -// return ''; -// }, + }, { + header : 'Comments', + dataIndex : 'comments', + flex : 1, + hidden : false + }, { + header : 'Creation Date', + dataIndex : 'creationDate', + hidden : true + } ]; - getBlueButton : function(text, args) { - var width = 70; - var height = 20; - var href = null; - if (args != null) { - if (args.width != null) { - width = args.width; - } - if (args.height != null) { - height = args.height; + if (this.btnEditVisible) { + columns.push({ + id : _this.id + 'buttonEdit', + text : '', + hidden : !_this.btnEditVisible, + width : 100, + sortable : false, + renderer : function(value, metaData, record, rowIndex, colIndex, store) { + return BUI.getGreenButton('EDIT'); } - if (args.href != null) { - href = args.href; + }); + } + + columns.push({ + id : _this.id + 'buttonRemove', + text : '', + width : 100, + sortable : false, + renderer : function(value, metaData, record, rowIndex, colIndex, store) { + var shipment = BIOSAXS.proposal.getShipmentById(record.data.shippingId); + if (shipment.dewarVOs.length == 0) { + return BUI.getRedButton('REMOVE'); } + } - if (href != null) { - return ''; - } else { - return ''; -// return ''; - } - }, - - getSubmitGreenButton : function(text) { - return ''; - }, + }); - getRedButton : function(text) { - return ''; + return columns; +}; - }, - getRectangleColorDIV : function(color, height, width) { - return '
'; - }, +ShipmentGrid.prototype._getTopButtons = function() { + var _this = this; + /** Actions buttons **/ + var actions = []; + actions.push(Ext.create('Ext.Action', { + icon : '../images/add.png', + text : 'Add Shipment', + handler : function(widget, event) { + //window.location = BUI.getCreateShipmentURL(); + var _this = this; - openBufferWindow : function(bufferId) { - var window = new BufferWindow(); - window.draw(tabs.experiment.getBufferById(bufferId), tabs.experiment); - }, + var shipmentForm = new ShipmentForm({ + creationMode : true, + showTitle : false + }); + shipmentForm.onSaved.attach(function(sender, shipment) { + _this.showShipmentTabs(shipment, targetId); + }); + + var window = Ext.create('Ext.window.Window', { + title : 'New Shipment', + height : 600, + width : 800, + layout : 'fit', + items : [ shipmentForm.getPanel() ] + }).show(); - /** Render for safety levels on grids **/ - safetyRenderer : function(val, y, specimen) { - var color = val; - if (val == "YELLOW") { - color = "#E9AB17"; } - return '' + val + ''; - }, + })); + return actions; +}; - getSampleColor : function() { - return '#CB181D'; - }, +ShipmentGrid.prototype.refresh = function(shippings) { + this.features = shippings; + this.store.loadData(this._prepareData(), false); +}; - getLightSampleColor : function() { - return '#FCBBA1'; - }, +ShipmentGrid.prototype._prepareData = function() { + var data = []; + for ( var i = 0; i < this.features.length; i++) { + data.push(this.features[i]); + } + return data; +}; - getBufferColor : function() { - return '#6A51A3'; - }, - getLightBufferColor : function() { - return '#BCBDDC'; - }, +ShipmentGrid.prototype.getPanel = function(shipments) { + this.features = shipments; + return this._renderGrid(); +}; - formatConcentration : function(val) { - if (val != null) { - return "" + Number(val).toFixed(2) + " mg/ml "; - } - return val; - }, +ShipmentGrid.prototype.edit = function(shippingId) { + window.location = BUI.getShippingURL(shippingId); +}; - formatVolume : function(sample, volume) { - if (Number(sample.data.volumeToLoad) > Number(sample.data.volume)) { - return "" + volume + " �l"; - } - if (Number(sample.data.volumeToLoad) == Number(sample.data.volume)) { - return "" + volume + " �l"; - } - return "" + volume + " �l"; - }, +ShipmentGrid.prototype._getStoreFields = function(data) { + var _this = this; + return [ { + name : 'shippingId' + }, { + name : 'shippingName' + }, { + name : 'shippingStatus' + }, { + name : 'shippingType' + }, { + name : 'creationDate' + }, { + name : 'comments' + } ]; +}; - getProposal : function() { - return new Proposal(); - }, +ShipmentGrid.prototype._renderGrid = function() { + var _this = this; - getSampleNameRenderer : function(val, y, record) { - var sample = record.data; - if (record.raw.macromolecule3VO == null) { - return '' + sample.code + ''; - } else { - return '' + sample.code + ''; - } - }, + /** Store **/ + var data = this._prepareData(); + this.store = Ext.create('Ext.data.Store', { + fields : this._getStoreFields(data), + autoload : true, + data : data + }); - getSafetyLevels : function() { - var safetyLevels = new Array(); - safetyLevels.push({ - safetyLevelId : "", - name : "UNKNOWN" - }); - safetyLevels.push({ - safetyLevelId : 1, - name : "GREEN" - }); - safetyLevels.push({ - safetyLevelId : 2, - name : "YELLOW" - }); - safetyLevels.push({ - safetyLevelId : 3, - name : "RED" - }); - return safetyLevels; - }, + this.store.loadData(data, false); + + this.grid = Ext.create('Ext.grid.Panel', { + title : "Shipping", + icon : '/ispyb/images/plane.gif', + width : this.width, + minWidth : this.minWidth, + height : this.height, + store : this.store, + columns : this._getColumns(), + viewConfig : { + stripeRows : true, + listeners : { + itemdblclick : function(dataview, record, item, e) { + _this.edit(record.raw.shippingId); + }, + cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonEdit') { + _this.edit(record.data.shippingId); + } - getErrorColor : function() { - return '#F6CED8'; - }, - getWarningColor : function() { - return '#F5DA81'; - }, - getValidColor : function() { - return '#E0F8E0'; - }, - getSamplePlateLetters : function() { - return [ "A", "B", "C", "D", "E", "F", "G", "H" ]; - }, - getSamplePositionHTML : function(sample, experiment) { - var plate = ""; - var row = ""; - var column = ""; - var rows = this.getSamplePlateLetters(); - if (sample.sampleplateposition3VO != null) { - var samplePlate = experiment.getSamplePlateById(sample.sampleplateposition3VO.samplePlateId); - if (samplePlate != null) { - plate = (samplePlate.slotPositionColumn); - row = (sample.sampleplateposition3VO.rowNumber); - column = (sample.sampleplateposition3VO.columnNumber); - // var html = "Plate: " + "" + plate + ""; - // html = html + ", Row: " + "" + rows[row - 1] + ""; - // html = html + ", Column: " + "" + column + ""; - var html = "Plate: " + plate - + "-" + rows[row - 1] + "" + column + ""; - return html; + if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonRemove') { + var shipment = BIOSAXS.proposal.getShipmentById(record.data.shippingId); + if (shipment.dewarVOs.length == 0) { + + var adapter = new BiosaxsDataAdapter(); + _this.grid.setLoading("ISPyB: Removing shipment"); + adapter.onSuccess.attach(function(sender) { + BIOSAXS.proposal.onInitialized.attach(function(sender) { + _this.refresh(BIOSAXS.proposal.getShipments()); + _this.grid.setLoading(false); + }); + BIOSAXS.proposal.init(); + }); + + adapter.onError.attach(function(sender) { + alert("Error"); + _this.grid.setLoading(false); + }); + adapter.removeShipment(record.data.shippingId); + } + } + } } + }, + selModel : { + mode : 'SINGLE' } - return ""; - }, + }); - getSafetyLabelName : function(safetyLevelId) { - var safetyLevels = BUI.getSafetyLevels(); - for ( var i = 0; i < safetyLevels.length; i++) { - if (safetyLevels[i].safetyLevelId == safetyLevelId) { - return safetyLevels[i].name; + var actions = _this._getTopButtons(); + this.grid.addDocked({ + xtype : 'toolbar', + items : actions + }); + + this.grid.getSelectionModel().on({ + selectionchange : function(sm, selections) { + if (selections.length) { + for ( var i = 0; i < actions.length; i++) { + if (actions[i].enable) { + actions[i].enable(); + } + } + } else { + for ( var i = 0; i < actions.length; i++) { + if (actions[i].alwaysEnabled == false) { + if (actions[i].disable) { + actions[i].disable(); + } + } + } } } - return "UNKNOWN"; - }, - /** generate random id **/ - id : function() { - var text = ""; - var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; - - for ( var i = 0; i < 5; i++) - text += possible.charAt(Math.floor(Math.random() * possible.length)); + }); + return this.grid; +}; - return text; - }, - showWarning : function(message) { - Ext.Msg.show({ - title : 'Warning', - msg : message, - icon : Ext.Msg.WARNING, - animEl : 'elId' - }); - }, - showError : function(message) { - Ext.Msg.show({ - title : 'Warning', - msg : message, - icon : Ext.Msg.ERROR, - animEl : 'elId' - }); - }, - getTipHTML : function(message) { - //return "
Tip
" - // + message + "
"; - return "
Tip
" - + message + "
"; - }, +ShipmentGrid.prototype.input = function() { + return { + proposal : new MeasurementGrid().input().proposal, + shippings : DATADOC.getShippings_10() + }; +}; - getWarningHTML : function(message) { - return "
Warning
" - + message + "
"; - }, +ShipmentGrid.prototype.test = function(targetId) { + var shipmentGrid = new ShipmentGrid({ + minHeight : 300, + height : 440 + }); + BIOSAXS.proposal = new Proposal(shipmentGrid.input().proposal); + var panel = shipmentGrid.getPanel(targetId); + panel.render(targetId); + shipmentGrid.refresh(shipmentGrid.input().shippings); +}; + +function SpecimenGrid(args) { + this.id = BUI.id(); + this.height = 500; + this.unitsFontSize = 9; + this.editEnabled = false; + this.isPositionColumnHidden = false; + this.removeBtnEnabled = false; - getErrorHTML : function(message) { - return "
Error
" - + message + "
"; - }, + this.selectionMode = "MULTI"; + this.updateRowEnabled = false; + this.grouped = true; + this.width = 900; + this.title = 'Specimens'; + + this.margin = "0 0 0 0"; +// this.experimentColorBased = false; - getProgessBar : function(percentage, text) { - /** percentage 100% green **/ - var color = "#0a0"; + if (args != null) { + if (args.height != null) { + this.height = args.height; + } - color = "#99CC00"; - if (percentage > 100) { - color = "yellow"; - percentage = 100; + if (args.showTitle == false) { + this.title = null; } - if (isNaN(percentage)) { - color = "white"; - percentage = 0; + + if (args.margin == false) { + this.margin = args.margin; } - var defaultText = percentage + "%"; - if (text != null) { - defaultText = text; + if (args.grouped == false) { + this.grouped = null; } - return "
" + defaultText + "
"; - }, - getFileName : function(filePath){ - if (filePath != null){ - return filePath.split("/")[filePath.split("/").length - 1] + if (args.width != null) { + this.width = args.width; } - return ""; - } -}; -Ext.ux.form.RequiredCombo = Ext.extend(Ext.form.ComboBox, { - config : { - cls : 'custom-field-text-required' - }, - initComponent : function() { - Ext.ux.form.RequiredCombo.superclass.initComponent.apply(this, arguments); - }, - checkChange : function() { - if ((this.getValue() == null) || String(this.getValue()).length == 0) { - this.addCls('custom-field-text-required'); - } else { - this.removeCls('custom-field-text-required'); + if (args.editEnabled != null) { + this.editEnabled = args.editEnabled; + } + if (args.removeBtnEnabled != null) { + this.removeBtnEnabled = args.removeBtnEnabled; + } + if (args.isPositionColumnHidden != null) { + this.isPositionColumnHidden = args.isPositionColumnHidden; + } + if (args.selectionMode != null) { + this.selectionMode = args.selectionMode; + } + if (args.updateRowEnabled != null) { + this.updateRowEnabled = args.updateRowEnabled; } + } -}); + this.onClick = new Event(this); + this.onSelected = new Event(this); + this.onRemoved = new Event(this); + this.onSpecimenChanged = new Event(); +} -Ext.define('Ext.form.field.RequiredNumber', { - extend : 'Ext.form.field.Number', - alias : 'widget.requirednumberfield', - alternateClassName : [ 'Ext.form.RequiredNumberField', 'Ext.form.RequiredNumber' ], - config : { - cls : 'custom-field-text-required' - }, +SpecimenGrid.prototype._prepareData = function(experiment) { + var data = []; - initComponent : function() { - var me = this; - me.callParent(); - me.setMinValue(me.minValue); - me.setMaxValue(me.maxValue); - }, + var samples = experiment.getSamples(); + for ( var i = 0; i < samples.length; i++) { + var sample = samples[i]; + if (sample.macromolecule3VO != null) { + sample.macromolecule = sample.macromolecule3VO.acronym; + sample.exposureTemperature = []; + sample.macromoleculeId = sample.macromolecule3VO.macromoleculeId; + } - onChange : function() { - if ((this.getValue() == null) || String(this.getValue()).length == 0) { - this.addCls('custom-field-text-required'); + if (sample.sampleplateposition3VO != null) { + if (sample.sampleplateposition3VO.samplePlateId != null) { + sample.samplePlateId = sample.sampleplateposition3VO.samplePlateId; + sample.rowNumber = sample.sampleplateposition3VO.rowNumber; + sample.columnNumber = sample.sampleplateposition3VO.columnNumber; + if (experiment.getSamplePlateById(sample.sampleplateposition3VO.samplePlateId).plategroup3VO != null) { + sample.plateGroupName = experiment.getSamplePlateById(sample.sampleplateposition3VO.samplePlateId).plategroup3VO.name; + sample.samplePlateName = experiment.getSamplePlateById(sample.sampleplateposition3VO.samplePlateId).name + " [" + sample.plateGroupName + "]"; + sample.slotPositionColumn = experiment.getSamplePlateById(sample.sampleplateposition3VO.samplePlateId).slotPositionColumn; + } + } } else { - this.removeCls('custom-field-text-required'); + sample.samplePlateName = "Unallocated Specimens"; } - this.toggleSpinners(); - this.callParent(arguments); - } -}); -Ext.define('Ext.form.field.RequiredText', { - extend : 'Ext.form.field.Text', - alias : 'widget.requiredtext', - requires : [ 'Ext.form.field.VTypes', 'Ext.layout.component.field.Text' ], - alternateClassName : [ 'Ext.form.RequiredTextField', 'Ext.form.RequiredText' ], - config : { - cls : 'custom-field-text-required' - }, - initComponent : function() { - var me = this; - if (me.allowOnlyWhitespace === false) { - me.allowBlank = false; + /** For grouping, because sencha has not option for multiple grouping I add a field to your store with a convert function that concatenates these two fields and then group by that field.**/ + sample.groupIndex = sample.bufferId + sample.macromoleculeId; + var macromolecule = BIOSAXS.proposal.getMacromoleculeById(sample.macromoleculeId); + + sample.acronym = "Buffers"; + if (macromolecule != null) { + sample.acronym = BIOSAXS.proposal.getMacromoleculeById(sample.macromoleculeId).acronym; } - me.callParent(); - me.addEvents( - /** - * @event autosize - * Fires when the **{@link #autoSize}** function is triggered and the field is resized according to the - * {@link #grow}/{@link #growMin}/{@link #growMax} configs as a result. This event provides a hook for the - * developer to apply additional logic at runtime to resize the field if needed. - * @param {Ext.form.field.Text} this This text field - * @param {Number} width The new field width - */ - 'autosize', - /** - * @event keydown - * Keydown input field event. This event only fires if **{@link #enableKeyEvents}** is set to true. - * @param {Ext.form.field.Text} this This text field - * @param {Ext.EventObject} e - */ - 'keydown', - /** - * @event keyup - * Keyup input field event. This event only fires if **{@link #enableKeyEvents}** is set to true. - * @param {Ext.form.field.Text} this This text field - * @param {Ext.EventObject} e - */ - 'keyup', - /** - * @event keypress - * Keypress input field event. This event only fires if **{@link #enableKeyEvents}** is set to true. - * @param {Ext.form.field.Text} this This text field - * @param {Ext.EventObject} e - */ - 'keypress'); - me.addStateEvents('change'); - me.setGrowSizePolicy(); - }, - checkChange : function() { - if ((this.getValue() == null) || String(this.getValue()).length == 0) { - this.addCls('custom-field-text-required'); - } else { - this.removeCls('custom-field-text-required'); + sample.buffer = experiment.getBufferById(sample.bufferId); + + sample.volumeToLoad = experiment.getVolumeToLoadBySampleId(sample.sampleId); + data.push(sample); + } + return data; +}; + +SpecimenGrid.prototype.deselectAll = function() { + this.grid.getSelectionModel().deselectAll(); +}; + +SpecimenGrid.prototype.selectById = function(specimenId) { + this.grid.getSelectionModel().deselectAll(); + for ( var i = 0; i < this.grid.getStore().data.items.length; i++) { + var item = this.grid.getStore().data.items[i].raw; + if (item.specimenId == specimenId) { + this.grid.getSelectionModel().select(i); } } -}); +}; -var BIOSAXS_COMBOMANAGER = { +SpecimenGrid.prototype.getStore = function() { + return this.store; +}; - getComboMacromoleculeByMacromolecules : function(macromolecules, args) { - var labelWidth = 150; - var margin = "0 0 5 0"; - var width = 300; +SpecimenGrid.prototype.getPlugins = function() { + var _this = this; - if (args != null) { - if (args.labelWidth != null) { - labelWidth = args.labelWidth; - } - if (args.margin != null) { - margin = args.margin; - } - if (args.width != null) { - width = args.width; - } - } + var plugins = []; - var store = Ext.create('Ext.data.Store', { - fields : [ 'macromoleculeId', 'acronym' ], - data : macromolecules, - sorters : [ 'acronym' ] - }); + if (this.updateRowEnabled) { + plugins.push(Ext.create('Ext.grid.plugin.RowEditing', { + clicksToEdit : 1, + listeners : { + validateedit : function(grid, e) { + var measurements = []; - return Ext.create('Ext.form.ComboBox', { - fieldLabel : 'Macromolecules', - labelWidth : labelWidth, - width : width, - margin : margin, - store : store, - editable: false, - queryMode : 'local', - displayField : 'acronym', - valueField : 'macromoleculeId' - }); - }, - getComboBuffers : function(buffers, args) { - var labelWidth = 150; - var margin = "0 0 5 0"; - var width = 300; - var fieldLabel = 'Buffer'; + if (e.newValues.bufferId != e.record.raw.bufferId) { + /** If buffer has changed we have to change all the specimens sharing same datacollection **/ + var dataCollections = []; + if (e.record.raw.macromoleculeId == null) { + dataCollections = dataCollections.concat(_this.experiment.getDataCollectionsBySpecimenId(e.record.raw.specimenId)); + } else { + var sampleDataCollections = _this.experiment.getDataCollectionsBySpecimenId(e.record.raw.specimenId); + for ( var i = 0; i < sampleDataCollections.length; i++) { + var sampleDataCollection = sampleDataCollections[i]; + if (sampleDataCollection != null) { + for ( var j = 0; j < sampleDataCollection.measurementtodatacollection3VOs.length; j++) { + var measurementTODc = sampleDataCollection.measurementtodatacollection3VOs[j]; + if (measurementTODc.dataCollectionOrder == 1) { + dataCollections = dataCollections.concat(_this.experiment.getDataCollectionsBySpecimenId(_this.experiment + .getMeasurementById(measurementTODc.measurementId).specimenId)); + } + } + } + } + } + var i = null; + for ( i = 0; i < dataCollections.length; i++) { + var dataCollection = dataCollections[i]; + var specimens = _this.experiment.getSpecimenByDataCollectionId(dataCollection.dataCollectionId); + measurements = measurements.concat(specimens); + } - if (args != null) { - if (args.labelWidth != null) { - labelWidth = args.labelWidth; - } - if (args.margin != null) { - margin = args.margin; - } - if (args.width != null) { - width = args.width; - } - if (args.noLabel != null) { - fieldLabel = null; - } - } + for ( i = 0; i < measurements.length; i++) { + var measurement = measurements[i]; + var specimen = _this.experiment.getSpecimenById(measurement.specimenId); + specimen.bufferId = e.newValues.bufferId; + new BiosaxsDataAdapter().saveSpecimen(specimen, _this.experiment); + } + } - var store = Ext.create('Ext.data.Store', { - fields : [ 'bufferId', 'acronym' ], - data : buffers, - sorters : [ 'acronym' ] - }); + /** Setting values **/ + e.record.raw.concentration = e.newValues.concentration; + e.record.raw.volume = e.newValues.volume; - return Ext.create('Ext.form.ComboBox', { - fieldLabel : fieldLabel, - labelWidth : labelWidth, - width : width, - margin : margin, - editable: false, - store : store, - queryMode : 'local', - displayField : 'acronym', - valueField : 'bufferId' - }); - }, - getComboSessions : function(sessions, args) { - var labelWidth = 150; - var margin = "0 0 5 0"; - var width = 300; + /** Position **/ + if (e.record.raw.sampleplateposition3VO != null) { + var samplePlate = _this.experiment.getSamplePlateBySlotPositionColumn(e.newValues.slotPositionColumn); + if (samplePlate != null) { + e.record.raw.sampleplateposition3VO = { + columnNumber : e.newValues.columnNumber, + rowNumber : e.newValues.rowNumber, + samplePlateId : samplePlate.samplePlateId + }; + } + } else { + if (e.newValues.slotPositionColumn != null) { + var samplePlate = _this.experiment.getSamplePlateBySlotPositionColumn(e.newValues.slotPositionColumn); + if (samplePlate != null) { + e.record.raw.sampleplateposition3VO = { + columnNumber : e.newValues.columnNumber, + rowNumber : e.newValues.rowNumber, + samplePlateId : samplePlate.samplePlateId + }; + } + } + } - if (args != null) { - if (args.labelWidth != null) { - labelWidth = args.labelWidth; - } - if (args.margin != null) { - margin = args.margin; - } - if (args.width != null) { - width = args.width; + var macromoleculeId = e.record.data.macromoleculeId; + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, specimen) { + /** Because macromolecule3VO is fecthed LAZY **/ + if (macromoleculeId != null) { + specimen.macromolecule3VO = BIOSAXS.proposal.getMacromoleculeById(macromoleculeId); + } + _this.onSpecimenChanged.notify(specimen); + _this.grid.setLoading(false); + }); + adapter.onError.attach(function() { + alert("Error"); + _this.grid.setLoading(false); + }); + _this.grid.setLoading(); + adapter.saveSpecimen(e.record.raw, _this.experiment); + } } - } - - for ( var i = 0; i < sessions.length; i++) { - sessions[i]["startDateFormatted"] = moment(sessions[i].startDate).format("MMM Do YY"); - sessions[i]["sorter"] = moment(sessions[i].startDate).format("YYYYMMDD"); - } + })); + } + return plugins; +}; - var store = Ext.create('Ext.data.Store', { - fields : [ 'sessionId', 'startDateFormatted', 'beamlineName', 'startDate', 'endDate', 'beamlineOperator' ], - data : sessions, - sorters : [ 'sorter' ] +SpecimenGrid.prototype._getRowCombo = function() { + var data = []; + for ( var i = 1; i <= 8; i++) { + data.push({ + rowNumber : i, + name : BUI.getSamplePlateLetters()[i - 1] }); + } - return Ext.create('Ext.form.ComboBox', { - fieldLabel : 'Sessions', - labelWidth : labelWidth, - width : width, - margin : margin, - store : store, - queryMode : 'local', - // displayField : 'startDate', - valueField : 'sessionId', - // Template for the dropdown menu. - // Note the use of "x-boundlist-item" class, - // this is required to make the items selectable. - tpl : Ext.create('Ext.XTemplate', '', - '
{startDateFormatted} {beamlineName}
', '
'), - // template for the content inside text field - displayTpl : Ext.create('Ext.XTemplate', '', '{startDateFormatted}', '') + var positionsStore = Ext.create('Ext.data.Store', { + fields : [ 'rowNumber', 'name' ], + data : data + }); - }); - } + return Ext.create('Ext.form.ComboBox', { + store : positionsStore, + queryMode : 'local', + displayField : 'name', + valueField : 'rowNumber' + }); }; - - function GenericWindow(args){ - this.title = "title"; - this.width = 700; - this.height = 500; - this.id = BUI.id(); - - this.close = false; - this.draggable = true; - this.modal = true; - - if (args != null){ - if (args.actions != null){ - this.actions = args.actions; - } - if (args.form != null){ - this.form = args.form; - } - if (args.width != null){ - this.width = args.width; - } - if (args.modal != null){ - this.modal = args.modal; - } - - if (args.height != null){ - this.height = args.height; - } - if (args.title != null){ - this.title = args.title; - } - if (args.form != null){ - this.form = args.form; - } - if (args.close != null){ - this.close = args.close; - } - if (args.draggable != null){ - this.draggable = args.draggable; - } - - } - /** Events **/ - this.onSaved = new Event(this); - }; - - -GenericWindow.prototype.getButtons = function() { - var _this = this; - - if (this.close){ - return [ { - text : 'Close', - handler : function() { - _this.panel.close(); - } - } ]; - } - else{ - return [ { - text : 'Save', - handler : function() { - _this.save(); - - } - }, { - text : 'Cancel', - handler : function() { - _this.panel.close(); - } - } ]; +SpecimenGrid.prototype._getColumnCombo = function() { + var data = []; + for ( var i = 1; i <= 12; i++) { + data.push({ + columnNumber : i + }); } -}; -GenericWindow.prototype.save = function (){ - alert("Method save of GenerciWindow class is abstract"); + var positionsStore = Ext.create('Ext.data.Store', { + fields : [ 'columnNumber' ], + data : data + }); + + return Ext.create('Ext.form.ComboBox', { + store : positionsStore, + queryMode : 'local', + displayField : 'columnNumber', + valueField : 'columnNumber' + }); }; -GenericWindow.prototype._postRender = function(data, experiment){ +SpecimenGrid.prototype._getSlotColumBombo = function() { + if (this.experiment){ + var length = this.experiment.getSamplePlates().length; + + var data = []; + for ( var i = 1; i <= length; i++) { + data.push({ + slotPositionColumn : i + }); + } + + var positionsStore = Ext.create('Ext.data.Store', { + fields : [ 'slotPositionColumn' ], + data : data + }); + return Ext.create('Ext.form.ComboBox', { + store : positionsStore, + queryMode : 'local', + displayField : 'slotPositionColumn', + valueField : 'slotPositionColumn' + }); + } }; - -GenericWindow.prototype.draw = function (data, experiment){ - this._render(data, experiment); +SpecimenGrid.prototype.getPanelByExperiment = function(experiment) { + this.experiment = experiment; + var data = this._prepareData(experiment); + return this.getPanel(data); }; -GenericWindow.prototype.refresh = function(data, experiment){ - this.data = data; - this.experiment = experiment; - this.form.refresh(data, experiment); +SpecimenGrid.prototype.refresh = function(experiment) { + this.experiment = experiment; + var data = this._prepareData(experiment); + this.store.loadData(data); }; -GenericWindow.prototype._render = function(data, experiment){ - this.data = data; - var _this = this; - if (this.panel == null){ - this.panel = Ext.create('Ext.Window', { - id : this.id, - title : this.title, - resizable : true, - constrain : true, - border : 1, - modal : this.modal, - frame : false, - draggable : this.draggable, - closable : true, - autoscroll : true, - layout : { type: 'vbox',align: 'stretch'}, - width : this.width, - height : this.form.height, - buttonAlign :'right', - buttons : this.getButtons(), - items : this.form.getPanel(data, experiment), - listeners: { - scope : this, - minimize : function(){ - this.panel.hide(); - }, - destroy : function(){ - delete this.panel; - } - } - }); - this.panel.setLoading(); - } - - this.panel.show(); - this._postRender(); -}; -function CalendarWidget(args) { - this.height = 740; +SpecimenGrid.prototype.getPanel = function() { + var _this = this; + this.store = Ext.create('Ext.data.Store', { + fields : [ + 'buffer', 'bufferId', 'code', 'macromolecule', 'acronym', 'macromoleculeId', 'concentration', 'volume', 'samplePlateId', + 'slotPositionColumn', 'rowNumber', 'columnNumber', 'groupIndex' ], + data : [], + groupField : 'acronym' + }); + this.store.sort([ { + property : 'concentration', + direction : 'ASC' + }, { + property : 'buffer', + direction : 'ASC' + } ]); - if (args != null) { - if (args.height != null) { - this.height = args.height; + var selModel = Ext.create('Ext.selection.RowModel', { + allowDeselect : true, + mode : this.selectionMode, + listeners : { + selectionchange : function(sm, selections) { + var selected = []; + for ( var i = 0; i < selections.length; i++) { + selected.push(selections[i].raw); + } + _this.onSelected.notify(selected); + } } + }); + + var features = []; + + if (this.grouped) { + features.push({ + ftype : 'grouping', + groupHeaderTpl : '{name}', + hideGroupedHeader : false, + startCollapsed : false, + id : 'myGroupedStore' + }); } - this.onClick = new Event(); -} + this.grid = Ext.create( + 'Ext.grid.Panel', + { + title : this.title, + height : this.height, + width : this.width, + selModel : selModel, + store : this.store, + features : features, + margin : this.margin, + plugins : this.getPlugins(), + columns : [ + { + text : '', + dataIndex : 'macromolecule', + width : 20, + renderer : function(val, y, sample) { + var macromoleculeId = null; + if (sample.raw.macromolecule3VO != null) { + macromoleculeId = sample.raw.macromolecule3VO.macromoleculeId; + } + else{ + macromoleculeId = sample.raw.macromoleculeId; + } + + if (macromoleculeId == null) return; + return BUI.getRectangleColorDIV(_this.experiment.macromoleculeColors[macromoleculeId], 10, 10); + } + }, + { + text : 'Macromolecule', + dataIndex : 'macromolecule', + width : 100 + }, + { + text : '', + dataIndex : 'buffer', + width : 20, + renderer : function(val, y, sample) { + var color = "black"; + if (sample.raw.bufferId != null) { + if (_this.experiment.getDataCollectionsBySpecimenId(sample.raw.specimenId)[0] != null){ + color = _this.experiment.getSpecimenColorByBufferId(_this.experiment.getMeasurementById(_this.experiment.getDataCollectionsBySpecimenId(sample.raw.specimenId)[0].measurementtodatacollection3VOs[0].measurementId).specimenId); + } + return BUI.getRectangleColorDIV(color, 10, 10); + } + } + }, { + text : 'Buffer', + dataIndex : 'bufferId', + width : 140, + renderer : function(val, y, sample) { + if (sample.raw.bufferId != null) { + return BIOSAXS.proposal.getBufferById(val).acronym; + } + }, + editor : BIOSAXS_COMBOMANAGER.getComboBuffers(BIOSAXS.proposal.getBuffers(), { + noLabel : true, + width : 300 + }) + }, { + text : 'Conc.', + dataIndex : 'concentration', + width : 100, + editor : { + allowBlank : false + }, + renderer : function(val, meta, sample) { + if (isNaN(val)) { + meta.tdCls = 'yellow-cell'; + return val; + } else { + if (val != 0) { + return BUI.formatValuesUnits(val, 'mg/ml', { + fontSize : 16, + decimals : 3, + unitsFontSize : this.unitsFontSize + }); + } else { + return; + } + } + } + }, { + text : 'Vol. Well', + dataIndex : 'volume', + width : 70, + editor : { + allowBlank : true + }, + renderer : function(val, y, sample) { + return BUI.formatValuesUnits(sample.data.volume, 'µl', { + fontSize : 12, + decimals : 2, + unitsFontSize : this.unitsFontSize + }); + } + }, { + text : 'Position', + hidden : true, + flex : 1, + renderer : function(val, y, sample) { + return BUI.getSamplePositionHTML(sample.raw, _this.experiment); + } + }, { + text : 'samplePlateId', + dataIndex : 'samplePlateId', + hidden : true + }, { + text : 'Plate', + hidden : this.isPositionColumnHidden, + dataIndex : 'slotPositionColumn', + editor : _this._getSlotColumBombo(), + flex : 1, + renderer : function(val, meta, sample) { + if ((val != null) & (val != "")) { + return val; + } else { + meta.tdCls = 'yellow-cell'; + } + } + }, { + text : 'Row', + hidden : this.isPositionColumnHidden, + dataIndex : 'rowNumber', + editor : this._getRowCombo(), + flex : 1, + renderer : function(val, meta, sample) { + if ((val != null) && (val != "")) { + return BUI.getSamplePlateLetters()[val - 1]; + } else { + meta.tdCls = 'yellow-cell'; + } + } + }, { + text : 'Well', + hidden : this.isPositionColumnHidden, + dataIndex : 'columnNumber', + editor : this._getColumnCombo(), + flex : 1, + renderer : function(val, meta, sample) { + if ((val != null) && (val != "")) { + return val; + } else { + meta.tdCls = 'yellow-cell'; + } + } + }, { + id : _this.id + 'buttonEditSample', + text : 'Edit', + width : 80, + sortable : false, + hidden : !_this.editEnabled, + renderer : function(value, metaData, record, rowIndex, colIndex, store) { + if (_this.editEnabled) { + return BUI.getGreenButton('EDIT'); + } + } + }, { + id : _this.id + 'buttonRemoveSample', + text : '', + hidden : !_this.removeBtnEnabled, + width : 100, + sortable : false, + renderer : function(value, metaData, record, rowIndex, colIndex, store) { + if (_this.removeBtnEnabled) { + return BUI.getRedButton('REMOVE'); + } + } + } -CalendarWidget.prototype.loadData = function(data) { - this.events = []; + ], + viewConfig : { + preserveScrollOnRefresh : true, + stripeRows : true, + getRowClass : function(record) { + var specimens = _this.experiment.getSampleByPosition(record.data.samplePlateId, record.data.rowNumber, + record.data.columnNumber); + if (specimens.length > 1) { + return 'red-row'; - for ( var i = 0; i < data.length; i++) { - var date = moment(data[i].creationDate); - var textColor = 'black'; + } + }, + listeners : { + selectionchange : function(grid, selected) { + _this.onClick.notify(record.raw); + }, + cellclick : function(grid, td, cellIndex, record, tr) { + if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonEditSample') { + } + if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonRemoveSample') { + grid.getStore().removeAt(rowIndex); + _this.onRemoved.notify(); + } - if (data[i].status == "FINISHED") { - textColor = 'green'; - } - if (data[i].status == "ABORTED") { - textColor = 'red'; - } - this.events.push({ - title : data[i].name, - start : date.format("YYYY-MM-DD HH:mm:ss"), - end : date.format("YYYY-MM-DD HH:mm:ss"), - date : date, - allDay : false, - color : textColor, - className : date.format("YYYY-MM-DD") - }); - } + } + } + } + }); + return this.grid; }; -CalendarWidget.prototype.draw = function(targetId) { - var _this = this; - $('#' + targetId).fullCalendar({ - eventClick : function(calEvent, jsEvent, view) { - _this.onClick.notify(calEvent.className[0]); - - }, - contentHeight : _this.height, - header : { - left : 'prev,next today', - center : 'title', - right : 'month,basicWeek,basicDay' - }, - editable : false, - events : this.events +SpecimenGrid.prototype.input = function() { + return { + experiment : DATADOC.getExperiment_10(), + proposal : DATADOC.getProposal_10() + }; +}; + +SpecimenGrid.prototype.test = function(targetId) { + var specimenGrid = new SpecimenGrid({ + height : 400, + maxHeight : 400, + width : 1000 }); + BIOSAXS.proposal = new Proposal(specimenGrid.input().proposal); + + var experiment = new Experiment(specimenGrid.input().experiment); + var panel = specimenGrid.getPanelByExperiment(experiment); + panel.render(targetId); }; /** - * It shows a table with the guinier and gnom data as well as passed and - * discarded measurements + * Shows a list of stock solutions with macromolecule, buffer, storage temperature, concentration, shipment and comments * - * @height - * @showBufferColumns + * @multiselect allows multiple selection + * @height + * @minHeight + * @width + * @tbar + * @showTitle + * @isPackedVisible shows is stock solution is in a box + * @btnEditVisible shows edit button + * @btnAddVisible + * @btnAddExisting + * @btnUnpackVisible allows to unpack a stock solution + * @btnRemoveVisible allow to remove a stock solution */ -function ConcentrationHTMLTableWidget(args) { + +function StockSolutionGrid(args) { this.id = BUI.id(); + this.height = 100; + this.width = null; + this.minHeight = null; + this.tbar = true; - this.showBufferColumns = true; + this.title = "Stock Solutions"; + + /** Visible buttons and actions **/ + this.btnEditVisible = true; + this.btnRemoveVisible = true; + this.btnAddVisible = true; + this.btnAddExisting = false; + this.isPackedVisible = true; + this.btnUnpackVisible = false; + + /** Selectors **/ + this.multiselect = false; + this.selectedStockSolutions = []; if (args != null) { + if (args.btnUnpackVisible != null) { + this.btnUnpackVisible = args.btnUnpackVisible; + } + if (args.multiselect != null) { + this.multiselect = args.multiselect; + } if (args.height != null) { this.height = args.height; } - if (args.showBufferColumns != null) { - this.showBufferColumns = args.showBufferColumns; + if (args.btnEditVisible != null) { + this.btnEditVisible = args.btnEditVisible; + } + if (args.btnAddVisible != null) { + this.btnAddVisible = args.btnAddVisible; + } + if (args.btnAddExisting != null) { + this.btnAddExisting = args.btnAddExisting; + } + if (args.width != null) { + this.width = args.width; + } + if (args.minHeight != null) { + this.minHeight = args.minHeight; + } + if (args.tbar != null) { + this.tbar = args.tbar; + } + if (args.btnRemoveVisible != null) { + this.btnRemoveVisible = args.btnRemoveVisible; + } + if (args.isPackedVisible != null) { + this.isPackedVisible = args.isPackedVisible; + } + if (args.showTitle != null) { + this.showTitle = args.showTitle; + if (this.showTitle == false) { + this.title = null; + } } - } -} - -ConcentrationHTMLTableWidget.prototype.refresh = function(parsedData) { - document.getElementById(this.id).innerHTML = this.getPanel(parsedData); -}; - -ConcentrationHTMLTableWidget.prototype.getPanel = function(parsedData) { - var html = "
"; - html = html + ""; - /** Title * */ - html = html + ""; - html = html + ""; - if (this.showBufferColumns) { - html = html + ""; } - html = html + ""; - html = html + ""; - html = html + ""; - html = html + ""; - html = html + ""; - html = html + ""; - html = html + ""; - html = html + ""; - html = html + ""; - html = html + ""; - html = html + ""; - html = html + ""; - html = html + ""; + /** Events **/ + this.onProposalChanged = new Event(this); + this.onStockSolutionSelected = new Event(this); +} - parsedData.concentration.concentrations.sort(function(a, b) { - return a.concentration - b.concentration; - }); - /** Row * */ - for ( var i = 0; i < parsedData.concentration.concentrations.length; i++) { - var row = parsedData[i]; +StockSolutionGrid.prototype._getColumns = function() { + var _this = this; + var columns = [ - var tr = ""; - if (i % 2 == 1) { - tr = ""; + { + header : 'Macromolecule', + dataIndex : 'macromolecule', + id : _this.id + 'macromolecule', + type : 'string', + renderer : function(val, y, specimen) { + return '' + val + ''; + }, + hidden : false, + flex : 1 + }, { + header : 'Buffer', + dataIndex : 'buffer', + name : 'buffer', + hidden : false, + renderer : function(val, y, specimen) { + return '' + val + ''; + }, + type : 'string', + flex : 1 + }, { + header : 'Acronym', + dataIndex : 'name', + name : 'name', + type : 'string', + flex : 1, + hidden : true + }, { + header : 'Storage Temp.', + dataIndex : 'storageTemperature', + name : 'storageTemperature', + type : 'string', + flex : 1, + hidden : false, + renderer : function(val) { + return BUI.formatValuesUnits(val, 'C', { + fontSize : 12, + decimals : 2, + unitsFontSize : 10 + }); } - var goodMeasurements = (parsedData.concentration.concentrations[i].frames.length - parsedData.concentration.concentrations[i].frames_warning); - if (goodMeasurements == 0) { - tr = ""; + }, { + header : 'Volume', + dataIndex : 'volume', + type : 'string', + flex : 1, + hidden : false, + renderer : function(val) { + return BUI.formatValuesUnits(val, 'µl', { + fontSize : 12, + decimals : 2, + unitsFontSize : 10 + }); } - html = html + tr; - html = html + ""; - if (this.showBufferColumns) { - html = html + ""; + }, { + header : 'Concentration', + dataIndex : 'concentration', + name : 'concentration', + type : 'string', + flex : 1, + renderer : function(val) { + return BUI.formatValuesUnits(val, 'mg/ml', { + fontSize : 12, + decimals : 2, + unitsFontSize : 10 + }); } - html = html + ""; - html = html + ""; - html = html + ""; - html = html + ""; - html = html + ""; - html = html + ""; - html = html + ""; - html = html + ""; + }, { + header : 'Packed', + dataIndex : 'comments', + id : _this.id + "box", + type : 'string', + width : 50, + hidden : !this.isPackedVisible, + renderer : function(val, cmp, a) { + if (a.raw.boxId != null) { + return "
"; + } + + } + }, { + header : 'Comments', + dataIndex : 'comments', + type : 'string', + flex : 1 + } ]; + + if (this.btnEditVisible) { + columns.push({ + id : _this.id + 'buttonEdit', + width : 85, + sortable : false, + renderer : function(value, metaData, record, rowIndex, colIndex, store) { + if (_this.btnEditVisible) { + return BUI.getGreenButton('EDIT'); + } + } + }); + } + + if (this.btnRemoveVisible) { + columns.push({ + id : _this.id + 'buttonRemove', + width : 85, + sortable : false, + renderer : function(value, metaData, record, rowIndex, colIndex, store) { + if (_this.btnRemoveVisible) { + return BUI.getRedButton('REMOVE'); + } + } + }); } - return html + "
ConcentrationBufferMeasurementsGuinierGnom
PassedDiscardedRgI0QualityRgdMax
" + parsedData.concentration.concentrations[i].concentration + "" + parsedData.concentration.concentrations[i].bufferAcronym + "" + goodMeasurements + " of " + parsedData.concentration.concentrations[i].frames.length + "" + parsedData.concentration.concentrations[i].frames_warning + "" + BUI.formatValuesErrorUnits(parsedData.concentration.concentrations[i].calculation.rgGuinier.avg, parsedData.concentration.concentrations[i].calculation.rgGuinier.std, "nm", { - lineBreak : false - }) + "" + BUI.formatValuesErrorUnits(parsedData.concentration.concentrations[i].calculation.i0Guinier.avg, parsedData.concentration.concentrations[i].calculation.i0Guinier.std, " ", { - lineBreak : false - }) + "" + BUI.formatValuesErrorUnits(parsedData.concentration.concentrations[i].calculation.quality.avg, parsedData.concentration.concentrations[i].calculation.quality.std, " ", { - lineBreak : false - }) + "" + BUI.formatValuesErrorUnits(parsedData.concentration.concentrations[i].calculation.rgGnom.avg, parsedData.concentration.concentrations[i].calculation.rgGnom.std, " ", { - lineBreak : false - }) + "" + BUI.formatValuesErrorUnits(parsedData.concentration.concentrations[i].calculation.dMax.avg, parsedData.concentration.concentrations[i].calculation.dMax.std, " ", { - lineBreak : false - }) + "
"; + + if (this.btnUnpackVisible) { + columns.push({ + id : _this.id + 'buttonUnpack', + width : 85, + sortable : false, + renderer : function(value, metaData, record, rowIndex, colIndex, store) { + if (_this.btnUnpackVisible) { + return BUI.getBlueButton('UNPACK'); + } + } + }); + } + return columns; }; -ConcentrationHTMLTableWidget.prototype.input = function() { - return { - data : { - "dataCollectionCount" : 4, - "buffers" : [ { - "bufferId" : "422" - } ], - "concentration" : { - "concentrations" : [ { - "concentration" : "7.17", - "id" : "7.1699999999999999", - "bufferId" : "422.00", - "bufferAcronym" : "B1", - "rgGuinier" : [ "5.12723" ], - "frames" : [ { - "total" : "0.515705406286", - "bufferBeforeMeasurementId" : 15045, - "sampleMergeId" : 8176, - "averagedModelId" : null, - "I0" : "183.457461646", - "proposalCode" : "OPD", - "averagedModel" : null, - "bufferAfterMergeId" : 8177, - "framesCount" : "10", - "bufferBeforeMergeId" : 8175, - "averageFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_065_ave.dat", - "bufferAfterMeasurementId" : 15048, - "rgGuinier" : "5.12723", - "subtractedFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_065_sub.dat", - "firstPointUsed" : "17", - "chi2RgFilePath" : null, - "abInitioModelId" : null, - "macromoleculeId" : 649, - "code" : "_4.0_7.17", - "transmission" : "100.0", - "timeStart" : "2013-06-05 15:43:54.348723", - "bufferAcronym" : "B1", - "quality" : "0.853011", - "shapeDeterminationModelId" : null, - "expermientComments" : "[BsxCube] Generated from BsxCube", - "bufferId" : 422, - "isagregated" : "False", - "dmax" : "25.63615", - "bufferBeforeAverageFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_064_ave.dat", - "exposureTemperature" : "4.0", - "subtractionId" : 3377, - "conc" : "7.1699999999999999", - "rapidShapeDeterminationModel" : null, - "experimentCreationDate" : "Jun 5, 2013 3:30:30 PM", - "lastPointUsed" : "36", - "modelListId" : null, - "framesMerge" : "9", - "rapidShapeDeterminationModelId" : null, - "bufferAfterAverageFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_066_ave.dat", - "nsdFilePath" : null, - "bufferAfterFramesMerged" : "10", - "experimentId" : 700, - "macromoleculeAcronym" : "L9", - "i0stdev" : "0.139364435146", - "sampleMeasurementId" : 15047, - "measurementComments" : "[5]", - "priorityLevelId" : 10, - "bufferBeforeFramesMerged" : "10", - "substractionCreationTime" : "Jun 5, 2013 3:46:31 PM", - "proposalNumber" : "29", - "rgGnom" : "5.40297914939", - "volume" : "475.302", - "shapeDeterminationModel" : null, - "comments" : null, - "groupeField" : "L9 B1" - } ], - "frames_warning" : 0, - "calculation" : { - "rgGuinier" : { - "std" : 0, - "sum" : 5.12723, - "avg" : 5.12723, - "validNumber" : 1, - "totalNumber" : 1, - "values" : [ "5.12723" ] - }, - "i0Guinier" : { - "std" : 0, - "sum" : 183.457461646, - "avg" : 183.457461646, - "validNumber" : 1, - "totalNumber" : 1, - "values" : [ "183.457461646" ] - }, - "quality" : { - "std" : 0, - "sum" : 0.853011, - "avg" : 0.853011, - "validNumber" : 1, - "totalNumber" : 1, - "values" : [ "0.853011" ] - }, - "rgGnom" : { - "std" : 0, - "sum" : 5.40297914939, - "avg" : 5.40297914939, - "validNumber" : 1, - "totalNumber" : 1, - "values" : [ "5.40297914939" ] - }, - "dMax" : { - "std" : 0, - "sum" : 25.63615, - "avg" : 25.63615, - "validNumber" : 1, - "totalNumber" : 1, - "values" : [ "25.63615" ] - } - } - }, { - "concentration" : "3.53", - "id" : "3.5299999999999998", - "bufferId" : "422.00", - "bufferAcronym" : "B1", - "rgGuinier" : [ "4.38278" ], - "frames" : [ { - "total" : "0.497164741078", - "bufferBeforeMeasurementId" : 15048, - "sampleMergeId" : 8178, - "averagedModelId" : null, - "I0" : "160.023229462", - "proposalCode" : "OPD", - "averagedModel" : null, - "bufferAfterMergeId" : 8179, - "framesCount" : "10", - "bufferBeforeMergeId" : 8177, - "averageFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_067_ave.dat", - "bufferAfterMeasurementId" : 15051, - "rgGuinier" : "4.38278", - "subtractedFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_067_sub.dat", - "firstPointUsed" : "36", - "chi2RgFilePath" : null, - "abInitioModelId" : null, - "macromoleculeId" : 649, - "code" : "_4.0_3.53", - "transmission" : "100.0", - "timeStart" : "2013-06-05 15:47:04.630129", - "bufferAcronym" : "B1", - "quality" : "0.742825", - "shapeDeterminationModelId" : null, - "expermientComments" : "[BsxCube] Generated from BsxCube", - "bufferId" : 422, - "isagregated" : "False", - "dmax" : "15.33973", - "bufferBeforeAverageFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_066_ave.dat", - "exposureTemperature" : "4.0", - "subtractionId" : 3378, - "conc" : "3.5299999999999998", - "rapidShapeDeterminationModel" : null, - "experimentCreationDate" : "Jun 5, 2013 3:30:30 PM", - "lastPointUsed" : "61", - "modelListId" : null, - "framesMerge" : "10", - "rapidShapeDeterminationModelId" : null, - "bufferAfterAverageFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_068_ave.dat", - "nsdFilePath" : null, - "bufferAfterFramesMerged" : "10", - "experimentId" : 700, - "macromoleculeAcronym" : "L9", - "i0stdev" : "0.1499898017", - "sampleMeasurementId" : 15050, - "measurementComments" : "[6]", - "priorityLevelId" : 12, - "bufferBeforeFramesMerged" : "10", - "substractionCreationTime" : "Jun 5, 2013 3:49:42 PM", - "proposalNumber" : "29", - "rgGnom" : "4.40615474861", - "volume" : "372.656", - "shapeDeterminationModel" : null, - "comments" : null, - "groupeField" : "L9 B1" - } ], - "frames_warning" : 0, - "calculation" : { - "rgGuinier" : { - "std" : 0, - "sum" : 4.38278, - "avg" : 4.38278, - "validNumber" : 1, - "totalNumber" : 1, - "values" : [ "4.38278" ] - }, - "i0Guinier" : { - "std" : 0, - "sum" : 160.023229462, - "avg" : 160.023229462, - "validNumber" : 1, - "totalNumber" : 1, - "values" : [ "160.023229462" ] - }, - "quality" : { - "std" : 0, - "sum" : 0.742825, - "avg" : 0.742825, - "validNumber" : 1, - "totalNumber" : 1, - "values" : [ "0.742825" ] - }, - "rgGnom" : { - "std" : 0, - "sum" : 4.40615474861, - "avg" : 4.40615474861, - "validNumber" : 1, - "totalNumber" : 1, - "values" : [ "4.40615474861" ] - }, - "dMax" : { - "std" : 0, - "sum" : 15.33973, - "avg" : 15.33973, - "validNumber" : 1, - "totalNumber" : 1, - "values" : [ "15.33973" ] +StockSolutionGrid.prototype._getTopButtons = function() { + var _this = this; + /** Actions buttons **/ + var actions = []; + + /** ADD BUTTON **/ + if (this.btnAddVisible) { + actions.push(Ext.create('Ext.Action', { + icon : '../images/add.png', + text : 'Add Stock Solution', + tooltip : 'Will create a new stock solution', + disabled : false, + alwaysEnabled : true, + handler : function(widget, event) { + _this.edit(); + } + })); + } + + if (this.btnAddExisting) { + actions.push(Ext.create('Ext.Action', { + icon : '../images/add.png', + text : 'Add Existing', + tooltip : 'Allows to select upacked stock solutions', + disabled : false, + alwaysEnabled : true, + handler : function(widget, event) { + var stockSolutionGrid = new StockSolutionGrid({ + btnAddVisible : false, + btnEditVisible : false, + btnRemoveVisible : false, + btnAddExisting : false, + isPackedVisible : true, + multiselect : true + }); + + var window = Ext.create('Ext.window.Window', { + title : 'Select', + height : 400, + width : 800, + layout : 'fit', + items : [ stockSolutionGrid.getPanel() ], + buttons : [ { + text : 'Pack', + handler : function() { + _this.onStockSolutionSelected.notify(stockSolutionGrid.selectedStockSolutions); + window.close(); + } + }, { + text : 'Cancel', + handler : function() { + window.close(); } + } ] + + }).show(); + + stockSolutionGrid.refresh(BIOSAXS.proposal.getUnpackedStockSolutions()); + } + })); + } + + return actions; +}; + +StockSolutionGrid.prototype.refresh = function(stockSolutions) { + this.features = stockSolutions; + this.store.loadData(this._prepareData(), false); +}; + +StockSolutionGrid.prototype._prepareData = function() { + var data = []; + for ( var i = 0; i < this.features.length; i++) { + var stockSolution = this.features[i]; + stockSolution.buffer = BIOSAXS.proposal.getBufferById(stockSolution.bufferId).acronym; + if (stockSolution.macromoleculeId != null) { + stockSolution.macromolecule = BIOSAXS.proposal.getMacromoleculeById(stockSolution.macromoleculeId).acronym; + } + data.push(stockSolution); + } + return data; +}; + +StockSolutionGrid.prototype.getPanel = function() { + return this._renderGrid(); +}; + +StockSolutionGrid.prototype.edit = function(stockSolutionId) { + var _this = this; + var stockSolutionWindow = new StockSolutionWindow(); + /** On stock solution SAVED **/ + stockSolutionWindow.onSaved.attach(function(sender, stockSolution) { + _this.onProposalChanged.notify(stockSolution); + }); + stockSolutionWindow.draw(BIOSAXS.proposal.getStockSolutionById(stockSolutionId)); +}; + +StockSolutionGrid.prototype._getStoreFields = function() { + return [ { + name : 'name', + type : 'string' + }, { + name : 'stockSolutionId', + type : 'string' + }, { + name : 'macromolecule', + type : 'string' + }, { + name : 'buffer', + type : 'string' + }, { + name : 'storageTemperature', + type : 'numeric' + }, { + name : 'volume', + type : 'string' + }, { + name : 'concentration', + type : 'string' + }, { + name : 'buffer', + type : 'string' + }, { + name : 'comments', + type : 'string' + } ]; +}; + +//StockSolutionGrid.prototype.refresh = function() { +// this.proposal.onInitialized.attach(function(sender){ +// +// }); +// this.proposal.init(Ext.urlDecode(window.location.href).sessionId); +//}; + +StockSolutionGrid.prototype._renderGrid = function() { + var _this = this; + + /** Store **/ + this.store = Ext.create('Ext.data.Store', { + fields : this._getStoreFields(), //columns, + autoload : true + }); + + var filters = { + ftype : 'filters', + local : true, + filters : this.filters + }; + + var selModel = null; + + if (this.multiselect) { + selModel = Ext.create('Ext.selection.CheckboxModel', { + //multiSelect : false,//this.multiselect, + mode : 'SINGLE', + listeners : { + selectionchange : function(sm, selections) { + _this.selectedStockSolutions = []; + for ( var i = 0; i < selections.length; i++) { + _this.selectedStockSolutions.push(selections[i].raw); } - }, { - "concentration" : "1.75", - "id" : "1.75", - "bufferId" : "422.00", - "bufferAcronym" : "B1", - "rgGuinier" : [], - "frames" : [ { - "total" : "0.5538035065", - "bufferBeforeMeasurementId" : 15051, - "sampleMergeId" : 8180, - "averagedModelId" : null, - "I0" : "146.927428571", - "proposalCode" : "OPD", - "averagedModel" : null, - "bufferAfterMergeId" : 8181, - "framesCount" : "10", - "bufferBeforeMergeId" : 8179, - "averageFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_069_ave.dat", - "bufferAfterMeasurementId" : 15054, - "rgGuinier" : "4.27139", - "subtractedFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_069_sub.dat", - "firstPointUsed" : "33", - "chi2RgFilePath" : null, - "abInitioModelId" : null, - "macromoleculeId" : 649, - "code" : "_4.0_1.75", - "transmission" : "100.0", - "timeStart" : "2013-06-05 15:50:09.395824", - "bufferAcronym" : "B1", - "quality" : "0.765999", - "shapeDeterminationModelId" : null, - "expermientComments" : "[BsxCube] Generated from BsxCube", - "bufferId" : 422, - "isagregated" : "False", - "dmax" : "14.949865", - "bufferBeforeAverageFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_068_ave.dat", - "exposureTemperature" : "4.0", - "subtractionId" : 3379, - "conc" : "1.75", - "rapidShapeDeterminationModel" : null, - "experimentCreationDate" : "Jun 5, 2013 3:30:30 PM", - "lastPointUsed" : "62", - "modelListId" : null, - "framesMerge" : "6", - "rapidShapeDeterminationModelId" : null, - "bufferAfterAverageFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_070_ave.dat", - "nsdFilePath" : null, - "bufferAfterFramesMerged" : "10", - "experimentId" : 700, - "macromoleculeAcronym" : "L9", - "i0stdev" : "0.191914285714", - "sampleMeasurementId" : 15053, - "measurementComments" : "[7]", - "priorityLevelId" : 14, - "bufferBeforeFramesMerged" : "10", - "substractionCreationTime" : "Jun 5, 2013 3:52:31 PM", - "proposalNumber" : "29", - "rgGnom" : "4.28379338749", - "volume" : "356.326", - "shapeDeterminationModel" : null, - "comments" : null, - "groupeField" : "L9 B1" - } ], - "frames_warning" : 1, - "calculation" : { - "rgGuinier" : { - "std" : null, - "sum" : 0, - "avg" : 0, - "validNumber" : 0, - "totalNumber" : 0, - "values" : [] - }, - "i0Guinier" : { - "std" : null, - "sum" : 0, - "avg" : 0, - "validNumber" : 0, - "totalNumber" : 0, - "values" : [] - }, - "quality" : { - "std" : null, - "sum" : 0, - "avg" : 0, - "validNumber" : 0, - "totalNumber" : 0, - "values" : [] - }, - "rgGnom" : { - "std" : null, - "sum" : 0, - "avg" : 0, - "validNumber" : 0, - "totalNumber" : 0, - "values" : [] - }, - "dMax" : { - "std" : null, - "sum" : 0, - "avg" : 0, - "validNumber" : 0, - "totalNumber" : 0, - "values" : [] + } + } + }); + } else { + selModel = { + mode : 'SINGLE' + }; + } + + this.store.sort("stockSolutionId", "desc"); + + this.grid = Ext.create('Ext.grid.Panel', { + style : { + padding : 5 + }, + icon : '/ispyb/images/SampleHolder_24x24_01.png', + title : this.title, + height : this.height, + width : this.width, + minWidth : this.minWidth, + selModel : selModel, + store : this.store, + columns : this._getColumns(), + viewConfig : { + stripeRows : true, + listeners : { + itemdblclick : function(dataview, record, item, e) { + _this.edit(record.raw.stockSolutionId); + }, + cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + var adapter = null; + if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonUnpack') { + _this.grid.setLoading("ISPyB: Unpacking stock solution"); + adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender) { + _this.onProposalChanged.notify(); + _this.grid.setLoading(false); + }); + adapter.onError.attach(function(sender) { + _this.onProposalChanged.notify(); + _this.grid.setLoading(false); + }); + record.raw.boxId = null; + adapter.saveStockSolution(record.raw); + } + + if (grid.getGridColumns()[cellIndex].getId() == _this.id + "box") { + window.location = BUI.getShippingURL(BIOSAXS.proposal.getShipmentByDewarId(record.raw.boxId).shippingId); + } + if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonEdit') { + _this.edit(record.data.stockSolutionId); + } + + if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonRemove') { + _this.grid.setLoading("ISPyB: Removing stock solution"); + adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender) { + _this.onProposalChanged.notify(); + _this.grid.setLoading(false); + }); + adapter.onError.attach(function(sender) { + _this.onProposalChanged.notify(); + _this.grid.setLoading(false); + }); + adapter.removeStockSolution(record.data.stockSolutionId); + } + } + } + } + + }); + + var actions = _this._getTopButtons(); + this.grid.addDocked({ + xtype : 'toolbar', + items : actions + }); + + var i = null; + this.grid.getSelectionModel().on({ + selectionchange : function(sm, selections) { + if (selections.length) { + for ( i = 0; i < actions.length; i++) { + if (actions[i].enable) { + actions[i].enable(); + } + } + } else { + for ( i = 0; i < actions.length; i++) { + if (actions[i].alwaysEnabled == false) { + if (actions[i].disable) { + actions[i].disable(); + } + } + } + } + } + }); + return this.grid; +}; + +StockSolutionGrid.prototype.input = function() { + return { + proposal : DATADOC.getProposal_10() + }; +}; + +StockSolutionGrid.prototype.test = function(targetId) { + var stockSolutionGrid = new StockSolutionGrid({ + height : 300, + width : 900 + }); + BIOSAXS.proposal = new Proposal(stockSolutionGrid.input().proposal); + var panel = stockSolutionGrid.getPanel(); + stockSolutionGrid.refresh(BIOSAXS.proposal.getStockSolutions()); + panel.render(targetId); +}; + + +function SuperpositionGrid(args) { + this.height = null; + this.width = null; + this.id = BUI.id(); + + if (args != null) { + if (args.height != null) { + this.height = args.height; + + if (args.width != null) { + this.width = args.width; + } + } + } + + this.onSelected = new Event(this); +} + +SuperpositionGrid.prototype._prepareData = function(data) { + return data; +}; + +SuperpositionGrid.prototype.refresh = function(subtractions) { + var data = []; + if (subtractions != null){ + if (subtractions.length > 0){ + for (j in subtractions){ + var subtraction = subtractions[j]; + if (subtraction.superposition3VOs != null){ + for (i in subtraction.superposition3VOs){ + data.push(subtraction.superposition3VOs[i]); + } + } + } + } + } + this.store.loadData(data); +}; + +SuperpositionGrid.prototype.getPanel = function() { + var _this = this; + this.store = Ext.create('Ext.data.Store', { + fields : [ 'abinitioModelPdbFilePath', 'aprioriPdbFilePath', 'alignedPdbFilePath' ], + data : [] + }); + + this.selModel = Ext.create('Ext.selection.RowModel', { + allowDeselect : true, + mode : 'MULTI', + listeners : { + selectionchange : function(sm, selections) { + var selected = []; + for (var i = 0; i < selections.length; i++) { + selected.push(selections[i].raw); + } + _this.onSelected.notify(selected); + } + } + }); + + this.store.sort([ { + property : 'name', + direction : 'ASC' + } ]); + + this.panel = Ext.create('Ext.grid.Panel', { + store : this.store, + selModel : this.selModel, + width : this.width, + height : this.height, + margin : 10, + deferredRender : false, + columns : [ { + text : 'Abinitio', + dataIndex : 'abinitioModelPdbFilePath', + hidden : false, + flex : 1, + renderer : function(val){ + return BUI.getFileName(val); + } + }, { + text : 'Apriori', + dataIndex : 'aprioriPdbFilePath', + hidden : false, + flex : 1, + renderer : function(val){ + return BUI.getFileName(val); + } + }, { + text : 'Aligned', + dataIndex : 'alignedPdbFilePath', + hidden : false, + flex : 1, + renderer : function(val){ + return BUI.getFileName(val); + } + } ], + + viewConfig : { + enableTextSelection : true, + preserveScrollOnRefresh : true + }, + listeners : { + cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + + }, + afterrender : function() { + } + } + }); + return this.panel; +}; + +/** + * See ExperimentGrid + * + */ +function TemplateGrid(args) { + + if (args == null) { + args = {}; + } + args.sorters = [ { + property : 'experimentId', + direction : 'DESC' + } ]; + + ExperimentGrid.prototype.constructor.call(this, args); +} + +TemplateGrid.prototype._getFilterTypes = ExperimentGrid.prototype._getFilterTypes; +TemplateGrid.prototype._prettyPrintMacromolecules = ExperimentGrid.prototype._prettyPrintMacromolecules; +TemplateGrid.prototype._getPercentage = ExperimentGrid.prototype._getPercentage; +TemplateGrid.prototype._getPercentageCollected = ExperimentGrid.prototype._getPercentageCollected; +TemplateGrid.prototype.getPercentageMerged = ExperimentGrid.prototype.getPercentageMerged; +TemplateGrid.prototype._prepareData = ExperimentGrid.prototype._prepareData; +TemplateGrid.prototype.getPanel = ExperimentGrid.prototype.getPanel; +TemplateGrid.prototype._renderGrid = ExperimentGrid.prototype._renderGrid; +TemplateGrid.prototype._editExperiment = ExperimentGrid.prototype._editExperiment; +TemplateGrid.prototype._removeExperimentById = ExperimentGrid.prototype._removeExperimentById; + +TemplateGrid.prototype._getTopButtons = function() { + /** Actions buttons * */ + var actions = []; + + actions.push(Ext.create('Ext.Action', { + icon : '../images/add.png', + text : 'Add experiment', + handler : function(widget, event) { + var wizardWidget = new WizardWidget({ + windowMode : true + }); + + wizardWidget.onFinished.attach(function(sender, result) { + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, data) { + var experiment = new Experiment(data); + BIOSAXS.openExperimentByExperiment(experiment); + wizardWidget.window.close(); + }); + wizardWidget.current.setLoading("ISPyB: Creating experiment"); + adapter.createTemplate(result.name, "comments", result.data); + }); + +// wizardWidget.draw(this.targetId, new ExperimentTypeWizardForm()); + wizardWidget.draw(this.targetId, new MeasurementCreatorStepWizardForm(BIOSAXS.proposal.getMacromolecules())); + } + })); + return actions; +}; + +TemplateGrid.prototype.refresh = function(data) { + var filtered = []; + for ( var i = 0; i < data.length; i++) { + if (data[i].experimentType == "TEMPLATE") { + filtered.push(data[i]); + } + } + this.store.loadData(this._prepareData(filtered), false); +}; + +TemplateGrid.prototype._getColumns = function() { + var _this = this; + function actionItemRenderer(value, meta, record, rowIx, ColIx, store) { + if (record.raw.buffer3VOs != null) { + if (record.raw.buffer3VOs.length > 0) { + return 'x-hide-display'; + } + } + + if (record.data.platesCount > 0) { + return 'x-hide-display'; + } + } + + return [ { + xtype : 'rownumberer', + width : 40 + }, { + text : 'experimentId', + dataIndex : 'experimentId', + name : 'experimentId', + type : 'string', + hidden : true + }, { + text : 'Name', + dataIndex : 'name', + name : 'name', + type : 'string', + flex : 1 + }, + + { + text : 'Type', + dataIndex : 'type', + name : 'type', + type : 'string', + hidden : true, + flex : 1, + renderer : function(val) { + return val; + } + }, { + text : 'Macromolecules', + name : 'macromolecules_names', + dataIndex : 'macromolecules_names', + flex : 1, + renderer : function(val) { + return " " + val + ""; + } + }, { + id : _this.id + 'GO', + width : 80, + sortable : false, + renderer : function(value, metaData, record, rowIndex, colIndex, store) { + return BUI.getGreenButton('EDIT'); + } + }, { + id : _this.id + 'REMOVE', + width : 100, + sortable : false, + renderer : function(value, metaData, record, rowIndex, colIndex, store) { + return BUI.getRedButton('REMOVE'); + } + } ]; +}; + +TemplateGrid.prototype.input = function() { + var experiments = DATADOC.getExperimentList_10(); + return { + experiments : experiments, + proposal : new MeasurementGrid().input().proposal + + }; +}; + +TemplateGrid.prototype.test = function(targetId) { + var experimentGrid = new TemplateGrid({ + height : 350, + minHeight : 350, + width : 1000, + gridType : 'Ext.grid.Panel', + title : 'Experiments', + grouping : false, + tbar : true + + }); + BIOSAXS.proposal = new Proposal(experimentGrid.input().proposal); + var panel = experimentGrid.getPanel(experimentGrid.input().experiments); + experimentGrid.refresh(experimentGrid.input().experiments); + panel.render(targetId); +}; + +function VolumeGrid() { + this.id = BUI.id(); +} + +VolumeGrid.prototype.getPanel = function(experiment) { + this.experiment = experiment; + return this.render(); +}; + +VolumeGrid.prototype.getVolumesPanel = function(data, title) { + var _this = this; + var store = Ext.create('Ext.data.Store', { + fields : [ 'name', 'volume', 'macromoleculeId', 'bufferId' ], + data : data + }); + store.sort([ { + property : 'name', + direction : 'ASC' + } ]); + + var grid = Ext.create('Ext.grid.Panel', { + title : title, + height : 400, + maxHeight : 400, + width : 900, + store : store, + margin : '10 0 50 10', + tbar : [ { + text : 'Go to Shipment', + icon : '../images/plane-small.gif', + handler : function() { + window.location = BUI.getCreateShipmentList(); + } + } ], + viewConfig : { + stripeRows : true, + listeners : { + cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonCreate') { + var stockSolutionWindow = new StockSolutionWindow(); + /** On stock solution SAVED **/ + stockSolutionWindow.onSaved.attach(function(sender, stockSolution) { + BIOSAXS.proposal.onInitialized.attach(function(sender, data) { + _this.refresh(_this.experiment); + }); + BIOSAXS.proposal.init(); + }); + var acronym = "ST"; + if (record.raw.macromoleculeId != null) { + acronym = acronym + "_" + BIOSAXS.proposal.getMacromoleculeById(record.raw.macromoleculeId).acronym; + } + if (record.raw.bufferId != null) { + acronym = acronym + "_" + BIOSAXS.proposal.getBufferById(record.raw.bufferId).acronym; } + stockSolutionWindow.draw({ + concentration : record.raw.concentration, + macromoleculeId : record.raw.macromoleculeId, + bufferId : record.raw.bufferId, + name : acronym, + volume : record.raw.volume + }); } - }, { - "concentration" : "0.91", - "id" : "0.91000000000000003", - "bufferId" : "422.00", - "bufferAcronym" : "B1", - "rgGuinier" : [], - "frames" : [ { - "total" : null, - "bufferBeforeMeasurementId" : 15054, - "sampleMergeId" : 8182, - "averagedModelId" : null, - "I0" : "0.0", - "proposalCode" : "OPD", - "averagedModel" : null, - "bufferAfterMergeId" : 8183, - "framesCount" : "10", - "bufferBeforeMergeId" : 8181, - "averageFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_071_ave.dat", - "bufferAfterMeasurementId" : 15057, - "rgGuinier" : null, - "subtractedFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_071_sub.dat", - "firstPointUsed" : "0", - "chi2RgFilePath" : null, - "abInitioModelId" : null, - "macromoleculeId" : 649, - "code" : "_4.0_.91", - "transmission" : "100.0", - "timeStart" : "2013-06-05 15:52:58.133705", - "bufferAcronym" : "B1", - "quality" : "0.0", - "shapeDeterminationModelId" : null, - "expermientComments" : "[BsxCube] Generated from BsxCube", - "bufferId" : 422, - "isagregated" : "False", - "dmax" : null, - "bufferBeforeAverageFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_070_ave.dat", - "exposureTemperature" : "4.0", - "subtractionId" : 3380, - "conc" : "0.91000000000000003", - "rapidShapeDeterminationModel" : null, - "experimentCreationDate" : "Jun 5, 2013 3:30:30 PM", - "lastPointUsed" : "0", - "modelListId" : null, - "framesMerge" : "10", - "rapidShapeDeterminationModelId" : null, - "bufferAfterAverageFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_072_ave.dat", - "nsdFilePath" : null, - "bufferAfterFramesMerged" : "10", - "experimentId" : 700, - "macromoleculeAcronym" : "L9", - "i0stdev" : "0.0", - "sampleMeasurementId" : 15056, - "measurementComments" : "[8]", - "priorityLevelId" : 16, - "bufferBeforeFramesMerged" : "10", - "substractionCreationTime" : "Jun 5, 2013 3:55:35 PM", - "proposalNumber" : "29", - "rgGnom" : null, - "volume" : null, - "shapeDeterminationModel" : null, - "comments" : null, - "groupeField" : "L9 B1" - } ], - "frames_warning" : 1, - "calculation" : { - "rgGuinier" : { - "std" : null, - "sum" : 0, - "avg" : 0, - "validNumber" : 0, - "totalNumber" : 0, - "values" : [] - }, - "i0Guinier" : { - "std" : null, - "sum" : 0, - "avg" : 0, - "validNumber" : 0, - "totalNumber" : 0, - "values" : [] + + if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonStockSolutions') { + var stockSolutionGrid = new StockSolutionGrid({ + btnAddVisible : false, + btnEditVisible : false, + btnRemoveVisible : false, + btnAddExisting : false, + isPackedVisible : true, + multiselect : false + }); + + var window = Ext.create('Ext.window.Window', { + title : 'Stock solutions by specimen', + height : 400, + width : 800, + layout : 'fit', + items : [ stockSolutionGrid.getPanel() ], + buttons : [ { + text : 'Close', + handler : function() { + window.close(); + } + } ] + + }).show(); + stockSolutionGrid.refresh(BIOSAXS.proposal.getStockSolutionsBySpecimen(record.raw.macromoleculeId, record.raw.bufferId)); + } + } + } + }, + columns : [ +// { +// text : '', +// dataIndex : 'macromoleculeId', +// width : 20, +// renderer : function(val, y, sample) { +// if (val != null) { +//// return BUI.getRectangleColorDIV(BIOSAXS.proposal.macromoleculeColors[val], 10, 10); +// return BUI.getRectangleColorDIV(_this.experiment.macromoleculeColors[val], 10, 10); +// } +// } +// }, +// { +// text : '', +// dataIndex : 'bufferId', +// width : 20, +// renderer : function(val, y, sample) { +// if (val != null) { +// return BUI.getRectangleColorDIV(_this.experiment.getSpecimenColorByBufferId(val), 10, 10); +// } +// } +// }, + { + text : 'Specimen', + dataIndex : 'name', + flex : 0.5 + }, + { + text : 'Estimated Volume', + dataIndex : 'volume', + tooltip : 'Estimation of the maximum volume needed for making this experiment', + flex : 0.5, + editor : { + allowBlank : true + }, + renderer : function(val, y, sample) { + return BUI.formatValuesUnits(sample.data.volume, 'µl', { + fontSize : 16, + decimals : 2, + unitsFontSize : this.unitsFontSize + }); + } + }, + { + text : 'Stock Solution', + id : _this.id + 'buttonStockSolutions', + dataIndex : 'name', + flex : 0.5, + tooltip : 'Stock Solutions containing this specimen in this proposal', + renderer : function(value, metaData, record, rowIndex, colIndex, store) { + var macromoleculeId = record.raw.macromoleculeId; + var bufferId = record.raw.bufferId; + var stockSolutions = BIOSAXS.proposal.getStockSolutionsBySpecimen(macromoleculeId, bufferId); + if (stockSolutions.length > 0) { + return "
" + stockSolutions.length + " x
"; + } + + } + }, { + id : _this.id + 'buttonCreate', + text : '', + tooltip : 'Create a new stock solution for shipping', + width : 170, + sortable : false, + renderer : function(value, metaData, record, rowIndex, colIndex, store) { + return BUI.getGreenButton('NEW STOCK SOLUTION', { + width : 160 + }); + } + } ] + }); + return grid; + +}; + +VolumeGrid.prototype._prepareData = function(experiment) { + var keys = {}; + for ( var i = 0; i < experiment.getSamples().length; i++) { + var sample = experiment.getSamples()[i]; + var key = ""; + if (sample.macromoleculeId == null) { + key = experiment.getBufferById(sample.bufferId).acronym; + if (keys[key] == null) { + keys[key] = { + macromoleculeId : sample.macromoleculeId, + name : key, + bufferId : sample.bufferId, + volume : 0 + }; + } + keys[key].volume = Number(sample.volume) + Number(keys[key].volume); + } + + if ((sample.macromolecule3VO != null) || (sample.macromoleculeId != null)) { + macromoleculeId = sample.macromoleculeId; + if (sample.macromoleculeId == null) { + sample.macromoleculeId = sample.macromolecule3VO.macromoleculeId; + } + key = BIOSAXS.proposal.getMacromoleculeById(sample.macromoleculeId).acronym + " + " + experiment.getBufferById(sample.bufferId).acronym; + if (keys[key] == null) { + keys[key] = { + macromoleculeId : sample.macromoleculeId, + name : key, + bufferId : sample.bufferId, + volume : 0 + }; + } + keys[key].volume = Number(sample.volume) + Number(keys[key].volume); + } + } + var data = []; + for (var keyId in keys) { + data.push(keys[keyId]); + } + + return data; +}; + +VolumeGrid.prototype.refresh = function(experiment) { + this.experiment = experiment; + this.macromoleculeGrid.getStore().loadData(this._prepareData(this.experiment), false); +}; + +VolumeGrid.prototype.render = function() { + this.macromoleculeGrid = this.getVolumesPanel(this._prepareData(this.experiment), "Estimation of required Volume"); + + return { + xtype : 'container', + layout : 'vbox', + margin : "0, 0, 0, 5", + items : [ { + xtype : 'container', + layout : 'hbox', + margin : "0, 0, 0, 0", + items : [ this.macromoleculeGrid ] + } ] + }; +}; + +VolumeGrid.prototype.input = function(experiment) { + return { + experiment : DATADOC.getExperiment_10() + }; +}; + +VolumeGrid.prototype.test = function(targetId) { + var volumeGrid = new VolumeGrid(); + BIOSAXS.proposal = new Proposal(new MeasurementGrid().input().proposal); + var panel = volumeGrid.getPanel(new Experiment(new VolumeGrid().input().experiment)); + Ext.create('Ext.panel.Panel', { + height : 500, + width : 1000, + renderTo : targetId, + items : [ panel ] + }); +}; + +/** + * Example of a tab panel to be populated with widgets + * + * @width width in pixels + * @height height in pixels + * + * #myEvent event that this class is supposed to throw + * + **/ +function ExampleTabs(args) { + this.width = 500; + this.height = 500; + + if (args != null){ + if (args.width != null){ + this.width= args.width; + } + if (args.height != null){ + this.height= args.height; + } + } + + /** Events **/ + this.myEvent = new Event(); +} + +/** Populate the widget **/ +ExampleTabs.prototype.refresh = function(macromolecule) { + +}; + +/** It creates a tab panel with the specified with and height **/ +ExampleTabs.prototype.getPanel = function() { + this.panel = Ext.createWidget('tabpanel', { + height : this.height, + width : this.width, + style : { + padding : 2 + }, + items : [ { + tabConfig : { + title : 'Test', + icon : '/ispyb/images/plane-small.gif' + }, + items : [ { + xtype : 'container', + margin : '5 5 5 5', + items : [ ] + } ] + } ] + }); + return this.panel; +}; + + +/** + * Shows an experiments with the specimens, measurements, analysis tabs where + * results are shown and the frames widget + * + * @targetId + */ +function ExperimentTabs(targetId) { + this.height = 900; + this.targetId = targetId; + + this.id = BUI.id(); + var _this = this; + + this.INTERVAL_UPDATE = BUI.getUpdateInterval(); + + this.gridHeight = 1000; + /** data * */ + this.experiment = null; + + + /** Specimen Widget contains a specimenGrid and a sampleChangerWidget than can be displayed with are vertical or horizontal layout **/ + this.specimenWidget = new SpecimenWidget({ + height : 600 + }); + + + /** For Overview * */ + /*this.samplePlateGroupWidget = new SamplePlateGroupWidget({ + showTitle : false, + height : 250, + margin : 5, + border : 0 + + });*/ + + /** For Measurements * */ + /*this.measurementSamplePlateGroupWidget = new SamplePlateGroupWidget({ + showTitle : false, + height : 250, + margin : '5 0 0 0', + border : 0 + });*/ + + this.measurementGridDone = new MeasurementGrid({ + height : 600, + minHeight : 400, + maxHeight : 800, + positionColumnsHidden : true, + showTitle : false, + estimateTime : true, + width : 900, + maxWidth : 1500, + addBtnEnable : false, + markDone : true, + removeBtnEnabled : false + }); + this.measurementGridDone.onSelected.attach(function(sender, measurements) { + var specimens = []; + for ( var i = 0; i < measurements.length; i++) { + specimens.push(_this.experiment.getSampleById(measurements[i].specimenId)); + } + //_this.measurementSamplePlateGroupWidget.selectSpecimens(specimens); + }); + + /** AnalysisGrid * */ + this.analysisGrid = new AnalysisGrid({ + height : Ext.getBody().getViewSize().height * 0.9 - 300, + positionColumnsHidden : true, + sorters : [ { + property : 'priorityLevelId', + direction : 'ASC' + } ] + }); + + + /** Queue * */ + this.queueGrid = new QueueGrid({ + url : BUI.getQueueUrlByExperiment(), + height : Ext.getBody().getViewSize().height * 0.9 - 300, + width : Ext.getBody().getViewSize().width * 0.9 - 300, + isGrouped : true, + tbar : false, + bbar : false, + sorter : [{ + property: 'measurementId', + direction: 'DESC' + }] + }); + +} + +ExperimentTabs.prototype.error = function(error) { + var e = JSON.parse(error); + showError(e); + this.panel.setLoading(false); +}; + +ExperimentTabs.prototype.draw = function(experiment) { + this.renderDataAcquisition(experiment); +}; + +ExperimentTabs.prototype.refreshAnalysisData = function() { + var _this = this; + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, data) { + _this.analysisGrid.refresh(data, {experiment : _this.experiment}); + }); + adapter.getAnalysisInformationByExperimentId(this.experiment.experimentId); + + /** Auto load **/ + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, data) { + _this.queueGrid.refresh(data); + }); + adapter.getCompactAnalysisByExperimentId(this.experiment.experimentId); + this.queueGrid.store.proxy.url = BUI.getQueueUrlByExperiment(this.experiment.experimentId); + this.queueGrid.refresh(); +}; + +ExperimentTabs.prototype.getSpecimenContainerHeight = function(experiment) { + var maxItems = 0; + if (maxItems < experiment.getSamples().length + 1) { + maxItems = experiment.getSamples().length + 1; + } + + var height = (maxItems + 1) * 40 + 40; + if (height > 400) { + height = 400; + } + return height; +}; + +ExperimentTabs.prototype.getExperimentTitle = function() { + var experimentHeaderForm = new ExperimentHeaderForm(); + return experimentHeaderForm.getPanel(this.experiment); +}; + +ExperimentTabs.prototype.renderDataAcquisition = function(experiment) { + var _this = this; + this.experiment = experiment; + + var specimenGrid = new SpecimenGrid({ + height : 400, + maxHeight : 500, + width : 890 + }); + + specimenGrid.onClick.attach(function(sender, args) { + }); + + specimenGrid.onSelected.attach(function(sender, specimens) { + //_this.samplePlateGroupWidget.selectSpecimens(specimens); + }); + var specimenContainer = this.specimenWidget.getPanel(); + this.specimenWidget.refresh(experiment); + /* + var specimenContainer = Ext.create('Ext.container.Container', { + layout : 'hbox', + width : 900, + padding : '10 0 0 2', + items : [ specimenGrid.getPanel() ] + }); + + specimenGrid.refresh(experiment);*/ + + var experimentList = new ExperimentList([ _this.experiment ]); + var measurementContainer = Ext.create('Ext.container.Container', { + layout : 'vbox', + padding : '5px 0px 0px 10px', + items : [] + }); + measurementContainer.insert(0, _this.measurementGridDone.getPanel(this.experiment.getMeasurements(), experimentList)); +// measurementContainer.insert(1, _this.measurementSamplePlateGroupWidget.getPanel(experiment)); + + // this.dataCollectionCurveVisualizer = new DataCollectionCurveVisualizer(); + // this.dataCollectionCurveVisualizer.experiments = [experiment]; + // this.dataCollectionCurveVisualizer.dataCollectionFrameTree.experiments = + // [experiment]; + // this.dataCollectionCurveVisualizer.dataCollections = + // experiment.getDataCollections(); + + this.panel = Ext.createWidget('tabpanel', { + plain : true, + style : { + padding : 2 + }, + items : [ { + tabConfig : { + id : 'genralTabl', + title : "Overview" + }, + items : [ specimenContainer ] + }, { + tabConfig : { + title : 'Measurements' + }, + items : [ measurementContainer] + }, { + tabConfig : { + id : 'SpecimenTab', + title : 'Analysis', + hidden : this.isTemplate() + }, + items : [ { + xtype : 'container', + layout : 'vbox', + padding : '5px 0px 10px 10px', + items : [ _this.analysisGrid.getPanel([]) ] + } ] + }, + { + tabConfig : { + id : 'newAnalysisTab', + title : 'Analysis BETA', + hidden : this.isTemplate() + }, + items : [ { + xtype : 'container', + layout : 'vbox', + padding : '5px 0px 10px 10px', + items : [ _this.queueGrid.getPanel([]) ] + } ] + } + ] + }); + + return this.getPanel(this.panel); +}; + +ExperimentTabs.prototype.isTemplate = function() { + if (this.experiment.json.type == "TEMPLATE") { + return true; + } + return false; +}; + +ExperimentTabs.prototype.update = function() { + var _this = this; + var inter; + if (!_this.isTemplate()) { + function updateExperiments() { + _this.refreshAnalysisData(); + window.clearInterval(inter); + inter = setInterval(function() { + updateExperiments(); + }, _this.INTERVAL_UPDATE); + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, data) { + var experiment = new Experiment(data); + _this.measurementGridDone.refresh(experiment.getMeasurements(), new ExperimentList([ experiment ])); + }); + adapter.getExperimentById(_this.experiment.json.experimentId, "MEDIUM"); + } + inter = setInterval(function() { + updateExperiments(); + }, _this.INTERVAL_UPDATE); + } +}; + +ExperimentTabs.prototype.getPanel = function(panel) { + var _this = this; + if (this.experimentPanel == null) { + this.experimentPanel = Ext.create('Ext.container.Container', { + bodyPadding : 2, + width : Ext.getBody().getViewSize().width * 0.9, + renderTo : this.targetId, + style : { + padding : 2 + }, + items : [ this.getExperimentTitle(), this.panel ], + listeners : { + afterrender : function() { + _this.refreshAnalysisData(); + _this.update(); + } + } + }); + } + + return this.experimentPanel; +}; + +ExperimentTabs.prototype.input = function() { + return { + experiment : DATADOC.getExperiment_10(), + proposal : DATADOC.getProposal_10() + }; +}; + +ExperimentTabs.prototype.test = function(targetId) { + var experimentTabs = new ExperimentTabs(targetId); + BIOSAXS.proposal = new Proposal(experimentTabs.input().proposal); + experimentTabs.draw(new Experiment(experimentTabs.input().experiment)); +}; + + + +/** + * Shows an experiments with the specimens, measurements, analysis tabs where results are shown and the frames widget + * + * @targetId + */ +function HPLCTabs(targetId) { + this.height = 1400; + this.targetId = targetId; + + this.id = BUI.id(); + + this.gridHeight = 600; + this.pointsCount = 1036; + this.plotHeight = 350; + this.plotWidth = 800; + this.plotPanelPadding = 5; + + var _this = this; + + this.analysisGrid = new HPLCAnalysisGrid({ + height : Ext.getBody().getViewSize().height * 0.9 - 300, + positionColumnsHidden : true, + sorters : [ { + property : 'priorityLevelId', + direction : 'ASC' + } ] + }); + + this.frameGrid = new FrameGrid({ + width : 600, + height : 60, + collapsed : false, + tbar : false + }); + + this.peakGrid = new PeakGrid({ + width : 550, + height : 500, + collapsed : false, + tbar : false + }); + + this.peakGrid2 = new PeakGrid({ + width : 102, + height : this.plotHeight, + collapsed : false, + tbar : false, + showExtendedColumns : true + }); + + this.mainPlotPanel = new HPLCGraph({ + title : 'I0', + width : this.plotWidth - 110, + height : this.plotHeight, + bbar : true, + plots : { + "I0" : true, + "Rg" : true + }, + xlabel : "HPLC Frames", + scaled : true, + interactionModel : { + 'dblclick' : function(event, g, context) { + _this._selectFrame(g.lastx_); + var annotations = []; + annotations.push({ + series : g.selPoints_[0].name, + x : g.lastx_, + width : 100, + height : 23, + tickHeight : 4, + shortText : g.lastx_, + text : g.lastx_, + attachAtBottom : true + }); + g.setAnnotations(annotations); + + } + } + }); + + this.intensityPlotPanel = new MergesHPLCGraph({ + title : 'Scattering', + width : this.plotWidth, + height : 500, + showRangeSelector : false, + xParam : 0, + xlabel : "scattering_I", + plots : { + "scattering_I" : true, + "subtracted_I" : true, + "buffer_I" : true + } + }); + +} + +HPLCTabs.prototype._selectFrame = function(frameNumber) { + try{ + this._renderScatteringCurve(frameNumber); + this.frameGrid.refresh([this.mainPlotPanel.getDataByFrameNumber(frameNumber)], this.experiment.experimentId); + } + catch(e){ + console.log(e); + } +}; + +HPLCTabs.prototype.error = function(error) { + var e = JSON.parse(error); + showError(e); + this.panel.setLoading(false); +}; + +HPLCTabs.prototype.loadDataMainPlot = function(data) { + var zeroArray = []; + for ( var i = 0; i < data.I0.length; i++) { + zeroArray.push(0); + } + data = [ { + param : "I0", + data : data.I0, + std : data.I0_Stdev, + color : '#0066CC', + label : "I0" + }, + { + param : "sum_I", + label : "sum_I", + color : "#00FF00", + data : data.sum_I, + std : zeroArray + }, + { + param : "Rg", + label : "Rg", + color : "#21610B", + data : data.Rg, + std : data.Rg_Stdev + }, { + param : "Mass", + data : data.mass, + std : data.mass_Stdev, + color : '#FF9900', + label : "Mass" + }, { + param : "Vc", + data : data.Vc, + std : data.Vc_Stdev, + color : '#990099', + label : "Vc" + }, { + param : "Qr", + data : data.Qr, + std : data.Qr_Stdev, + color : '#FF0066', + label : "Qr" + }, { + param : "quality", + label : "quality", + color : "#FF00FF", + data : data.quality, + std : zeroArray + } ]; + this.data = data; + this.mainPlotPanel.loadData(data); +}; + +HPLCTabs.prototype._loadIntensityPlotByFrameNumber = function(frameNumber) { + var _this = this; + + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, data) { + var array = []; + data = [ { + param : "q", + data : data[frameNumber].q, + std : data[frameNumber].q, + fstd : function(a) { + return parseFloat(a); + }, + color : '#green', + label : "q", + showOnMenu : false + }, + { + param : "scattering_I", + data : data[frameNumber].scattering_I, + fdata : function(a) { + return Math.log(parseFloat(a)); + }, + std : data[frameNumber].scattering_Stdev, + fstd : function(a) { + return Math.log(Math.abs(parseFloat(a))); + }, + color : 'green', + label : "Log(I) Sample" + }, + { + param : "buffer_I", + data : data[frameNumber].buffer_I, + fdata : function(a) { + return Math.log(parseFloat(a)); + }, + fstd : function(a) { + return Math.log(Math.abs(parseFloat(a))); + }, + std : data[frameNumber].subtracted_Stdev, + color : '#0000FF', + label : "Log(I) Avg Buf." + }, + { + param : "subtracted_I", + data : data[frameNumber].subtracted_I, + fdata : function(a) { + var value = (Math.log(parseFloat(a))); + if (isNumber(value)) + return value; + }, + fstd : function(a) { + var value = Math.log(Math.abs(parseFloat(a))); + if (isNumber(value)) + return value; + }, + std : data[frameNumber].subtracted_Stdev, + color : 'red', + label : "Log(I) Sub." + } + ]; + + _this.intensityPlotPanel.xlabel = "Frame " + frameNumber + " (q, nm-1)"; + + if (_this.intensityPlotPanel.hplcData == null) { + /** It creates also top bar **/ + _this.intensityPlotPanel.loadData(data); + } else { + _this.intensityPlotPanel.reloadData(data); + + } + _this.intensityPlotPanel.panel.setLoading(false); + }); + adapter.onError.attach(function(sender, data) { + _this.intensityPlotPanel.panel.setLoading(false); + }); + this.intensityPlotPanel.panel.setLoading("Retrieving Frame " + frameNumber); + adapter.getH5FrameScattering(this.experiment.json.experimentId, frameNumber); +}; + +HPLCTabs.prototype._renderScatteringCurve = function(frameNumber) { + var _this = this; + + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, data) { + _this.intensityPlotPanel.setPeaks(data); + _this._loadIntensityPlotByFrameNumber(frameNumber); + try{ + var peaks = []; + for(key in data){ + peaks.push({ + start : key.split("-")[0], + end : key.split("-")[1], + experimentId : _this.experiment.json.experimentId + }); + } + _this.peakGrid2.refresh(JSON.parse(JSON.stringify(peaks))); + _this.peakGrid.refresh(peaks); +// console.log(peaks); + } + catch(e){ + showError(e); + } + }); + adapter.onError.attach(function(sender, data) { + _this.intensityPlotPanel.setLoading(false); + }); + this.intensityPlotPanel.panel.setLoading("Reading HDF5 File "); + adapter.getH5FramesMerge(this.experiment.json.experimentId); +}; + +HPLCTabs.prototype._postRenderOverviewPanel = function() { + var _this = this; + var adapter = new BiosaxsDataAdapter(); + adapter.onError.attach(function(sender, data) { + data = data.replace(/NaN/g, '0'); + _this.loadDataMainPlot(JSON.parse(data)); + + + }); + adapter.onSuccess.attach(function(sender, data) { + _this.loadDataMainPlot(data); + _this._renderScatteringCurve(0); + }); + adapter.getH5fileParameters(this.experiment.json.experimentId, [ + "I0", "I0_Stdev", "sum_I","Rg", "Rg_Stdev", "Vc", "Vc_Stdev", "Qr", "Qr_Stdev", "mass", "mass_Stdev", "quality" ]); + +}; + +HPLCTabs.prototype._postRenderDetailsPanel = function() { + if (this.data != null) { + this.I0PlotPanel.loadData(this.data); + this.RgPlotPanel.loadData(this.data); + this.MassPlotPanel.loadData(this.data); + this.VcPlotPanel.loadData(this.data); + } + +}; + +HPLCTabs.prototype.draw = function(experiment) { + var _this = this; + this.renderDataAcquisition(experiment); +}; + +HPLCTabs.prototype.getExperimentTitle = function() { + var experimentHeaderForm = new ExperimentHeaderForm(); + return experimentHeaderForm.getPanel(this.experiment); +}; + +HPLCTabs.prototype.renderDataAcquisition = function(experiment) { + var _this = this; + this.experiment = experiment; + if (this.experimentPanel == null) { + this.experimentPanel = Ext.create('Ext.container.Container', { + bodyPadding : 2, + height : this.height, + width : Ext.getBody().getViewSize().width * 0.9, + renderTo : this.targetId, + style : { + padding : 2 + }, + items : [ this.getExperimentTitle(), this.getPanel() ], + listeners : { + afterrender : function() { + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, data) { + _this.analysisGrid.refresh(data); + }); + adapter.getAnalysisInformationByExperimentId(_this.experiment.experimentId); + } + } + }); + } + return this.experimentPanel; +}; + +HPLCTabs.prototype.getPanel = function() { + var _this = this; + this.panel = Ext.createWidget('tabpanel', + { + plain : true, + height : this.height, +// width : 900, + style : { + padding : 2 + }, + items : [ + { + tabConfig : { + title : "Overview" + }, - "quality" : { - "std" : null, - "sum" : 0, - "avg" : 0, - "validNumber" : 0, - "totalNumber" : 0, - "values" : [] + items : [ { + xtype : 'container', + layout : 'hbox', + border : 1, + flex : 1, + items : [ { + xtype : 'container', + layout : 'vbox', + border : 1, + items : [ { + xtype : 'container', + margin : '10px', + border : 1, + items : [ + { + xtype : 'container', + margin : '0px', + layout : 'hbox', + border : 1, + items : [ + this.peakGrid2.getPanel([]), + this.mainPlotPanel.getPanel() + ] + }, + { + xtype : 'container', + layout : 'vbox', + border : 1, + items : [ + { + html: '
Select a frame by double-clicking on the HPLC Frames plot
', + margin: 5, + border : 0 + }, + this.frameGrid.getPanel([]), + this.intensityPlotPanel.getPanel() + ] + } + + ] + }], + listeners : { + afterrender : function() { + _this._postRenderOverviewPanel(); + } + } + } ] + } ] + }, +// { +// tabConfig : { +// title : "Details" +// }, +// items : [ { +// xtype : 'container', +// layout : 'hbox', +// border : 1, +// flex : 1, +// items : [ { +// xtype : 'container', +// layout : 'vbox', +// border : 1, +// items : [ { +// xtype : 'container', +// padding : '1px', +// items : [ +// this.I0PlotPanel.getPanel(), this.RgPlotPanel.getPanel(), this.MassPlotPanel.getPanel(), +// this.VcPlotPanel.getPanel() ] +// } ], +// listeners : { +// afterrender : function() { +// _this._postRenderDetailsPanel(); +// } +// } +// } ] +// } ] +// }, + { + tabConfig : { + title : "Analysis" }, - "rgGnom" : { - "std" : null, - "sum" : 0, - "avg" : 0, - "validNumber" : 0, - "totalNumber" : 0, - "values" : [] + items : [ { + xtype : 'container', + layout : 'vbox', + padding : '5px 0px 10px 10px', + items : [ _this.analysisGrid.getPanel([]) ] + } ] + }, + { + tabConfig : { + title : "File Manager" }, - "dMax" : { - "std" : null, - "sum" : 0, - "avg" : 0, - "validNumber" : 0, - "totalNumber" : 0, - "values" : [] + items : [ { + xtype : 'container', + layout : 'vbox', + padding : '5px 0px 10px 10px', + items : [ + { + html: '
Custom Download
', + margin : '10 0 10 5', + border : 0 + }, + { + html: '
Download has been temporary disabled. Contact your labcontact if you want to extract the frames from the HDF5 file
', + margin : '10 0 10 5', + hidden : _this.experiment.experimentId == 2602, + border : 0 + }, + { + xtype : 'container', + layout : 'hbox', + margin : '0 0 0 20', + flex : 1, + items :[ + { + xtype: 'numberfield', + id: 'field_start', + fieldLabel: 'Frames from', + value: 0, + minValue: 0 + }, + { + xtype: 'numberfield', + id: 'field_end', + fieldLabel: 'to', + labelWidth : 20, + margin : '0 0 0 10', + minValue: 0 + }, + { + xtype: 'button', + /** allowing test account to download frames **/ + disabled : _this.experiment.experimentId != 2602, + text : 'Download', + margin : '0 0 0 10', + handler : function() { + var experimentId = _this.experiment.experimentId; + var start = Ext.getCmp("field_start").getValue(); + var end = Ext.getCmp("field_end").getValue(); + /** Checking if start is bigger than end **/ + if (start > end){ + var aux = end; + end = start; + start = aux; + } + var url = BUI.getZipFrameHPLCUrl(experimentId, start, end); + window.open(url) + } + } + ] + }, + _this.peakGrid.getPanel([]) + + ] + } + ] + } + ] + }); + + return this.panel; +}; + +/** + * Main form tab for macromolecule + * + * @width + * @height + * + * #onClose when user clicks on close button in the MacromoleculeForm + * #onSave when macromole is saved by macromoleculeForm + */ +function MacromoleculeTabs(args) { + this.width = 500; + this.height = 500; + + if (args != null) { + if (args.width != null) { + this.width = args.width; + } + if (args.height != null) { + this.height = args.height; + } + } + + var _this = this; + + /** Widgets **/ + + /** Macromolecule Form **/ + this.macromoleculeForm = new MacromoleculeForm({ + width : this.width - 30, + height : this.height - 50, + }); + + this.macromoleculeForm.onClose.attach(function(sender) { + _this.onClose.notify(); + }); + + this.macromoleculeForm.onSave.attach(function(sender, macromolecule) { + _this.onSave.notify(macromolecule); + }); + + this.assemblyForm = new AssemblyForm({ + width : this.width - 30, + height : this.height - 50, + }); + + this.rigibBodyModelingForm = new RigibBodyModelingForm({ + width : this.width - 30, + height : this.height - 50, + }); + + this.rigibBodyModelingForm.onSave.attach(function(sender, macromolecule) { + _this.onSave.notify(macromolecule); + }); + + + /** Events **/ + this.onClose = new Event(this); + this.onSave = new Event(this); +} + +/** Populate the widget **/ +MacromoleculeTabs.prototype.refresh = function(macromolecule) { + this.macromoleculeForm.refresh(macromolecule); + this.assemblyForm.refresh(macromolecule); + this.rigibBodyModelingForm.refresh(macromolecule); + + if (macromolecule != null){ + if (macromolecule.macromoleculeId == null){ + Ext.getCmp(this.id + "_advancedTab").disable(); + Ext.getCmp(this.id + "_assemblyTab").disable(); + } + else{ + Ext.getCmp(this.id + "_advancedTab").enable(); + Ext.getCmp(this.id + "_assemblyTab").enable(); + } + } + else{ + Ext.getCmp(this.id + "_advancedTab").disable(); + Ext.getCmp(this.id + "_assemblyTab").disable(); + } +}; + +MacromoleculeTabs.prototype.getItems = function() { + return [ { + tabConfig : { + title : 'General' + }, + items : [ { + xtype : 'container', + items : [ this.macromoleculeForm.getPanel() ] + } ] + }, { + id : this.id + "_assemblyTab", + tabConfig : { + title : 'Assembly' + }, + items : [ { + xtype : 'container', + items : [ this.assemblyForm.getPanel() ] + } ] + },{ + id : this.id + "_advancedTab", + tabConfig : { + title : 'Advanced' + }, + items : [ this.rigibBodyModelingForm.getPanel() ] + } ]; +}; + +MacromoleculeTabs.prototype.getPanel = function() { + this.panel = Ext.createWidget('tabpanel', { + height : this.height, + width : this.width, + plain : true, + margin : 5, + border : 0, + items : this.getItems() + }); + return this.panel; +}; + +function ResultTabs() { +} + +ResultTabs.prototype.draw = function(targetId, data, macromoleculeId) { + var panel = this.getPanel(targetId, data, macromoleculeId); + return Ext.create('Ext.container.Container', { + renderTo : targetId, + items : [ BUI.getMacromoleculeHeader(macromoleculeId), panel ] + }); + +}; + +ResultTabs.prototype._splitBySpecimen = function(data) { + var splitted = {}; + + for ( var i = 0; i < data.length; i++) { + var row = data[i]; + if (splitted[row.macromoleculeId + "_" + row.bufferId] == null) { + splitted[row.macromoleculeId + "_" + row.bufferId] = []; + } + splitted[row.macromoleculeId + "_" + row.bufferId].push(row); + } + return splitted; + +}; + +ResultTabs.prototype._getTabTitle = function(key, data) { + var macromoleculeId = key.split("_")[0]; + var bufferId = key.split("_")[1]; + + return BIOSAXS.proposal.getMacromoleculeById(macromoleculeId).acronym + " + " + BIOSAXS.proposal.getBufferById(bufferId).acronym + "(" + data.length + ")"; +}; + +ResultTabs.prototype.getPanel = function(targetId, data, macromoleculeId) { + + var dataFiltered = new AnalysisGrid({ + hideNulls : true, + grouped : true, + sorters : [ { + property : 'quality', + direction : 'DESC' + } ] + })._prepareData(data); + + var items = [ { + tabConfig : { + title : 'Analysis (' + dataFiltered.length + ')' + }, + items : [ { + xtype : 'container', + layout : 'vbox', + padding : 10, + items : [ new AnalysisGrid({ + isScatteringPlotVisible : false + }).getPanel(dataFiltered) ] + } ] + }, { + tabConfig : { + title : 'Concentration Effects' + }, + items : [ new ResultSummaryForm().getPanel(data) ] + } ]; + + var splitted = this._splitBySpecimen(dataFiltered); + for ( var key in splitted) { + items.push({ + tabConfig : { + title : this._getTabTitle(key, splitted[key]) + }, + items : [ new ResultSummaryForm().getPanel(splitted[key]) ] + }); + } + + this.panel = Ext.createWidget('tabpanel', { + style : { + padding : 2 + }, + items : items + }); + return this.panel; + +}; + + +function ShipmentTabs(targetId) { + this.targetId = targetId; + + var _this = this; + this.gridHeight = 500; + /** data **/ + this.shipment = null; + + /** Shipment Form **/ + this.shipmentForm = new ShipmentForm({ + creationMode : false, + showTitle : false + }); + this.shipmentForm.onSaved.attach(function(sender, data) { + _this.refresh(data); + + }); + + + /** Cases grid **/ + this.caseGrid = new CaseGrid({ + height : this.gridHeight + }); + + this.caseGrid.onAddButtonClicked.attach(function(sender, dewar) { + _this.caseGrid.grid.setLoading("ISPyB: Creating a new case"); + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, shipment) { + /** updateing shipment on proposal **/ + for ( var i = 0; i < BIOSAXS.proposal.shippings.length; i++) { + if (BIOSAXS.proposal.shippings[i].shippingId == shipment.shippingId) { + BIOSAXS.proposal.shippings[i] = shipment; + } + } + _this.refresh(shipment); + }); + adapter.onError.attach(function(sender, shipment) { + _this.caseGrid.grid.setLoading(false); + }); + adapter.addCase(_this.shipment.json.shippingId); + }); + + this.caseGrid.onRemoveButtonClicked.attach(function(sender, dewarId) { + _this.panel.setLoading("ISPyB: removing case"); + _this.shipment.onSaved.attach(function(sender, shipment) { + _this.refresh(shipment); + + }); + _this.shipment.removeCase(dewarId); + }); +} + +ShipmentTabs.prototype.refresh = function(shipment) { + + var _this = this; + this.shipment = shipment; + var proposal = new Proposal(); + proposal.onDataRetrieved.attach(function(sender, plates) { + + _this.refreshWithPlates(shipment, plates); + _this.caseGrid.grid.setLoading(false); + }); + proposal.getPlatesByProposal(); +}; + +ShipmentTabs.prototype.refreshWithPlates = function(shipment, plates) { + this.shipment = new Shipment(shipment); + + this.caseGrid.refresh(this.shipment.getDewars(), plates); + this.panel.setLoading(false); +}; + +ShipmentTabs.prototype.error = function(error) { + var e = JSON.parse(error); + showError(e); + this.panel.setLoading(false); +}; + +//ShipmentTabs.prototype.refreshTabTitles = function() { +/*Ext.getCmp("MacromoleculeTab").setText(this.getMacromoleculeTitle()); +Ext.getCmp("BufferTab").setText(this.getBuffersTitle()); +Ext.getCmp("SpecimenTab").setText(this.getSpecimenTitle()); +Ext.getCmp("PlatesTab").setText(this.getPlatesTitle()); +Ext.getCmp("AssembliesTab").setText(this.getShipmentTitle()); +Ext.getCmp("PlateGroupsTab").setText(this.getPlateGroupsTitle());*/ +//}; +ShipmentTabs.prototype.draw = function(shipment) { + var _this = this; + _this.plates = []; + _this.shipment = shipment; + _this.render(shipment); + + // var proposal = new Proposal(); + // proposal.onDataRetrieved.attach(function(sender, plates){ + // _this.plates = plates; + // _this.shipment = shipment; + // _this.render(shipment); + // + // }); + // proposal.getPlatesByProposal(); +}; + +//ShipmentTabs.prototype.getShipmentTitle = function() { +// return 'Shipment'; +//}; + +//ShipmentTabs.prototype.getMacromoleculeTitle = function() { +// return 'Macromolecules (' + this.experiment.getMacromolecules().length + ')'; +//}; + +//ShipmentTabs.prototype.getBuffersTitle = function() { +// return 'Buffers (' + this.experiment.getBuffers().length + ')'; +//}; + +//ShipmentTabs.prototype.getPlateGroupsTitle = function() { +// return 'Plate Groups (' + this.experiment.getPlateGroups().length + ')'; +//}; + +//ShipmentTabs.prototype.getSampleChangerTitle = function() { +// return 'Sample Changer'; +//}; + +//ShipmentTabs.prototype.getSpecimenTitle = function() { +// return 'Specimens(' + this.experiment.getSpecimenCount() + ')'; +//}; + +//ShipmentTabs.prototype.getPlatesTitle = function() { +// return 'Plates(' + this.experiment.getSamplePlates().length + ')'; +//}; + +//ShipmentTabs.prototype.getBuffersTip = function() { +/*if (this.experiment.getBuffers().length == 0){ + return BUI.getWarningHTML("There are no buffers. Click on add to create new ones. Click on edit button or double click to edit them"); + +} +else{ + return BUI.getTipHTML("Click on edit button or double click to edit them. Click on duplicate to create an identical buffer including its additives") +}*/ +//}; + +//ShipmentTabs.prototype.refreshTips = function() { +/*Ext.getCmp("BufferTabTip").update(this.getBuffersTip()); +Ext.getCmp("SpecimenTabTip").update(this.getSpecimensTip());*/ +//}; + +ShipmentTabs.prototype.render = function(shipment) { + return this.getPanel(shipment); +}; + +ShipmentTabs.prototype.getShipmentHeader = function(shipment) { + var _this = this; + function getHTMLSource() { + var name = shipment.json.shippingName; + var status = shipment.json.shippingStatus; + var creationDate = shipment.json.creationDate; + var html = BUI.createFormLabel("Name :", name, 75, 400); + html = html + BUI.createFormLabel("Status :", status, 75, 400); + html = html + BUI.createFormLabel("Date :", creationDate, 75, 400); + return html; + } + + return Ext.create('Ext.container.Container', { + frame : false, + layout : 'hbox', + title : 'General', + padding : 5, + bodyPadding : '5 5 0 0', + width : 890, + margin : '0 0 10 0', + height : 100, + style : { + borderColor : '#BDBDBD', + borderStyle : 'solid', + borderWidth : '1px' + }, + fieldDefaults : { + msgTarget : 'side', + labelWidth : 100 + }, + items : [ { + margin : "0 0 0 0", + width : 475, + border : 0, + html : getHTMLSource() + } + ] + }); +}; + +ShipmentTabs.prototype.getTabPanel = function(shipment) { + this.panel = Ext.createWidget('tabpanel', { + height : 600, + style : { + padding : 2 + }, + items : [ { + tabConfig : { + id : 'Shipment', + title : 'Shipment', + icon : '/ispyb/images/plane-small.gif' + }, + items : [ { + xtype : 'container', + margin : '5 5 5 5', + items : [ this.shipmentForm.getPanel(shipment) ] + } ] + }, { + tabConfig : { + id : 'Cases', + title : 'Cases', + icon : '../images/box-icon-very-small.png' + }, + items : [ { + xtype : 'container', + margin : '5 5 5 5', + items : [ this.caseGrid.getPanel(shipment.getDewars(), this.plates) ] + } ] + } ] + }); + return this.panel; +}; + +ShipmentTabs.prototype.getPanel = function(shipment) { + var _this = this; + this.shipment = shipment; + if (this.plates == null) { + this.plates = []; + } + + if (this.shipPanel == null) { + this.shipPanel = Ext.create('Ext.container.Container', { + bodyPadding : 2, + width : Ext.getBody().getViewSize().width * 0.9, + renderTo : this.targetId, + style : { + padding : 2 + }, + items : [ this.getShipmentHeader(shipment), this.getTabPanel(shipment) ] + }); + } + + return this.shipPanel; +}; + +/** + * Shows an template with the specimens, measurements and the experiment's + * requirement + * + * @targetId + */ +function TemplateTabs(targetId) { + this.height = 600; + this.targetId = targetId; + + this.id = BUI.id(); + var _this = this; + + this.gridHeight = 1000; + /** data * */ + this.experiment = null; + + this.specimenSelected = null; + + /** Specimen Widget contains a specimenGrid and a sampleChangerWidget than can be displayed with are vertical or horizontal layout **/ + this.specimenWidget = new SpecimenWidget({ + height : this.height + }); + + this.samplePlateGroupWidget = new SamplePlateGroupWidget({ + showTitle : false, + height : 250, + margin : 5, + bbar : true + }); + + this.samplePlateGroupWidget.onExperimentChanged.attach(function(sender, experiment) { + _this.refresh(experiment); + }); + + this.samplePlateGroupWidget.onClick.attach(function(sender, args) { + }); + + this.volumePlanificator = new VolumeGrid(); + + /** For Measurements * */ + var storeViscosity = Ext.create('Ext.data.Store', { + fields : [ 'name' ], + data : [ { + "name" : "low" + }, { + "name" : "medium" + }, { + "name" : "high" + } ] + }); + + // Create the combo box, attached to the states data store + var viscosityEditor = Ext.create('Ext.form.ComboBox', { + fieldLabel : '', + store : storeViscosity, + queryMode : 'local', + displayField : 'name', + valueField : 'name' + }); + + this.measurementGrid = new MeasurementGrid({ + maxWidth : 1500, + estimateTime : false, + positionColumnsHidden : true, + isPriorityColumnHidden : true, + isStatusColumnHidden : true, + isTimeColumnHidden : true, + updateRowEnabled : true, + collapsed : true, + removeBtnEnabled : true, + showTitle : false, + collapseBtnEnable : false, + addBtnMultipleEdit : true, + sortingBtnEnable : true, + editor : { + exposureTemperature : { + xtype : 'textfield', + allowBlank : true + }, + comments : { + xtype : 'textfield', + allowBlank : true + }, + volumeToLoad : { + xtype : 'numberfield', + allowBlank : true + }, + transmission : { + xtype : 'numberfield', + allowBlank : true + }, + viscosity : viscosityEditor, + waitTime : { + xtype : 'numberfield', + allowBlank : true + }, + flow : { + xtype : 'checkbox', + allowBlank : true + } + } + }); + + this.measurementGrid.onSelected.attach(function(sender, measurements) { + var specimens = []; + for ( var i = 0; i < measurements.length; i++) { + specimens.push(_this.experiment.getSampleById(measurements[i].specimenId)); + } + }); + + this.measurementGrid.onMeasurementChanged.attach(function(sender, measurement) { + _this.experiment.setMeasurement(measurement); + _this.refresh(_this.experiment); + }); + + this.measurementGrid.onExperimentChanged.attach(function(sender, json) { + _this.refresh(new Experiment(json)); + }); + + this.measurementGrid.onRemoved.attach(function(sender, experiment) { + _this.refreshSpecimen(new Experiment(experiment)); + }); + + this.measurementGrid.onUpdateTime.attach(function(sender, args) { + document.getElementById(_this.id + "_counter").innerHTML = args.hours + 'h, ' + args.minutes + 'min and ' + args.seconds + ' seconds'; + }); + +} + +TemplateTabs.prototype.refreshMeasurement = function(experiment) { + this.experiment = experiment; + this.experiment.onSaved = new Event(this); + this.experiment.onSpecimenSaved = new Event(this); + var experimentList = new ExperimentList([ this.experiment ]); + this.measurementGrid.refresh(experimentList.getMeasurementsNotCollected(), experimentList); +}; + +TemplateTabs.prototype.refreshSpecimen = function(experiment) { + this.experiment = experiment; + this.experiment.onSaved = new Event(this); + this.experiment.onSpecimenSaved = new Event(this); + this.samplePlateGroupWidget.refresh(this.experiment); + this.specimenWidget.refresh(this.experiment); + this.volumePlanificator.refresh(this.experiment); +}; + +TemplateTabs.prototype.refresh = function(experiment) { + // var start = new Date().getTime(); + this.experiment = experiment; + this.experiment.onSaved = new Event(this); + this.experiment.onSpecimenSaved = new Event(this); + + // var experimentList = new ExperimentList([this.experiment]); + this.refreshMeasurement(experiment); + this.refreshSpecimen(experiment); + /** Refreshing grids * */ + this.panel.setLoading(false); + +}; + +TemplateTabs.prototype.error = function(error) { + var e = JSON.parse(error); + showError(e); + this.panel.setLoading(false); + +}; + +TemplateTabs.prototype.draw = function(experiment) { + this.render(experiment); +}; + + +TemplateTabs.prototype.getExperimentTitle = function() { + var _this = this; + var experimentHeaderForm = new ExperimentHeaderForm(); + return experimentHeaderForm.getPanel(this.experiment); +}; + +TemplateTabs.prototype.render = function(experiment) { + var _this = this; + this.experiment = experiment; + + /** + * + * Depending on the sample Changer configuration we want to display the plates vertically or horizontally + * Default is vertical + * + * */ + + var specimenContainer = this.specimenWidget.getPanel(); + this.specimenWidget.refresh(experiment); + + var experimentList = new ExperimentList([ _this.experiment ]); + var measurementContainer = Ext.create('Ext.container.Container', { + layout : { + type : 'vbox' + }, + defaults : { + style : { + padding : '5px 0px 0px 10px' + } + }, + items : [ _this.measurementGrid.getPanel(experimentList.getMeasurementsNotCollected(), experimentList) ] + }); + + this.panel = Ext + .createWidget( + 'tabpanel', + { + plain : true, + items : [ + { + tabConfig : { + title : 'Measurements' + }, + items : [ { + xtype : 'container', + layout : 'vbox', + border : 1, +// height : _this.gridHeight, + margin : "0 0 10 0", + items : [ measurementContainer ] + } + + ] + }, + { + tabConfig : { + id : 'genralTabl', + title : "Specimens" + // width : 900, + // border : 3 + + }, + items : [ specimenContainer ] + }, + { + tabConfig : { + title : "Requirements" + + }, + items : [ + { + html : BUI.getTipHTML("Estimated volume is the maximum volume required. Depending on the order of your measurements you may use less. Click on create stock solutions if you plan to ship these stock solutions"), + margin : "10 10 10 10", + border : 0 + }, this.volumePlanificator.getPanel(experiment) ] + } ] + }); + // ); + return this.getPanel(this.panel); +}; + +TemplateTabs.prototype.isTemplate = function() { + if (this.experiment.json.type == "TEMPLATE") { + return true; + } + return false; +}; + + +TemplateTabs.prototype.getPanel = function(panel) { + var _this = this; + if (this.experimentPanel == null) { + this.experimentPanel = Ext.create('Ext.container.Container', { + bodyPadding : 2, + width : 1000,//Ext.getBody().getViewSize().width * 0.9, + renderTo : this.targetId, + height : 500, +// style : { +// padding : 2 +// }, + items : [ + this.getExperimentTitle(), + this.panel + ], + listeners : { + afterrender : function(thisCmp) { + $("#SchemeReport" + _this.experiment.experimentId).click(function() { + $(this).target = "_blank"; + window.open('viewProjectList.do?reqCode=display&menu=platescheme&experimentId=' + _this.experiment.experimentId); + return false; + }); + + } + } + }); + } + return this.experimentPanel; +}; + +TemplateTabs.prototype.input = function(targetId) { + return new ExperimentTabs().input(); +}; + +TemplateTabs.prototype.test = function(targetId) { + var experimentTabs = new TemplateTabs(targetId); + BIOSAXS.proposal = new Proposal(experimentTabs.input().proposal); + experimentTabs.draw(new Experiment(experimentTabs.input().experiment)); +}; + +var BUI = { + //interval : 60000, + interval : 40000, + rainbow : function(numOfSteps, step) { + // This function generates vibrant, "evenly spaced" colours (i.e. no clustering). This is ideal for creating easily distinguishable vibrant markers in Google Maps and other apps. + // Adam Cole, 2011-Sept-14 + // HSV to RBG adapted from: http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript + var r, g, b; + var h = step / numOfSteps; + var i = ~~(h * 6); + var f = h * 6 - i; + var q = 1 - f; + switch (i % 6) { + case 0: + r = 1, g = f, b = 0; + break; + case 1: + r = q, g = 1, b = 0; + break; + case 2: + r = 0, g = 1, b = f; + break; + case 3: + r = 0, g = q, b = 1; + break; + case 4: + r = f, g = 0, b = 1; + break; + case 5: + r = 1, g = 0, b = q; + break; + } + var c = "#" + ("00" + (~~(r * 255)).toString(16)).slice(-2) + ("00" + (~~(g * 255)).toString(16)).slice(-2) + + ("00" + (~~(b * 255)).toString(16)).slice(-2); + return (c); + }, + getFileNameByPath : function(filePath) { + if (filePath != null){ + var split = filePath.split("/"); + if (split.length > 0){ + return split[split.length - 1]; + } + } + return "Not file"; + }, + getUpdateInterval : function() { + this.interval = this.interval + 2000; + return this.interval; + }, + getRadiationDamageThreshold : function() { + return 0.7; + }, + getQualityThreshold : function() { + return 0.7; + }, + getCreateShipmentURL : function() { + return '/ispyb/user/viewProjectList.do?reqCode=display&menu=create_shipment'; + }, + getCreateShipmentList : function() { + return '/ispyb/user/viewProjectList.do?reqCode=display&menu=list_shipment'; + }, + getShippingURL : function(shippingId) { + return '/ispyb/user/viewProjectList.do?reqCode=display&menu=shipment&shippingId=' + shippingId; + }, + + getMacromoleculeResultsURLByMultipleSearch : function(array) { + return '/ispyb/user/viewProjectList.do?reqCode=display&menu=macromolecule&search=' + + JSON.stringify(array).replace(new RegExp("\"", 'g'), "'"); + }, + getMacromoleculeResultsURL : function(macromoleculeId) { + return '/ispyb/user/viewProjectList.do?reqCode=display&menu=macromolecule¯omoleculeId=' + macromoleculeId; + }, + getMacromoleculeBufferResultsURL : function(macromoleculeId, bufferId) { + return '/ispyb/user/viewProjectList.do?reqCode=display&menu=macromolecule&bufferId=' + bufferId + '¯omoleculeId=' + macromoleculeId; + }, + + getMacromoleculeHeader : function(macromoleculeId) { + + function getHTMLSource(macromoleculeId) { + if (macromoleculeId != null) { + var html = BUI.createFormLabel("Name :", BIOSAXS.proposal.getMacromoleculeById(macromoleculeId).name, 75, 400); + html = html + BUI.createFormLabel("Acronym :", BIOSAXS.proposal.getMacromoleculeById(macromoleculeId).acronym, 75, 400); + if (BIOSAXS.proposal.getMacromoleculeById(macromoleculeId).comments != null) { + html = html + BUI.createFormLabel("Comments :", BIOSAXS.proposal.getMacromoleculeById(macromoleculeId).comments, 75, 400); + } + return html; + } + } + + return Ext.create('Ext.container.Container', { + frame : false, + layout : 'hbox', + title : 'Macromolecule', + bodyPadding : '5', + width : 890, + margin : '0 0 10 0', + height : 100, + style : { + borderColor : '#BDBDBD', + borderStyle : 'solid', + borderWidth : '1px' + }, + fieldDefaults : { + msgTarget : 'side', + labelWidth : 100 + }, + items : [ { + margin : "10 0 0 10", + width : 475, + border : 0, + html : getHTMLSource(macromoleculeId) + }, { + margin : "10 0 0 10", + width : 475, + border : 0, + html : BUI.getZipHTMLByMacromoleculeId(macromoleculeId) + } + ] + }); + }, + + getZipHTMLByMacromoleculeId : function(macromoleculeId) { + if (macromoleculeId != null) { + var fileName = BIOSAXS.proposal.getMacromoleculeById(macromoleculeId).acronym; + return ""; + } + }, + getZipHTMLByExperimentId : function(experimentId, filename) { + if (filename == null){ + filename = "experiment"; + } + return ""; + }, + + getZipURLByAverageId : function(averageId, filename) { + if (filename == null){ + filename = "experiment"; + } + return "/ispyb/user/dataadapter.do?reqCode=getZipFileByAverageListId&f&mergeIdsList=" + averageId + "&fileName=" + filename; + }, + getZipURLBySubtractionId : function(subtractionId, filename) { + if (filename == null){ + filename = "experiment"; + } + return "/ispyb/user/dataadapter.do?reqCode=getZipFileByAverageListId&f&subtractionIdList=" + subtractionId + "&fileName=" + filename; + }, + + + getZipHTMLByFrameRangeId : function(experimentId, start, end) { + var fileName = "experiment"; + return ""; + }, + getZipFrameHPLCUrl : function(experimentId, start, end) { + return "/ispyb/user/dataadapter.do?reqCode=getZipFileH5ByFramesRange&f&experimentId=" + experimentId + "&start=" + Number(start) +"&end="+ Number(end); + }, + + getQueueUrl : function() { + return "/ispyb/user/dataadapter.do?reqCode=getPagingCompactAnalysisByProposalId"; + }, + + getQueueUrlByExperiment: function(experimentId) { + return "/ispyb/user/dataadapter.do?reqCode=getPagingCompactAnalysisByExperimentId&f&experimentId=" + experimentId; + }, + getStandardDeviation : function(values) { + var sum = 0; + var count = 0; + var avg = null; + + var curatedValues = new Array(); + for ( var i = 0; i < values.length; i++) { + var value = values[i]; + if (value != null) { + if (!isNaN(value)) { + count = count + 1; + sum = sum + Number(value); + curatedValues.push(Number(value)); + } + } + } + + if (count > 0) { + avg = sum / count; + } else { + avg = sum; + } + var aux = 0; + for ( var i = 0; i < curatedValues.length; i++) { + var value = curatedValues[i]; + aux = aux + Math.pow(value - avg, 2); + } + /** std **/ + var std = Math.sqrt(aux / count); + return { + std : (std), + sum : (sum), + avg : (avg), + validNumber : count, + totalNumber : values.length, + values : values + }; + }, + + getHTMLTableForFrameAveraged : function(bufferAcronym, macromoleculeAcronym, bbmerges, molmerges, bamerges, totalframes, bufferId,macromoleculeId, macromoleculeColor) { + + function getFrameSpan(framesMerged, total) { + return "(" + framesMerged + " of " + total + ")"; + } + + function getColorFrame(framesMerged, total) { + if (framesMerged / total < 0.5) { + return "#FA5858"; + } + if ((framesMerged / total >= 0.5) && (framesMerged / total < 0.8)) { + return "#FF9900"; + } + return "white"; + } + + function getRow(color, acroynm, framesMerged, totalframes) { + return " " + + BUI.getRectangleColorDIV(color, 10, 10) + + " " + acroynm + "" + + getFrameSpan(framesMerged, totalframes) + ""; + } + + var html = ""; + + /** Buffer Before **/ + if (bufferAcronym != null) { + if (bbmerges != null) { + color = BIOSAXS.proposal.bufferColors[bufferId]; + html = html + getRow(color, bufferAcronym, bbmerges, totalframes); + } + } + + /** Molecule **/ + if (macromoleculeAcronym != null) { + if (molmerges != null) { + if (macromoleculeColor == null){ + color = BIOSAXS.proposal.macromoleculeColors[macromoleculeId]; + } + else{ + color = macromoleculeColor; //BIOSAXS.proposal.macromoleculeColors[macromoleculeId]; + } + html = html + getRow(color, macromoleculeAcronym, molmerges, totalframes); + } + } + + /** Buffer After **/ + if (bufferAcronym != null) { + if (bamerges != null) { + color = BIOSAXS.proposal.bufferColors[bufferId]; + html = html + getRow(color, bufferAcronym, bamerges, totalframes); + } + } + return html + "
"; + }, + isWebGLEnabled : function(return_context) { + if (!!window.WebGLRenderingContext) { + var canvas = document.createElement("canvas"); + names = [ "webgl", "experimental-webgl", "moz-webgl", "webkit-3d" ]; + context = false; + for ( var i = 0; i < 4; i++) { + try { + context = canvas.getContext(names[i]); + if (context && typeof context.getParameter == "function") { + // WebGL is enabled + if (return_context) { + // return WebGL object if the function's argument is present + return { + name : names[i], + gl : context + }; } + // else, return just true + return true; } - } ] - }, - "concentrationLabel" : "7.17 mg/ml ,3.53 mg/ml ,1.75 mg/ml ,0.91 mg/ml " + } catch (e) { + } + } + // WebGL is supported, but disabled + return false; + } + // WebGL not supported28. + return false; + }, + getHTMLTableForPrefixes : function(bufferBeforeaverageFilePath, averageFilePath, bufferAfterAverageFilePath) { + + function getRow(bufferBeforeaverageFilePath) { + file = bufferBeforeaverageFilePath; + try { + file = bufferBeforeaverageFilePath.split("/")[bufferBeforeaverageFilePath.split("/").length - 1]; + } catch (e) { + file = "NA"; + } + return "" + file + ""; + } + var html = ""; + + /** Buffer Before **/ + if (bufferBeforeaverageFilePath != null) { + html = html + getRow(bufferBeforeaverageFilePath); + } + + /** Molecule **/ + if (averageFilePath != null) { + html = html + getRow(averageFilePath); + } + + /** Buffer After **/ + if (bufferAfterAverageFilePath != null) { + html = html + getRow(bufferAfterAverageFilePath); + } + return html + "
"; + }, + + getBaseURL : function() { + return '/ispyb/user/dataadapter.do'; + }, + + getPrintcomponentURL : function(dewarId) { + return '/ispyb/user/viewDewarAction.do?reqCode=generateLabels&dewarId=' + dewarId; + + }, + getPDBVisualizerURL : function(modelId, subtractionId, experimentId) { + return '/ispyb/user/viewProjectList.do?reqCode=display&menu=PDBVisualizer&modelId=' + modelId + '&experimentId=' + experimentId + + '&subtractionId=' + subtractionId; + }, + + getURL : function() { + return this.getBaseURL() + '?reqCode=getImage'; + + }, + getAbinitioImageURL : function() { + return this.getBaseURL() + '?reqCode=getAbinitioImage'; + }, + getNSDImageURL : function(modelListId) { + return BUI.getAbinitioImageURL() + '&type=NSD&modelListId=' + modelListId; + }, + getCHI2ImageURL : function(modelListId) { + return BUI.getAbinitioImageURL() + '&type=CHI2&modelListId=' + modelListId; + }, + getModelFile : function(type, modelId, format) { + return this.getBaseURL() + '?reqCode=getModelFile' + "&type=" + type + "&modelId=" + modelId + "&format=" + format; + }, + getPdbURL : function() { + return this.getBaseURL() + '?reqCode=getPdbFiles'; + }, + getStvArray : function(value, error) { + value = Number(value); + error = Number(error); + return [ value - error, value, value + error ]; + }, + getPointArrayForDygraph : function(x, y, error) { + return [ x, BUI.getStvArray(y, error) ]; + }, + createDIV : function(text, width, className, backgroundColor) { + var nameContainer = document.createElement("div"); + var nameSpan = document.createElement("span"); + if (className != null) { + nameSpan.setAttribute("class", className); + } + if (backgroundColor != null) { + nameSpan.setAttribute("style", "float:left;width:" + width + "px;height:18px;background-color:" + backgroundColor); + } else { + nameSpan.setAttribute("style", "float:left;width:" + width + "px;height:18px;"); + } + nameSpan.appendChild(document.createTextNode(text)); + nameContainer.appendChild(nameSpan); + return nameContainer; + }, + + createFormLabel : function(labelText, text, labelWidth, textWidth, backgroundColor) { + var div = document.createElement("div"); + + div.appendChild(BUI.createDIV(labelText, labelWidth, "bLabel", backgroundColor)); + div.appendChild(BUI.createDIV(text, textWidth, "btext", backgroundColor)); + return div.innerHTML; + }, + + createTextArea : function(labelText, text, labelWidth, rows, cols) { + var div = document.createElement("div"); + div.appendChild(BUI.createDIV(labelText, labelWidth, "bLabel")); + var textArea = document.createElement("textarea"); + textArea.setAttribute("rows", rows); + textArea.setAttribute("cols", cols); + textArea.appendChild(document.createTextNode(text)); + div.appendChild(textArea); + return div.innerHTML; + }, + + showBetaWarning : function() { + alert("ISPyB for Biosaxs version Beta has not this functionality enabled"); + }, + + formatValuesErrorUnitsScientificFormat : function(val, error, unit, args) { + var fontSize = 14; + var decimals = 2; + var errorFontSize = 10; + /** line break **/ + var lineBreak = true; + if (args != null) { + if (args.fontSize != null) { + fontSize = args.fontSize; + } + if (args.decimals != null) { + decimals = args.decimals; + } + if (args.errorFontSize != null) { + errorFontSize = args.errorFontSize; + } + if (args.lineBreak != null) { + lineBreak = args.lineBreak; + } + + } + + if (val == "") { + return ""; + } + if (error == null) { + return "" + Number(val).toFixed(decimals) + ""; + } + var html = "" + Number(val).toFixed(decimals) + " " + unit + ""; + if (lineBreak) { + html = html + "
"; + } + return html + " ± " + Number(Number(error).toFixed(3)).toExponential() + + ""; + }, + + formatValuesErrorUnits : function(val, error, unit, args) { + var fontSize = 16; + var decimals = 2; + var errorFontSize = 10; + /** line break **/ + var lineBreak = true; + if (args != null) { + if (args.fontSize != null) { + fontSize = args.fontSize; + } + if (args.decimals != null) { + decimals = args.decimals; + } + if (args.errorFontSize != null) { + errorFontSize = args.errorFontSize; + } + if (args.lineBreak != null) { + lineBreak = args.lineBreak; + } + + } + + if (val == "") { + return ""; + } + if (error == null) { + return "" + Number(val).toFixed(decimals) + ""; + } + var html = "" + Number(val).toFixed(decimals) + " " + unit + ""; + if (lineBreak) { + html = html + "
"; + } + return html + " ± " + Number(Number(error).toFixed(8)).toExponential() + + ""; + }, + + formatValuesUnits : function(val, unit, args) { + var fontSize = 12; + var decimals = 2; + var unitsFontSize = 10; + if (args != null) { + if (args.fontSize != null) { + fontSize = args.fontSize; + } + if (args.decimals != null) { + decimals = args.decimals; + } + if (args.unitsFontSize != null) { + unitsFontSize = args.unitsFontSize; + } + + } + + if (val === "") { + return ""; + } + if (unit == null) { + return "" + Number(val).toFixed(decimals) + ""; + } + return "" + Number(val).toFixed(decimals) + " " + unit + ""; + }, + + getGreenButton : function(text, args) { + var width = 70; + var height = 20; + if (args != null) { + if (args.width != null) { + width = args.width; + } + if (args.height != null) { + height = args.height; + } + } + + return ''; + }, +// getBlueButton : function(text, args) { +// var width = 70; +// var height = 20; +// if (args != null) { +// if (args.width != null) { +// width = args.width; +// } +// if (args.height != null) { +// height = args.height; +// } +// } +// +// return ''; +// }, + + getBlueButton : function(text, args) { + var width = 70; + var height = 20; + var href = null; + if (args != null) { + if (args.width != null) { + width = args.width; + } + if (args.height != null) { + height = args.height; + } + if (args.href != null) { + href = args.href; + } + } + if (href != null) { + return ''; + } else { + return ''; +// return ''; + } + }, + + getSubmitGreenButton : function(text) { + return ''; + }, + + getRedButton : function(text) { + return ''; + + }, + getRectangleColorDIV : function(color, height, width) { + return '
'; + }, + + openBufferWindow : function(bufferId) { + var window = new BufferWindow(); + window.draw(tabs.experiment.getBufferById(bufferId), tabs.experiment); + }, + + /** Render for safety levels on grids **/ + safetyRenderer : function(val, y, specimen) { + var color = val; + if (val == "YELLOW") { + color = "#E9AB17"; + } + return '' + val + ''; + }, + + getSampleColor : function() { + return '#CB181D'; + }, + + getLightSampleColor : function() { + return '#FCBBA1'; + }, + + getBufferColor : function() { + return '#6A51A3'; + }, + getLightBufferColor : function() { + return '#BCBDDC'; + }, + + formatConcentration : function(val) { + if (val != null) { + return "" + Number(val).toFixed(2) + " mg/ml "; + } + return val; + }, + + formatVolume : function(sample, volume) { + if (Number(sample.data.volumeToLoad) > Number(sample.data.volume)) { + return "" + volume + " �l"; + } + if (Number(sample.data.volumeToLoad) == Number(sample.data.volume)) { + return "" + volume + " �l"; + } + return "" + volume + " �l"; + }, + + getProposal : function() { + return new Proposal(); + }, + + getSampleNameRenderer : function(val, y, record) { + var sample = record.data; + if (record.raw.macromolecule3VO == null) { + return '' + sample.code + ''; + } else { + return '' + sample.code + ''; + } + }, + + getSafetyLevels : function() { + var safetyLevels = new Array(); + safetyLevels.push({ + safetyLevelId : "", + name : "UNKNOWN" + }); + safetyLevels.push({ + safetyLevelId : 1, + name : "GREEN" + }); + safetyLevels.push({ + safetyLevelId : 2, + name : "YELLOW" + }); + safetyLevels.push({ + safetyLevelId : 3, + name : "RED" + }); + return safetyLevels; + }, + + getErrorColor : function() { + return '#F6CED8'; + }, + getWarningColor : function() { + return '#F5DA81'; + }, + getValidColor : function() { + return '#E0F8E0'; + }, + getSamplePlateLetters : function() { + return [ "A", "B", "C", "D", "E", "F", "G", "H" ]; + }, + getSamplePositionHTML : function(sample, experiment) { + var plate = ""; + var row = ""; + var column = ""; + var rows = this.getSamplePlateLetters(); + if (sample.sampleplateposition3VO != null) { + var samplePlate = experiment.getSamplePlateById(sample.sampleplateposition3VO.samplePlateId); + if (samplePlate != null) { + plate = (samplePlate.slotPositionColumn); + row = (sample.sampleplateposition3VO.rowNumber); + column = (sample.sampleplateposition3VO.columnNumber); + // var html = "Plate: " + "" + plate + ""; + // html = html + ", Row: " + "" + rows[row - 1] + ""; + // html = html + ", Column: " + "" + column + ""; + var html = "Plate: " + plate + + "-" + rows[row - 1] + "" + column + ""; + return html; + } + } + return ""; + }, + + getSafetyLabelName : function(safetyLevelId) { + var safetyLevels = BUI.getSafetyLevels(); + for ( var i = 0; i < safetyLevels.length; i++) { + if (safetyLevels[i].safetyLevelId == safetyLevelId) { + return safetyLevels[i].name; + } + } + return "UNKNOWN"; + }, + /** generate random id **/ + id : function() { + var text = ""; + var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + + for ( var i = 0; i < 5; i++) + text += possible.charAt(Math.floor(Math.random() * possible.length)); + + return text; + }, + showWarning : function(message) { + Ext.Msg.show({ + title : 'Warning', + msg : message, + icon : Ext.Msg.WARNING, + animEl : 'elId' + }); + }, + showError : function(message) { + Ext.Msg.show({ + title : 'Warning', + msg : message, + icon : Ext.Msg.ERROR, + animEl : 'elId' + }); + }, + getTipHTML : function(message) { + //return "
Tip
" + // + message + "
"; + return "
Tip
" + + message + "
"; + }, + + getWarningHTML : function(message) { + return "
Warning
" + + message + "
"; + }, + + getErrorHTML : function(message) { + return "
Error
" + + message + "
"; + }, + + getProgessBar : function(percentage, text) { + /** percentage 100% green **/ + var color = "#0a0"; + + color = "#99CC00"; + if (percentage > 100) { + color = "yellow"; + percentage = 100; + } + if (isNaN(percentage)) { + color = "white"; + percentage = 0; + } + + var defaultText = percentage + "%"; + if (text != null) { + defaultText = text; + } + + return "
" + defaultText + "
"; + }, + getFileName : function(filePath){ + if (filePath != null){ + return filePath.split("/")[filePath.split("/").length - 1] + } + return ""; + } + +}; + +Ext.ux.form.RequiredCombo = Ext.extend(Ext.form.ComboBox, { + config : { + cls : 'custom-field-text-required' + }, + initComponent : function() { + Ext.ux.form.RequiredCombo.superclass.initComponent.apply(this, arguments); + }, + checkChange : function() { + if ((this.getValue() == null) || String(this.getValue()).length == 0) { + this.addCls('custom-field-text-required'); + } else { + this.removeCls('custom-field-text-required'); + } + } +}); + +Ext.define('Ext.form.field.RequiredNumber', { + extend : 'Ext.form.field.Number', + alias : 'widget.requirednumberfield', + alternateClassName : [ 'Ext.form.RequiredNumberField', 'Ext.form.RequiredNumber' ], + config : { + cls : 'custom-field-text-required' + }, + + initComponent : function() { + var me = this; + me.callParent(); + me.setMinValue(me.minValue); + me.setMaxValue(me.maxValue); + }, + + onChange : function() { + if ((this.getValue() == null) || String(this.getValue()).length == 0) { + this.addCls('custom-field-text-required'); + } else { + this.removeCls('custom-field-text-required'); + } + this.toggleSpinners(); + this.callParent(arguments); + } +}); + +Ext.define('Ext.form.field.RequiredText', { + extend : 'Ext.form.field.Text', + alias : 'widget.requiredtext', + requires : [ 'Ext.form.field.VTypes', 'Ext.layout.component.field.Text' ], + alternateClassName : [ 'Ext.form.RequiredTextField', 'Ext.form.RequiredText' ], + config : { + cls : 'custom-field-text-required' + }, + initComponent : function() { + var me = this; + if (me.allowOnlyWhitespace === false) { + me.allowBlank = false; } - }; -}; + me.callParent(); + me.addEvents( + /** + * @event autosize + * Fires when the **{@link #autoSize}** function is triggered and the field is resized according to the + * {@link #grow}/{@link #growMin}/{@link #growMax} configs as a result. This event provides a hook for the + * developer to apply additional logic at runtime to resize the field if needed. + * @param {Ext.form.field.Text} this This text field + * @param {Number} width The new field width + */ + 'autosize', -ConcentrationHTMLTableWidget.prototype.test = function(targetId) { - var concentrationHTMLTableWidget = new ConcentrationHTMLTableWidget(); - document.getElementById(targetId).innerHTML = concentrationHTMLTableWidget.getPanel(concentrationHTMLTableWidget.input().data); -}; - -function DataCollectionWidget() { + /** + * @event keydown + * Keydown input field event. This event only fires if **{@link #enableKeyEvents}** is set to true. + * @param {Ext.form.field.Text} this This text field + * @param {Ext.EventObject} e + */ + 'keydown', + /** + * @event keyup + * Keyup input field event. This event only fires if **{@link #enableKeyEvents}** is set to true. + * @param {Ext.form.field.Text} this This text field + * @param {Ext.EventObject} e + */ + 'keyup', + /** + * @event keypress + * Keypress input field event. This event only fires if **{@link #enableKeyEvents}** is set to true. + * @param {Ext.form.field.Text} this This text field + * @param {Ext.EventObject} e + */ + 'keypress'); + me.addStateEvents('change'); + me.setGrowSizePolicy(); + }, + checkChange : function() { + if ((this.getValue() == null) || String(this.getValue()).length == 0) { + this.addCls('custom-field-text-required'); + } else { + this.removeCls('custom-field-text-required'); + } + } +}); -}; +var BIOSAXS_COMBOMANAGER = { -DataCollectionWidget.prototype.refresh = function(subtractionId) { + getComboMacromoleculeByMacromolecules : function(macromolecules, args) { + var labelWidth = 150; + var margin = "0 0 5 0"; + var width = 300; -}; + if (args != null) { + if (args.labelWidth != null) { + labelWidth = args.labelWidth; + } + if (args.margin != null) { + margin = args.margin; + } + if (args.width != null) { + width = args.width; + } + } -DataCollectionWidget.prototype.getMacromolecule = function(data) { - for (var i = 0; i < data.length; i++) { - if (data[i].macromoleculeId != null) { - return BIOSAXS.proposal.getMacromoleculeById(data[i].macromoleculeId); + var store = Ext.create('Ext.data.Store', { + fields : [ 'macromoleculeId', 'acronym' ], + data : macromolecules, + sorters : [ 'acronym' ] + }); + + return Ext.create('Ext.form.ComboBox', { + fieldLabel : 'Macromolecules', + labelWidth : labelWidth, + width : width, + margin : margin, + store : store, + editable: false, + queryMode : 'local', + displayField : 'acronym', + valueField : 'macromoleculeId' + }); + }, + getComboBuffers : function(buffers, args) { + var labelWidth = 150; + var margin = "0 0 5 0"; + var width = 300; + var fieldLabel = 'Buffer'; + + if (args != null) { + if (args.labelWidth != null) { + labelWidth = args.labelWidth; + } + if (args.margin != null) { + margin = args.margin; + } + if (args.width != null) { + width = args.width; + } + if (args.noLabel != null) { + fieldLabel = null; + } } - } - return null; -}; -DataCollectionWidget.prototype.getMacromoleculeContainer = function(data) { - var macromolecule = this.getMacromolecule(data); + var store = Ext.create('Ext.data.Store', { + fields : [ 'bufferId', 'acronym' ], + data : buffers, + sorters : [ 'acronym' ] + }); - var disabled = false; - if (macromolecule == null) { - disabled = true; - } + return Ext.create('Ext.form.ComboBox', { + fieldLabel : fieldLabel, + labelWidth : labelWidth, + width : width, + margin : margin, + editable: false, + store : store, + queryMode : 'local', + displayField : 'acronym', + valueField : 'bufferId' + }); + }, + getComboSessions : function(sessions, args) { + var labelWidth = 150; + var margin = "0 0 5 0"; + var width = 300; - var acronym = (macromolecule == null ? "" : macromolecule.acronym); - var mm = (macromolecule == null ? "" : macromolecule.molecularMass); - var comments = (macromolecule == null ? "" : macromolecule.comments); + if (args != null) { + if (args.labelWidth != null) { + labelWidth = args.labelWidth; + } + if (args.margin != null) { + margin = args.margin; + } + if (args.width != null) { + width = args.width; + } + } - return Ext.create('Ext.Panel', { - width : 500, - height : 200, - disabled : disabled, - title : "Macromolecule", - layout : 'form', - bodyPadding : 5, - defaultType : 'textfield', - tbar : { - defaultButtonUI : 'default', - items : [ { - xtype : 'button', - text : 'Edit', - cls : 'btn-with-border', - handler : function() { - var macromoleculeWindow = new MacromoleculeWindow(); - macromoleculeWindow.draw(macromolecule); - /** TODO: update when save **/ - } - } ] - }, - items : [ { - fieldLabel : 'Acronym', - name : 'first', - readOnly : true, - value : acronym - }, { - fieldLabel : 'Molecular Mass', - name : 'last', - readOnly : true, - value : mm - }, { - xtype : 'textareafield', - fieldLabel : 'Comments', - value : '', - name : 'last', - readOnly : true, - value : comments - } ] - }); -}; + for ( var i = 0; i < sessions.length; i++) { + sessions[i]["startDateFormatted"] = moment(sessions[i].startDate).format("MMM Do YY"); + sessions[i]["sorter"] = moment(sessions[i].startDate).format("YYYYMMDD"); + } -DataCollectionWidget.prototype.getBufferContainer = function(data) { - var _this = this; - var buffer = BIOSAXS.proposal.getBufferById(data[0].bufferId); + var store = Ext.create('Ext.data.Store', { + fields : [ 'sessionId', 'startDateFormatted', 'beamlineName', 'startDate', 'endDate', 'beamlineOperator' ], + data : sessions, + sorters : [ 'sorter' ] + }); - return Ext.create('Ext.Panel', { - width : 500, - height : 200, - title : "Buffer", - layout : 'form', - // collapsed : true, - // collapsible : true, - bodyPadding : 5, - defaultType : 'textfield', - style : { - padding : '0px 0px 0px 2px' - }, - tbar : { - defaultButtonUI : 'default', - items : [ { - xtype : 'button', - text : 'Edit', - cls : 'btn-with-border', - handler : function() { - var bufferWindow = new BufferWindow(); - bufferWindow.draw(BIOSAXS.proposal.getBufferById(data[0].bufferId)); - /** TODO: update when save **/ - } + return Ext.create('Ext.form.ComboBox', { + fieldLabel : 'Sessions', + labelWidth : labelWidth, + width : width, + margin : margin, + store : store, + queryMode : 'local', + // displayField : 'startDate', + valueField : 'sessionId', + // Template for the dropdown menu. + // Note the use of "x-boundlist-item" class, + // this is required to make the items selectable. + tpl : Ext.create('Ext.XTemplate', '', + '
{startDateFormatted} {beamlineName}
', '
'), + // template for the content inside text field + displayTpl : Ext.create('Ext.XTemplate', '', '{startDateFormatted}', '') - } ] - }, - items : [ { - fieldLabel : 'Buffer', - name : 'acronym', - readOnly : true, - value : buffer.acronym - }, { - fieldLabel : 'Composition', - name : 'last', - readOnly : true, - value : buffer.composition + }); + } +}; + + function GenericWindow(args){ + this.title = "title"; + this.width = 700; + this.height = 500; + this.id = BUI.id(); + + this.close = false; + this.draggable = true; + this.modal = true; + + if (args != null){ + if (args.actions != null){ + this.actions = args.actions; + } + if (args.form != null){ + this.form = args.form; + } + if (args.width != null){ + this.width = args.width; + } + if (args.modal != null){ + this.modal = args.modal; + } + + if (args.height != null){ + this.height = args.height; + } + if (args.title != null){ + this.title = args.title; + } + if (args.form != null){ + this.form = args.form; + } + if (args.close != null){ + this.close = args.close; + } + if (args.draggable != null){ + this.draggable = args.draggable; + } + + } + /** Events **/ + this.onSaved = new Event(this); + }; + + + +GenericWindow.prototype.getButtons = function() { + var _this = this; + + if (this.close){ + return [ { + text : 'Close', + handler : function() { + _this.panel.close(); + } + } ]; + } + else{ + return [ { + text : 'Save', + handler : function() { + _this.save(); + + } }, { - xtype : 'textareafield', - fieldLabel : 'Comments', - value : buffer.comments, - name : 'last', - readOnly : true - - } ] - }); + text : 'Cancel', + handler : function() { + _this.panel.close(); + } + } ]; + } }; -DataCollectionWidget.prototype.getSpecimenContainer = function(data) { - return Ext.create('Ext.container.Container', { - layout : { - type : 'hbox' - }, - style : { - margin : '10px 10px 10px 10px' - }, - border : 0, - style : { - borderColor : '#000000', - borderStyle : 'solid', - borderWidth : '1px' - }, - defaults : { - labelWidth : 80, - // implicitly create Container by specifying xtype - xtype : 'datefield', - flex : 1, - - }, - items : [ this.getMacromoleculeContainer(data), this.getBufferContainer(data) ] - }); +GenericWindow.prototype.save = function (){ + alert("Method save of GenerciWindow class is abstract"); }; -DataCollectionWidget.prototype.getSeparator = function() { - return { - html : "
", - border : 0 - } +GenericWindow.prototype._postRender = function(data, experiment){ + }; -DataCollectionWidget.prototype.getFitStructurePanel = function(data) { - var _this = this; - - return Ext.create('Ext.container.Container', { - layout : { - type : 'hbox' - }, - style : { - margin : '10px 10px 10px 10px' - }, - border : 0, - style : { - borderColor : '#000000', - borderStyle : 'solid', - borderWidth : '1px' - }, - defaults : { - labelWidth : 80, - xtype : 'datefield', - flex : 1 - }, - items : [ - ] - }); +GenericWindow.prototype.draw = function (data, experiment){ + this._render(data, experiment); }; -DataCollectionWidget.prototype.getFitStructurePanelWorkflow = function(data) { - var _this = this; - var macromolecule = this.getMacromolecule(data); - - var items = []; +GenericWindow.prototype.refresh = function(data, experiment){ + this.data = data; + this.experiment = experiment; + this.form.refresh(data, experiment); +}; - /** Adding all the pdb files linked to the macromolecule **/ - // if (macromolecule.structure3VOs != null) { - // if (macromolecule.structure3VOs.length > 0) { - // - // items.push(); - // } - // } else { - // items.push({ - // html : "No apriori information added to this macromolecule. Apriori information needed for further analysis", - // margin : "5 5 5 5" - // }); - // } - var fitStructureToExperimentDataGrid = new FitStructureToExperimentDataGrid(); - - fitStructureToExperimentDataGrid.refresh(_this.data[0].subtractionId, _this.getMacromolecule(data)); - return Ext.create('Ext.container.Container', { - style : { - margin : '10px 10px 10px 10px' - }, - border : 0, +GenericWindow.prototype._render = function(data, experiment){ + this.data = data; + var _this = this; + if (this.panel == null){ + this.panel = Ext.create('Ext.Window', { + id : this.id, + title : this.title, + resizable : true, + constrain : true, + border : 1, + modal : this.modal, + frame : false, + draggable : this.draggable, + closable : true, + autoscroll : true, + layout : { type: 'vbox',align: 'stretch'}, + width : this.width, + height : this.form.height, + buttonAlign :'right', + buttons : this.getButtons(), + items : this.form.getPanel(data, experiment), + listeners: { + scope : this, + minimize : function(){ + this.panel.hide(); + }, + destroy : function(){ + delete this.panel; + } + } + }); + this.panel.setLoading(); + } + + this.panel.show(); + this._postRender(); +}; +function CalendarWidget(args) { + this.height = 740; - style : { - borderColor : '#000000', - borderStyle : 'solid', - borderWidth : '1px' - }, - defaults : { - labelWidth : 80, - xtype : 'datefield', - flex : 1 - }, - items : [ fitStructureToExperimentDataGrid.getPanel() ], - listeners : { - afterrender : function() { - } + if (args != null) { + if (args.height != null) { + this.height = args.height; } - }); -}; -/** Static method **/ -DataCollectionWidget.prototype.RUNFitScattering = function(subtractionId, structureId) { - var _this = this; - /** Add to Workflow **/ - var adapter = new BiosaxsDataAdapter(); - var workflow = { - 'workflowTitle' : 'FitExperimentalDatatoStructure', - 'comments' : 'FitExperimentalDatatoStructure run from ISPyB for subtractionId: ' + subtractionId + ' and structureId ' + structureId } - var inputs = [ { - name : 'subtractionId', - value : subtractionId - }, { - name : 'structureId', - value : structureId - } ]; + this.onClick = new Event(); +} - adapter.onSuccess.attach(function(sender, data) { - /** Add to Fit **/ - var adapter2 = new BiosaxsDataAdapter(); - var fit = { - 'workflowId' : data.workflowId, - 'subtractionId' : subtractionId, - 'structureId' : structureId, - 'comments' : 'Sent to workflow engine' - } - adapter2.onSuccess.attach(function(sender, fit) { +CalendarWidget.prototype.loadData = function(data) { + this.events = []; - }); - adapter2.addFitStructureData(fit); + for ( var i = 0; i < data.length; i++) { + var date = moment(data[i].creationDate); + var textColor = 'black'; - }); - adapter.addWorkflow(workflow, inputs); + if (data[i].status == "FINISHED") { + textColor = 'green'; + } + if (data[i].status == "ABORTED") { + textColor = 'red'; + } + this.events.push({ + title : data[i].name, + start : date.format("YYYY-MM-DD HH:mm:ss"), + end : date.format("YYYY-MM-DD HH:mm:ss"), + date : date, + allDay : false, + color : textColor, + className : date.format("YYYY-MM-DD") + }); + } }; -DataCollectionWidget.prototype.getSuperpositionTab = function(data) { +CalendarWidget.prototype.draw = function(targetId) { var _this = this; + $('#' + targetId).fullCalendar({ + eventClick : function(calEvent, jsEvent, view) { + _this.onClick.notify(calEvent.className[0]); - this.superpositionGrid = new SuperpositionGrid(); - - return Ext.create('Ext.container.Container', { - style : { - margin : '10px 10px 10px 10px' - }, - border : 0, - style : { - borderColor : '#000000', - borderStyle : 'solid', - borderWidth : '1px' - }, - items : [ _this.superpositionGrid.getPanel() ], - listeners : { - cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { - - }, - afterrender : function() { - /** Getting Rigid bodies **/ - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, result) { - _this.superpositionGrid.refresh(result); - }); - adapter.getSuperpositionBySubtractionId(data[1].subtractionId); - } - } - }); -}; - -DataCollectionWidget.prototype.getAdvancedTab = function(data) { - var _this = this; - return Ext.create('Ext.container.Container', { - style : { - margin : '10px 10px 10px 10px' }, - border : 0, - style : { - borderColor : '#000000', - borderStyle : 'solid', - borderWidth : '1px' + contentHeight : _this.height, + header : { + left : 'prev,next today', + center : 'title', + right : 'month,basicWeek,basicDay' }, - items : [ _this.getFitStructurePanel(data), _this.getFitStructurePanelWorkflow(data) ] + editable : false, + events : this.events }); -}; - -DataCollectionWidget.prototype.getRigiBodyForm = function(data) { - var _this = this; - _this.rigiBodyGrid = new RigidModelGrid(); - return _this.rigiBodyGrid.getPanel(); }; + +/** + * It shows a table with the guinier and gnom data as well as passed and + * discarded measurements + * + * @height + * @showBufferColumns + */ +function ConcentrationHTMLTableWidget(args) { + this.id = BUI.id(); -DataCollectionWidget.prototype.getRigidBodyModelingTab = function(data) { - var _this = this; - return Ext.create('Ext.container.Container', { - style : { - margin : '10px 10px 10px 10px' - }, - border : 0, - style : { - borderColor : '#000000', - borderStyle : 'solid', - borderWidth : '1px' - }, - items : [ _this.getRigiBodyForm(data) ], - listeners : { - cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + this.showBufferColumns = true; - }, - afterrender : function() { - /** Getting Rigid bodies **/ - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, result) { - _this.rigiBodyGrid.refresh(result); - }); - adapter.getRigidBodyModelingBySubtractionId(data[1].subtractionId); - } + if (args != null) { + if (args.height != null) { + this.height = args.height; } - }); -}; - -DataCollectionWidget.prototype.getTabs = function(data) { - var _this = this; - - this.subtractionCurveVisualizer = new SubtractionCurveVisualizer(); - - this.tabs = Ext.createWidget('tabpanel', { - activeTab : 1, - plain : true, - defaults : { - autoScroll : true, - bodyPadding : 10 - }, - items : [ { - title : 'Solution', - items : [ _this.getSpecimenContainer(data) ] - }, { - title : 'Primary Data Reduction', - active : true, - tabConfig : { - xtype : 'tab', - margins : '0 0 0 20', - }, - items : [ _this.subtractionCurveVisualizer.getPanel() ] - }, { - title : 'Abinitio', - items : [ _this.getAbinitioModellingContainer(data), _this.getSeparator(), _this.getModelViz(data) ] - }, { - title : 'Mixtures', - items : [ _this.getAdvancedTab(data) ] - }, { - title : 'Superpositions', - items : [ _this.getSuperpositionTab(data) ] - }, { - title : 'Rigid Body Modeling', - items : [ _this.getRigidBodyModelingTab(data) ] - } ] - }); - return this.tabs; -}; + if (args.showBufferColumns != null) { + this.showBufferColumns = args.showBufferColumns; + } + } +} -DataCollectionWidget.prototype.getPanel = function(data) { - var _this = this; - _this.data = data; - this.panel = Ext.create('Ext.container.Container', { - width : 1000, - layout : { - type : 'vbox', - align : 'stretch', - padding : 5 - }, - items : [ _this.getTabs(data) +ConcentrationHTMLTableWidget.prototype.refresh = function(parsedData) { + document.getElementById(this.id).innerHTML = this.getPanel(parsedData); +}; - ] - }); +ConcentrationHTMLTableWidget.prototype.getPanel = function(parsedData) { + var html = "
"; + html = html + ""; - this.panel.on("afterrender", function() { - _this.subtractionCurveVisualizer.refresh([ _this.data[1].mergeId ], [ _this.data[1].subtractionId ]); - }); - return this.panel; -}; + /** Title * */ + html = html + ""; + html = html + ""; + if (this.showBufferColumns) { + html = html + ""; + } + html = html + ""; + html = html + ""; + html = html + ""; + html = html + ""; -DataCollectionWidget.prototype.getModelViz = function(data) { - this.modelViz = new ModelVisualizerForm({ - height : 800, - width : 1000 - }); - return this.modelViz.getPanel(); -}; + html = html + ""; + html = html + ""; + html = html + ""; + html = html + ""; + html = html + ""; + html = html + ""; + html = html + ""; + html = html + ""; + html = html + ""; -/** - * PRIMARY DATA PROCESSING - */ -DataCollectionWidget.prototype.getPrimaryDataProcessingContainer = function(data) { - var _this = this; - this.primaryDataProcessingGrid = new PrimaryDataProcessingGrid({ - height : 250, - width : 1000, - title : 'Primary Data Processing' + parsedData.concentration.concentrations.sort(function(a, b) { + return a.concentration - b.concentration; }); + /** Row * */ + for ( var i = 0; i < parsedData.concentration.concentrations.length; i++) { + var row = parsedData[i]; - this.primaryDataProcessingGrid.onSelected.attach(function(sender, selected) { - /** Refresh tabs **/ - var averagesId = []; - var subtractionIds = []; - - var subtractionKeys = {}; - for (var i = 0; i < selected.length; i++) { - if (selected[i].mergeId != null) { - averagesId.push(selected[i].mergeId); - } + var tr = ""; + if (i % 2 == 1) { + tr = ""; + } + var goodMeasurements = (parsedData.concentration.concentrations[i].frames.length - parsedData.concentration.concentrations[i].frames_warning); + if (goodMeasurements == 0) { + tr = ""; + } + html = html + tr; + html = html + ""; + if (this.showBufferColumns) { + html = html + ""; + } + html = html + ""; + html = html + ""; + html = html + ""; + html = html + ""; + html = html + ""; + html = html + ""; + html = html + ""; + html = html + ""; + } + return html + "
ConcentrationBufferMeasurementsGuinierGnom
PassedDiscardedRgI0QualityRgdMax
" + parsedData.concentration.concentrations[i].concentration + "" + parsedData.concentration.concentrations[i].bufferAcronym + "" + goodMeasurements + " of " + parsedData.concentration.concentrations[i].frames.length + "" + parsedData.concentration.concentrations[i].frames_warning + "" + BUI.formatValuesErrorUnits(parsedData.concentration.concentrations[i].calculation.rgGuinier.avg, parsedData.concentration.concentrations[i].calculation.rgGuinier.std, "nm", { + lineBreak : false + }) + "" + BUI.formatValuesErrorUnits(parsedData.concentration.concentrations[i].calculation.i0Guinier.avg, parsedData.concentration.concentrations[i].calculation.i0Guinier.std, " ", { + lineBreak : false + }) + "" + BUI.formatValuesErrorUnits(parsedData.concentration.concentrations[i].calculation.quality.avg, parsedData.concentration.concentrations[i].calculation.quality.std, " ", { + lineBreak : false + }) + "" + BUI.formatValuesErrorUnits(parsedData.concentration.concentrations[i].calculation.rgGnom.avg, parsedData.concentration.concentrations[i].calculation.rgGnom.std, " ", { + lineBreak : false + }) + "" + BUI.formatValuesErrorUnits(parsedData.concentration.concentrations[i].calculation.dMax.avg, parsedData.concentration.concentrations[i].calculation.dMax.std, " ", { + lineBreak : false + }) + "
"; +}; - if (selected[i].subtractionId != null) { - if (selected[i].macromoleculeId != null) { - if (subtractionKeys[selected[i].subtractionId] == null) { - subtractionIds.push(selected[i].subtractionId); +ConcentrationHTMLTableWidget.prototype.input = function() { + return { + data : { + "dataCollectionCount" : 4, + "buffers" : [ { + "bufferId" : "422" + } ], + "concentration" : { + "concentrations" : [ { + "concentration" : "7.17", + "id" : "7.1699999999999999", + "bufferId" : "422.00", + "bufferAcronym" : "B1", + "rgGuinier" : [ "5.12723" ], + "frames" : [ { + "total" : "0.515705406286", + "bufferBeforeMeasurementId" : 15045, + "sampleMergeId" : 8176, + "averagedModelId" : null, + "I0" : "183.457461646", + "proposalCode" : "OPD", + "averagedModel" : null, + "bufferAfterMergeId" : 8177, + "framesCount" : "10", + "bufferBeforeMergeId" : 8175, + "averageFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_065_ave.dat", + "bufferAfterMeasurementId" : 15048, + "rgGuinier" : "5.12723", + "subtractedFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_065_sub.dat", + "firstPointUsed" : "17", + "chi2RgFilePath" : null, + "abInitioModelId" : null, + "macromoleculeId" : 649, + "code" : "_4.0_7.17", + "transmission" : "100.0", + "timeStart" : "2013-06-05 15:43:54.348723", + "bufferAcronym" : "B1", + "quality" : "0.853011", + "shapeDeterminationModelId" : null, + "expermientComments" : "[BsxCube] Generated from BsxCube", + "bufferId" : 422, + "isagregated" : "False", + "dmax" : "25.63615", + "bufferBeforeAverageFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_064_ave.dat", + "exposureTemperature" : "4.0", + "subtractionId" : 3377, + "conc" : "7.1699999999999999", + "rapidShapeDeterminationModel" : null, + "experimentCreationDate" : "Jun 5, 2013 3:30:30 PM", + "lastPointUsed" : "36", + "modelListId" : null, + "framesMerge" : "9", + "rapidShapeDeterminationModelId" : null, + "bufferAfterAverageFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_066_ave.dat", + "nsdFilePath" : null, + "bufferAfterFramesMerged" : "10", + "experimentId" : 700, + "macromoleculeAcronym" : "L9", + "i0stdev" : "0.139364435146", + "sampleMeasurementId" : 15047, + "measurementComments" : "[5]", + "priorityLevelId" : 10, + "bufferBeforeFramesMerged" : "10", + "substractionCreationTime" : "Jun 5, 2013 3:46:31 PM", + "proposalNumber" : "29", + "rgGnom" : "5.40297914939", + "volume" : "475.302", + "shapeDeterminationModel" : null, + "comments" : null, + "groupeField" : "L9 B1" + } ], + "frames_warning" : 0, + "calculation" : { + "rgGuinier" : { + "std" : 0, + "sum" : 5.12723, + "avg" : 5.12723, + "validNumber" : 1, + "totalNumber" : 1, + "values" : [ "5.12723" ] + }, + "i0Guinier" : { + "std" : 0, + "sum" : 183.457461646, + "avg" : 183.457461646, + "validNumber" : 1, + "totalNumber" : 1, + "values" : [ "183.457461646" ] + }, + "quality" : { + "std" : 0, + "sum" : 0.853011, + "avg" : 0.853011, + "validNumber" : 1, + "totalNumber" : 1, + "values" : [ "0.853011" ] + }, + "rgGnom" : { + "std" : 0, + "sum" : 5.40297914939, + "avg" : 5.40297914939, + "validNumber" : 1, + "totalNumber" : 1, + "values" : [ "5.40297914939" ] + }, + "dMax" : { + "std" : 0, + "sum" : 25.63615, + "avg" : 25.63615, + "validNumber" : 1, + "totalNumber" : 1, + "values" : [ "25.63615" ] + } + } + }, { + "concentration" : "3.53", + "id" : "3.5299999999999998", + "bufferId" : "422.00", + "bufferAcronym" : "B1", + "rgGuinier" : [ "4.38278" ], + "frames" : [ { + "total" : "0.497164741078", + "bufferBeforeMeasurementId" : 15048, + "sampleMergeId" : 8178, + "averagedModelId" : null, + "I0" : "160.023229462", + "proposalCode" : "OPD", + "averagedModel" : null, + "bufferAfterMergeId" : 8179, + "framesCount" : "10", + "bufferBeforeMergeId" : 8177, + "averageFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_067_ave.dat", + "bufferAfterMeasurementId" : 15051, + "rgGuinier" : "4.38278", + "subtractedFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_067_sub.dat", + "firstPointUsed" : "36", + "chi2RgFilePath" : null, + "abInitioModelId" : null, + "macromoleculeId" : 649, + "code" : "_4.0_3.53", + "transmission" : "100.0", + "timeStart" : "2013-06-05 15:47:04.630129", + "bufferAcronym" : "B1", + "quality" : "0.742825", + "shapeDeterminationModelId" : null, + "expermientComments" : "[BsxCube] Generated from BsxCube", + "bufferId" : 422, + "isagregated" : "False", + "dmax" : "15.33973", + "bufferBeforeAverageFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_066_ave.dat", + "exposureTemperature" : "4.0", + "subtractionId" : 3378, + "conc" : "3.5299999999999998", + "rapidShapeDeterminationModel" : null, + "experimentCreationDate" : "Jun 5, 2013 3:30:30 PM", + "lastPointUsed" : "61", + "modelListId" : null, + "framesMerge" : "10", + "rapidShapeDeterminationModelId" : null, + "bufferAfterAverageFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_068_ave.dat", + "nsdFilePath" : null, + "bufferAfterFramesMerged" : "10", + "experimentId" : 700, + "macromoleculeAcronym" : "L9", + "i0stdev" : "0.1499898017", + "sampleMeasurementId" : 15050, + "measurementComments" : "[6]", + "priorityLevelId" : 12, + "bufferBeforeFramesMerged" : "10", + "substractionCreationTime" : "Jun 5, 2013 3:49:42 PM", + "proposalNumber" : "29", + "rgGnom" : "4.40615474861", + "volume" : "372.656", + "shapeDeterminationModel" : null, + "comments" : null, + "groupeField" : "L9 B1" + } ], + "frames_warning" : 0, + "calculation" : { + "rgGuinier" : { + "std" : 0, + "sum" : 4.38278, + "avg" : 4.38278, + "validNumber" : 1, + "totalNumber" : 1, + "values" : [ "4.38278" ] + }, + "i0Guinier" : { + "std" : 0, + "sum" : 160.023229462, + "avg" : 160.023229462, + "validNumber" : 1, + "totalNumber" : 1, + "values" : [ "160.023229462" ] + }, + "quality" : { + "std" : 0, + "sum" : 0.742825, + "avg" : 0.742825, + "validNumber" : 1, + "totalNumber" : 1, + "values" : [ "0.742825" ] + }, + "rgGnom" : { + "std" : 0, + "sum" : 4.40615474861, + "avg" : 4.40615474861, + "validNumber" : 1, + "totalNumber" : 1, + "values" : [ "4.40615474861" ] + }, + "dMax" : { + "std" : 0, + "sum" : 15.33973, + "avg" : 15.33973, + "validNumber" : 1, + "totalNumber" : 1, + "values" : [ "15.33973" ] + } + } + }, { + "concentration" : "1.75", + "id" : "1.75", + "bufferId" : "422.00", + "bufferAcronym" : "B1", + "rgGuinier" : [], + "frames" : [ { + "total" : "0.5538035065", + "bufferBeforeMeasurementId" : 15051, + "sampleMergeId" : 8180, + "averagedModelId" : null, + "I0" : "146.927428571", + "proposalCode" : "OPD", + "averagedModel" : null, + "bufferAfterMergeId" : 8181, + "framesCount" : "10", + "bufferBeforeMergeId" : 8179, + "averageFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_069_ave.dat", + "bufferAfterMeasurementId" : 15054, + "rgGuinier" : "4.27139", + "subtractedFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_069_sub.dat", + "firstPointUsed" : "33", + "chi2RgFilePath" : null, + "abInitioModelId" : null, + "macromoleculeId" : 649, + "code" : "_4.0_1.75", + "transmission" : "100.0", + "timeStart" : "2013-06-05 15:50:09.395824", + "bufferAcronym" : "B1", + "quality" : "0.765999", + "shapeDeterminationModelId" : null, + "expermientComments" : "[BsxCube] Generated from BsxCube", + "bufferId" : 422, + "isagregated" : "False", + "dmax" : "14.949865", + "bufferBeforeAverageFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_068_ave.dat", + "exposureTemperature" : "4.0", + "subtractionId" : 3379, + "conc" : "1.75", + "rapidShapeDeterminationModel" : null, + "experimentCreationDate" : "Jun 5, 2013 3:30:30 PM", + "lastPointUsed" : "62", + "modelListId" : null, + "framesMerge" : "6", + "rapidShapeDeterminationModelId" : null, + "bufferAfterAverageFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_070_ave.dat", + "nsdFilePath" : null, + "bufferAfterFramesMerged" : "10", + "experimentId" : 700, + "macromoleculeAcronym" : "L9", + "i0stdev" : "0.191914285714", + "sampleMeasurementId" : 15053, + "measurementComments" : "[7]", + "priorityLevelId" : 14, + "bufferBeforeFramesMerged" : "10", + "substractionCreationTime" : "Jun 5, 2013 3:52:31 PM", + "proposalNumber" : "29", + "rgGnom" : "4.28379338749", + "volume" : "356.326", + "shapeDeterminationModel" : null, + "comments" : null, + "groupeField" : "L9 B1" + } ], + "frames_warning" : 1, + "calculation" : { + "rgGuinier" : { + "std" : null, + "sum" : 0, + "avg" : 0, + "validNumber" : 0, + "totalNumber" : 0, + "values" : [] + }, + "i0Guinier" : { + "std" : null, + "sum" : 0, + "avg" : 0, + "validNumber" : 0, + "totalNumber" : 0, + "values" : [] + }, + "quality" : { + "std" : null, + "sum" : 0, + "avg" : 0, + "validNumber" : 0, + "totalNumber" : 0, + "values" : [] + }, + "rgGnom" : { + "std" : null, + "sum" : 0, + "avg" : 0, + "validNumber" : 0, + "totalNumber" : 0, + "values" : [] + }, + "dMax" : { + "std" : null, + "sum" : 0, + "avg" : 0, + "validNumber" : 0, + "totalNumber" : 0, + "values" : [] + } + } + }, { + "concentration" : "0.91", + "id" : "0.91000000000000003", + "bufferId" : "422.00", + "bufferAcronym" : "B1", + "rgGuinier" : [], + "frames" : [ { + "total" : null, + "bufferBeforeMeasurementId" : 15054, + "sampleMergeId" : 8182, + "averagedModelId" : null, + "I0" : "0.0", + "proposalCode" : "OPD", + "averagedModel" : null, + "bufferAfterMergeId" : 8183, + "framesCount" : "10", + "bufferBeforeMergeId" : 8181, + "averageFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_071_ave.dat", + "bufferAfterMeasurementId" : 15057, + "rgGuinier" : null, + "subtractedFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_071_sub.dat", + "firstPointUsed" : "0", + "chi2RgFilePath" : null, + "abInitioModelId" : null, + "macromoleculeId" : 649, + "code" : "_4.0_.91", + "transmission" : "100.0", + "timeStart" : "2013-06-05 15:52:58.133705", + "bufferAcronym" : "B1", + "quality" : "0.0", + "shapeDeterminationModelId" : null, + "expermientComments" : "[BsxCube] Generated from BsxCube", + "bufferId" : 422, + "isagregated" : "False", + "dmax" : null, + "bufferBeforeAverageFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_070_ave.dat", + "exposureTemperature" : "4.0", + "subtractionId" : 3380, + "conc" : "0.91000000000000003", + "rapidShapeDeterminationModel" : null, + "experimentCreationDate" : "Jun 5, 2013 3:30:30 PM", + "lastPointUsed" : "0", + "modelListId" : null, + "framesMerge" : "10", + "rapidShapeDeterminationModelId" : null, + "bufferAfterAverageFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_072_ave.dat", + "nsdFilePath" : null, + "bufferAfterFramesMerged" : "10", + "experimentId" : 700, + "macromoleculeAcronym" : "L9", + "i0stdev" : "0.0", + "sampleMeasurementId" : 15056, + "measurementComments" : "[8]", + "priorityLevelId" : 16, + "bufferBeforeFramesMerged" : "10", + "substractionCreationTime" : "Jun 5, 2013 3:55:35 PM", + "proposalNumber" : "29", + "rgGnom" : null, + "volume" : null, + "shapeDeterminationModel" : null, + "comments" : null, + "groupeField" : "L9 B1" + } ], + "frames_warning" : 1, + "calculation" : { + "rgGuinier" : { + "std" : null, + "sum" : 0, + "avg" : 0, + "validNumber" : 0, + "totalNumber" : 0, + "values" : [] + }, + "i0Guinier" : { + "std" : null, + "sum" : 0, + "avg" : 0, + "validNumber" : 0, + "totalNumber" : 0, + "values" : [] + }, + "quality" : { + "std" : null, + "sum" : 0, + "avg" : 0, + "validNumber" : 0, + "totalNumber" : 0, + "values" : [] + }, + "rgGnom" : { + "std" : null, + "sum" : 0, + "avg" : 0, + "validNumber" : 0, + "totalNumber" : 0, + "values" : [] + }, + "dMax" : { + "std" : null, + "sum" : 0, + "avg" : 0, + "validNumber" : 0, + "totalNumber" : 0, + "values" : [] + } } - subtractionKeys[selected[i].subtractionId] = true; - } - } + } ] + }, + "concentrationLabel" : "7.17 mg/ml ,3.53 mg/ml ,1.75 mg/ml ,0.91 mg/ml " } - - /** Refreshing Primary Data Reduction **/ - _this.subtractionCurveVisualizer.refresh(averagesId, subtractionIds); - - }); - return this.primaryDataProcessingGrid.getPanel(data); + }; }; -/** - * getAbinitioModellingContainer - */ -DataCollectionWidget.prototype.getAbinitioModellingContainer = function(data) { - var _this = this; - - this.abinitioGrid = new AbinitioGrid(); - this.abinitioGrid.onSelected.attach(function(sender, models) { - _this.modelViz.refresh(models); - - }); - /** It may be abinitio models linked to the buffers **/ - var abinitioIdList = []; - for (var i = 0; i < data.length; i++) { - abinitioIdList.push(data[i].abInitioId); - } - - var uniqueIds = []; - $.each(abinitioIdList, function(i, el) { - if ($.inArray(el, uniqueIds) === -1) - uniqueIds.push(el); - }); - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, data) { - _this.abinitioGrid.refresh(data); - }); - adapter.getAbinitioByIdsList(uniqueIds); - return this.abinitioGrid.getPanel([]); +ConcentrationHTMLTableWidget.prototype.test = function(targetId) { + var concentrationHTMLTableWidget = new ConcentrationHTMLTableWidget(); + document.getElementById(targetId).innerHTML = concentrationHTMLTableWidget.getPanel(concentrationHTMLTableWidget.input().data); }; +function DataCollectionWidget() { + +}; + +DataCollectionWidget.prototype.refresh = function(subtractionId) { + +}; + +DataCollectionWidget.prototype.getMacromolecule = function(data) { + for (var i = 0; i < data.length; i++) { + if (data[i].macromoleculeId != null) { + return BIOSAXS.proposal.getMacromoleculeById(data[i].macromoleculeId); + } + } + return null; +}; + +DataCollectionWidget.prototype.getMacromoleculeContainer = function(data) { + var macromolecule = this.getMacromolecule(data); + + var disabled = false; + if (macromolecule == null) { + disabled = true; + } + + var acronym = (macromolecule == null ? "" : macromolecule.acronym); + var mm = (macromolecule == null ? "" : macromolecule.molecularMass); + var comments = (macromolecule == null ? "" : macromolecule.comments); + + return Ext.create('Ext.Panel', { + width : 500, + height : 200, + disabled : disabled, + title : "Macromolecule", + layout : 'form', + bodyPadding : 5, + defaultType : 'textfield', + tbar : { + defaultButtonUI : 'default', + items : [ { + xtype : 'button', + text : 'Edit', + cls : 'btn-with-border', + handler : function() { + var macromoleculeWindow = new MacromoleculeWindow(); + macromoleculeWindow.draw(macromolecule); + /** TODO: update when save **/ + } + } ] + }, + items : [ { + fieldLabel : 'Acronym', + name : 'first', + readOnly : true, + value : acronym + }, { + fieldLabel : 'Molecular Mass', + name : 'last', + readOnly : true, + value : mm + }, { + xtype : 'textareafield', + fieldLabel : 'Comments', + value : '', + name : 'last', + readOnly : true, + value : comments + } ] + }); +}; + +DataCollectionWidget.prototype.getBufferContainer = function(data) { + var _this = this; + var buffer = BIOSAXS.proposal.getBufferById(data[0].bufferId); + + return Ext.create('Ext.Panel', { + width : 500, + height : 200, + title : "Buffer", + layout : 'form', + // collapsed : true, + // collapsible : true, + bodyPadding : 5, + defaultType : 'textfield', + style : { + padding : '0px 0px 0px 2px' + }, + tbar : { + defaultButtonUI : 'default', + items : [ { + xtype : 'button', + text : 'Edit', + cls : 'btn-with-border', + handler : function() { + var bufferWindow = new BufferWindow(); + bufferWindow.draw(BIOSAXS.proposal.getBufferById(data[0].bufferId)); + /** TODO: update when save **/ + } + + } ] + }, + items : [ { + fieldLabel : 'Buffer', + name : 'acronym', + readOnly : true, + value : buffer.acronym + }, { + fieldLabel : 'Composition', + name : 'last', + readOnly : true, + value : buffer.composition + }, { + xtype : 'textareafield', + fieldLabel : 'Comments', + value : buffer.comments, + name : 'last', + readOnly : true + + } ] + }); +}; + +DataCollectionWidget.prototype.getSpecimenContainer = function(data) { + return Ext.create('Ext.container.Container', { + layout : { + type : 'hbox' + }, + style : { + margin : '10px 10px 10px 10px' + }, + border : 0, + style : { + borderColor : '#000000', + borderStyle : 'solid', + borderWidth : '1px' + }, + defaults : { + labelWidth : 80, + // implicitly create Container by specifying xtype + xtype : 'datefield', + flex : 1, + + }, + items : [ this.getMacromoleculeContainer(data), this.getBufferContainer(data) ] + }); +}; + +DataCollectionWidget.prototype.getSeparator = function() { + return { + html : "
", + border : 0 + } +}; + +DataCollectionWidget.prototype.getFitStructurePanel = function(data) { + var _this = this; + + return Ext.create('Ext.container.Container', { + layout : { + type : 'hbox' + }, + style : { + margin : '10px 10px 10px 10px' + }, + border : 0, + style : { + borderColor : '#000000', + borderStyle : 'solid', + borderWidth : '1px' + }, + defaults : { + labelWidth : 80, + xtype : 'datefield', + flex : 1 + }, + items : [ + + ] + }); +}; + +DataCollectionWidget.prototype.getFitStructurePanelWorkflow = function(data) { + var _this = this; + var macromolecule = this.getMacromolecule(data); + + var items = []; + + /** Adding all the pdb files linked to the macromolecule **/ + // if (macromolecule.structure3VOs != null) { + // if (macromolecule.structure3VOs.length > 0) { + // + // items.push(); + // } + // } else { + // items.push({ + // html : "No apriori information added to this macromolecule. Apriori information needed for further analysis", + // margin : "5 5 5 5" + // }); + // } + var fitStructureToExperimentDataGrid = new FitStructureToExperimentDataGrid(); + + fitStructureToExperimentDataGrid.refresh(_this.data[0].subtractionId, _this.getMacromolecule(data)); + return Ext.create('Ext.container.Container', { + style : { + margin : '10px 10px 10px 10px' + }, + border : 0, + + style : { + borderColor : '#000000', + borderStyle : 'solid', + borderWidth : '1px' + }, + defaults : { + labelWidth : 80, + xtype : 'datefield', + flex : 1 + }, + items : [ fitStructureToExperimentDataGrid.getPanel() ], + listeners : { + afterrender : function() { + } + } + }); +}; +/** Static method **/ +DataCollectionWidget.prototype.RUNFitScattering = function(subtractionId, structureId) { + var _this = this; + /** Add to Workflow **/ + var adapter = new BiosaxsDataAdapter(); + var workflow = { + 'workflowTitle' : 'FitExperimentalDatatoStructure', + 'comments' : 'FitExperimentalDatatoStructure run from ISPyB for subtractionId: ' + subtractionId + ' and structureId ' + structureId + } + + var inputs = [ { + name : 'subtractionId', + value : subtractionId + }, { + name : 'structureId', + value : structureId + } ]; + + adapter.onSuccess.attach(function(sender, data) { + /** Add to Fit **/ + var adapter2 = new BiosaxsDataAdapter(); + var fit = { + 'workflowId' : data.workflowId, + 'subtractionId' : subtractionId, + 'structureId' : structureId, + 'comments' : 'Sent to workflow engine' + } + adapter2.onSuccess.attach(function(sender, fit) { + + }); + adapter2.addFitStructureData(fit); + + }); + adapter.addWorkflow(workflow, inputs); + +}; + +DataCollectionWidget.prototype.getSuperpositionTab = function(data) { + var _this = this; + + this.superpositionGrid = new SuperpositionGrid(); + + return Ext.create('Ext.container.Container', { + style : { + margin : '10px 10px 10px 10px' + }, + border : 0, + style : { + borderColor : '#000000', + borderStyle : 'solid', + borderWidth : '1px' + }, + items : [ _this.superpositionGrid.getPanel() ], + listeners : { + cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + + }, + afterrender : function() { + /** Getting Rigid bodies **/ + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, result) { + _this.superpositionGrid.refresh(result); + }); + adapter.getSuperpositionBySubtractionId(data[1].subtractionId); + } + } + }); +}; + +DataCollectionWidget.prototype.getAdvancedTab = function(data) { + var _this = this; + return Ext.create('Ext.container.Container', { + style : { + margin : '10px 10px 10px 10px' + }, + border : 0, + style : { + borderColor : '#000000', + borderStyle : 'solid', + borderWidth : '1px' + }, + items : [ _this.getFitStructurePanel(data), _this.getFitStructurePanelWorkflow(data) ] + }); +}; + +DataCollectionWidget.prototype.getRigiBodyForm = function(data) { + var _this = this; + _this.rigiBodyGrid = new RigidModelGrid(); + return _this.rigiBodyGrid.getPanel(); + +}; + +DataCollectionWidget.prototype.getRigidBodyModelingTab = function(data) { + var _this = this; + return Ext.create('Ext.container.Container', { + style : { + margin : '10px 10px 10px 10px' + }, + border : 0, + style : { + borderColor : '#000000', + borderStyle : 'solid', + borderWidth : '1px' + }, + items : [ _this.getRigiBodyForm(data) ], + listeners : { + cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + + }, + afterrender : function() { + /** Getting Rigid bodies **/ + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, result) { + _this.rigiBodyGrid.refresh(result); + }); + adapter.getRigidBodyModelingBySubtractionId(data[1].subtractionId); + } + } + }); +}; + +DataCollectionWidget.prototype.getTabs = function(data) { + var _this = this; + + this.subtractionCurveVisualizer = new SubtractionCurveVisualizer(); + + this.tabs = Ext.createWidget('tabpanel', { + activeTab : 1, + plain : true, + defaults : { + autoScroll : true, + bodyPadding : 10 + }, + items : [ { + title : 'Solution', + items : [ _this.getSpecimenContainer(data) ] + }, { + title : 'Primary Data Reduction', + active : true, + tabConfig : { + xtype : 'tab', + margins : '0 0 0 20', + }, + items : [ _this.subtractionCurveVisualizer.getPanel() ] + }, { + title : 'Abinitio', + items : [ _this.getAbinitioModellingContainer(data), _this.getSeparator(), _this.getModelViz(data) ] + }, { + title : 'Mixtures', + items : [ _this.getAdvancedTab(data) ] + }, { + title : 'Superpositions', + items : [ _this.getSuperpositionTab(data) ] + }, { + title : 'Rigid Body Modeling', + items : [ _this.getRigidBodyModelingTab(data) ] + } ] + }); + return this.tabs; +}; + +DataCollectionWidget.prototype.getPanel = function(data) { + var _this = this; + _this.data = data; + this.panel = Ext.create('Ext.container.Container', { + width : 1000, + layout : { + type : 'vbox', + align : 'stretch', + padding : 5 + }, + items : [ _this.getTabs(data) + + ] + }); + + this.panel.on("afterrender", function() { + _this.subtractionCurveVisualizer.refresh([ _this.data[1].mergeId ], [ _this.data[1].subtractionId ]); + }); + return this.panel; +}; + +DataCollectionWidget.prototype.getModelViz = function(data) { + this.modelViz = new ModelVisualizerForm({ + height : 800, + width : 1000 + }); + return this.modelViz.getPanel(); +}; + +/** + * PRIMARY DATA PROCESSING + */ +DataCollectionWidget.prototype.getPrimaryDataProcessingContainer = function(data) { + var _this = this; + this.primaryDataProcessingGrid = new PrimaryDataProcessingGrid({ + height : 250, + width : 1000, + title : 'Primary Data Processing' + }); + + this.primaryDataProcessingGrid.onSelected.attach(function(sender, selected) { + /** Refresh tabs **/ + var averagesId = []; + var subtractionIds = []; + + var subtractionKeys = {}; + for (var i = 0; i < selected.length; i++) { + if (selected[i].mergeId != null) { + averagesId.push(selected[i].mergeId); + } + + if (selected[i].subtractionId != null) { + if (selected[i].macromoleculeId != null) { + if (subtractionKeys[selected[i].subtractionId] == null) { + subtractionIds.push(selected[i].subtractionId); + } + subtractionKeys[selected[i].subtractionId] = true; + } + } + } + + /** Refreshing Primary Data Reduction **/ + _this.subtractionCurveVisualizer.refresh(averagesId, subtractionIds); + + }); + return this.primaryDataProcessingGrid.getPanel(data); +}; +/** + * getAbinitioModellingContainer + */ +DataCollectionWidget.prototype.getAbinitioModellingContainer = function(data) { + var _this = this; + + this.abinitioGrid = new AbinitioGrid(); + this.abinitioGrid.onSelected.attach(function(sender, models) { + _this.modelViz.refresh(models); + + }); + /** It may be abinitio models linked to the buffers **/ + var abinitioIdList = []; + for (var i = 0; i < data.length; i++) { + abinitioIdList.push(data[i].abInitioId); + } + + var uniqueIds = []; + $.each(abinitioIdList, function(i, el) { + if ($.inArray(el, uniqueIds) === -1) + uniqueIds.push(el); + }); + + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, data) { + _this.abinitioGrid.refresh(data); + }); + adapter.getAbinitioByIdsList(uniqueIds); + return this.abinitioGrid.getPanel([]); +}; + /** * Edit the information of a buffer * @@ -29163,258 +29163,258 @@ ShippingWidget.prototype.refresh = function() { }; -/** - * Widget container of Specimen grid and samplePlate widget - * Depending of the sample changer layout it may be displayed vertically or horizontally - * - * @param args - * - * #onExperimentChanged It happens when specimen are modified - */ -function SpecimenWidget(args){ - - this.width = 1000; - this.height = 600; - - if (args != null){ - if (args.width != null){ - this.width = args.width; - } - if (args.height != null){ - this.height = args.height; - } - } - - var _this = this; - - /** Specimen Grid **/ - this.specimenGrid = new SpecimenGrid({ - minHeight : 425, - selectionMode : "SINGLE", - editEnabled : false, - updateRowEnabled : true, - width : 900, - showTitle : false - }); - - - this.specimenGrid.onSpecimenChanged.attach(function(sender, specimen) { - _this.experiment.setSpecimenById(specimen); - _this.refresh(_this.experiment); - }); - - this.specimenGrid.onSelected.attach(function(sender, specimens) { - if (specimens.length > 0) { - _this.specimenSelected = specimens[0]; - } else { - _this.specimenSelected = null; - } - _this.samplePlateGroupWidget.selectSpecimens(specimens); - }); - - - /** Sample plate Widget **/ - this.samplePlateGroupWidget = new SamplePlateGroupWidget({ - showTitle : false, - height : 250, - margin : 5, - bbar : true - }); - - - this.samplePlateGroupWidget.onExperimentChanged.attach(function(sender, json) { - _this.refresh(new Experiment(json)); - }); - - this.samplePlateGroupWidget.onClick.attach(function(sender, args) { - /** Clicking on a plate * */ - var row = args.row; - var column = args.column; - var samplePlateId = args.samplePlate.samplePlateId; - - /** is specimen selected on the grid? * */ - if (_this.specimenSelected != null) { - /** Is position target empty * */ - if (_this.experiment.getSampleByPosition(args.samplePlate.samplePlateId, args.row, args.column).length == 0) { - var specimen = _this.experiment.getSampleById(_this.specimenSelected.specimenId); - if (specimen.sampleplateposition3VO == null) { - specimen.sampleplateposition3VO = {}; - } - - specimen.sampleplateposition3VO = { - columnNumber : column, - rowNumber : row, - samplePlateId : samplePlateId - }; - - _this.samplePlateGroupWidget.panel.setLoading("ISPyB: Saving specimen"); - var adapter = new BiosaxsDataAdapter(); - /** If success * */ - adapter.onSuccess.attach(function(sender, experiment) { - _this.samplePlateGroupWidget.panel.setLoading(false); - }); - - adapter.onError.attach(function(sender, error) { - _this.samplePlateGroupWidget.panel.setLoading(false); - showError(error); - }); - - adapter.saveSpecimen(specimen, _this.experiment); - - _this.samplePlateGroupWidget.refresh(_this.experiment); - _this.specimenGrid.refresh(_this.experiment); - //_this.refresh(_this.experiment); - _this.specimenGrid.deselectAll(); - - } else { - /** - * Can we merge? We can merge when specimen are the - * same. So, same buffer, macromolecule, concentration * - */ - var target = _this.experiment.getSampleByPosition(args.samplePlate.samplePlateId, args.row, args.column)[0]; - var specimen = _this.experiment.getSampleById(_this.specimenSelected.specimenId); - - if ((specimen.bufferId == target.bufferId) && (specimen.concentration == target.concentration)) { - if (((specimen.macromolecule3VO != null) && (target.macromolecule3VO != null) && (specimen.macromolecule3VO.macromoleculeId == target.macromolecule3VO.macromoleculeId)) || - ((specimen.macromolecule3VO == null) && (target.macromolecule3VO == null))) { - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, data) { - _this.refresh(new Experiment(data)); - _this.samplePlateGroupWidget.panel.setLoading(false); - - _this.onExperimentChanged.notify(experiment); - }); - adapter.onError.attach(function(sender, error) { - _this.samplePlateGroupWidget.panel.setLoading(false); - showError(error); - }); - _this.samplePlateGroupWidget.panel.setLoading("ISPyB: Merging specimens"); - adapter.mergeSpecimens(specimen.specimenId, target.specimenId); - } - } else { - alert("Well is not empty. Select another well!"); - } - } - } else { - var specimen = _this.experiment.getSampleByPosition(args.samplePlate.samplePlateId, args.row, args.column)[0]; - if (specimen != null) { - _this.specimenGrid.selectById(specimen.specimenId); - } - } - }); - - /** Events **/ - this.onExperimentChanged = new Event(this); -}; - -/** - * Return vbox or hbox depending on the slot positions of the plates - */ -SpecimenWidget.prototype.getContainerLayoutConfiguration = function(experiment){ - var dimensions = this.samplePlateGroupWidget.getDimensions(experiment.getSamplePlates()); - if (dimensions.maxSlotPositionRow < dimensions.maxSlotPositionColumn){ - return { - layout : "vbox", - specimenGridWidth : this.width - 10, - specimenGridHeight : this.height - 260, - samplePlateGroupWidth : this.width - 10, - samplePlateGroupHeight : 250 - }; - } - return { - layout : "hbox", - samplePlateGroupWidth : this.width*1/3 -10, - samplePlateGroupHeight : this.height - 10, - specimenGridWidth : this.width*2/3, - specimenGridHeight : this.height - 10 - }; - -}; - - -SpecimenWidget.prototype.refresh = function(experiment){ - this.experiment = experiment; - - /** Removing all components **/ - this.panel.removeAll(); - - var layoutConfiguration = this.getContainerLayoutConfiguration(experiment); - - /** Setting new width and height for layout vbox and hbox **/ - this.specimenGrid.width = layoutConfiguration.specimenGridWidth; - this.specimenGrid.height = layoutConfiguration.specimenGridHeight; - - this.samplePlateGroupWidget.width = layoutConfiguration.samplePlateGroupWidth; - this.samplePlateGroupWidget.height = layoutConfiguration.samplePlateGroupHeight; - - if (layoutConfiguration.layout == "hbox"){ - this.specimenGrid.margin = "0 0 0 5"; - this.specimenGrid.width =this.specimenGrid.width - 5; - } - /** Insert container depending on layout [vertical|horizontal] */ - var container = Ext.create('Ext.container.Container', { - layout : layoutConfiguration.layout, - height : this.height, - width : this.width, - padding : '2px', - items : [ ] - }); - if (layoutConfiguration.layout == "vbox"){ - container.insert(this.specimenGrid.getPanel()); - container.insert(this.samplePlateGroupWidget.getPanel()); - } - else{ - container.insert(this.samplePlateGroupWidget.getPanel()); - container.insert(this.specimenGrid.getPanel()); - } - - /** Insert Widget **/ - this.panel.insert(container); - - /** Load data **/ - this.specimenGrid.refresh(experiment); - this.samplePlateGroupWidget.refresh(experiment); - - -}; - -/** It creates a dummy container to be inserted the plates once the method refresh has been called - * This is necessay because we can not know the sample changer layout before hand - * **/ -SpecimenWidget.prototype.getPanel = function(){ - this.panel = Ext.create('Ext.container.Container', { - layout : 'vbox', - height : this.height, - border : 0, - margin : 5, - width : this.width, - items : [] - }); - return this.panel; -}; - - -SpecimenWidget.prototype.input = function() { - return { - experiment : DATADOC.getExperiment_10(), - proposal : DATADOC.getProposal_10() - }; -}; - -SpecimenWidget.prototype.test = function(targetId) { - var specimenWidget = new SpecimenWidget({ - height : 500, - width : 1000 - }); - BIOSAXS.proposal = new Proposal(specimenWidget.input().proposal); - var experiment = new Experiment(specimenWidget.input().experiment); - var panel = specimenWidget.getPanel(); - panel.render(targetId); - specimenWidget.refresh(experiment); - -}; - - +/** + * Widget container of Specimen grid and samplePlate widget + * Depending of the sample changer layout it may be displayed vertically or horizontally + * + * @param args + * + * #onExperimentChanged It happens when specimen are modified + */ +function SpecimenWidget(args){ + + this.width = 1000; + this.height = 600; + + if (args != null){ + if (args.width != null){ + this.width = args.width; + } + if (args.height != null){ + this.height = args.height; + } + } + + var _this = this; + + /** Specimen Grid **/ + this.specimenGrid = new SpecimenGrid({ + minHeight : 425, + selectionMode : "SINGLE", + editEnabled : false, + updateRowEnabled : true, + width : 900, + showTitle : false + }); + + + this.specimenGrid.onSpecimenChanged.attach(function(sender, specimen) { + _this.experiment.setSpecimenById(specimen); + _this.refresh(_this.experiment); + }); + + this.specimenGrid.onSelected.attach(function(sender, specimens) { + if (specimens.length > 0) { + _this.specimenSelected = specimens[0]; + } else { + _this.specimenSelected = null; + } + _this.samplePlateGroupWidget.selectSpecimens(specimens); + }); + + + /** Sample plate Widget **/ + this.samplePlateGroupWidget = new SamplePlateGroupWidget({ + showTitle : false, + height : 250, + margin : 5, + bbar : true + }); + + + this.samplePlateGroupWidget.onExperimentChanged.attach(function(sender, json) { + _this.refresh(new Experiment(json)); + }); + + this.samplePlateGroupWidget.onClick.attach(function(sender, args) { + /** Clicking on a plate * */ + var row = args.row; + var column = args.column; + var samplePlateId = args.samplePlate.samplePlateId; + + /** is specimen selected on the grid? * */ + if (_this.specimenSelected != null) { + /** Is position target empty * */ + if (_this.experiment.getSampleByPosition(args.samplePlate.samplePlateId, args.row, args.column).length == 0) { + var specimen = _this.experiment.getSampleById(_this.specimenSelected.specimenId); + if (specimen.sampleplateposition3VO == null) { + specimen.sampleplateposition3VO = {}; + } + + specimen.sampleplateposition3VO = { + columnNumber : column, + rowNumber : row, + samplePlateId : samplePlateId + }; + + _this.samplePlateGroupWidget.panel.setLoading("ISPyB: Saving specimen"); + var adapter = new BiosaxsDataAdapter(); + /** If success * */ + adapter.onSuccess.attach(function(sender, experiment) { + _this.samplePlateGroupWidget.panel.setLoading(false); + }); + + adapter.onError.attach(function(sender, error) { + _this.samplePlateGroupWidget.panel.setLoading(false); + showError(error); + }); + + adapter.saveSpecimen(specimen, _this.experiment); + + _this.samplePlateGroupWidget.refresh(_this.experiment); + _this.specimenGrid.refresh(_this.experiment); + //_this.refresh(_this.experiment); + _this.specimenGrid.deselectAll(); + + } else { + /** + * Can we merge? We can merge when specimen are the + * same. So, same buffer, macromolecule, concentration * + */ + var target = _this.experiment.getSampleByPosition(args.samplePlate.samplePlateId, args.row, args.column)[0]; + var specimen = _this.experiment.getSampleById(_this.specimenSelected.specimenId); + + if ((specimen.bufferId == target.bufferId) && (specimen.concentration == target.concentration)) { + if (((specimen.macromolecule3VO != null) && (target.macromolecule3VO != null) && (specimen.macromolecule3VO.macromoleculeId == target.macromolecule3VO.macromoleculeId)) || + ((specimen.macromolecule3VO == null) && (target.macromolecule3VO == null))) { + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, data) { + _this.refresh(new Experiment(data)); + _this.samplePlateGroupWidget.panel.setLoading(false); + + _this.onExperimentChanged.notify(experiment); + }); + adapter.onError.attach(function(sender, error) { + _this.samplePlateGroupWidget.panel.setLoading(false); + showError(error); + }); + _this.samplePlateGroupWidget.panel.setLoading("ISPyB: Merging specimens"); + adapter.mergeSpecimens(specimen.specimenId, target.specimenId); + } + } else { + alert("Well is not empty. Select another well!"); + } + } + } else { + var specimen = _this.experiment.getSampleByPosition(args.samplePlate.samplePlateId, args.row, args.column)[0]; + if (specimen != null) { + _this.specimenGrid.selectById(specimen.specimenId); + } + } + }); + + /** Events **/ + this.onExperimentChanged = new Event(this); +}; + +/** + * Return vbox or hbox depending on the slot positions of the plates + */ +SpecimenWidget.prototype.getContainerLayoutConfiguration = function(experiment){ + var dimensions = this.samplePlateGroupWidget.getDimensions(experiment.getSamplePlates()); + if (dimensions.maxSlotPositionRow < dimensions.maxSlotPositionColumn){ + return { + layout : "vbox", + specimenGridWidth : this.width - 10, + specimenGridHeight : this.height - 260, + samplePlateGroupWidth : this.width - 10, + samplePlateGroupHeight : 250 + }; + } + return { + layout : "hbox", + samplePlateGroupWidth : this.width*1/3 -10, + samplePlateGroupHeight : this.height - 10, + specimenGridWidth : this.width*2/3, + specimenGridHeight : this.height - 10 + }; + +}; + + +SpecimenWidget.prototype.refresh = function(experiment){ + this.experiment = experiment; + + /** Removing all components **/ + this.panel.removeAll(); + + var layoutConfiguration = this.getContainerLayoutConfiguration(experiment); + + /** Setting new width and height for layout vbox and hbox **/ + this.specimenGrid.width = layoutConfiguration.specimenGridWidth; + this.specimenGrid.height = layoutConfiguration.specimenGridHeight; + + this.samplePlateGroupWidget.width = layoutConfiguration.samplePlateGroupWidth; + this.samplePlateGroupWidget.height = layoutConfiguration.samplePlateGroupHeight; + + if (layoutConfiguration.layout == "hbox"){ + this.specimenGrid.margin = "0 0 0 5"; + this.specimenGrid.width =this.specimenGrid.width - 5; + } + /** Insert container depending on layout [vertical|horizontal] */ + var container = Ext.create('Ext.container.Container', { + layout : layoutConfiguration.layout, + height : this.height, + width : this.width, + padding : '2px', + items : [ ] + }); + if (layoutConfiguration.layout == "vbox"){ + container.insert(this.specimenGrid.getPanel()); + container.insert(this.samplePlateGroupWidget.getPanel()); + } + else{ + container.insert(this.samplePlateGroupWidget.getPanel()); + container.insert(this.specimenGrid.getPanel()); + } + + /** Insert Widget **/ + this.panel.insert(container); + + /** Load data **/ + this.specimenGrid.refresh(experiment); + this.samplePlateGroupWidget.refresh(experiment); + + +}; + +/** It creates a dummy container to be inserted the plates once the method refresh has been called + * This is necessay because we can not know the sample changer layout before hand + * **/ +SpecimenWidget.prototype.getPanel = function(){ + this.panel = Ext.create('Ext.container.Container', { + layout : 'vbox', + height : this.height, + border : 0, + margin : 5, + width : this.width, + items : [] + }); + return this.panel; +}; + + +SpecimenWidget.prototype.input = function() { + return { + experiment : DATADOC.getExperiment_10(), + proposal : DATADOC.getProposal_10() + }; +}; + +SpecimenWidget.prototype.test = function(targetId) { + var specimenWidget = new SpecimenWidget({ + height : 500, + width : 1000 + }); + BIOSAXS.proposal = new Proposal(specimenWidget.input().proposal); + var experiment = new Experiment(specimenWidget.input().experiment); + var panel = specimenWidget.getPanel(); + panel.render(targetId); + specimenWidget.refresh(experiment); + +}; + + function WarningWidget(args) { this.actions = []; diff --git a/ispyb-ws/src/main/java/ispyb/ws/rest/mx/DataCollectionRestWebService.java b/ispyb-ws/src/main/java/ispyb/ws/rest/mx/DataCollectionRestWebService.java index 82f7cae2b..5a80a23f0 100644 --- a/ispyb-ws/src/main/java/ispyb/ws/rest/mx/DataCollectionRestWebService.java +++ b/ispyb-ws/src/main/java/ispyb/ws/rest/mx/DataCollectionRestWebService.java @@ -217,16 +217,16 @@ public Response getViewDataCollectionBySessionId(@PathParam("token") String toke @RolesAllowed({"User", "Manager", "Industrial", "Localcontact"}) @GET - @Path("{token}/proposal/{proposal}/mx/datacollection/session/{sessionId}/report/{reportType}/pdf") + @Path("{token}/proposal/{proposal}/mx/datacollection/session/{sessionId}/report/{nbRows}/pdf") @Produces({ "application/pdf" }) public Response getDataCollectionsReportBySessionIdPDF(@PathParam("token") String token, @PathParam("proposal") String proposal, - @PathParam("sessionId") String sessionId, @PathParam("reportType") String reportType) throws NamingException { + @PathParam("sessionId") String sessionId, @PathParam("nbRows") String nbRows) throws NamingException { String methodName = "getDataCollectionReportyBySessionIdPdf"; long start = this.logInit(methodName, logger, token, proposal, sessionId); try { - byte[] byteToExport = this.getPdfRtf(sessionId, proposal, reportType, false); + byte[] byteToExport = this.getPdfRtf(sessionId, proposal, nbRows, false); this.logFinish(methodName, start, logger); return this.downloadFile(byteToExport, "DataCollectionsReport.pdf"); @@ -237,16 +237,16 @@ public Response getDataCollectionsReportBySessionIdPDF(@PathParam("token") Strin @RolesAllowed({"User", "Manager", "Industrial", "Localcontact"}) @GET - @Path("{token}/proposal/{proposal}/mx/datacollection/session/{sessionId}/report/{reportType}/rtf") + @Path("{token}/proposal/{proposal}/mx/datacollection/session/{sessionId}/report/{nbRows}/rtf") @Produces({ "application/rtf" }) public Response getDataCollectionsReportBySessionIdRTF(@PathParam("token") String token, @PathParam("proposal") String proposal, - @PathParam("sessionId") String sessionId, @PathParam("reportType") String reportType) throws NamingException { + @PathParam("sessionId") String sessionId, @PathParam("nbRows") String nbRows) throws NamingException { String methodName = "getDataCollectionReportyBySessionIdRtf"; long start = this.logInit(methodName, logger, token, proposal, sessionId); try { - byte[] byteToExport = this.getPdfRtf(sessionId, proposal, reportType, true); + byte[] byteToExport = this.getPdfRtf(sessionId, proposal, nbRows, true); this.logFinish(methodName, start, logger); return this.downloadFile(byteToExport, "DataCollectionsReport.rtf"); @@ -355,22 +355,18 @@ public Response getViewDataCollectionByProteinAcronym(@PathParam("token") String } } - private byte [] getPdfRtf(String sessionId, String proposal, String reportType, boolean isRtf) throws NamingException, Exception { + private byte [] getPdfRtf(String sessionId, String proposal, String nbRows, boolean isRtf) throws NamingException, Exception { Integer id = new Integer(sessionId); List> dataCollections = this.getWebServiceDataCollectionGroup3Service().getViewDataCollectionBySessionId(this.getProposalId(proposal), id); - // for testing purposes, to be removed later - Integer nbRowsMax = new Integer(reportType); + Integer nbRowsMax = new Integer(nbRows); ExiPdfRtfExporter pdf = new ExiPdfRtfExporter(proposal, id , dataCollections, nbRowsMax); byte [] byteToExport = pdf.exportDataCollectionReport(isRtf).toByteArray(); - if (reportType.equals("1")) { - byteToExport = pdf.exportDataCollectionReport(isRtf).toByteArray(); - } return byteToExport; } From 7087e7b15847557329797a450ef69adb40e1a745 Mon Sep 17 00:00:00 2001 From: delageniere Date: Tue, 17 Oct 2017 17:36:14 +0200 Subject: [PATCH 13/38] reports improvements --- .../common/util/export/ExiPdfRtfExporter.java | 62 ++++++++++--------- 1 file changed, 34 insertions(+), 28 deletions(-) diff --git a/ispyb-ejb/src/main/java/ispyb/common/util/export/ExiPdfRtfExporter.java b/ispyb-ejb/src/main/java/ispyb/common/util/export/ExiPdfRtfExporter.java index ce9035ac5..be8cd28be 100644 --- a/ispyb-ejb/src/main/java/ispyb/common/util/export/ExiPdfRtfExporter.java +++ b/ispyb-ejb/src/main/java/ispyb/common/util/export/ExiPdfRtfExporter.java @@ -154,7 +154,8 @@ public class ExiPdfRtfExporter { public final static float CRYSTAL_IMAGE_HEIGHT = 174; - public final static float IMAGE_HEIGHT = 120; + //public final static float IMAGE_HEIGHT = 120; + public final static float IMAGE_HEIGHT = 100; // public final static float CRYSTAL_IMAGE_WIDTH = 160; // public final static float CRYSTAL_IMAGE_HEIGHT = 99; @@ -425,10 +426,10 @@ private void setDataCollectionTable(Document document) throws Exception { LOG.info("dcMap=" + dataCollectionMapData.toString()); setDataCollectionMapData(document, dataCollectionMapData); - if (getCellParam(dataCollectionMapData, "DataCollectionGroup_crystalClass") != null) { - String dcgId = getCellParam(dataCollectionMapData, "DataCollectionGroup_dataCollectionGroupId"); + if (getCellParam(dataCollectionMapData, "DataCollectionGroup_crystalClass", null) != null) { + String dcgId = getCellParam(dataCollectionMapData, "DataCollectionGroup_dataCollectionGroupId", null); if (!mapDataCollectionGroupIdCClass.containsKey(dcgId)){ - mapDataCollectionGroupIdCClass.put(dcgId, getCellParam(dataCollectionMapData, "DataCollectionGroup_crystalClass")); + mapDataCollectionGroupIdCClass.put(dcgId, getCellParam(dataCollectionMapData, "DataCollectionGroup_crystalClass", null)); } } i++; @@ -454,19 +455,20 @@ private void setDataCollectionTable(Document document) throws Exception { private void setDataCollectionMapData(Document document, Map dataCollectionMapItem) throws Exception { // 1st row - String parag = getCellParam(dataCollectionMapItem, "DataCollectionGroup_experimentType") - + " " + getCellParam(dataCollectionMapItem, "DataCollection_startTime"); + String parag = getCellParam(dataCollectionMapItem, "DataCollectionGroup_experimentType", null) + + " " + getCellParam(dataCollectionMapItem, "DataCollection_startTime", null); Paragraph p = new Paragraph(parag, FONT_DOC_BLUE); document.add(p); //row2 - parag = getCellParam(dataCollectionMapItem,"DataCollection_imageDirectory"); + parag = getCellParam(dataCollectionMapItem,"DataCollection_imageDirectory", null); document.add(new Paragraph(parag, FONT_DOC_ITALIC)); //row3 Table table = new Table(NB_COL_DATACOLLECTION); table.getDefaultCell().setHorizontalAlignment(Element.ALIGN_LEFT); table.getDefaultCell().setBorderWidth(0); + table.setBorder(0); // 1st Cell parag = "Workflow: \n" @@ -481,13 +483,13 @@ private void setDataCollectionMapData(Document document, Map dat table.addCell(p); // Cell2 - parag = getCellParam(dataCollectionMapItem, "Workflow_workflowType") + "\n" - + getCellParam(dataCollectionMapItem, "Protein_acronym") + "\n" - + getCellParam(dataCollectionMapItem, "BLSample_name") + "\n" - + getCellParam(dataCollectionMapItem, "DataCollection_imagePrefix") + "\n" - + getCellParam(dataCollectionMapItem, "DataCollection_dataCollectionNumber") + "\n" - + getCellParam(dataCollectionMapItem, "Protein_acronym") + "\n" - + getCellParam(dataCollectionMapItem, "DataCollection_transmission") + "\n"; + parag = getCellParam(dataCollectionMapItem, "Workflow_workflowType", null) + "\n" + + getCellParam(dataCollectionMapItem, "Protein_acronym", null) + "\n" + + getCellParam(dataCollectionMapItem, "BLSample_name", null) + "\n" + + getCellParam(dataCollectionMapItem, "DataCollection_imagePrefix", null) + "\n" + + getCellParam(dataCollectionMapItem, "DataCollection_dataCollectionNumber", null) + "\n" + + getCellParam(dataCollectionMapItem, "DataCollection_numberOfImages", null) + "\n" + + getCellParam(dataCollectionMapItem, "DataCollection_transmission", df2) + "\n"; LOG.info("parag=" + parag); p = new Paragraph(parag, FONT_DOC_BOLD); table.addCell(p); @@ -506,13 +508,13 @@ private void setDataCollectionMapData(Document document, Map dat // Cell 4 - parag = getCellParam(dataCollectionMapItem, "DataCollection_resolutionAtCorner")+ "\n" - + getCellParam(dataCollectionMapItem, "DataCollection_wavelength") + "\n" - + getCellParam(dataCollectionMapItem, "DataCollection_axisRange") + "\n" - + getCellParam(dataCollectionMapItem, "DataCollection_omegaStart") + "\n" - + getCellParam(dataCollectionMapItem, "exposureTime") + "\n" - + getCellParam(dataCollectionMapItem, "DataCollection_flux") + "\n" - + getCellParam(dataCollectionMapItem, "DataCollection_flux_end") + "\n" ; + parag = getCellParam(dataCollectionMapItem, "DataCollection_resolutionAtCorner", df2)+ "\n" + + getCellParam(dataCollectionMapItem, "DataCollection_wavelength", df3) + "\n" + + getCellParam(dataCollectionMapItem, "DataCollection_axisRange", df2) + "\n" + + getCellParam(dataCollectionMapItem, "DataCollection_omegaStart", df2) + "\n" + + getCellParam(dataCollectionMapItem, "exposureTime", df2) + "\n" + + getCellParam(dataCollectionMapItem, "DataCollection_flux", null) + "\n" + + getCellParam(dataCollectionMapItem, "DataCollection_flux_end", null) + "\n" ; table.addCell(new Paragraph(parag, FONT_DOC_BOLD)); @@ -523,8 +525,8 @@ private void setDataCollectionMapData(Document document, Map dat // 6 Cell : thumbnail - if (!getCellParam(dataCollectionMapItem, "lastImageId").isEmpty()) { - String thumbnailPath = (imageService.findByPk(new Integer(getCellParam(dataCollectionMapItem, "lastImageId")))).getJpegThumbnailFileFullPath(); + if (!getCellParam(dataCollectionMapItem, "lastImageId", null).isEmpty()) { + String thumbnailPath = (imageService.findByPk(new Integer(getCellParam(dataCollectionMapItem, "lastImageId", null)))).getJpegThumbnailFileFullPath(); Cell cellThumbnail = getCellImage(thumbnailPath); cellThumbnail.setBorderWidth(0); table.addCell(cellThumbnail); @@ -542,8 +544,8 @@ private void setDataCollectionMapData(Document document, Map dat table.addCell(cellSnapshot); // 8 Cell : graph or other plot - if (!getCellParam(dataCollectionMapItem, "DataCollection_dataCollectionId").isEmpty()) { - String plotPath = (dcService.findByPk(new Integer(getCellParam(dataCollectionMapItem, "DataCollection_dataCollectionId")), false, false)).getImageQualityIndicatorsPlotPath(); + if (!getCellParam(dataCollectionMapItem, "DataCollection_dataCollectionId", null).isEmpty()) { + String plotPath = (dcService.findByPk(new Integer(getCellParam(dataCollectionMapItem, "DataCollection_dataCollectionId", null)), false, false)).getImageQualityIndicatorsPlotPath(); Cell cellGraph = getCellImage(plotPath); cellGraph.setBorderWidth(0); table.addCell(cellGraph); @@ -569,11 +571,15 @@ private void setDataCollectionMapData(Document document, Map dat * @param dataCollectionMap * @throws Exception */ - private String getCellParam(Map dataCollectionMap, String param) throws Exception { + private String getCellParam(Map dataCollectionMap, String param, DecimalFormat df) throws Exception { String paramValue = ""; - if (dataCollectionMap.get(param) != null) { - paramValue = dataCollectionMap.get(param).toString(); + if (dataCollectionMap.get(param) != null) { + if (df == null){ + paramValue = dataCollectionMap.get(param).toString(); + } else { + paramValue = df.format(dataCollectionMap.get(param)).toString(); + } } return paramValue; From 1831bb4f53c348fd40bc27b57e2d3cf573f66504 Mon Sep 17 00:00:00 2001 From: delageniere Date: Wed, 18 Oct 2017 12:23:24 +0200 Subject: [PATCH 14/38] new type of report with analalysis data --- .../common/util/export/ExiPdfRtfExporter.java | 152 +++++++++++++++++- 1 file changed, 145 insertions(+), 7 deletions(-) diff --git a/ispyb-ejb/src/main/java/ispyb/common/util/export/ExiPdfRtfExporter.java b/ispyb-ejb/src/main/java/ispyb/common/util/export/ExiPdfRtfExporter.java index be8cd28be..7318750d4 100644 --- a/ispyb-ejb/src/main/java/ispyb/common/util/export/ExiPdfRtfExporter.java +++ b/ispyb-ejb/src/main/java/ispyb/common/util/export/ExiPdfRtfExporter.java @@ -130,6 +130,8 @@ public class ExiPdfRtfExporter { public final static Font FONT_INDEXING_SUCCESS = new Font(Font.HELVETICA, 8, Font.NORMAL, GREEN_COLOR); public final static int NB_COL_DATACOLLECTION = 8; + + public final static int NB_COL_DATA_ANALYSIS = 11; public final static int SIZE_FONT = 8; @@ -156,6 +158,7 @@ public class ExiPdfRtfExporter { //public final static float IMAGE_HEIGHT = 120; public final static float IMAGE_HEIGHT = 100; + public final static float IMAGE_HEIGHT_SMALL = 50; // public final static float CRYSTAL_IMAGE_WIDTH = 160; // public final static float CRYSTAL_IMAGE_HEIGHT = 99; @@ -440,6 +443,36 @@ private void setDataCollectionTable(Document document) throws Exception { document.add(new Paragraph(" ")); } + + /** + * set the dataCollection table + * + * @param document + * @throws Exception + */ + private void setDataAnalysisTable(Document document) throws Exception { + document.add(new Paragraph("Data Collections + Analysis:", FONT_TITLE)); + document.add(new Paragraph(" ")); + if (dataCollections.isEmpty()) { + document.add(new Paragraph("There is no data collection in this report", FONT_DOC)); + } else { + document.add(new Paragraph(" ")); + + // DataCollection Rows + Iterator> it = dataCollections.iterator(); + int i = 0; + while (it.hasNext() && i < nbRowsMax) { + Map dataCollectionMapData = it.next(); + LOG.info("dcMap=" + dataCollectionMapData.toString()); + setDataAnalysisMapData(document, dataCollectionMapData); + + i++; + } + document.add(new Paragraph(" ")); + } + document.add(new Paragraph(" ")); + + } /** * set a line for a specified dataCollection in the dataCollection table @@ -527,7 +560,7 @@ private void setDataCollectionMapData(Document document, Map dat if (!getCellParam(dataCollectionMapItem, "lastImageId", null).isEmpty()) { String thumbnailPath = (imageService.findByPk(new Integer(getCellParam(dataCollectionMapItem, "lastImageId", null)))).getJpegThumbnailFileFullPath(); - Cell cellThumbnail = getCellImage(thumbnailPath); + Cell cellThumbnail = getCellImage(thumbnailPath, IMAGE_HEIGHT); cellThumbnail.setBorderWidth(0); table.addCell(cellThumbnail); } else { @@ -538,7 +571,7 @@ private void setDataCollectionMapData(Document document, Map dat // 7 Cell : snapshot - Cell cellSnapshot = getCellImage(dataCollectionMapItem,"DataCollection_xtalSnapshotFullPath1"); + Cell cellSnapshot = getCellImage(dataCollectionMapItem,"DataCollection_xtalSnapshotFullPath1", IMAGE_HEIGHT); //cellSnapshot.setRowspan(nbRows); cellSnapshot.setBorderWidth(0); table.addCell(cellSnapshot); @@ -546,7 +579,7 @@ private void setDataCollectionMapData(Document document, Map dat // 8 Cell : graph or other plot if (!getCellParam(dataCollectionMapItem, "DataCollection_dataCollectionId", null).isEmpty()) { String plotPath = (dcService.findByPk(new Integer(getCellParam(dataCollectionMapItem, "DataCollection_dataCollectionId", null)), false, false)).getImageQualityIndicatorsPlotPath(); - Cell cellGraph = getCellImage(plotPath); + Cell cellGraph = getCellImage(plotPath, IMAGE_HEIGHT); cellGraph.setBorderWidth(0); table.addCell(cellGraph); } else { @@ -564,6 +597,111 @@ private void setDataCollectionMapData(Document document, Map dat return; } + /** + * set a line for a specified dataCollection in the dataCollection table + * + * @param document + * @param table + * @param col + * @param session + * @param df2 + * @param df3 + * @throws Exception + */ + private void setDataAnalysisMapData(Document document, Map dataCollectionMapItem) throws Exception { + + // 1st row + String parag = getCellParam(dataCollectionMapItem, "DataCollectionGroup_experimentType", null) + + " " + getCellParam(dataCollectionMapItem, "DataCollection_startTime", null); + Paragraph p = new Paragraph(parag, FONT_DOC_BLUE); + document.add(p); + + //row2 + parag = getCellParam(dataCollectionMapItem,"DataCollection_imageDirectory", null); + document.add(new Paragraph(parag, FONT_DOC_ITALIC)); + + //row 1 + Table table = new Table(NB_COL_DATA_ANALYSIS); + table.getDefaultCell().setHorizontalAlignment(Element.ALIGN_LEFT); + table.getDefaultCell().setBorderWidth(0); + table.setBorder(0); + + // 1st Cell + parag = "Protein: \n" + + " \n" + + "Prefix: \n" + + " \n" + + "Images: \n" + + " \n"; + LOG.info("parag=" + parag); + p = new Paragraph(parag, FONT_DOC); + table.addCell(p); + + // Cell 2 + parag = getCellParam(dataCollectionMapItem, "Protein_acronym", null) + "\n\n" + + getCellParam(dataCollectionMapItem, "DataCollection_imagePrefix", null) + "\n\n" + + getCellParam(dataCollectionMapItem, "DataCollection_numberOfImages", null) + "\n\n" ; + LOG.info("parag=" + parag); + p = new Paragraph(parag, FONT_DOC_BOLD); + table.addCell(p); + + // Cell 3 + parag = "Type: \n" + + "Res. (corner): \n" + + "Wavelength: \n" ; + LOG.info("parag=" + parag); + p = new Paragraph(parag, FONT_DOC); + table.addCell(p); + + // Cell 4 + parag = getCellParam(dataCollectionMapItem, "Workflow_workflowType", null) + "\n" + + getCellParam(dataCollectionMapItem, "DataCollection_resolutionAtCorner", df2)+ "\n" + + getCellParam(dataCollectionMapItem, "DataCollection_wavelength", df3) + "\n" ; + LOG.info("parag=" + parag); + p = new Paragraph(parag, FONT_DOC_BOLD); + table.addCell(p); + + // Cell 5 : image quality indicator plot + if (!getCellParam(dataCollectionMapItem, "DataCollection_dataCollectionId", null).isEmpty()) { + String plotPath = (dcService.findByPk(new Integer(getCellParam(dataCollectionMapItem, "DataCollection_dataCollectionId", null)), false, false)).getImageQualityIndicatorsPlotPath(); + Cell cellGraph = getCellImage(plotPath, IMAGE_HEIGHT); + cellGraph.setBorderWidth(0); + table.addCell(cellGraph); + } else { + table.addCell(" "); + } + + // Cell 6 indexed/strategy or completeness + //dataCollectionGroup.ScreeningOutput_indexingSuccess + //ScreeningOutput_strategySuccess + table.addCell(" "); + + // Cell 7 + //dataCollectionGroup.ScreeningOutput_mosaicity + //dataCollectionGroup.ScreeningOutputLattice_spaceGroup + table.addCell(" "); + + // Cell 8 + + //if (dataCollectionGroup.ScreeningOutputLattice_spaceGroup){ +//dataCollectionGroup.ScreeningOutputLattice_unitCell_a + ", " + dataCollectionGroup.ScreeningOutputLattice_unitCell_b + ", " + dataCollectionGroup.ScreeningOutputLattice_unitCell_c, + + table.addCell(" "); + + // Cell 9 + table.addCell(" "); + + // Cell 10 + table.addCell(" "); + + // Cell 11 + table.addCell(" "); + + document.add(table); + + return; + } + /** * get the value or replace by blank if null to fill a cell paragraph * @@ -606,14 +744,14 @@ private Cell getCellValue(String value) { * @return * @throws Exception */ - private Cell getCellImage(Map dataCollectionMapItem, String imageParam) throws Exception { + private Cell getCellImage(Map dataCollectionMapItem, String imageParam, float image_size) throws Exception { if (dataCollectionMapItem.get(imageParam) != null && !(dataCollectionMapItem.get(imageParam).toString()).equals("") ) { String image = dataCollectionMapItem.get(imageParam).toString(); image = PathUtils.getPath(image); try { Image jpg1 = Image.getInstance(image); - jpg1.scaleAbsolute(jpg1.getWidth() * IMAGE_HEIGHT / jpg1.getHeight(), IMAGE_HEIGHT); + jpg1.scaleAbsolute(jpg1.getWidth() * image_size / jpg1.getHeight(), image_size); Cell cell = new Cell(jpg1); cell.setLeading(0); cell.setBorderWidth(0); @@ -634,13 +772,13 @@ private Cell getCellImage(Map dataCollectionMapItem, String imag * @return * @throws Exception */ - private Cell getCellImage(String imagePath) throws Exception { + private Cell getCellImage(String imagePath, float image_size) throws Exception { if (imagePath != null ) { String image = PathUtils.getPath(imagePath); try { Image jpg1 = Image.getInstance(image); - jpg1.scaleAbsolute(jpg1.getWidth() * IMAGE_HEIGHT / jpg1.getHeight(), IMAGE_HEIGHT); + jpg1.scaleAbsolute(jpg1.getWidth() * image_size / jpg1.getHeight(), image_size); Cell cell = new Cell(jpg1); cell.setLeading(0); cell.setBorderWidth(0); From 3fda215b046f2585b0a6fcd755dbdb561266c775 Mon Sep 17 00:00:00 2001 From: delageniere Date: Thu, 19 Oct 2017 10:43:59 +0200 Subject: [PATCH 15/38] new ws for new report type --- .../common/util/export/ExiPdfRtfExporter.java | 67 +++++++++++++++---- .../rest/mx/DataCollectionRestWebService.java | 56 ++++++++++++++++ 2 files changed, 111 insertions(+), 12 deletions(-) diff --git a/ispyb-ejb/src/main/java/ispyb/common/util/export/ExiPdfRtfExporter.java b/ispyb-ejb/src/main/java/ispyb/common/util/export/ExiPdfRtfExporter.java index 7318750d4..2096c7c06 100644 --- a/ispyb-ejb/src/main/java/ispyb/common/util/export/ExiPdfRtfExporter.java +++ b/ispyb-ejb/src/main/java/ispyb/common/util/export/ExiPdfRtfExporter.java @@ -271,6 +271,50 @@ public ByteArrayOutputStream exportDataCollectionReport(boolean rtfFormat) throw } + public ByteArrayOutputStream exportDataCollectionAnalysisReport(boolean rtfFormat) throws Exception { + + this.init(); + // create simple doc and write to a ByteArrayOutputStream + Document document = new Document(PageSize.A4.rotate(), 20, 20, 20, 20); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + if (!rtfFormat) { + PdfWriter.getInstance(document, baos); + } else { + RtfWriter2.getInstance(document, baos); + } + + // ============================= + // Header + footer + // ============================= + + setHeader(document); + setFooter(document); + document.open(); + + // =============================== + // Body + // =============================== + // Crystallographer added only for IFX proposal in case of MXPress + // experiment + setCrystallographer(document); + // Session comments + setSessionComments(document); + // session title& info + setSessionTable(document); + + // ====================== + // Data Collection analysis table + // ====================== + document.add(new Paragraph(" ")); + setDataAnalysisTable(document); + + // ====================== + // End of file + // ====================== + document.close(); + return baos; + + } /** * sets the header in the specified document * @@ -609,16 +653,6 @@ private void setDataCollectionMapData(Document document, Map dat * @throws Exception */ private void setDataAnalysisMapData(Document document, Map dataCollectionMapItem) throws Exception { - - // 1st row - String parag = getCellParam(dataCollectionMapItem, "DataCollectionGroup_experimentType", null) - + " " + getCellParam(dataCollectionMapItem, "DataCollection_startTime", null); - Paragraph p = new Paragraph(parag, FONT_DOC_BLUE); - document.add(p); - - //row2 - parag = getCellParam(dataCollectionMapItem,"DataCollection_imageDirectory", null); - document.add(new Paragraph(parag, FONT_DOC_ITALIC)); //row 1 Table table = new Table(NB_COL_DATA_ANALYSIS); @@ -627,14 +661,14 @@ private void setDataAnalysisMapData(Document document, Map dataC table.setBorder(0); // 1st Cell - parag = "Protein: \n" + String parag = "Protein: \n" + " \n" + "Prefix: \n" + " \n" + "Images: \n" + " \n"; LOG.info("parag=" + parag); - p = new Paragraph(parag, FONT_DOC); + Paragraph p = new Paragraph(parag, FONT_DOC); table.addCell(p); // Cell 2 @@ -672,6 +706,15 @@ private void setDataAnalysisMapData(Document document, Map dataC } // Cell 6 indexed/strategy or completeness + String indexed = "success"; + if (!getCellParam(dataCollectionMapItem, "ScreeningOutput_indexingSuccess", null){ + indexed = "failed"; + } + parag = "Indexed: \n" + + "Strategy: \n" + ; + p = new Paragraph(parag, FONT_DOC); + table.addCell(p); //dataCollectionGroup.ScreeningOutput_indexingSuccess //ScreeningOutput_strategySuccess table.addCell(" "); diff --git a/ispyb-ws/src/main/java/ispyb/ws/rest/mx/DataCollectionRestWebService.java b/ispyb-ws/src/main/java/ispyb/ws/rest/mx/DataCollectionRestWebService.java index 5a80a23f0..dc246eb55 100644 --- a/ispyb-ws/src/main/java/ispyb/ws/rest/mx/DataCollectionRestWebService.java +++ b/ispyb-ws/src/main/java/ispyb/ws/rest/mx/DataCollectionRestWebService.java @@ -255,6 +255,46 @@ public Response getDataCollectionsReportBySessionIdRTF(@PathParam("token") Strin } } + @RolesAllowed({"User", "Manager", "Industrial", "Localcontact"}) + @GET + @Path("{token}/proposal/{proposal}/mx/datacollection/session/{sessionId}/analysisreport/{nbRows}/pdf") + @Produces({ "application/pdf" }) + public Response getDataCollectionsAnalysisReportBySessionIdPDF(@PathParam("token") String token, + @PathParam("proposal") String proposal, + @PathParam("sessionId") String sessionId, @PathParam("nbRows") String nbRows) throws NamingException { + + String methodName = "getDataCollectionAnalysisReportyBySessionIdPdf"; + long start = this.logInit(methodName, logger, token, proposal, sessionId); + try { + byte[] byteToExport = this.getAnalysisPdfRtf(sessionId, proposal, nbRows, false); + this.logFinish(methodName, start, logger); + return this.downloadFile(byteToExport, "DataCollectionsAnalysisReport.pdf"); + + } catch (Exception e) { + return this.logError(methodName, e, start, logger); + } + } + + @RolesAllowed({"User", "Manager", "Industrial", "Localcontact"}) + @GET + @Path("{token}/proposal/{proposal}/mx/datacollection/session/{sessionId}/analysisreport/{nbRows}/rtf") + @Produces({ "application/rtf" }) + public Response getDataCollectionsAnalysisReportBySessionIdRTF(@PathParam("token") String token, + @PathParam("proposal") String proposal, + @PathParam("sessionId") String sessionId, @PathParam("nbRows") String nbRows) throws NamingException { + + String methodName = "getDataCollectionReportyBySessionIdRtf"; + long start = this.logInit(methodName, logger, token, proposal, sessionId); + try { + byte[] byteToExport = this.getAnalysisPdfRtf(sessionId, proposal, nbRows, true); + this.logFinish(methodName, start, logger); + return this.downloadFile(byteToExport, "DataCollectionsAnalysisReport.rtf"); + + } catch (Exception e) { + return this.logError(methodName, e, start, logger); + } + } + @RolesAllowed({ "User", "Manager", "Industrial", "Localcontact" }) @GET @@ -370,4 +410,20 @@ public Response getViewDataCollectionByProteinAcronym(@PathParam("token") String return byteToExport; } + + private byte [] getAnalysisPdfRtf(String sessionId, String proposal, String nbRows, boolean isRtf) throws NamingException, Exception { + + Integer id = new Integer(sessionId); + + List> dataCollections = + this.getWebServiceDataCollectionGroup3Service().getViewDataCollectionBySessionId(this.getProposalId(proposal), id); + + Integer nbRowsMax = new Integer(nbRows); + + ExiPdfRtfExporter pdf = new ExiPdfRtfExporter(proposal, id , dataCollections, nbRowsMax); + + byte [] byteToExport = pdf.exportDataCollectionAnalysisReport(isRtf).toByteArray(); + + return byteToExport; + } } From 3bb2af8fbe928a59b87d842ce326a3c93983d055 Mon Sep 17 00:00:00 2001 From: delageniere Date: Thu, 19 Oct 2017 11:32:23 +0200 Subject: [PATCH 16/38] new ws for new report type --- .../common/util/export/ExiPdfRtfExporter.java | 55 ++++++++++++++++--- 1 file changed, 47 insertions(+), 8 deletions(-) diff --git a/ispyb-ejb/src/main/java/ispyb/common/util/export/ExiPdfRtfExporter.java b/ispyb-ejb/src/main/java/ispyb/common/util/export/ExiPdfRtfExporter.java index 2096c7c06..b1ce118d3 100644 --- a/ispyb-ejb/src/main/java/ispyb/common/util/export/ExiPdfRtfExporter.java +++ b/ispyb-ejb/src/main/java/ispyb/common/util/export/ExiPdfRtfExporter.java @@ -706,15 +706,32 @@ private void setDataAnalysisMapData(Document document, Map dataC } // Cell 6 indexed/strategy or completeness - String indexed = "success"; - if (!getCellParam(dataCollectionMapItem, "ScreeningOutput_indexingSuccess", null){ - indexed = "failed"; + String indexed = "failed"; + String strateg = "failed"; + Boolean indexing = getBoolean(dataCollectionMapItem, "ScreeningOutput_indexingSuccess"); + Boolean strategy = getBoolean(dataCollectionMapItem, "ScreeningOutput_strategySuccess"); + + if (indexing != null && strategy != null){ + + if (indexing ){ + indexed = "success"; + } + + if (strategy ){ + strateg = "success"; + } + + parag = "Indexed: "+ indexed + "\n" + + "Strategy:"+ strateg + "\n" + ; + p = new Paragraph(parag, FONT_DOC); + table.addCell(p); + + } else { + table.addCell(" "); } - parag = "Indexed: \n" - + "Strategy: \n" - ; - p = new Paragraph(parag, FONT_DOC); - table.addCell(p); + + //dataCollectionGroup.ScreeningOutput_indexingSuccess //ScreeningOutput_strategySuccess table.addCell(" "); @@ -765,6 +782,28 @@ private String getCellParam(Map dataCollectionMap, String param, return paramValue; } + + /** + * get the value as boolean to fill a cell and return a null value if no value or not an integer + * + * @param param + * @param dataCollectionMap + * @throws Exception + */ + private Boolean getBoolean(Map dataCollectionMap, String param) throws Exception { + + Boolean cellBool = null; + + if (dataCollectionMap.get(param) != null) { + + if (dataCollectionMap.get(param).equals("1") ){ + cellBool = true; + } else { + cellBool = false; + } + } + return cellBool; + } /** * returns a simple cell witha given value inside From d4e8fbe62c7ad5041f2aeb6f2071a7531437d34c Mon Sep 17 00:00:00 2001 From: delageniere Date: Thu, 19 Oct 2017 16:04:54 +0200 Subject: [PATCH 17/38] new ws for new report type --- .../common/util/export/ExiPdfRtfExporter.java | 129 ++++++++++-------- 1 file changed, 75 insertions(+), 54 deletions(-) diff --git a/ispyb-ejb/src/main/java/ispyb/common/util/export/ExiPdfRtfExporter.java b/ispyb-ejb/src/main/java/ispyb/common/util/export/ExiPdfRtfExporter.java index b1ce118d3..54ca6757a 100644 --- a/ispyb-ejb/src/main/java/ispyb/common/util/export/ExiPdfRtfExporter.java +++ b/ispyb-ejb/src/main/java/ispyb/common/util/export/ExiPdfRtfExporter.java @@ -61,6 +61,7 @@ import ispyb.server.mx.vos.collections.DataCollectionGroup3VO; import ispyb.server.mx.vos.collections.IspybCrystalClass3VO; import ispyb.server.mx.vos.collections.Session3VO; +import sun.tools.jstat.Alignment; /** * allows creation of PDF or RTF report - general report for EXI, available in the @@ -108,6 +109,8 @@ public class ExiPdfRtfExporter { public final static Font FONT_DOC = new Font(Font.HELVETICA, 8, Font.NORMAL, Color.BLACK); + public final static Font FONT_DOC_SMALL = new Font(Font.HELVETICA, 6, Font.NORMAL, Color.BLACK); + public final static Font FONT_DOC_BLUE = new Font(Font.HELVETICA, 8, Font.NORMAL, Color.BLUE); public final static Font FONT_DOC_ITALIC = new Font(Font.HELVETICA, 8, Font.ITALIC, Color.BLACK); @@ -121,6 +124,11 @@ public class ExiPdfRtfExporter { public final static Font FONT_DOC_EXPONENT_BLUE = new Font(Font.HELVETICA, 11, Font.NORMAL, Color.BLUE); public final static Font FONT_DOC_BOLD = new Font(Font.HELVETICA, 8, Font.BOLD); + + public final static Font FONT_DOC_SMALL_BOLD = new Font(Font.HELVETICA, 6, Font.BOLD); + + public final static Font FONT_DOC_SMALL_CENTERED = new Font(Font.HELVETICA, 6, Font.BOLD); + public final static Font FONT_DOC_PARAM_TITLE = new Font(Font.HELVETICA, 8, Font.NORMAL, LIGHT_GREY_COLOR); public final static Font FONT_INDEXING_NOTDONE = new Font(Font.HELVETICA, 8, Font.NORMAL, LIGHT_GREY_COLOR); @@ -495,12 +503,12 @@ private void setDataCollectionTable(Document document) throws Exception { * @throws Exception */ private void setDataAnalysisTable(Document document) throws Exception { - document.add(new Paragraph("Data Collections + Analysis:", FONT_TITLE)); + document.add(new Paragraph("Data Collections & Analysis results:", FONT_TITLE)); document.add(new Paragraph(" ")); if (dataCollections.isEmpty()) { document.add(new Paragraph("There is no data collection in this report", FONT_DOC)); } else { - document.add(new Paragraph(" ")); + //document.add(new Paragraph(" ")); // DataCollection Rows Iterator> it = dataCollections.iterator(); @@ -658,41 +666,40 @@ private void setDataAnalysisMapData(Document document, Map dataC Table table = new Table(NB_COL_DATA_ANALYSIS); table.getDefaultCell().setHorizontalAlignment(Element.ALIGN_LEFT); table.getDefaultCell().setBorderWidth(0); - table.setBorder(0); + table.setBorder(1); // 1st Cell - String parag = "Protein: \n" - + " \n" - + "Prefix: \n" - + " \n" - + "Images: \n" - + " \n"; + String parag = "Protein: \n\n" + + "Prefix: \n\n" + + "Images: \n\n" ; + LOG.info("parag=" + parag); - Paragraph p = new Paragraph(parag, FONT_DOC); + Paragraph p = new Paragraph(parag, FONT_DOC_SMALL); table.addCell(p); - // Cell 2 - parag = getCellParam(dataCollectionMapItem, "Protein_acronym", null) + "\n\n" - + getCellParam(dataCollectionMapItem, "DataCollection_imagePrefix", null) + "\n\n" - + getCellParam(dataCollectionMapItem, "DataCollection_numberOfImages", null) + "\n\n" ; + // 2st Cell + parag = getCellParam(dataCollectionMapItem, "Protein_acronym", null)+ "\n\n" + + getCellParam(dataCollectionMapItem, "DataCollection_imagePrefix", null) + "\n\n" + + getCellParam(dataCollectionMapItem, "DataCollection_numberOfImages", null) + "\n\n" ; + LOG.info("parag=" + parag); - p = new Paragraph(parag, FONT_DOC_BOLD); + p = new Paragraph(parag, FONT_DOC_SMALL_BOLD); table.addCell(p); - + // Cell 3 parag = "Type: \n" - + "Res. (corner): \n" - + "Wavelength: \n" ; + + "Res. (corner): \n" + + "Wavelength: \n" ; LOG.info("parag=" + parag); - p = new Paragraph(parag, FONT_DOC); + p = new Paragraph(parag, FONT_DOC_SMALL); table.addCell(p); // Cell 4 parag = getCellParam(dataCollectionMapItem, "Workflow_workflowType", null) + "\n" + getCellParam(dataCollectionMapItem, "DataCollection_resolutionAtCorner", df2)+ "\n" + getCellParam(dataCollectionMapItem, "DataCollection_wavelength", df3) + "\n" ; - LOG.info("parag=" + parag); - p = new Paragraph(parag, FONT_DOC_BOLD); + + p = new Paragraph(parag, FONT_DOC_SMALL_BOLD); table.addCell(p); // Cell 5 : image quality indicator plot @@ -706,56 +713,76 @@ private void setDataAnalysisMapData(Document document, Map dataC } // Cell 6 indexed/strategy or completeness - String indexed = "failed"; - String strateg = "failed"; Boolean indexing = getBoolean(dataCollectionMapItem, "ScreeningOutput_indexingSuccess"); Boolean strategy = getBoolean(dataCollectionMapItem, "ScreeningOutput_strategySuccess"); + p = new Paragraph(); + + Chunk chu1 = new Chunk("Indexed: ", FONT_DOC_SMALL); if (indexing != null && strategy != null){ + + Chunk chu2 = new Chunk( "Failed", FONT_INDEXING_FAILED); + if (indexing.booleanValue() ){ + chu2 = new Chunk( "Success", FONT_INDEXING_SUCCESS); + } + p.add(chu1); + p.add(chu2); - if (indexing ){ - indexed = "success"; - } - - if (strategy ){ - strateg = "success"; + chu1 = new Chunk("Strategy: ", FONT_DOC_SMALL); + chu2 = new Chunk( "Failed", FONT_INDEXING_FAILED); + if (strategy.booleanValue() ){ + chu2 = new Chunk( "Success", FONT_INDEXING_SUCCESS); } + p.add(chu1); + p.add(chu2); - parag = "Indexed: "+ indexed + "\n" - + "Strategy:"+ strateg + "\n" - ; - p = new Paragraph(parag, FONT_DOC); table.addCell(p); } else { table.addCell(" "); } - + LOG.info("parag=" + p.toString()); - //dataCollectionGroup.ScreeningOutput_indexingSuccess - //ScreeningOutput_strategySuccess - table.addCell(" "); - // Cell 7 - //dataCollectionGroup.ScreeningOutput_mosaicity - //dataCollectionGroup.ScreeningOutputLattice_spaceGroup - table.addCell(" "); + parag = "Space group: \n" + + "Mosaicity: \n" ; + LOG.info("parag=" + parag); + p = new Paragraph(parag, FONT_DOC_SMALL); + table.addCell(p); + // Cell 8 + parag = getCellParam(dataCollectionMapItem, "ScreeningOutputLattice_spaceGroup", null) + "\n" + + getCellParam(dataCollectionMapItem, "ScreeningOutput_mosaicity", null)+ "\n" ; + p = new Paragraph(parag, FONT_DOC_SMALL_BOLD); + table.addCell(p); + //if (dataCollectionGroup.ScreeningOutputLattice_spaceGroup){ //dataCollectionGroup.ScreeningOutputLattice_unitCell_a + ", " + dataCollectionGroup.ScreeningOutputLattice_unitCell_b + ", " + dataCollectionGroup.ScreeningOutputLattice_unitCell_c, - table.addCell(" "); // Cell 9 - table.addCell(" "); + parag = "a \n" + getCellParam(dataCollectionMapItem, "ScreeningOutputLattice_unitCell_a", null) + + "\n alpha \n" + getCellParam(dataCollectionMapItem, "ScreeningOutputLattice_unitCell_alpha", null) ; + p = new Paragraph(parag, FONT_DOC_SMALL_CENTERED); + p.setAlignment(Element.ALIGN_CENTER); + table.addCell(p); // Cell 10 - table.addCell(" "); + parag = "b \n" + getCellParam(dataCollectionMapItem, "ScreeningOutputLattice_unitCell_b", null) + + "\n beta \n" + getCellParam(dataCollectionMapItem, "ScreeningOutputLattice_unitCell_beta", null) ; + p = new Paragraph(parag, FONT_DOC_SMALL_CENTERED); + p.setAlignment(Element.ALIGN_CENTER); + table.addCell(p); // Cell 11 - table.addCell(" "); + parag = "c \n" + getCellParam(dataCollectionMapItem, "ScreeningOutputLattice_unitCell_c", null) + + "\n gamma \n" + getCellParam(dataCollectionMapItem, "ScreeningOutputLattice_unitCell_gama", null) ; + + p = new Paragraph(parag, FONT_DOC_SMALL_CENTERED); + p.setAlignment(Element.ALIGN_CENTER); + table.addCell(p); document.add(table); @@ -792,15 +819,9 @@ private String getCellParam(Map dataCollectionMap, String param, */ private Boolean getBoolean(Map dataCollectionMap, String param) throws Exception { - Boolean cellBool = null; - - if (dataCollectionMap.get(param) != null) { - - if (dataCollectionMap.get(param).equals("1") ){ - cellBool = true; - } else { - cellBool = false; - } + Boolean cellBool = null; + if (dataCollectionMap.get(param) != null) { + cellBool = new Boolean (dataCollectionMap.get(param).toString()); } return cellBool; } From fc90306d8dbaee56c9a635320b4eaae4ac1e2ffd Mon Sep 17 00:00:00 2001 From: delageniere Date: Thu, 19 Oct 2017 16:26:58 +0200 Subject: [PATCH 18/38] new version 5.2.1 --- ispyb-bcr-ear/pom.xml | 4 ++-- ispyb-bcr/pom.xml | 2 +- ispyb-ear/pom.xml | 6 +++--- ispyb-ejb/pom.xml | 2 +- ispyb-ui/pom.xml | 2 +- ispyb-ui/src/main/webapp/help/release_notes.txt | 9 +++++++++ ispyb-ws/pom.xml | 4 ++-- pom.xml | 2 +- 8 files changed, 20 insertions(+), 11 deletions(-) diff --git a/ispyb-bcr-ear/pom.xml b/ispyb-bcr-ear/pom.xml index cdac8b23b..14062c216 100644 --- a/ispyb-bcr-ear/pom.xml +++ b/ispyb-bcr-ear/pom.xml @@ -4,7 +4,7 @@ ispyb ispyb-parent - 5.2.0 + 5.2.1 ispyb-bcr-ear ear @@ -15,7 +15,7 @@ ispyb ispyb-ejb3 ejb - 5.2.0 + 5.2.1 provided diff --git a/ispyb-bcr/pom.xml b/ispyb-bcr/pom.xml index a3a64ce4b..c3ffd9a25 100644 --- a/ispyb-bcr/pom.xml +++ b/ispyb-bcr/pom.xml @@ -31,7 +31,7 @@ ispyb ispyb-ejb3 - 5.2.0 + 5.2.1 provided diff --git a/ispyb-ear/pom.xml b/ispyb-ear/pom.xml index ce16ed6b7..9f7d773e1 100644 --- a/ispyb-ear/pom.xml +++ b/ispyb-ear/pom.xml @@ -4,7 +4,7 @@ ispyb ispyb-parent - 5.2.0 + 5.2.1 ispyb-ear ear @@ -13,7 +13,7 @@ ispyb ispyb-ejb3 ejb - 5.2.0 + 5.2.1 @@ -26,7 +26,7 @@ ispyb ispyb-ws war - 5.2.0 + 5.2.1 diff --git a/ispyb-ejb/pom.xml b/ispyb-ejb/pom.xml index c6632b948..26e05d19d 100644 --- a/ispyb-ejb/pom.xml +++ b/ispyb-ejb/pom.xml @@ -4,7 +4,7 @@ ispyb ispyb-parent - 5.2.0 + 5.2.1 ispyb-ejb3 jar diff --git a/ispyb-ui/pom.xml b/ispyb-ui/pom.xml index 8710e2a4b..dd69688fc 100644 --- a/ispyb-ui/pom.xml +++ b/ispyb-ui/pom.xml @@ -22,7 +22,7 @@ ispyb ispyb-ejb3 - 5.2.0 + 5.2.1 provided diff --git a/ispyb-ui/src/main/webapp/help/release_notes.txt b/ispyb-ui/src/main/webapp/help/release_notes.txt index fd87424b4..b107e4d6f 100644 --- a/ispyb-ui/src/main/webapp/help/release_notes.txt +++ b/ispyb-ui/src/main/webapp/help/release_notes.txt @@ -1,3 +1,12 @@ +New TAG : 5.2.1 + + +************** +* 19.10.2017 * +************** + +- work in progress: #135 + ************** * 22.09.2017 * ************** diff --git a/ispyb-ws/pom.xml b/ispyb-ws/pom.xml index e3f4f6072..a4ba244f9 100644 --- a/ispyb-ws/pom.xml +++ b/ispyb-ws/pom.xml @@ -4,7 +4,7 @@ ispyb ispyb-parent - 5.2.0 + 5.2.1 ispyb-ws war @@ -12,7 +12,7 @@ ispyb ispyb-ejb3 - 5.2.0 + 5.2.1 provided diff --git a/pom.xml b/pom.xml index 3ab16191a..cbcba335f 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ 4.0.0 ispyb ispyb-parent - 5.2.0 + 5.2.1 pom ispyb-ejb From 8a24edbbacde3da0031f890531c0f949755845ed Mon Sep 17 00:00:00 2001 From: delageniere Date: Thu, 19 Oct 2017 16:32:12 +0200 Subject: [PATCH 19/38] new version 5.3.2 --- ispyb-bcr-ear/pom.xml | 4 ++-- ispyb-bcr/pom.xml | 2 +- ispyb-ear/pom.xml | 6 +++--- ispyb-ejb/pom.xml | 2 +- ispyb-ui/pom.xml | 2 +- ispyb-ws/pom.xml | 4 ++-- pom.xml | 2 +- 7 files changed, 11 insertions(+), 11 deletions(-) diff --git a/ispyb-bcr-ear/pom.xml b/ispyb-bcr-ear/pom.xml index 14062c216..f68ff45ae 100644 --- a/ispyb-bcr-ear/pom.xml +++ b/ispyb-bcr-ear/pom.xml @@ -4,7 +4,7 @@ ispyb ispyb-parent - 5.2.1 + 5.3.2 ispyb-bcr-ear ear @@ -15,7 +15,7 @@ ispyb ispyb-ejb3 ejb - 5.2.1 + 5.3.2 provided diff --git a/ispyb-bcr/pom.xml b/ispyb-bcr/pom.xml index c3ffd9a25..7ec9b4460 100644 --- a/ispyb-bcr/pom.xml +++ b/ispyb-bcr/pom.xml @@ -31,7 +31,7 @@ ispyb ispyb-ejb3 - 5.2.1 + 5.3.2 provided diff --git a/ispyb-ear/pom.xml b/ispyb-ear/pom.xml index 9f7d773e1..cbbe85d12 100644 --- a/ispyb-ear/pom.xml +++ b/ispyb-ear/pom.xml @@ -4,7 +4,7 @@ ispyb ispyb-parent - 5.2.1 + 5.3.2 ispyb-ear ear @@ -13,7 +13,7 @@ ispyb ispyb-ejb3 ejb - 5.2.1 + 5.3.2 @@ -26,7 +26,7 @@ ispyb ispyb-ws war - 5.2.1 + 5.3.2 diff --git a/ispyb-ejb/pom.xml b/ispyb-ejb/pom.xml index 26e05d19d..a2132d7f1 100644 --- a/ispyb-ejb/pom.xml +++ b/ispyb-ejb/pom.xml @@ -4,7 +4,7 @@ ispyb ispyb-parent - 5.2.1 + 5.3.2 ispyb-ejb3 jar diff --git a/ispyb-ui/pom.xml b/ispyb-ui/pom.xml index dd69688fc..0b3af3d9c 100644 --- a/ispyb-ui/pom.xml +++ b/ispyb-ui/pom.xml @@ -22,7 +22,7 @@ ispyb ispyb-ejb3 - 5.2.1 + 5.3.2 provided diff --git a/ispyb-ws/pom.xml b/ispyb-ws/pom.xml index a4ba244f9..c05712229 100644 --- a/ispyb-ws/pom.xml +++ b/ispyb-ws/pom.xml @@ -4,7 +4,7 @@ ispyb ispyb-parent - 5.2.1 + 5.3.2 ispyb-ws war @@ -12,7 +12,7 @@ ispyb ispyb-ejb3 - 5.2.1 + 5.3.2 provided diff --git a/pom.xml b/pom.xml index cbcba335f..1d979d091 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ 4.0.0 ispyb ispyb-parent - 5.2.1 + 5.3.2 pom ispyb-ejb From b511554f1930342cdb69eaeb83d2d83d3706c4f5 Mon Sep 17 00:00:00 2001 From: delageniere Date: Fri, 20 Oct 2017 16:48:20 +0200 Subject: [PATCH 20/38] modifications in reports --- .../util/beamlines/ESRFBeamlineEnum.java | 48 ++++++++++++------ .../common/util/export/ExiPdfRtfExporter.java | 50 ++++++++++--------- .../rest/mx/DataCollectionRestWebService.java | 37 ++++++++------ 3 files changed, 81 insertions(+), 54 deletions(-) diff --git a/ispyb-ejb/src/main/java/ispyb/common/util/beamlines/ESRFBeamlineEnum.java b/ispyb-ejb/src/main/java/ispyb/common/util/beamlines/ESRFBeamlineEnum.java index b99a02f86..13a97882c 100644 --- a/ispyb-ejb/src/main/java/ispyb/common/util/beamlines/ESRFBeamlineEnum.java +++ b/ispyb-ejb/src/main/java/ispyb/common/util/beamlines/ESRFBeamlineEnum.java @@ -30,37 +30,53 @@ public enum ESRFBeamlineEnum { ID14_1("ID14-1", "id14eh1", new String[] { "ID14 1" }, "+33476882323", - null, false, false), ID14_2("ID14-2", "id14eh2", new String[] { "ID14 2" }, + null, false, false), + ID14_2("ID14-2", "id14eh2", new String[] { "ID14 2" }, "+33476882565", - null, false, false), ID14_3("ID14-3", "id14eh3", new String[] { "ID14 3" }, + null, false, false), + ID14_3("ID14-3", "id14eh3", new String[] { "ID14 3" }, "+33476882786", - null, false, false), ID14_4("ID14-4", "id14eh4", new String[] { "ID14 4" }, + null, false, false), + ID14_4("ID14-4", "id14eh4", new String[] { "ID14 4" }, "+33476882322", - null, false, false), ID23_1("ID23-1", "id23eh1", new String[] { "ID23 1" }, + null, false, false), + ID23_1("ID23-1", "id23eh1", new String[] { "ID23 1" }, "+33476882261", - new String[] { "x_geo_corr.cbf", "y_geo_corr.cbf" }, false, true), ID23_2("ID23-2", "id23eh2", new String[] { "ID23 2" }, + new String[] { "x_geo_corr.cbf", "y_geo_corr.cbf" }, false, true), + ID23_2("ID23-2", "id23eh2", new String[] { "ID23 2" }, "+33476882590", - new String[] { "x_geo_corr.cbf", "y_geo_corr.cbf" }, false, true), ID29("ID29", "id29", new String[] { "ID29" }, + new String[] { "x_geo_corr.cbf", "y_geo_corr.cbf" }, false, true), + ID29("ID29", "id29", new String[] { "ID29" }, "+33476882805", - new String[] { "x_geo_corr.cbf", "y_geo_corr.cbf" }, false, true), ID30A1("ID30A-1", "id30a1", new String[] { "ID30A1" }, + new String[] { "x_geo_corr.cbf", "y_geo_corr.cbf" }, false, true), + ID30A1("ID30A-1", "id30a1", new String[] { "ID30A1" }, "+3347688****", - null, true, true), ID30A2("ID30A-2", "id30a2", new String[] { "ID30A2" }, + null, true, true), + ID30A2("ID30A-2", "id30a2", new String[] { "ID30A2" }, "+3347688****", - null, true, true), ID30A3("ID30A-3", "id30a3", new String[] { "ID30A3" }, + null, true, true), + ID30A3("ID30A-3", "id30a3", new String[] { "ID30A3" }, "+3347688****", - null, true, true), ID30B("ID30B", "id30b", null, + null, true, true), + ID30B("ID30B", "id30b", null, "+3347688****", - null, true, true), BM14("BM14", "bm14", new String[] { "BM14U" }, + null, true, true), + BM14("BM14", "bm14", new String[] { "BM14U" }, "+33476882703", - null, false, true), BM16("BM16", "bm16", null, + null, false, true), + BM16("BM16", "bm16", null, "+33476882614", - null, false, false), BM29("BM29", "bm29", null, + null, false, false), + BM29("BM29", "bm29", null, "+33476882628", - null, false, true), BM30A("BM30A", "bm30a", null, + null, false, true), + BM30A("BM30A", "bm30a", null, "+33476882787", - null, false, true), ID19("ID19", "id19", null, + null, false, true), + ID19("ID19", "id19", null, "+33476882700", - null, false, true); + null, false, true), + CM01("CM01", "cm01", null,"+3347688****", null, false, true); private ESRFBeamlineEnum(String beamlineName, String directoryName, String[] associatedName, String phoneNumber, String[] correctionFiles, boolean emailNotification, boolean inActivity) { diff --git a/ispyb-ejb/src/main/java/ispyb/common/util/export/ExiPdfRtfExporter.java b/ispyb-ejb/src/main/java/ispyb/common/util/export/ExiPdfRtfExporter.java index 54ca6757a..d77581366 100644 --- a/ispyb-ejb/src/main/java/ispyb/common/util/export/ExiPdfRtfExporter.java +++ b/ispyb-ejb/src/main/java/ispyb/common/util/export/ExiPdfRtfExporter.java @@ -137,7 +137,7 @@ public class ExiPdfRtfExporter { public final static Font FONT_INDEXING_SUCCESS = new Font(Font.HELVETICA, 8, Font.NORMAL, GREEN_COLOR); - public final static int NB_COL_DATACOLLECTION = 8; + public final static int NB_COL_DATACOLLECTION = 7; public final static int NB_COL_DATA_ANALYSIS = 11; @@ -593,22 +593,18 @@ private void setDataCollectionMapData(Document document, Map dat // Cell 4 - parag = getCellParam(dataCollectionMapItem, "DataCollection_resolutionAtCorner", df2)+ "\n" + parag = getCellParam(dataCollectionMapItem, "DataCollection_resolution", df2) + + "("+ getCellParam(dataCollectionMapItem, "DataCollection_resolutionAtCorner", df2) + ") \n" + getCellParam(dataCollectionMapItem, "DataCollection_wavelength", df3) + "\n" + getCellParam(dataCollectionMapItem, "DataCollection_axisRange", df2) + "\n" + getCellParam(dataCollectionMapItem, "DataCollection_omegaStart", df2) + "\n" - + getCellParam(dataCollectionMapItem, "exposureTime", df2) + "\n" + + getCellParam(dataCollectionMapItem, "DataCollection_exposureTime", df2) + "\n" + getCellParam(dataCollectionMapItem, "DataCollection_flux", null) + "\n" + getCellParam(dataCollectionMapItem, "DataCollection_flux_end", null) + "\n" ; table.addCell(new Paragraph(parag, FONT_DOC_BOLD)); - - - - // 5 Cell add cell containing autoproc results - table.addCell(" "); - // 6 Cell : thumbnail + // 5 Cell : thumbnail if (!getCellParam(dataCollectionMapItem, "lastImageId", null).isEmpty()) { String thumbnailPath = (imageService.findByPk(new Integer(getCellParam(dataCollectionMapItem, "lastImageId", null)))).getJpegThumbnailFileFullPath(); @@ -622,13 +618,13 @@ private void setDataCollectionMapData(Document document, Map dat //cellThumbnail.setRowspan(nbRows); - // 7 Cell : snapshot + // 6 Cell : snapshot Cell cellSnapshot = getCellImage(dataCollectionMapItem,"DataCollection_xtalSnapshotFullPath1", IMAGE_HEIGHT); //cellSnapshot.setRowspan(nbRows); cellSnapshot.setBorderWidth(0); table.addCell(cellSnapshot); - // 8 Cell : graph or other plot + // 7 Cell : graph or other plot if (!getCellParam(dataCollectionMapItem, "DataCollection_dataCollectionId", null).isEmpty()) { String plotPath = (dcService.findByPk(new Integer(getCellParam(dataCollectionMapItem, "DataCollection_dataCollectionId", null)), false, false)).getImageQualityIndicatorsPlotPath(); Cell cellGraph = getCellImage(plotPath, IMAGE_HEIGHT); @@ -696,7 +692,8 @@ private void setDataAnalysisMapData(Document document, Map dataC // Cell 4 parag = getCellParam(dataCollectionMapItem, "Workflow_workflowType", null) + "\n" - + getCellParam(dataCollectionMapItem, "DataCollection_resolutionAtCorner", df2)+ "\n" + + getCellParam(dataCollectionMapItem, "DataCollection_resolution", df2) + + "("+ getCellParam(dataCollectionMapItem, "DataCollection_resolutionAtCorner", df2) + ") \n" + getCellParam(dataCollectionMapItem, "DataCollection_wavelength", df3) + "\n" ; p = new Paragraph(parag, FONT_DOC_SMALL_BOLD); @@ -715,6 +712,7 @@ private void setDataAnalysisMapData(Document document, Map dataC // Cell 6 indexed/strategy or completeness Boolean indexing = getBoolean(dataCollectionMapItem, "ScreeningOutput_indexingSuccess"); Boolean strategy = getBoolean(dataCollectionMapItem, "ScreeningOutput_strategySuccess"); + String autoprocSpaceGroup = getCellParam(dataCollectionMapItem, "AutoProc_spaceGroup", null); p = new Paragraph(); @@ -734,34 +732,38 @@ private void setDataAnalysisMapData(Document document, Map dataC chu2 = new Chunk( "Success", FONT_INDEXING_SUCCESS); } p.add(chu1); - p.add(chu2); - + p.add(chu2); table.addCell(p); + } else if (autoprocSpaceGroup != null && !autoprocSpaceGroup.isEmpty()){ + parag = autoprocSpaceGroup + " Completeness \n" + + "Overall" + "\n" + + "Inner" + "\n" + + "Outer" + "\n"; + p = new Paragraph(parag, FONT_DOC_SMALL); + } else { + table.addCell(" "); + } - LOG.info("parag=" + p.toString()); // Cell 7 - parag = "Space group: \n" + if (indexing != null && strategy != null){ + parag = "Space group: \n" + "Mosaicity: \n" ; - LOG.info("parag=" + parag); - p = new Paragraph(parag, FONT_DOC_SMALL); + p = new Paragraph(parag, FONT_DOC_SMALL); + } else { + p = new Paragraph(" "); + } table.addCell(p); - // Cell 8 parag = getCellParam(dataCollectionMapItem, "ScreeningOutputLattice_spaceGroup", null) + "\n" + getCellParam(dataCollectionMapItem, "ScreeningOutput_mosaicity", null)+ "\n" ; p = new Paragraph(parag, FONT_DOC_SMALL_BOLD); table.addCell(p); - - //if (dataCollectionGroup.ScreeningOutputLattice_spaceGroup){ -//dataCollectionGroup.ScreeningOutputLattice_unitCell_a + ", " + dataCollectionGroup.ScreeningOutputLattice_unitCell_b + ", " + dataCollectionGroup.ScreeningOutputLattice_unitCell_c, - - // Cell 9 parag = "a \n" + getCellParam(dataCollectionMapItem, "ScreeningOutputLattice_unitCell_a", null) + "\n alpha \n" + getCellParam(dataCollectionMapItem, "ScreeningOutputLattice_unitCell_alpha", null) ; diff --git a/ispyb-ws/src/main/java/ispyb/ws/rest/mx/DataCollectionRestWebService.java b/ispyb-ws/src/main/java/ispyb/ws/rest/mx/DataCollectionRestWebService.java index dc246eb55..ad02d3c92 100644 --- a/ispyb-ws/src/main/java/ispyb/ws/rest/mx/DataCollectionRestWebService.java +++ b/ispyb-ws/src/main/java/ispyb/ws/rest/mx/DataCollectionRestWebService.java @@ -1,8 +1,5 @@ package ispyb.ws.rest.mx; -import ispyb.common.util.export.ExiPdfRtfExporter; -import ispyb.server.mx.vos.collections.DataCollection3VO; - import java.io.File; import java.nio.file.Files; import java.nio.file.Paths; @@ -18,11 +15,15 @@ import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; import javax.ws.rs.core.Response; import org.apache.log4j.Logger; import org.jboss.resteasy.annotations.GZIP; +import ispyb.common.util.export.ExiPdfRtfExporter; +import ispyb.server.mx.vos.collections.DataCollection3VO; + @Path("/") public class DataCollectionRestWebService extends MXRestWebService { @@ -217,11 +218,11 @@ public Response getViewDataCollectionBySessionId(@PathParam("token") String toke @RolesAllowed({"User", "Manager", "Industrial", "Localcontact"}) @GET - @Path("{token}/proposal/{proposal}/mx/datacollection/session/{sessionId}/report/{nbRows}/pdf") + @Path("{token}/proposal/{proposal}/mx/datacollection/session/{sessionId}/report/pdf") @Produces({ "application/pdf" }) public Response getDataCollectionsReportBySessionIdPDF(@PathParam("token") String token, @PathParam("proposal") String proposal, - @PathParam("sessionId") String sessionId, @PathParam("nbRows") String nbRows) throws NamingException { + @PathParam("sessionId") String sessionId, @QueryParam("nbRows") String nbRows) throws NamingException { String methodName = "getDataCollectionReportyBySessionIdPdf"; long start = this.logInit(methodName, logger, token, proposal, sessionId); @@ -237,11 +238,11 @@ public Response getDataCollectionsReportBySessionIdPDF(@PathParam("token") Strin @RolesAllowed({"User", "Manager", "Industrial", "Localcontact"}) @GET - @Path("{token}/proposal/{proposal}/mx/datacollection/session/{sessionId}/report/{nbRows}/rtf") + @Path("{token}/proposal/{proposal}/mx/datacollection/session/{sessionId}/report/rtf") @Produces({ "application/rtf" }) public Response getDataCollectionsReportBySessionIdRTF(@PathParam("token") String token, @PathParam("proposal") String proposal, - @PathParam("sessionId") String sessionId, @PathParam("nbRows") String nbRows) throws NamingException { + @PathParam("sessionId") String sessionId, @QueryParam("nbRows") String nbRows) throws NamingException { String methodName = "getDataCollectionReportyBySessionIdRtf"; long start = this.logInit(methodName, logger, token, proposal, sessionId); @@ -257,11 +258,11 @@ public Response getDataCollectionsReportBySessionIdRTF(@PathParam("token") Strin @RolesAllowed({"User", "Manager", "Industrial", "Localcontact"}) @GET - @Path("{token}/proposal/{proposal}/mx/datacollection/session/{sessionId}/analysisreport/{nbRows}/pdf") + @Path("{token}/proposal/{proposal}/mx/datacollection/session/{sessionId}/analysisreport/pdf") @Produces({ "application/pdf" }) public Response getDataCollectionsAnalysisReportBySessionIdPDF(@PathParam("token") String token, @PathParam("proposal") String proposal, - @PathParam("sessionId") String sessionId, @PathParam("nbRows") String nbRows) throws NamingException { + @PathParam("sessionId") String sessionId, @QueryParam("nbRows") String nbRows) throws NamingException { String methodName = "getDataCollectionAnalysisReportyBySessionIdPdf"; long start = this.logInit(methodName, logger, token, proposal, sessionId); @@ -277,11 +278,11 @@ public Response getDataCollectionsAnalysisReportBySessionIdPDF(@PathParam("token @RolesAllowed({"User", "Manager", "Industrial", "Localcontact"}) @GET - @Path("{token}/proposal/{proposal}/mx/datacollection/session/{sessionId}/analysisreport/{nbRows}/rtf") + @Path("{token}/proposal/{proposal}/mx/datacollection/session/{sessionId}/analysisreport/rtf") @Produces({ "application/rtf" }) public Response getDataCollectionsAnalysisReportBySessionIdRTF(@PathParam("token") String token, @PathParam("proposal") String proposal, - @PathParam("sessionId") String sessionId, @PathParam("nbRows") String nbRows) throws NamingException { + @PathParam("sessionId") String sessionId, @QueryParam("nbRows") String nbRows) throws NamingException { String methodName = "getDataCollectionReportyBySessionIdRtf"; long start = this.logInit(methodName, logger, token, proposal, sessionId); @@ -401,8 +402,12 @@ public Response getViewDataCollectionByProteinAcronym(@PathParam("token") String List> dataCollections = this.getWebServiceDataCollectionGroup3Service().getViewDataCollectionBySessionId(this.getProposalId(proposal), id); - - Integer nbRowsMax = new Integer(nbRows); + + Integer nbRowsMax = dataCollections.size(); + + if (nbRows != null && !nbRows.isEmpty()) { + nbRowsMax = new Integer(nbRows); + } ExiPdfRtfExporter pdf = new ExiPdfRtfExporter(proposal, id , dataCollections, nbRowsMax); @@ -418,7 +423,11 @@ public Response getViewDataCollectionByProteinAcronym(@PathParam("token") String List> dataCollections = this.getWebServiceDataCollectionGroup3Service().getViewDataCollectionBySessionId(this.getProposalId(proposal), id); - Integer nbRowsMax = new Integer(nbRows); + Integer nbRowsMax = dataCollections.size(); + + if (nbRows != null && !nbRows.isEmpty()) { + nbRowsMax = new Integer(nbRows); + } ExiPdfRtfExporter pdf = new ExiPdfRtfExporter(proposal, id , dataCollections, nbRowsMax); From 131ef93d599abd093799a47048d5af7b30ba4e88 Mon Sep 17 00:00:00 2001 From: delageniere Date: Mon, 23 Oct 2017 16:31:21 +0200 Subject: [PATCH 21/38] improvements for reports --- .../common/util/export/ExiPdfRtfExporter.java | 257 +++++++++++++----- .../rest/mx/DataCollectionRestWebService.java | 6 +- 2 files changed, 197 insertions(+), 66 deletions(-) diff --git a/ispyb-ejb/src/main/java/ispyb/common/util/export/ExiPdfRtfExporter.java b/ispyb-ejb/src/main/java/ispyb/common/util/export/ExiPdfRtfExporter.java index d77581366..1613ddb08 100644 --- a/ispyb-ejb/src/main/java/ispyb/common/util/export/ExiPdfRtfExporter.java +++ b/ispyb-ejb/src/main/java/ispyb/common/util/export/ExiPdfRtfExporter.java @@ -26,6 +26,7 @@ import java.text.DecimalFormat; import java.text.NumberFormat; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; import java.util.Iterator; import java.util.List; @@ -57,11 +58,9 @@ import ispyb.server.mx.services.collections.DataCollection3Service; import ispyb.server.mx.services.collections.Image3Service; import ispyb.server.mx.services.collections.IspybCrystalClass3Service; -import ispyb.server.mx.vos.collections.DataCollection3VO; -import ispyb.server.mx.vos.collections.DataCollectionGroup3VO; +import ispyb.server.mx.services.ws.rest.datacollectiongroup.DataCollectionGroupRestWsService; import ispyb.server.mx.vos.collections.IspybCrystalClass3VO; import ispyb.server.mx.vos.collections.Session3VO; -import sun.tools.jstat.Alignment; /** * allows creation of PDF or RTF report - general report for EXI, available in the @@ -133,13 +132,13 @@ public class ExiPdfRtfExporter { public final static Font FONT_INDEXING_NOTDONE = new Font(Font.HELVETICA, 8, Font.NORMAL, LIGHT_GREY_COLOR); - public final static Font FONT_INDEXING_FAILED = new Font(Font.HELVETICA, 8, Font.NORMAL, RED_COLOR); + public final static Font FONT_INDEXING_FAILED = new Font(Font.HELVETICA, 6, Font.BOLD, RED_COLOR); - public final static Font FONT_INDEXING_SUCCESS = new Font(Font.HELVETICA, 8, Font.NORMAL, GREEN_COLOR); + public final static Font FONT_INDEXING_SUCCESS = new Font(Font.HELVETICA, 6, Font.BOLD, GREEN_COLOR); public final static int NB_COL_DATACOLLECTION = 7; - public final static int NB_COL_DATA_ANALYSIS = 11; + public final static int NB_COL_DATA_ANALYSIS = 12; public final static int SIZE_FONT = 8; @@ -174,6 +173,8 @@ public class ExiPdfRtfExporter { public final static float DIFF_IMAGE_HEIGHT = 174; + //proposalId + int proposalId; // proposal String proposalDesc; @@ -199,9 +200,11 @@ public class ExiPdfRtfExporter { private DataCollection3Service dcService; + private DataCollectionGroupRestWsService dcGroupService; + private Image3Service imageService; - public ExiPdfRtfExporter(String proposalDesc, Integer sessionId, + public ExiPdfRtfExporter(int proposalId, String proposalDesc, Integer sessionId, List> dataCollections, Integer nbRowsMax) throws Exception { this.proposalDesc = proposalDesc; this.sessionId = sessionId; @@ -220,6 +223,8 @@ private void init() throws Exception { .getLocalService(Session3Service.class); dcService = (DataCollection3Service) ejb3ServiceLocator .getLocalService(DataCollection3Service.class); + dcGroupService = (DataCollectionGroupRestWsService) ejb3ServiceLocator + .getLocalService(DataCollectionGroupRestWsService.class); imageService = (Image3Service) ejb3ServiceLocator .getLocalService(Image3Service.class); @@ -516,8 +521,7 @@ private void setDataAnalysisTable(Document document) throws Exception { while (it.hasNext() && i < nbRowsMax) { Map dataCollectionMapData = it.next(); LOG.info("dcMap=" + dataCollectionMapData.toString()); - setDataAnalysisMapData(document, dataCollectionMapData); - + setDataAnalysisMapData(document, dataCollectionMapData); i++; } document.add(new Paragraph(" ")); @@ -709,82 +713,138 @@ private void setDataAnalysisMapData(Document document, Map dataC table.addCell(" "); } - // Cell 6 indexed/strategy or completeness + // Cell 6 7 8 9 10 11 indexed/strategy or completeness Boolean indexing = getBoolean(dataCollectionMapItem, "ScreeningOutput_indexingSuccess"); Boolean strategy = getBoolean(dataCollectionMapItem, "ScreeningOutput_strategySuccess"); String autoprocSpaceGroup = getCellParam(dataCollectionMapItem, "AutoProc_spaceGroup", null); p = new Paragraph(); - - Chunk chu1 = new Chunk("Indexed: ", FONT_DOC_SMALL); + String [] bestRmerge = null; + if (indexing != null && strategy != null){ - - Chunk chu2 = new Chunk( "Failed", FONT_INDEXING_FAILED); + // Cell 6 + parag = "\nIndexed: \n " + + "\nStrategy: \n"; + p = new Paragraph(parag, FONT_DOC_SMALL); + table.addCell(p); + + // Cell 6 + p = new Paragraph(); + Chunk chu2 = new Chunk( "KO", FONT_INDEXING_FAILED); if (indexing.booleanValue() ){ - chu2 = new Chunk( "Success", FONT_INDEXING_SUCCESS); + chu2 = new Chunk( "OK", FONT_INDEXING_SUCCESS); } - p.add(chu1); p.add(chu2); - chu1 = new Chunk("Strategy: ", FONT_DOC_SMALL); - chu2 = new Chunk( "Failed", FONT_INDEXING_FAILED); + chu2 = new Chunk( "KO", FONT_INDEXING_FAILED); if (strategy.booleanValue() ){ - chu2 = new Chunk( "Success", FONT_INDEXING_SUCCESS); + chu2 = new Chunk( "OK", FONT_INDEXING_SUCCESS); } - p.add(chu1); + p.add("\n"); p.add(chu2); table.addCell(p); - - } else if (autoprocSpaceGroup != null && !autoprocSpaceGroup.isEmpty()){ - parag = autoprocSpaceGroup + " Completeness \n" - + "Overall" + "\n" - + "Inner" + "\n" - + "Outer" + "\n"; + + // Cell 7 + parag = "Space group: \n" + + "Mosaicity: \n" ; p = new Paragraph(parag, FONT_DOC_SMALL); - - } else { - - table.addCell(" "); + table.addCell(p); - } - - // Cell 7 - if (indexing != null && strategy != null){ - parag = "Space group: \n" - + "Mosaicity: \n" ; + // Cell 8 + parag = getCellParam(dataCollectionMapItem, "ScreeningOutputLattice_spaceGroup", null) + "\n" + + getCellParam(dataCollectionMapItem, "ScreeningOutput_mosaicity", null)+ "\n" ; + p = new Paragraph(parag, FONT_DOC_SMALL_BOLD); + table.addCell(p); + + // Cell 9 + parag = "a \n" + getCellParam(dataCollectionMapItem, "ScreeningOutputLattice_unitCell_a", null) + + "\n alpha \n" + getCellParam(dataCollectionMapItem, "ScreeningOutputLattice_unitCell_alpha", null) ; + p = new Paragraph(parag, FONT_DOC_SMALL_CENTERED); + p.setAlignment(Element.ALIGN_CENTER); + table.addCell(p); + + // Cell 10 + parag = "b \n" + getCellParam(dataCollectionMapItem, "ScreeningOutputLattice_unitCell_b", null) + + "\n beta \n" + getCellParam(dataCollectionMapItem, "ScreeningOutputLattice_unitCell_beta", null) ; + p = new Paragraph(parag, FONT_DOC_SMALL_CENTERED); + p.setAlignment(Element.ALIGN_CENTER); + table.addCell(p); + + // Cell 11 + parag = "c \n" + getCellParam(dataCollectionMapItem, "ScreeningOutputLattice_unitCell_c", null) + + "\n gamma \n" + getCellParam(dataCollectionMapItem, "ScreeningOutputLattice_unitCell_gama", null) ; + + p = new Paragraph(parag, FONT_DOC_SMALL_CENTERED); + p.setAlignment(Element.ALIGN_CENTER); + table.addCell(p); + + + } else if (autoprocSpaceGroup != null && !autoprocSpaceGroup.isEmpty() && extractBestRmerge(dataCollectionMapItem) != null){ + // Cell 6 + bestRmerge = extractBestRmerge(dataCollectionMapItem); + parag = autoprocSpaceGroup + "\n" + + "Overall\n" + + "Inner\n" + + "Outer\n"; p = new Paragraph(parag, FONT_DOC_SMALL); - } else { - p = new Paragraph(" "); - } - table.addCell(p); + table.addCell(p); - // Cell 8 - parag = getCellParam(dataCollectionMapItem, "ScreeningOutputLattice_spaceGroup", null) + "\n" - + getCellParam(dataCollectionMapItem, "ScreeningOutput_mosaicity", null)+ "\n" ; - p = new Paragraph(parag, FONT_DOC_SMALL_BOLD); - table.addCell(p); + // Cell 7 + parag ="Completeness\n" + + bestRmerge[16] + "\n" + + bestRmerge[2] + "\n" + + bestRmerge[12] + "\n"; + p = new Paragraph(parag, FONT_DOC_SMALL); + table.addCell(p); - // Cell 9 - parag = "a \n" + getCellParam(dataCollectionMapItem, "ScreeningOutputLattice_unitCell_a", null) - + "\n alpha \n" + getCellParam(dataCollectionMapItem, "ScreeningOutputLattice_unitCell_alpha", null) ; - p = new Paragraph(parag, FONT_DOC_SMALL_CENTERED); - p.setAlignment(Element.ALIGN_CENTER); - table.addCell(p); + // Cell 8 + parag = "Res. \n" + + bestRmerge[17]+ "\n " + + bestRmerge[3] + "\n" + + bestRmerge[13] + "\n" ; + p = new Paragraph(parag, FONT_DOC_SMALL); + table.addCell(p); - // Cell 10 - parag = "b \n" + getCellParam(dataCollectionMapItem, "ScreeningOutputLattice_unitCell_b", null) - + "\n beta \n" + getCellParam(dataCollectionMapItem, "ScreeningOutputLattice_unitCell_beta", null) ; - p = new Paragraph(parag, FONT_DOC_SMALL_CENTERED); - p.setAlignment(Element.ALIGN_CENTER); - table.addCell(p); + // Cell 9 + parag = "Rmerge \n" + + bestRmerge[15] + "\n" + + bestRmerge[1] + "\n" + + bestRmerge[11] + "\n"; + p = new Paragraph(parag, FONT_DOC_SMALL); + table.addCell(p); - // Cell 11 - parag = "c \n" + getCellParam(dataCollectionMapItem, "ScreeningOutputLattice_unitCell_c", null) - + "\n gamma \n" + getCellParam(dataCollectionMapItem, "ScreeningOutputLattice_unitCell_gama", null) ; + // cell parameters of innerShell + // Cell 10 a alpha + parag = "a \n" + bestRmerge[4] + + "\n alpha \n" + bestRmerge[7] ; + p = new Paragraph(parag, FONT_DOC_SMALL_CENTERED); + p.setAlignment(Element.ALIGN_CENTER); + table.addCell(p); + + // Cell 11 b beta + parag = "b \n" + bestRmerge[5] + + "\n beta \n" + bestRmerge[8] ; + p = new Paragraph(parag, FONT_DOC_SMALL_CENTERED); + p.setAlignment(Element.ALIGN_CENTER); + table.addCell(p); - p = new Paragraph(parag, FONT_DOC_SMALL_CENTERED); - p.setAlignment(Element.ALIGN_CENTER); - table.addCell(p); + // Cell 12 + parag = "c \n" + bestRmerge[6] + + "\n gamma \n" + bestRmerge[9] ; + + p = new Paragraph(parag, FONT_DOC_SMALL_CENTERED); + p.setAlignment(Element.ALIGN_CENTER); + table.addCell(p); + + } else { + table.addCell(" "); + table.addCell(" "); + table.addCell(" "); + table.addCell(" "); + table.addCell(" "); + table.addCell(" "); + table.addCell(" "); + } document.add(table); @@ -1010,5 +1070,76 @@ private List getListOfNbCrystalPerClass(List list return listOfNbCrystalPerClass; } + private String[] extractBestRmerge(Map dataCollectionMapItem) throws Exception { + + String listString = (String)dataCollectionMapItem.get("completenessList"); + listString.trim(); + List completenessList = new ArrayList(Arrays.asList((listString.split(",")))); + LOG.info("completenessList = " + completenessList.toString()); + + String [] bestRmerge = null; + + if (completenessList != null && !completenessList.isEmpty()) { + + bestRmerge = new String[18]; + List rmergesList = new ArrayList(Arrays.asList(((String)dataCollectionMapItem.get("rMerges")).trim().split(","))); + LOG.info("rmergesList = " + rmergesList.size() + rmergesList.toString() ); + List scalingStatisticsTypesList = new ArrayList(Arrays.asList(((String)dataCollectionMapItem.get("scalingStatisticsTypes")).trim().split(","))); + LOG.info("scalingStatisticsTypesList = " + scalingStatisticsTypesList.size() + scalingStatisticsTypesList.toString()); + int i = 0; + Double rmergeMin = 1000.0; + int indexRmergeMin = 0; + + for (Iterator iterator = scalingStatisticsTypesList.iterator(); iterator.hasNext();) { + String type = (String) iterator.next(); + if (type.equals("innerShell")){ + if (new Double(rmergesList.get(i)) < rmergeMin) { + rmergeMin = new Double(rmergesList.get(i)); + indexRmergeMin = i; + } + } + i=i+1; + } + List spaceGroupsList = new ArrayList(Arrays.asList(((String)dataCollectionMapItem.get("AutoProc_spaceGroups")).trim().split(","))); + LOG.info("spaceGroupsList = " + spaceGroupsList.size() + spaceGroupsList.toString()); + List resolutionsLimitLowList = new ArrayList(Arrays.asList(((String)dataCollectionMapItem.get("resolutionsLimitLow")).trim().split(","))); + LOG.info("resolutionsLimitLowList = " + resolutionsLimitLowList.size() + resolutionsLimitLowList.toString()); + List resolutionsLimitHighList = new ArrayList(Arrays.asList(((String)dataCollectionMapItem.get("resolutionsLimitHigh")).trim().split(","))); + LOG.info("resolutionsLimitHighList = " + resolutionsLimitHighList.toString()); + + + bestRmerge[0] = spaceGroupsList.get(indexRmergeMin); + bestRmerge[1] = rmergeMin.toString(); + bestRmerge[2]= completenessList.get(indexRmergeMin); + bestRmerge[3] = resolutionsLimitLowList.get(indexRmergeMin) + "/" + resolutionsLimitHighList.get(indexRmergeMin); + + List tmpList = new ArrayList(Arrays.asList(((String)dataCollectionMapItem.get("Autoprocessing_cell_a")).trim().split(","))); + bestRmerge[4] = tmpList.get(indexRmergeMin); + tmpList = new ArrayList(Arrays.asList(((String)dataCollectionMapItem.get("Autoprocessing_cell_b")).trim().split(","))); + bestRmerge[5] = tmpList.get(indexRmergeMin); + tmpList = new ArrayList(Arrays.asList(((String)dataCollectionMapItem.get("Autoprocessing_cell_c")).trim().split(","))); + bestRmerge[6] = tmpList.get(indexRmergeMin); + tmpList = new ArrayList(Arrays.asList(((String)dataCollectionMapItem.get("Autoprocessing_cell_alpha")).trim().split(","))); + bestRmerge[7] = tmpList.get(indexRmergeMin); + tmpList = new ArrayList(Arrays.asList(((String)dataCollectionMapItem.get("Autoprocessing_cell_beta")).trim().split(","))); + bestRmerge[8] = tmpList.get(indexRmergeMin); + tmpList = new ArrayList(Arrays.asList(((String)dataCollectionMapItem.get("Autoprocessing_cell_gamma")).trim().split(","))); + bestRmerge[9] = tmpList.get(indexRmergeMin); + + //outer + bestRmerge[10] = spaceGroupsList.get(indexRmergeMin+1); + bestRmerge[11] = rmergesList.get(indexRmergeMin+1); + bestRmerge[12]= completenessList.get(indexRmergeMin+1); + bestRmerge[13] = resolutionsLimitLowList.get(indexRmergeMin+1) + "/" + resolutionsLimitHighList.get(indexRmergeMin+1); + + //overall + bestRmerge[14] = spaceGroupsList.get(indexRmergeMin+2); + bestRmerge[15] = rmergesList.get(indexRmergeMin+2); + bestRmerge[16]= completenessList.get(indexRmergeMin+2); + bestRmerge[17] = resolutionsLimitLowList.get(indexRmergeMin+2) + "/" + resolutionsLimitHighList.get(indexRmergeMin+2); + } + LOG.info("bestRmerge = " + "- " + bestRmerge[0] + "- " + bestRmerge[1]+ "- " + bestRmerge[2]+ "- " + bestRmerge[3]); + return bestRmerge; + } } diff --git a/ispyb-ws/src/main/java/ispyb/ws/rest/mx/DataCollectionRestWebService.java b/ispyb-ws/src/main/java/ispyb/ws/rest/mx/DataCollectionRestWebService.java index ad02d3c92..0b2a26526 100644 --- a/ispyb-ws/src/main/java/ispyb/ws/rest/mx/DataCollectionRestWebService.java +++ b/ispyb-ws/src/main/java/ispyb/ws/rest/mx/DataCollectionRestWebService.java @@ -404,12 +404,12 @@ public Response getViewDataCollectionByProteinAcronym(@PathParam("token") String this.getWebServiceDataCollectionGroup3Service().getViewDataCollectionBySessionId(this.getProposalId(proposal), id); Integer nbRowsMax = dataCollections.size(); - + if (nbRows != null && !nbRows.isEmpty()) { nbRowsMax = new Integer(nbRows); } - ExiPdfRtfExporter pdf = new ExiPdfRtfExporter(proposal, id , dataCollections, nbRowsMax); + ExiPdfRtfExporter pdf = new ExiPdfRtfExporter(this.getProposalId(proposal), proposal, id , dataCollections, nbRowsMax); byte [] byteToExport = pdf.exportDataCollectionReport(isRtf).toByteArray(); @@ -429,7 +429,7 @@ public Response getViewDataCollectionByProteinAcronym(@PathParam("token") String nbRowsMax = new Integer(nbRows); } - ExiPdfRtfExporter pdf = new ExiPdfRtfExporter(proposal, id , dataCollections, nbRowsMax); + ExiPdfRtfExporter pdf = new ExiPdfRtfExporter(this.getProposalId(proposal), proposal, id , dataCollections, nbRowsMax); byte [] byteToExport = pdf.exportDataCollectionAnalysisReport(isRtf).toByteArray(); From 81e1baae4ebc06f2e82043786a79cb57874de43f Mon Sep 17 00:00:00 2001 From: delageniere Date: Mon, 23 Oct 2017 17:10:47 +0200 Subject: [PATCH 22/38] improvements for reports --- .../ispyb/common/util/export/ExiPdfRtfExporter.java | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/ispyb-ejb/src/main/java/ispyb/common/util/export/ExiPdfRtfExporter.java b/ispyb-ejb/src/main/java/ispyb/common/util/export/ExiPdfRtfExporter.java index 1613ddb08..095a08356 100644 --- a/ispyb-ejb/src/main/java/ispyb/common/util/export/ExiPdfRtfExporter.java +++ b/ispyb-ejb/src/main/java/ispyb/common/util/export/ExiPdfRtfExporter.java @@ -47,6 +47,8 @@ import com.lowagie.text.Paragraph; import com.lowagie.text.Phrase; import com.lowagie.text.Table; +import com.lowagie.text.pdf.PdfPCell; +import com.lowagie.text.pdf.PdfPTable; import com.lowagie.text.pdf.PdfWriter; import com.lowagie.text.rtf.RtfWriter2; @@ -666,7 +668,6 @@ private void setDataAnalysisMapData(Document document, Map dataC Table table = new Table(NB_COL_DATA_ANALYSIS); table.getDefaultCell().setHorizontalAlignment(Element.ALIGN_LEFT); table.getDefaultCell().setBorderWidth(0); - table.setBorder(1); // 1st Cell String parag = "Protein: \n\n" @@ -845,8 +846,12 @@ private void setDataAnalysisMapData(Document document, Map dataC table.addCell(" "); table.addCell(" "); } - - document.add(table); + // to avoid splitting table + Table containingTable = new Table(1); + Cell cell = new Cell(table); + containingTable.addCell(cell); + + document.add(containingTable); return; } From 31896ee93ff9b7fd4848805b6a8e27605fcac69e Mon Sep 17 00:00:00 2001 From: delageniere Date: Tue, 24 Oct 2017 11:50:07 +0200 Subject: [PATCH 23/38] modif for reports + IM proposal update --- .../common/util/export/ExiPdfRtfExporter.java | 68 ++++++++++++++----- .../bodies/common/proposal/updateISPYBdb.jsp | 3 +- 2 files changed, 53 insertions(+), 18 deletions(-) diff --git a/ispyb-ejb/src/main/java/ispyb/common/util/export/ExiPdfRtfExporter.java b/ispyb-ejb/src/main/java/ispyb/common/util/export/ExiPdfRtfExporter.java index 095a08356..6a3bbc43a 100644 --- a/ispyb-ejb/src/main/java/ispyb/common/util/export/ExiPdfRtfExporter.java +++ b/ispyb-ejb/src/main/java/ispyb/common/util/export/ExiPdfRtfExporter.java @@ -113,6 +113,8 @@ public class ExiPdfRtfExporter { public final static Font FONT_DOC_SMALL = new Font(Font.HELVETICA, 6, Font.NORMAL, Color.BLACK); public final static Font FONT_DOC_BLUE = new Font(Font.HELVETICA, 8, Font.NORMAL, Color.BLUE); + public final static Font FONT_DOC_ORANGE = new Font(Font.HELVETICA, 8, Font.NORMAL, Color.ORANGE); + public final static Font FONT_DOC_RED = new Font(Font.HELVETICA, 8, Font.NORMAL, Color.RED); public final static Font FONT_DOC_ITALIC = new Font(Font.HELVETICA, 8, Font.ITALIC, Color.BLACK); @@ -718,6 +720,9 @@ private void setDataAnalysisMapData(Document document, Map dataC Boolean indexing = getBoolean(dataCollectionMapItem, "ScreeningOutput_indexingSuccess"); Boolean strategy = getBoolean(dataCollectionMapItem, "ScreeningOutput_strategySuccess"); String autoprocSpaceGroup = getCellParam(dataCollectionMapItem, "AutoProc_spaceGroup", null); + boolean existAutoProcSpaceGroup = (autoprocSpaceGroup != null && !autoprocSpaceGroup.isEmpty() ) + || ( dataCollectionMapItem.get("AutoProc_spaceGroups") != null && !((String)dataCollectionMapItem.get("AutoProc_spaceGroups")).isEmpty()); + p = new Paragraph(); String [] bestRmerge = null; @@ -780,22 +785,27 @@ private void setDataAnalysisMapData(Document document, Map dataC table.addCell(p); - } else if (autoprocSpaceGroup != null && !autoprocSpaceGroup.isEmpty() && extractBestRmerge(dataCollectionMapItem) != null){ + } else if (existAutoProcSpaceGroup && extractBestRmerge(dataCollectionMapItem) != null){ // Cell 6 bestRmerge = extractBestRmerge(dataCollectionMapItem); - parag = autoprocSpaceGroup + "\n" + parag = bestRmerge[0] + "\n" + "Overall\n" + "Inner\n" + "Outer\n"; p = new Paragraph(parag, FONT_DOC_SMALL); table.addCell(p); - // Cell 7 - parag ="Completeness\n" - + bestRmerge[16] + "\n" - + bestRmerge[2] + "\n" - + bestRmerge[12] + "\n"; - p = new Paragraph(parag, FONT_DOC_SMALL); + // Cell 7 + p = new Paragraph("Completeness\n", FONT_DOC_SMALL); + Chunk chu = getCompletenessChunk(bestRmerge[16]); + p.add(chu); + p.add("\n"); + chu = getCompletenessChunk(bestRmerge[2]); + p.add(chu); + p.add("\n"); + chu = getCompletenessChunk(bestRmerge[12]); + p.add(chu); + table.addCell(p); // Cell 8 @@ -1092,15 +1102,20 @@ private String[] extractBestRmerge(Map dataCollectionMapItem) th List scalingStatisticsTypesList = new ArrayList(Arrays.asList(((String)dataCollectionMapItem.get("scalingStatisticsTypes")).trim().split(","))); LOG.info("scalingStatisticsTypesList = " + scalingStatisticsTypesList.size() + scalingStatisticsTypesList.toString()); int i = 0; - Double rmergeMin = 1000.0; + Double rmergeMin = 1000.000; int indexRmergeMin = 0; for (Iterator iterator = scalingStatisticsTypesList.iterator(); iterator.hasNext();) { String type = (String) iterator.next(); - if (type.equals("innerShell")){ - if (new Double(rmergesList.get(i)) < rmergeMin) { - rmergeMin = new Double(rmergesList.get(i)); + if (type.contains("innerShell")){ + double rm = new Double(rmergesList.get(i)).doubleValue(); + LOG.info("rm = " + rm); + if (rm > 0 && rm < rmergeMin) { + rmergeMin = rm; indexRmergeMin = i; + // TODO replace this by a look for the higher spacegroup + if (rmergeMin < 10) + break; } } i=i+1; @@ -1138,13 +1153,32 @@ private String[] extractBestRmerge(Map dataCollectionMapItem) th bestRmerge[13] = resolutionsLimitLowList.get(indexRmergeMin+1) + "/" + resolutionsLimitHighList.get(indexRmergeMin+1); //overall - bestRmerge[14] = spaceGroupsList.get(indexRmergeMin+2); - bestRmerge[15] = rmergesList.get(indexRmergeMin+2); - bestRmerge[16]= completenessList.get(indexRmergeMin+2); - bestRmerge[17] = resolutionsLimitLowList.get(indexRmergeMin+2) + "/" + resolutionsLimitHighList.get(indexRmergeMin+2); + int overallIndex = indexRmergeMin-1; + if (overallIndex < 0 || scalingStatisticsTypesList.get(0).contains("innerShell")) { + overallIndex = indexRmergeMin+2; + } + + bestRmerge[14] = spaceGroupsList.get(overallIndex); + bestRmerge[15] = rmergesList.get(overallIndex); + bestRmerge[16]= completenessList.get(overallIndex); + //TODO format to 2 figures after . + bestRmerge[17] = resolutionsLimitLowList.get(overallIndex) + "/" + resolutionsLimitHighList.get(overallIndex); } - LOG.info("bestRmerge = " + "- " + bestRmerge[0] + "- " + bestRmerge[1]+ "- " + bestRmerge[2]+ "- " + bestRmerge[3]); + LOG.info("bestRmerge = " + bestRmerge[0] + "- " + bestRmerge[1]+ "- " + bestRmerge[2]+ "- " + bestRmerge[3]); return bestRmerge; } + + private Chunk getCompletenessChunk(String completeness) { + Chunk chu = new Chunk( completeness, FONT_DOC_SMALL_BOLD); + chu.setBackground(BLUE_COLOR); + if (completeness != null && new Double(completeness) < 80 ) { + if (new Double(completeness) < 10) { + chu.setBackground(RED_COLOR); + } else { + chu.setBackground(LIGHT_YELLOW_COLOR); + } + } + return chu; + } } diff --git a/ispyb-ui/src/main/webapp/tiles/bodies/common/proposal/updateISPYBdb.jsp b/ispyb-ui/src/main/webapp/tiles/bodies/common/proposal/updateISPYBdb.jsp index badc48481..692b6a7af 100644 --- a/ispyb-ui/src/main/webapp/tiles/bodies/common/proposal/updateISPYBdb.jsp +++ b/ispyb-ui/src/main/webapp/tiles/bodies/common/proposal/updateISPYBdb.jsp @@ -66,7 +66,8 @@ Contributors : S. Delageniere, R. Leal, L. Launer, K. Levik, S. Veyrier, P. Bren - + + From de9c172f312a487253971d58b0c3eb3e19353c48 Mon Sep 17 00:00:00 2001 From: delageniere Date: Thu, 26 Oct 2017 15:39:49 +0200 Subject: [PATCH 24/38] analysis report improvement --- .../common/util/export/ExiPdfRtfExporter.java | 84 +++++++++++++------ .../getViewTableQuery.sql | 2 +- .../getViewTableQuery.sql | 2 +- 3 files changed, 60 insertions(+), 28 deletions(-) diff --git a/ispyb-ejb/src/main/java/ispyb/common/util/export/ExiPdfRtfExporter.java b/ispyb-ejb/src/main/java/ispyb/common/util/export/ExiPdfRtfExporter.java index 6a3bbc43a..a99b77023 100644 --- a/ispyb-ejb/src/main/java/ispyb/common/util/export/ExiPdfRtfExporter.java +++ b/ispyb-ejb/src/main/java/ispyb/common/util/export/ExiPdfRtfExporter.java @@ -28,10 +28,12 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; +import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.Set; import org.apache.log4j.Logger; @@ -47,8 +49,6 @@ import com.lowagie.text.Paragraph; import com.lowagie.text.Phrase; import com.lowagie.text.Table; -import com.lowagie.text.pdf.PdfPCell; -import com.lowagie.text.pdf.PdfPTable; import com.lowagie.text.pdf.PdfWriter; import com.lowagie.text.rtf.RtfWriter2; @@ -57,10 +57,12 @@ import ispyb.common.util.PathUtils; import ispyb.server.common.services.sessions.Session3Service; import ispyb.server.common.util.ejb.Ejb3ServiceLocator; +import ispyb.server.mx.services.autoproc.SpaceGroup3Service; import ispyb.server.mx.services.collections.DataCollection3Service; import ispyb.server.mx.services.collections.Image3Service; import ispyb.server.mx.services.collections.IspybCrystalClass3Service; import ispyb.server.mx.services.ws.rest.datacollectiongroup.DataCollectionGroupRestWsService; +import ispyb.server.mx.vos.autoproc.SpaceGroup3VO; import ispyb.server.mx.vos.collections.IspybCrystalClass3VO; import ispyb.server.mx.vos.collections.Session3VO; @@ -176,6 +178,8 @@ public class ExiPdfRtfExporter { public final static float DIFF_IMAGE_WIDTH = 174; public final static float DIFF_IMAGE_HEIGHT = 174; + + public final static double MIN_RMERGE = 10; //proposalId int proposalId; @@ -207,6 +211,10 @@ public class ExiPdfRtfExporter { private DataCollectionGroupRestWsService dcGroupService; private Image3Service imageService; + + private SpaceGroup3Service spacegroupService; + + private Map spgMap = new HashMap (); public ExiPdfRtfExporter(int proposalId, String proposalDesc, Integer sessionId, List> dataCollections, Integer nbRowsMax) throws Exception { @@ -231,8 +239,18 @@ private void init() throws Exception { .getLocalService(DataCollectionGroupRestWsService.class); imageService = (Image3Service) ejb3ServiceLocator .getLocalService(Image3Service.class); + spacegroupService = (SpaceGroup3Service) ejb3ServiceLocator + .getLocalService(SpaceGroup3Service.class); slv = sessionService.findByPk(sessionId, false/*withDataCollectionGroup*/, false/*withEnergyScan*/, false/*withXFESpectrum*/); + + List spaceGroups = spacegroupService.findAll(); + + for (Iterator iterator = spaceGroups.iterator(); iterator.hasNext();) { + SpaceGroup3VO spg = (SpaceGroup3VO) iterator.next(); + spgMap.put(spg.getSpaceGroupName(), spg.getSpaceGroupNumber()); + } + LOG.info("spgMap=" + spgMap.toString()); } @@ -785,9 +803,9 @@ private void setDataAnalysisMapData(Document document, Map dataC table.addCell(p); - } else if (existAutoProcSpaceGroup && extractBestRmerge(dataCollectionMapItem) != null){ + } else if (existAutoProcSpaceGroup && extractBestAutoproc(dataCollectionMapItem) != null){ // Cell 6 - bestRmerge = extractBestRmerge(dataCollectionMapItem); + bestRmerge = extractBestAutoproc(dataCollectionMapItem); parag = bestRmerge[0] + "\n" + "Overall\n" + "Inner\n" @@ -1085,51 +1103,64 @@ private List getListOfNbCrystalPerClass(List list return listOfNbCrystalPerClass; } - private String[] extractBestRmerge(Map dataCollectionMapItem) throws Exception { + private String[] extractBestAutoproc(Map dataCollectionMapItem) throws Exception { - String listString = (String)dataCollectionMapItem.get("completenessList"); - listString.trim(); - List completenessList = new ArrayList(Arrays.asList((listString.split(",")))); - LOG.info("completenessList = " + completenessList.toString()); - String [] bestRmerge = null; + String listString = (String)dataCollectionMapItem.get("completenessList"); - if (completenessList != null && !completenessList.isEmpty()) { - - bestRmerge = new String[18]; + if (dataCollectionMapItem.get("completenessList") != null && !listString.isEmpty() && dataCollectionMapItem.get("AutoProc_spaceGroups") != null) { + + listString.trim(); + List completenessList = new ArrayList(Arrays.asList((listString.split(",")))); + LOG.info("completenessList = " + completenessList.toString()); + List spaceGroupsList = new ArrayList(Arrays.asList(((String)dataCollectionMapItem.get("AutoProc_spaceGroups")).trim().split(","))); + LOG.info("spaceGroupsList = " + spaceGroupsList.size() + spaceGroupsList.toString()); + List resolutionsLimitLowList = new ArrayList(Arrays.asList(((String)dataCollectionMapItem.get("resolutionsLimitLow")).trim().split(","))); + LOG.info("resolutionsLimitLowList = " + resolutionsLimitLowList.size() + resolutionsLimitLowList.toString()); + List resolutionsLimitHighList = new ArrayList(Arrays.asList(((String)dataCollectionMapItem.get("resolutionsLimitHigh")).trim().split(","))); + LOG.info("resolutionsLimitHighList = " + resolutionsLimitHighList.toString()); List rmergesList = new ArrayList(Arrays.asList(((String)dataCollectionMapItem.get("rMerges")).trim().split(","))); LOG.info("rmergesList = " + rmergesList.size() + rmergesList.toString() ); List scalingStatisticsTypesList = new ArrayList(Arrays.asList(((String)dataCollectionMapItem.get("scalingStatisticsTypes")).trim().split(","))); LOG.info("scalingStatisticsTypesList = " + scalingStatisticsTypesList.size() + scalingStatisticsTypesList.toString()); + + bestRmerge = new String[18]; int i = 0; Double rmergeMin = 1000.000; int indexRmergeMin = 0; + Set indexSet = new HashSet(); - for (Iterator iterator = scalingStatisticsTypesList.iterator(); iterator.hasNext();) { + for (Iterator iterator = scalingStatisticsTypesList.iterator(); iterator.hasNext();) { String type = (String) iterator.next(); if (type.contains("innerShell")){ double rm = new Double(rmergesList.get(i)).doubleValue(); LOG.info("rm = " + rm); if (rm > 0 && rm < rmergeMin) { rmergeMin = rm; - indexRmergeMin = i; - // TODO replace this by a look for the higher spacegroup - if (rmergeMin < 10) - break; + indexRmergeMin = i; + if (rmergeMin < MIN_RMERGE) + indexSet.add(i); } } i=i+1; } - List spaceGroupsList = new ArrayList(Arrays.asList(((String)dataCollectionMapItem.get("AutoProc_spaceGroups")).trim().split(","))); - LOG.info("spaceGroupsList = " + spaceGroupsList.size() + spaceGroupsList.toString()); - List resolutionsLimitLowList = new ArrayList(Arrays.asList(((String)dataCollectionMapItem.get("resolutionsLimitLow")).trim().split(","))); - LOG.info("resolutionsLimitLowList = " + resolutionsLimitLowList.size() + resolutionsLimitLowList.toString()); - List resolutionsLimitHighList = new ArrayList(Arrays.asList(((String)dataCollectionMapItem.get("resolutionsLimitHigh")).trim().split(","))); - LOG.info("resolutionsLimitHighList = " + resolutionsLimitHighList.toString()); + if (!indexSet.isEmpty()) { + String spgTemp; + Integer spgNb = 0; + for (Iterator iterator = indexSet.iterator(); iterator.hasNext();) { + Integer index = (Integer) iterator.next(); + spgTemp = spaceGroupsList.get(index); + + if (spgMap.get(spgTemp)!= null && spgNb < spgMap.get(spgTemp)) { + spgNb = spgMap.get(spgTemp); + indexRmergeMin = index; + } + } + } bestRmerge[0] = spaceGroupsList.get(indexRmergeMin); - bestRmerge[1] = rmergeMin.toString(); + bestRmerge[1] = rmergesList.get(indexRmergeMin); bestRmerge[2]= completenessList.get(indexRmergeMin); bestRmerge[3] = resolutionsLimitLowList.get(indexRmergeMin) + "/" + resolutionsLimitHighList.get(indexRmergeMin); @@ -1163,8 +1194,9 @@ private String[] extractBestRmerge(Map dataCollectionMapItem) th bestRmerge[16]= completenessList.get(overallIndex); //TODO format to 2 figures after . bestRmerge[17] = resolutionsLimitLowList.get(overallIndex) + "/" + resolutionsLimitHighList.get(overallIndex); + LOG.info("bestRmerge = " + bestRmerge[0] + "- " + bestRmerge[1]+ "- " + bestRmerge[2]+ "- " + bestRmerge[3]); } - LOG.info("bestRmerge = " + bestRmerge[0] + "- " + bestRmerge[1]+ "- " + bestRmerge[2]+ "- " + bestRmerge[3]); + return bestRmerge; } diff --git a/ispyb-ejb/src/main/resources/queries/DataCollectionGroupRestWsServiceBean/getViewTableQuery.sql b/ispyb-ejb/src/main/resources/queries/DataCollectionGroupRestWsServiceBean/getViewTableQuery.sql index ce9f0a09a..85fd97aa0 100644 --- a/ispyb-ejb/src/main/resources/queries/DataCollectionGroupRestWsServiceBean/getViewTableQuery.sql +++ b/ispyb-ejb/src/main/resources/queries/DataCollectionGroupRestWsServiceBean/getViewTableQuery.sql @@ -20,7 +20,7 @@ GROUP_CONCAT(`scalingStatisticsType` SEPARATOR ', ') AS `scalingStatisticsTypes` GROUP_CONCAT(`resolutionLimitHigh` SEPARATOR ', ') AS `resolutionsLimitHigh`, GROUP_CONCAT(`resolutionLimitLow` SEPARATOR ', ') AS `resolutionsLimitLow`, GROUP_CONCAT(`rMerge` SEPARATOR ', ') AS `rMerges`, -GROUP_CONCAT(`completeness` SEPARATOR ', ') AS `completenessList`, +GROUP_CONCAT(`completeness` SEPARATOR ', ') AS `completenessList`, GROUP_CONCAT(`AutoProc_spaceGroup` SEPARATOR ', ') AS `AutoProc_spaceGroups`, (SELECT count(*) diff --git a/ispyb-ejb/src/main/resources/queries/DataCollectionRestWsServiceBean/getViewTableQuery.sql b/ispyb-ejb/src/main/resources/queries/DataCollectionRestWsServiceBean/getViewTableQuery.sql index 89b48324c..c90e57e0c 100644 --- a/ispyb-ejb/src/main/resources/queries/DataCollectionRestWsServiceBean/getViewTableQuery.sql +++ b/ispyb-ejb/src/main/resources/queries/DataCollectionRestWsServiceBean/getViewTableQuery.sql @@ -16,7 +16,7 @@ GROUP_CONCAT(`scalingStatisticsType` SEPARATOR ', ') AS `scalingStatisticsTypes` GROUP_CONCAT(`resolutionLimitHigh` SEPARATOR ', ') AS `resolutionsLimitHigh`, GROUP_CONCAT(`resolutionLimitLow` SEPARATOR ', ') AS `resolutionsLimitLow`, GROUP_CONCAT(`rMerge` SEPARATOR ', ') AS `rMerges`, -GROUP_CONCAT(`completeness` SEPARATOR ', ') AS `completenessList`, +GROUP_CONCAT(`completeness` SEPARATOR ', ') AS `completenessList`, GROUP_CONCAT(`AutoProc_spaceGroup` SEPARATOR ', ') AS `AutoProc_spaceGroups`, (SELECT count(*) From 3db304f02120622a17379b2764371a72871c3528 Mon Sep 17 00:00:00 2001 From: delageniere Date: Thu, 26 Oct 2017 16:38:49 +0200 Subject: [PATCH 25/38] change the name of reports + reverse order --- .../common/util/export/ExiPdfRtfExporter.java | 19 ++++++++++++------- .../rest/mx/DataCollectionRestWebService.java | 14 ++++++++++---- 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/ispyb-ejb/src/main/java/ispyb/common/util/export/ExiPdfRtfExporter.java b/ispyb-ejb/src/main/java/ispyb/common/util/export/ExiPdfRtfExporter.java index a99b77023..d6a93da60 100644 --- a/ispyb-ejb/src/main/java/ispyb/common/util/export/ExiPdfRtfExporter.java +++ b/ispyb-ejb/src/main/java/ispyb/common/util/export/ExiPdfRtfExporter.java @@ -36,6 +36,7 @@ import java.util.Set; import org.apache.log4j.Logger; +import org.jboss.util.collection.ReverseListIterator; import com.lowagie.text.BadElementException; import com.lowagie.text.Cell; @@ -501,10 +502,12 @@ private void setDataCollectionTable(Document document) throws Exception { Map mapDataCollectionGroupIdCClass = new HashMap(); // DataCollection Rows - Iterator> it = dataCollections.iterator(); + //Iterator> it = dataCollections.iterator(); + Iterator> it2 = new ReverseListIterator >(dataCollections); + int i = 0; - while (it.hasNext() && i < nbRowsMax) { - Map dataCollectionMapData = it.next(); + while (it2.hasNext() && i < nbRowsMax) { + Map dataCollectionMapData = it2.next(); LOG.info("dcMap=" + dataCollectionMapData.toString()); setDataCollectionMapData(document, dataCollectionMapData); @@ -516,7 +519,7 @@ private void setDataCollectionTable(Document document) throws Exception { } i++; } - setCrystalClassSummary(document, mapDataCollectionGroupIdCClass); + //setCrystalClassSummary(document, mapDataCollectionGroupIdCClass); document.add(new Paragraph(" ")); } document.add(new Paragraph(" ")); @@ -538,10 +541,12 @@ private void setDataAnalysisTable(Document document) throws Exception { //document.add(new Paragraph(" ")); // DataCollection Rows - Iterator> it = dataCollections.iterator(); + //Iterator> it = dataCollections.iterator(); + Iterator> it2 = new ReverseListIterator >(dataCollections); + int i = 0; - while (it.hasNext() && i < nbRowsMax) { - Map dataCollectionMapData = it.next(); + while (it2.hasNext() && i < nbRowsMax) { + Map dataCollectionMapData = it2.next(); LOG.info("dcMap=" + dataCollectionMapData.toString()); setDataAnalysisMapData(document, dataCollectionMapData); i++; diff --git a/ispyb-ws/src/main/java/ispyb/ws/rest/mx/DataCollectionRestWebService.java b/ispyb-ws/src/main/java/ispyb/ws/rest/mx/DataCollectionRestWebService.java index 0b2a26526..b5197f889 100644 --- a/ispyb-ws/src/main/java/ispyb/ws/rest/mx/DataCollectionRestWebService.java +++ b/ispyb-ws/src/main/java/ispyb/ws/rest/mx/DataCollectionRestWebService.java @@ -23,6 +23,7 @@ import ispyb.common.util.export.ExiPdfRtfExporter; import ispyb.server.mx.vos.collections.DataCollection3VO; +import ispyb.server.mx.vos.collections.Session3VO; @Path("/") public class DataCollectionRestWebService extends MXRestWebService { @@ -229,7 +230,8 @@ public Response getDataCollectionsReportBySessionIdPDF(@PathParam("token") Strin try { byte[] byteToExport = this.getPdfRtf(sessionId, proposal, nbRows, false); this.logFinish(methodName, start, logger); - return this.downloadFile(byteToExport, "DataCollectionsReport.pdf"); + Session3VO ses = this.getSession3Service().findByPk(new Integer(sessionId), false, false, false); + return this.downloadFile(byteToExport, "Report_" + proposal + "_"+ ses.getBeamlineName()+ "_" + ses.getStartDate() + ".pdf"); } catch (Exception e) { return this.logError(methodName, e, start, logger); @@ -249,7 +251,8 @@ public Response getDataCollectionsReportBySessionIdRTF(@PathParam("token") Strin try { byte[] byteToExport = this.getPdfRtf(sessionId, proposal, nbRows, true); this.logFinish(methodName, start, logger); - return this.downloadFile(byteToExport, "DataCollectionsReport.rtf"); + Session3VO ses = this.getSession3Service().findByPk(new Integer(sessionId), false, false, false); + return this.downloadFile(byteToExport, "Report_" + proposal + "_"+ ses.getBeamlineName()+ "_" + ses.getStartDate() + ".rtf"); } catch (Exception e) { return this.logError(methodName, e, start, logger); @@ -269,7 +272,9 @@ public Response getDataCollectionsAnalysisReportBySessionIdPDF(@PathParam("token try { byte[] byteToExport = this.getAnalysisPdfRtf(sessionId, proposal, nbRows, false); this.logFinish(methodName, start, logger); - return this.downloadFile(byteToExport, "DataCollectionsAnalysisReport.pdf"); + Session3VO ses = this.getSession3Service().findByPk(new Integer(sessionId), false, false, false); + return this.downloadFile(byteToExport, "AnalysisReport_" + proposal + "_"+ ses.getBeamlineName()+ "_" + ses.getStartDate() + ".pdf"); + } catch (Exception e) { return this.logError(methodName, e, start, logger); @@ -289,7 +294,8 @@ public Response getDataCollectionsAnalysisReportBySessionIdRTF(@PathParam("token try { byte[] byteToExport = this.getAnalysisPdfRtf(sessionId, proposal, nbRows, true); this.logFinish(methodName, start, logger); - return this.downloadFile(byteToExport, "DataCollectionsAnalysisReport.rtf"); + Session3VO ses = this.getSession3Service().findByPk(new Integer(sessionId), false, false, false); + return this.downloadFile(byteToExport, "AnalysisReport_" + proposal + "_"+ ses.getBeamlineName()+ "_" + ses.getStartDate() + ".rtf"); } catch (Exception e) { return this.logError(methodName, e, start, logger); From 9d2539d6a1ff5959019a698f1747b1f441825fc2 Mon Sep 17 00:00:00 2001 From: delageniere Date: Fri, 27 Oct 2017 09:49:46 +0200 Subject: [PATCH 26/38] change the version of java in doc --- documentation/ISPyB_DevelopersGuide.doc | Bin 206336 -> 207360 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/documentation/ISPyB_DevelopersGuide.doc b/documentation/ISPyB_DevelopersGuide.doc index f5ca5375e93d6644fb114ae5863d11ec624f0f0c..65267a340e0591758ce0c0de5773e13591b99997 100644 GIT binary patch delta 13192 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

3YE#z(B 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 delta 13016 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 mo?fkdT|C{x!d!wq+ Date: Fri, 27 Oct 2017 14:14:55 +0200 Subject: [PATCH 27/38] reports enhancement --- .../common/util/export/ExiPdfRtfExporter.java | 21 +++++++++++------- .../rest/mx/DataCollectionRestWebService.java | 22 ++++++++++++++----- 2 files changed, 29 insertions(+), 14 deletions(-) diff --git a/ispyb-ejb/src/main/java/ispyb/common/util/export/ExiPdfRtfExporter.java b/ispyb-ejb/src/main/java/ispyb/common/util/export/ExiPdfRtfExporter.java index d6a93da60..9b3ac7606 100644 --- a/ispyb-ejb/src/main/java/ispyb/common/util/export/ExiPdfRtfExporter.java +++ b/ispyb-ejb/src/main/java/ispyb/common/util/export/ExiPdfRtfExporter.java @@ -553,8 +553,6 @@ private void setDataAnalysisTable(Document document) throws Exception { } document.add(new Paragraph(" ")); } - document.add(new Paragraph(" ")); - } /** @@ -691,6 +689,9 @@ private void setDataAnalysisMapData(Document document, Map dataC //row 1 Table table = new Table(NB_COL_DATA_ANALYSIS); + table.setCellsFitPage(true); + table.setBorder(0); + table.getDefaultCell().setHorizontalAlignment(Element.ALIGN_LEFT); table.getDefaultCell().setBorderWidth(0); @@ -757,7 +758,7 @@ private void setDataAnalysisMapData(Document document, Map dataC p = new Paragraph(parag, FONT_DOC_SMALL); table.addCell(p); - // Cell 6 + // Cell 7 p = new Paragraph(); Chunk chu2 = new Chunk( "KO", FONT_INDEXING_FAILED); if (indexing.booleanValue() ){ @@ -773,33 +774,33 @@ private void setDataAnalysisMapData(Document document, Map dataC p.add(chu2); table.addCell(p); - // Cell 7 + // Cell 8 parag = "Space group: \n" + "Mosaicity: \n" ; p = new Paragraph(parag, FONT_DOC_SMALL); table.addCell(p); - // Cell 8 + // Cell 9 parag = getCellParam(dataCollectionMapItem, "ScreeningOutputLattice_spaceGroup", null) + "\n" + getCellParam(dataCollectionMapItem, "ScreeningOutput_mosaicity", null)+ "\n" ; p = new Paragraph(parag, FONT_DOC_SMALL_BOLD); table.addCell(p); - // Cell 9 + // Cell 10 parag = "a \n" + getCellParam(dataCollectionMapItem, "ScreeningOutputLattice_unitCell_a", null) + "\n alpha \n" + getCellParam(dataCollectionMapItem, "ScreeningOutputLattice_unitCell_alpha", null) ; p = new Paragraph(parag, FONT_DOC_SMALL_CENTERED); p.setAlignment(Element.ALIGN_CENTER); table.addCell(p); - // Cell 10 + // Cell 11 parag = "b \n" + getCellParam(dataCollectionMapItem, "ScreeningOutputLattice_unitCell_b", null) + "\n beta \n" + getCellParam(dataCollectionMapItem, "ScreeningOutputLattice_unitCell_beta", null) ; p = new Paragraph(parag, FONT_DOC_SMALL_CENTERED); p.setAlignment(Element.ALIGN_CENTER); table.addCell(p); - // Cell 11 + // Cell 12 parag = "c \n" + getCellParam(dataCollectionMapItem, "ScreeningOutputLattice_unitCell_c", null) + "\n gamma \n" + getCellParam(dataCollectionMapItem, "ScreeningOutputLattice_unitCell_gama", null) ; @@ -881,6 +882,10 @@ private void setDataAnalysisMapData(Document document, Map dataC } // to avoid splitting table Table containingTable = new Table(1); + containingTable.setCellsFitPage(true); + containingTable.setBorder(0); + containingTable.getDefaultCell().setBorderWidth(0); + Cell cell = new Cell(table); containingTable.addCell(cell); diff --git a/ispyb-ws/src/main/java/ispyb/ws/rest/mx/DataCollectionRestWebService.java b/ispyb-ws/src/main/java/ispyb/ws/rest/mx/DataCollectionRestWebService.java index b5197f889..45f112b79 100644 --- a/ispyb-ws/src/main/java/ispyb/ws/rest/mx/DataCollectionRestWebService.java +++ b/ispyb-ws/src/main/java/ispyb/ws/rest/mx/DataCollectionRestWebService.java @@ -231,7 +231,10 @@ public Response getDataCollectionsReportBySessionIdPDF(@PathParam("token") Strin byte[] byteToExport = this.getPdfRtf(sessionId, proposal, nbRows, false); this.logFinish(methodName, start, logger); Session3VO ses = this.getSession3Service().findByPk(new Integer(sessionId), false, false, false); - return this.downloadFile(byteToExport, "Report_" + proposal + "_"+ ses.getBeamlineName()+ "_" + ses.getStartDate() + ".pdf"); + if (ses != null) + return this.downloadFile(byteToExport, "Report_" + proposal + "_"+ ses.getBeamlineName()+ "_" + ses.getStartDate() + ".pdf"); + else + return this.downloadFile(byteToExport, "No_session.pdf"); } catch (Exception e) { return this.logError(methodName, e, start, logger); @@ -252,7 +255,10 @@ public Response getDataCollectionsReportBySessionIdRTF(@PathParam("token") Strin byte[] byteToExport = this.getPdfRtf(sessionId, proposal, nbRows, true); this.logFinish(methodName, start, logger); Session3VO ses = this.getSession3Service().findByPk(new Integer(sessionId), false, false, false); - return this.downloadFile(byteToExport, "Report_" + proposal + "_"+ ses.getBeamlineName()+ "_" + ses.getStartDate() + ".rtf"); + if (ses != null) + return this.downloadFile(byteToExport, "Report_" + proposal + "_"+ ses.getBeamlineName()+ "_" + ses.getStartDate() + ".rtf"); + else + return this.downloadFile(byteToExport, "No_session.pdf"); } catch (Exception e) { return this.logError(methodName, e, start, logger); @@ -273,8 +279,10 @@ public Response getDataCollectionsAnalysisReportBySessionIdPDF(@PathParam("token byte[] byteToExport = this.getAnalysisPdfRtf(sessionId, proposal, nbRows, false); this.logFinish(methodName, start, logger); Session3VO ses = this.getSession3Service().findByPk(new Integer(sessionId), false, false, false); - return this.downloadFile(byteToExport, "AnalysisReport_" + proposal + "_"+ ses.getBeamlineName()+ "_" + ses.getStartDate() + ".pdf"); - + if (ses !=null) + return this.downloadFile(byteToExport, "AnalysisReport_" + proposal + "_"+ ses.getBeamlineName()+ "_" + ses.getStartDate() + ".pdf"); + else + return this.downloadFile(byteToExport, "No_session.pdf"); } catch (Exception e) { return this.logError(methodName, e, start, logger); @@ -295,8 +303,10 @@ public Response getDataCollectionsAnalysisReportBySessionIdRTF(@PathParam("token byte[] byteToExport = this.getAnalysisPdfRtf(sessionId, proposal, nbRows, true); this.logFinish(methodName, start, logger); Session3VO ses = this.getSession3Service().findByPk(new Integer(sessionId), false, false, false); - return this.downloadFile(byteToExport, "AnalysisReport_" + proposal + "_"+ ses.getBeamlineName()+ "_" + ses.getStartDate() + ".rtf"); - + if (ses !=null) + return this.downloadFile(byteToExport, "AnalysisReport_" + proposal + "_"+ ses.getBeamlineName()+ "_" + ses.getStartDate() + ".rtf"); + else + return this.downloadFile(byteToExport, "No_session.pdf"); } catch (Exception e) { return this.logError(methodName, e, start, logger); } From 5c8b688387d39b9d6012265d013f7a487bcb04e9 Mon Sep 17 00:00:00 2001 From: delageniere Date: Mon, 30 Oct 2017 15:53:54 +0100 Subject: [PATCH 28/38] fixes #176 --- .../main/webapp/tiles/bodies/biosaxs/welcome.jsp | 4 +++- .../tiles/bodies/common/help/mxcubeHelp.jsp | 13 +++++++------ .../tiles/bodies/common/help/prepareHelp.jsp | 16 +++++++++------- .../bodies/mx/prepare/fillSampleChanger.jsp | 12 +++++++----- 4 files changed, 26 insertions(+), 19 deletions(-) diff --git a/ispyb-ui/src/main/webapp/tiles/bodies/biosaxs/welcome.jsp b/ispyb-ui/src/main/webapp/tiles/bodies/biosaxs/welcome.jsp index 37f289009..bd9e8d15c 100644 --- a/ispyb-ui/src/main/webapp/tiles/bodies/biosaxs/welcome.jsp +++ b/ispyb-ui/src/main/webapp/tiles/bodies/biosaxs/welcome.jsp @@ -91,8 +91,10 @@ <% String targetDataAcquisitions = request.getContextPath() + "/user/viewProjectList.do?reqCode=display"; %> <% String targetListShipment = request.getContextPath() + "/user/viewProjectList.do?reqCode=display&menu=list_shipment"; %> <% String targetPrepareExperiment = request.getContextPath() + "/user/viewProjectList.do?reqCode=display&menu=prepareexperiment"; %> +<% String toUpdateDB = request.getContextPath() + "/updateDB.do?reqCode=updateProposal"; %> -

In case of problems when creating shipments/samples, update ISPyB database (this may take a few minutes). + +

In case of problems when creating shipments/samples, update ISPyB database (this may take a few minutes).


diff --git a/ispyb-ui/src/main/webapp/tiles/bodies/common/help/mxcubeHelp.jsp b/ispyb-ui/src/main/webapp/tiles/bodies/common/help/mxcubeHelp.jsp index 7c010e569..2f9bc3f28 100644 --- a/ispyb-ui/src/main/webapp/tiles/bodies/common/help/mxcubeHelp.jsp +++ b/ispyb-ui/src/main/webapp/tiles/bodies/common/help/mxcubeHelp.jsp @@ -18,6 +18,7 @@ Contributors : S. Delageniere, R. Leal, L. Launer, K. Levik, S. Veyrier, P. Bren --------------------------------------------------------------------------------------------------%> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <%@page import="ispyb.common.util.Constants"%> +<%@ page isELIgnored="false" %> Help page for MxCube @@ -33,25 +34,25 @@ Contributors : S. Delageniere, R. Leal, L. Launer, K. Levik, S. Veyrier, P. Bren <%-- 1) Select shipment --%> - 1- Select the dewars you want for processing.
+ 1- Select the dewars you want for processing.
<%-- 2) Fill Sample changer --%> - 2- Fill the sample changer: assign a location for your containers (only required if not using Damatrix codes)
+ 2- Fill the sample changer: assign a location for your containers (only required if not using Damatrix codes)
- 2- Fill the sample changer: assign a location for your containers
+ 2- Fill the sample changer: assign a location for your containers
- 2- Fill the sample changer: assign a location for your containers
+ 2- Fill the sample changer: assign a location for your containers
- 2- Fill the sample changer: assign a location for your containers
+ 2- Fill the sample changer: assign a location for your containers
- 2- Fill the sample changer: assign a location for your containers
+ 2- Fill the sample changer: assign a location for your containers
diff --git a/ispyb-ui/src/main/webapp/tiles/bodies/common/help/prepareHelp.jsp b/ispyb-ui/src/main/webapp/tiles/bodies/common/help/prepareHelp.jsp index e265a0fac..633193999 100644 --- a/ispyb-ui/src/main/webapp/tiles/bodies/common/help/prepareHelp.jsp +++ b/ispyb-ui/src/main/webapp/tiles/bodies/common/help/prepareHelp.jsp @@ -19,6 +19,8 @@ Contributors : S. Delageniere, R. Leal, L. Launer, K. Levik, S. Veyrier, P. Bren <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <%@page import="ispyb.common.util.Constants"%> +<%@ page isELIgnored="false" %> + Help page for Prepare Experiment @@ -29,25 +31,25 @@ Contributors : S. Delageniere, R. Leal, L. Launer, K. Levik, S. Veyrier, P. Bren <%-- 1) Select shipment --%> - 1- Select the dewars you want for processing.
+ 1- Select the dewars you want for processing.
<%-- 2) Fill Sample changer --%> - 2- Fill the sample changer: assign a location for your containers (only required if not using Damatrix codes)
+ 2- Fill the sample changer: assign a location for your containers (only required if not using Damatrix codes)
- 2- Fill the sample changer: assign a location for your containers
+ 2- Fill the sample changer: assign a location for your containers
- 2- Fill the sample changer: assign a location for your containers
+ 2- Fill the sample changer: assign a location for your containers
- 2- Fill the sample changer: assign a location for your containers
+ 2- Fill the sample changer: assign a location for your containers
- 2- Fill the sample changer: assign a location for your containers
+ 2- Fill the sample changer: assign a location for your containers
@@ -66,7 +68,7 @@ Contributors : S. Delageniere, R. Leal, L. Launer, K. Levik, S. Veyrier, P. Bren - +

diff --git a/ispyb-ui/src/main/webapp/tiles/bodies/mx/prepare/fillSampleChanger.jsp b/ispyb-ui/src/main/webapp/tiles/bodies/mx/prepare/fillSampleChanger.jsp index a799d3335..6f31ef7a4 100644 --- a/ispyb-ui/src/main/webapp/tiles/bodies/mx/prepare/fillSampleChanger.jsp +++ b/ispyb-ui/src/main/webapp/tiles/bodies/mx/prepare/fillSampleChanger.jsp @@ -25,6 +25,8 @@ Contributors : S. Delageniere, R. Leal, L. Launer, K. Levik, S. Veyrier, P. Bren <%@page import="ispyb.common.util.Constants"%> +<%@ page isELIgnored="false" %> + <% String buttonLabel = "Next step: Link Samples in " + Constants.BCM_NAME; %> @@ -42,25 +44,25 @@ Contributors : S. Delageniere, R. Leal, L. Launer, K. Levik, S. Veyrier, P. Bren <%-- 1) Select shipment --%> - 1- Select the dewars you want for processing.
+ 1- Select the dewars you want for processing.
<%-- 2) Fill Sample changer --%> - 2- Fill the sample changer: assign a location for your containers (only required if not using Damatrix codes)
+ 2- Fill the sample changer: assign a location for your containers (only required if not using Damatrix codes)
- 2- Fill the sample changer: assign a location for your containers
+ 2- Fill the sample changer: assign a location for your containers
- 2- Fill the sample changer: assign a location for your containers
+ 2- Fill the sample changer: assign a location for your containers
- 2- Fill the sample changer: assign a location for your containers
+ 2- Fill the sample changer: assign a location for your containers
From c4f83777fc321493bfbe75f5ac643c435a112d75 Mon Sep 17 00:00:00 2001 From: Alejandro De Maria Antolinos Date: Tue, 31 Oct 2017 10:24:22 +0100 Subject: [PATCH 29/38] Added security cheks for downloads --- .../services/sessions/Session3Service.java | 4 + .../sessions/Session3ServiceBean.java | 41 + .../src/main/webapp/js/ispyb/min/ispyb-bx.js | 24046 ++++++++-------- .../mx/AutoprocintegrationRestWebService.java | 62 +- 4 files changed, 12115 insertions(+), 12038 deletions(-) diff --git a/ispyb-ejb/src/main/java/ispyb/server/common/services/sessions/Session3Service.java b/ispyb-ejb/src/main/java/ispyb/server/common/services/sessions/Session3Service.java index a3fe103c2..472d285e6 100644 --- a/ispyb-ejb/src/main/java/ispyb/server/common/services/sessions/Session3Service.java +++ b/ispyb-ejb/src/main/java/ispyb/server/common/services/sessions/Session3Service.java @@ -175,6 +175,8 @@ public List findByStartDateAndBeamLineNameAndNbShifts(final Integer */ public Session3VO findByAutoProcScalingId(final Integer autoProcScalingId) throws Exception; + public Session3VO findByAutoProcProgramAttachmentId(final Integer autoProcProgramAttachmentId) throws Exception; + public void protectSession(Integer sessionId) throws Exception; /** @@ -195,6 +197,8 @@ public List findByStartDateAndBeamLineNameAndNbShifts(final Integer */ public Integer getNbOfTests(final Integer sesId) throws Exception; + public Session3VO findByAutoProcProgramId(int autoProcProgramId); + } \ No newline at end of file diff --git a/ispyb-ejb/src/main/java/ispyb/server/common/services/sessions/Session3ServiceBean.java b/ispyb-ejb/src/main/java/ispyb/server/common/services/sessions/Session3ServiceBean.java index 401f24495..27b4debdc 100644 --- a/ispyb-ejb/src/main/java/ispyb/server/common/services/sessions/Session3ServiceBean.java +++ b/ispyb-ejb/src/main/java/ispyb/server/common/services/sessions/Session3ServiceBean.java @@ -111,6 +111,19 @@ private static final String FIND_ALL(boolean fetchDataCollectionGroup, boolean f + " where s.sessionId = g.sessionId and " + " g.dataCollectionGroupId = c.dataCollectionGroupId and " + " c.dataCollectionId = api.dataCollectionId and " + " api.autoProcIntegrationId = apshi.autoProcIntegrationId and " + " apshi.autoProcScalingId = aps.autoProcScalingId and " + " aps.autoProcScalingId = :autoProcScalingId "; + + private static final String FIND_BY_AUTOPROCPROGRAMATTACHMENT_ID = "select s.* from BLSession s, " + + " DataCollectionGroup g, DataCollection c, AutoProcIntegration api, AutoProcProgram autoprocProgram, AutoProcProgramAttachment autoProcProgramAttachment" + + " where s.sessionId = g.sessionId and g.dataCollectionGroupId = c.dataCollectionGroupId and autoprocProgram.autoProcProgramId = api.autoProcProgramId" + + " and c.dataCollectionId = api.dataCollectionId and autoprocProgram.autoProcProgramId = autoProcProgramAttachment.autoProcProgramId " + + " and autoProcProgramAttachment.autoProcProgramAttachmentId = :autoProcProgramAttachmentId "; + + + private static final String FIND_BY_AUTOPROCPROGRAM_ID = "select s.* from BLSession s, " + + " DataCollectionGroup g, DataCollection c, AutoProcIntegration api, AutoProcProgram autoprocProgram " + + " where s.sessionId = g.sessionId and g.dataCollectionGroupId = c.dataCollectionGroupId and autoprocProgram.autoProcProgramId = api.autoProcProgramId" + + " and c.dataCollectionId = api.dataCollectionId and autoprocProgram.autoProcProgramId = :autoProcProgramId "; + private static String getProposalCodeNumberQuery() { String query = "select * " + " FROM BLSession ses, Proposal pro " @@ -525,6 +538,32 @@ public Session3VO findByAutoProcScalingId(final Integer autoProcScalingId) throw } return null; } + + + @SuppressWarnings("unchecked") + public Session3VO findByAutoProcProgramAttachmentId(final Integer autoProcProgramAttachmentId) throws Exception { + String query = FIND_BY_AUTOPROCPROGRAMATTACHMENT_ID; + List col = this.entityManager.createNativeQuery(query, "sessionNativeQuery") + .setParameter("autoProcProgramAttachmentId", autoProcProgramAttachmentId).getResultList(); + if (col != null && col.size() > 0) { + return col.get(0); + } + return null; + } + + + @Override + public Session3VO findByAutoProcProgramId(int autoProcProgramId) { + String query = FIND_BY_AUTOPROCPROGRAM_ID; + @SuppressWarnings("unchecked") + List col = this.entityManager.createNativeQuery(query, "sessionNativeQuery") + .setParameter("autoProcProgramId", autoProcProgramId).getResultList(); + if (col != null && col.size() > 0) { + return col.get(0); + } + return null; + } + /** @@ -818,4 +857,6 @@ private void checkChangeRemoveAccess(Session3VO vo) throws AccessDeniedException + + } \ No newline at end of file diff --git a/ispyb-ui/src/main/webapp/js/ispyb/min/ispyb-bx.js b/ispyb-ui/src/main/webapp/js/ispyb/min/ispyb-bx.js index 23e07209a..a98a06520 100644 --- a/ispyb-ui/src/main/webapp/js/ispyb/min/ispyb-bx.js +++ b/ispyb-ui/src/main/webapp/js/ispyb/min/ispyb-bx.js @@ -38,321 +38,321 @@ Event.prototype = { } }; -function GenericGraph(args) { - this.width = 600; - this.height = 400; - - this.targetId = null; - /** Free spaces in the borders * */ - this.top = 10; - this.left = 10; - this.bottom = 50; - this.right = 40; - - /** Ruler * */ - this.rulerHeight = 50; - this.rulerWidth = 50; - this.rulerStroke = 2; - this.rulerVerticalMarksNumber = 5; - - this.rulerMaxValue = null; - this.rulerMinValue = null; - - /** plot options * */ - this.plotPoints = true; - this.pointRadius = 2; - this.fillOpacityPoint = 0.2; - this.strokeOpacityPoint = 0.2; - - /** Cluster titles * */ - this.clusterTitleHeight = 20; - this.interClassesSpace = 2; - this.interClustersSpace = 4; - this.fontSize = 7; - - /** - * If true classes and title will be rendender in the rule otherwise will be - * integer values - */ - this.plotHorizontalByCluster = true; - - if (args != null) { - if (args.targetId != null) { - this.targetId = args.targetId; - } - if (args.plotHorizontalByCluster != null) { - this.plotHorizontalByCluster = args.plotHorizontalByCluster; - } - if (args.height != null) { - this.height = args.height; - } - if (args.width != null) { - this.width = args.width; - } - if (args.rulerMinValue != null) { - this.rulerMinValue = args.rulerMinValue; - } - if (args.rulerMaxValue != null) { - this.rulerMaxValue = args.rulerMaxValue; - } - if (args.rulerHeight != null) { - this.rulerHeight = args.rulerHeight; - } - - } -} - -GenericGraph.prototype.calculate = function(data) { - var result = {}; - - var checked = this.cleanArray(data); - - /** sorting array * */ - checked.sort(function(a, b) { - return a - b; - }); - - var median = this.getMedian(checked); - - result.median = median; - result.Q1 = Number(this.getQ1(checked)); - result.Q2 = Number(this.getQ2(checked)); - result.Q3 = Number(this.getQ3(checked)); - result.population = checked; - - result.IQR = Number(result.Q3 - result.Q1); - result.outlier_below_limit = result.Q1 - (1.5 * result.IQR); - result.outlier_above_limit = result.Q3 + (1.5 * result.IQR); - result.below_outliers = this.getBelowOutliers(result.outlier_below_limit, checked); - result.above_outliers = this.getAboveOutliers(result.outlier_above_limit, checked); - - result.min_whisker = this.minValueWhisker(result.outlier_below_limit, result.Q1, checked); - result.max_whisker = this.maxValueWhisker(result.outlier_above_limit, result.Q3, checked); - - return result; -}; - -GenericGraph.prototype.drawSVGVerticalText = function(x, y, text, properties) { - var transform = [ "transform", "translate(" + x + ", " + y + "), rotate(-90) " ]; - properties.push(transform); - SVG.drawText(0, 0, text, this.svg, properties); -}; - -/** Plot the numbers on the axis * */ -GenericGraph.prototype.plotRuler = function(rulerProperties) { - - SVG.drawRectangle(rulerProperties.vertical.x, rulerProperties.vertical.y, rulerProperties.vertical.width, rulerProperties.vertical.height, - this.svg, [ [ "fill", "black" ] ]); - var distance = rulerProperties.vertical.height / (this.rulerVerticalMarksNumber + 1); - for ( var i = 0; i <= this.rulerVerticalMarksNumber + 1; i++) { - var deltaHeight = distance * i; - var aux = rulerProperties.vertical.height - deltaHeight; - - var text = Number((aux / rulerProperties.vertical.height) * (rulerProperties.maxPoint - rulerProperties.minPoint) + rulerProperties.minPoint) - .toFixed(3); - - SVG.drawLine(rulerProperties.horizontal.x, this.top + deltaHeight, rulerProperties.horizontal.x - rulerProperties.markWidth, - this.top + deltaHeight, this.svg, [ [ "stroke", "black" ] ]); - SVG.drawText(this.left, this.top + distance * i + 5, text, this.svg, [ [ "style", "font-size:xx-small" ] ]); - /** Drawing the mark up to the end * */ - SVG.drawLine(rulerProperties.horizontal.x, this.top + deltaHeight, this.width - this.right, this.top + deltaHeight, this.svg, [ - [ "stroke", rulerProperties.markColor ], [ "stroke-width", "0.3" ] ]); - - if (i == this.rulerVerticalMarksNumber + 1) { - SVG.drawLine(rulerProperties.horizontal.x, this.top + deltaHeight, this.width - this.right, this.top + deltaHeight, this.svg, [ [ - "stroke", rulerProperties.markColor ] ]); - } - } - - /** Drawing horizontal rulers * */ - if (!this.plotHorizontalByCluster) { - var width = rulerProperties.horizontal.width; - var ratio = width / (rulerProperties.horizontal.xValues.range); - for ( i = 0; i < rulerProperties.horizontal.xValues.values.length; i++) { - var coorX = rulerProperties.horizontal.xValues.values[i] - rulerProperties.horizontal.xValues.min; - SVG.drawText(rulerProperties.horizontal.x + coorX * ratio, this.height - (this.bottom + (this.clusterTitleHeight)), - rulerProperties.horizontal.xValues.values[i], this.svg, [ [ "style", "font-size:small" ] ]); - } - } -}; - -GenericGraph.prototype.plotAxes = function(properties) { - /** - * Drawing canvas plot-free spaces SVG.drawRectangle(0, this.top, this.left, - * this.height, this.svg, [["fill", "pink"]]); SVG.drawRectangle(this.width - - * this.right, this.top, plot.width, plot.height, this.svg, [["fill", - * "pink"]]); SVG.drawRectangle(0, 0, this.width, this.top, this.svg, - * [["fill", "red"]]); SVG.drawRectangle(0, this.height - this.bottom, - * this.width, this.bottom, this.svg, [["fill", "red"]]); - */ - - /** Drawing ruler Space * */ - this.plotRuler({ - minPoint : Number(properties.minPoint), - maxPoint : Number(properties.maxPoint), - markColor : "black", - markWidth : 20, - vertical : { - x : this.left + this.rulerWidth - this.rulerStroke, - y : this.top, - width : this.rulerStroke, - height : this.height - (this.top + this.bottom + this.clusterTitleHeight) - this.rulerHeight - }, - horizontal : { - x : this.left + this.rulerWidth - this.rulerStroke, - y : this.height - (this.bottom + this.clusterTitleHeight + this.rulerHeight), - width : properties.width, - height : this.rulerStroke, - xValues : properties.xValues - } - }); - -}; - -/** Remove nulls and NaN elements in the array * */ -GenericGraph.prototype.cleanArray = function(data) { - var checked = []; - - /** checking data are numbers * */ - for ( var i = 0; i < data.length; i++) { - if (data[i] != null) { - if (!isNaN(data[i])) { - checked.push(data[i]); - } - } - } - return checked; -}; - -GenericGraph.prototype.refresh = function(data) { - document.getElementById(this.targetId).innerHTML = ""; - this.draw(this.targetId, data); -}; - -GenericGraph.prototype.getClassColor = function(className) { - for ( var i = 0; i < this.data.clusters.length; i++) { - var cluster = this.data.clusters[i]; - for ( var j = 0; j < cluster.classes.length; j++) { - var classes = cluster.classes[j]; - if (classes.name == className) { - if (classes.color != null) { - return classes.color; - } - } - } - } - return "black"; -}; - -GenericGraph.prototype.getDimensions = function(data) { - var results = {}; - var points = []; - - this.data = data; - var classesNumber = 0; - var xValues = []; - - for ( var i = 0; i < data.clusters.length; i++) { - var cluster = data.clusters[i]; - if (!this.plotHorizontalByCluster) { - xValues.push(data.clusters[i].x); - } - for ( var j = 0; j < cluster.classes.length; j++) { - var classes = cluster.classes[j]; - points = points.concat(classes.values); - classesNumber = classesNumber + 1; - } - } - - var checked = this.cleanArray(points); - - checked.sort(function(a, b) { - return a - b; - }); - - results.minPoint = checked[0]; - if (this.rulerMinValue != null) { - results.minPoint = this.rulerMinValue; - } - results.maxPoint = checked[checked.length - 1]; - if (this.rulerMaxValue != null) { - results.maxPoint = this.rulerMaxValue; - } - - results.classesNumber = classesNumber; - results.clusterNumber = data.clusters.length; - - var totalInterClassesFreeSpace = (classesNumber - data.clusters.length) * this.interClassesSpace; - var totalInterClusterFreeSpace = (data.clusters.length) * this.interClustersSpace; - results.classWidth = (this.width - (this.rulerWidth + this.left + this.right + totalInterClassesFreeSpace + totalInterClusterFreeSpace))/ classesNumber; - results.width = this.width - (this.right + this.left) - this.rulerWidth + this.rulerStroke; - - results.xValues = {}; - - xValues.sort(function(a, b) { - return a - b; - }); - results.xValues.values = xValues; - if (xValues.length > 0) { - results.xValues.min = xValues[0]; - results.xValues.max = xValues[xValues.length - 1]; - results.xValues.range = results.xValues.max - results.xValues.min; - } - return results; - -}; - -GenericGraph.prototype.draw = function(targetId, data) { - this.targetId = targetId; - var properties = (this.getDimensions(data)); - this.svg = SVG.createSVGCanvas(document.getElementById(this.targetId), [ [ "width", this.width ], [ "height", this.height ] ]); - this.plotAxes(properties); -}; - -GenericGraph.prototype.getMedian = function(checked) { - /** Calculating median * */ - if (checked.length % 2 == 1) { - return checked[Math.floor(checked.length / 2)]; - } else { - return (Number(checked[(checked.length / 2) - 1]) + Number(checked[checked.length / 2])) / 2; - } -}; - -GenericGraph.prototype.pointToPixel = function(value, boxProperties) { - var ratio = (value - boxProperties.minPoint) / (boxProperties.maxPoint - boxProperties.minPoint); - var pixelLength = boxProperties.height - boxProperties.y + this.top; - return (-1 * ratio) * (pixelLength) + (boxProperties.y + boxProperties.height); -}; - -GenericGraph.prototype.maxValueWhisker = function(aboveLimit, q3, array) { - var points = []; - for ( var i = 0; i < array.length; i++) { - if ((array[i] > q3) && (array[i]) <= aboveLimit) { - points.push(array[i]); - } - } - if (points.length > 0) { - points.sort(function(a, b) { - return a - b; - }); - return points[points.length - 1]; - } - return null; -}; - -GenericGraph.prototype.input = function() { - return DATADOC.getBoxWhikerData(); -}; - -GenericGraph.prototype.test = function(targetId) { - var plot = new GenericGraph({ - targetId : targetId, - height : 350, - width : 450, - maxBoxWidth : 25 - }); - plot.refresh(this.input()); -}; +function GenericGraph(args) { + this.width = 600; + this.height = 400; + + this.targetId = null; + /** Free spaces in the borders * */ + this.top = 10; + this.left = 10; + this.bottom = 50; + this.right = 40; + + /** Ruler * */ + this.rulerHeight = 50; + this.rulerWidth = 50; + this.rulerStroke = 2; + this.rulerVerticalMarksNumber = 5; + + this.rulerMaxValue = null; + this.rulerMinValue = null; + + /** plot options * */ + this.plotPoints = true; + this.pointRadius = 2; + this.fillOpacityPoint = 0.2; + this.strokeOpacityPoint = 0.2; + + /** Cluster titles * */ + this.clusterTitleHeight = 20; + this.interClassesSpace = 2; + this.interClustersSpace = 4; + this.fontSize = 7; + + /** + * If true classes and title will be rendender in the rule otherwise will be + * integer values + */ + this.plotHorizontalByCluster = true; + + if (args != null) { + if (args.targetId != null) { + this.targetId = args.targetId; + } + if (args.plotHorizontalByCluster != null) { + this.plotHorizontalByCluster = args.plotHorizontalByCluster; + } + if (args.height != null) { + this.height = args.height; + } + if (args.width != null) { + this.width = args.width; + } + if (args.rulerMinValue != null) { + this.rulerMinValue = args.rulerMinValue; + } + if (args.rulerMaxValue != null) { + this.rulerMaxValue = args.rulerMaxValue; + } + if (args.rulerHeight != null) { + this.rulerHeight = args.rulerHeight; + } + + } +} + +GenericGraph.prototype.calculate = function(data) { + var result = {}; + + var checked = this.cleanArray(data); + + /** sorting array * */ + checked.sort(function(a, b) { + return a - b; + }); + + var median = this.getMedian(checked); + + result.median = median; + result.Q1 = Number(this.getQ1(checked)); + result.Q2 = Number(this.getQ2(checked)); + result.Q3 = Number(this.getQ3(checked)); + result.population = checked; + + result.IQR = Number(result.Q3 - result.Q1); + result.outlier_below_limit = result.Q1 - (1.5 * result.IQR); + result.outlier_above_limit = result.Q3 + (1.5 * result.IQR); + result.below_outliers = this.getBelowOutliers(result.outlier_below_limit, checked); + result.above_outliers = this.getAboveOutliers(result.outlier_above_limit, checked); + + result.min_whisker = this.minValueWhisker(result.outlier_below_limit, result.Q1, checked); + result.max_whisker = this.maxValueWhisker(result.outlier_above_limit, result.Q3, checked); + + return result; +}; + +GenericGraph.prototype.drawSVGVerticalText = function(x, y, text, properties) { + var transform = [ "transform", "translate(" + x + ", " + y + "), rotate(-90) " ]; + properties.push(transform); + SVG.drawText(0, 0, text, this.svg, properties); +}; + +/** Plot the numbers on the axis * */ +GenericGraph.prototype.plotRuler = function(rulerProperties) { + + SVG.drawRectangle(rulerProperties.vertical.x, rulerProperties.vertical.y, rulerProperties.vertical.width, rulerProperties.vertical.height, + this.svg, [ [ "fill", "black" ] ]); + var distance = rulerProperties.vertical.height / (this.rulerVerticalMarksNumber + 1); + for ( var i = 0; i <= this.rulerVerticalMarksNumber + 1; i++) { + var deltaHeight = distance * i; + var aux = rulerProperties.vertical.height - deltaHeight; + + var text = Number((aux / rulerProperties.vertical.height) * (rulerProperties.maxPoint - rulerProperties.minPoint) + rulerProperties.minPoint) + .toFixed(3); + + SVG.drawLine(rulerProperties.horizontal.x, this.top + deltaHeight, rulerProperties.horizontal.x - rulerProperties.markWidth, + this.top + deltaHeight, this.svg, [ [ "stroke", "black" ] ]); + SVG.drawText(this.left, this.top + distance * i + 5, text, this.svg, [ [ "style", "font-size:xx-small" ] ]); + /** Drawing the mark up to the end * */ + SVG.drawLine(rulerProperties.horizontal.x, this.top + deltaHeight, this.width - this.right, this.top + deltaHeight, this.svg, [ + [ "stroke", rulerProperties.markColor ], [ "stroke-width", "0.3" ] ]); + + if (i == this.rulerVerticalMarksNumber + 1) { + SVG.drawLine(rulerProperties.horizontal.x, this.top + deltaHeight, this.width - this.right, this.top + deltaHeight, this.svg, [ [ + "stroke", rulerProperties.markColor ] ]); + } + } + + /** Drawing horizontal rulers * */ + if (!this.plotHorizontalByCluster) { + var width = rulerProperties.horizontal.width; + var ratio = width / (rulerProperties.horizontal.xValues.range); + for ( i = 0; i < rulerProperties.horizontal.xValues.values.length; i++) { + var coorX = rulerProperties.horizontal.xValues.values[i] - rulerProperties.horizontal.xValues.min; + SVG.drawText(rulerProperties.horizontal.x + coorX * ratio, this.height - (this.bottom + (this.clusterTitleHeight)), + rulerProperties.horizontal.xValues.values[i], this.svg, [ [ "style", "font-size:small" ] ]); + } + } +}; + +GenericGraph.prototype.plotAxes = function(properties) { + /** + * Drawing canvas plot-free spaces SVG.drawRectangle(0, this.top, this.left, + * this.height, this.svg, [["fill", "pink"]]); SVG.drawRectangle(this.width - + * this.right, this.top, plot.width, plot.height, this.svg, [["fill", + * "pink"]]); SVG.drawRectangle(0, 0, this.width, this.top, this.svg, + * [["fill", "red"]]); SVG.drawRectangle(0, this.height - this.bottom, + * this.width, this.bottom, this.svg, [["fill", "red"]]); + */ + + /** Drawing ruler Space * */ + this.plotRuler({ + minPoint : Number(properties.minPoint), + maxPoint : Number(properties.maxPoint), + markColor : "black", + markWidth : 20, + vertical : { + x : this.left + this.rulerWidth - this.rulerStroke, + y : this.top, + width : this.rulerStroke, + height : this.height - (this.top + this.bottom + this.clusterTitleHeight) - this.rulerHeight + }, + horizontal : { + x : this.left + this.rulerWidth - this.rulerStroke, + y : this.height - (this.bottom + this.clusterTitleHeight + this.rulerHeight), + width : properties.width, + height : this.rulerStroke, + xValues : properties.xValues + } + }); + +}; + +/** Remove nulls and NaN elements in the array * */ +GenericGraph.prototype.cleanArray = function(data) { + var checked = []; + + /** checking data are numbers * */ + for ( var i = 0; i < data.length; i++) { + if (data[i] != null) { + if (!isNaN(data[i])) { + checked.push(data[i]); + } + } + } + return checked; +}; + +GenericGraph.prototype.refresh = function(data) { + document.getElementById(this.targetId).innerHTML = ""; + this.draw(this.targetId, data); +}; + +GenericGraph.prototype.getClassColor = function(className) { + for ( var i = 0; i < this.data.clusters.length; i++) { + var cluster = this.data.clusters[i]; + for ( var j = 0; j < cluster.classes.length; j++) { + var classes = cluster.classes[j]; + if (classes.name == className) { + if (classes.color != null) { + return classes.color; + } + } + } + } + return "black"; +}; + +GenericGraph.prototype.getDimensions = function(data) { + var results = {}; + var points = []; + + this.data = data; + var classesNumber = 0; + var xValues = []; + + for ( var i = 0; i < data.clusters.length; i++) { + var cluster = data.clusters[i]; + if (!this.plotHorizontalByCluster) { + xValues.push(data.clusters[i].x); + } + for ( var j = 0; j < cluster.classes.length; j++) { + var classes = cluster.classes[j]; + points = points.concat(classes.values); + classesNumber = classesNumber + 1; + } + } + + var checked = this.cleanArray(points); + + checked.sort(function(a, b) { + return a - b; + }); + + results.minPoint = checked[0]; + if (this.rulerMinValue != null) { + results.minPoint = this.rulerMinValue; + } + results.maxPoint = checked[checked.length - 1]; + if (this.rulerMaxValue != null) { + results.maxPoint = this.rulerMaxValue; + } + + results.classesNumber = classesNumber; + results.clusterNumber = data.clusters.length; + + var totalInterClassesFreeSpace = (classesNumber - data.clusters.length) * this.interClassesSpace; + var totalInterClusterFreeSpace = (data.clusters.length) * this.interClustersSpace; + results.classWidth = (this.width - (this.rulerWidth + this.left + this.right + totalInterClassesFreeSpace + totalInterClusterFreeSpace))/ classesNumber; + results.width = this.width - (this.right + this.left) - this.rulerWidth + this.rulerStroke; + + results.xValues = {}; + + xValues.sort(function(a, b) { + return a - b; + }); + results.xValues.values = xValues; + if (xValues.length > 0) { + results.xValues.min = xValues[0]; + results.xValues.max = xValues[xValues.length - 1]; + results.xValues.range = results.xValues.max - results.xValues.min; + } + return results; + +}; + +GenericGraph.prototype.draw = function(targetId, data) { + this.targetId = targetId; + var properties = (this.getDimensions(data)); + this.svg = SVG.createSVGCanvas(document.getElementById(this.targetId), [ [ "width", this.width ], [ "height", this.height ] ]); + this.plotAxes(properties); +}; + +GenericGraph.prototype.getMedian = function(checked) { + /** Calculating median * */ + if (checked.length % 2 == 1) { + return checked[Math.floor(checked.length / 2)]; + } else { + return (Number(checked[(checked.length / 2) - 1]) + Number(checked[checked.length / 2])) / 2; + } +}; + +GenericGraph.prototype.pointToPixel = function(value, boxProperties) { + var ratio = (value - boxProperties.minPoint) / (boxProperties.maxPoint - boxProperties.minPoint); + var pixelLength = boxProperties.height - boxProperties.y + this.top; + return (-1 * ratio) * (pixelLength) + (boxProperties.y + boxProperties.height); +}; + +GenericGraph.prototype.maxValueWhisker = function(aboveLimit, q3, array) { + var points = []; + for ( var i = 0; i < array.length; i++) { + if ((array[i] > q3) && (array[i]) <= aboveLimit) { + points.push(array[i]); + } + } + if (points.length > 0) { + points.sort(function(a, b) { + return a - b; + }); + return points[points.length - 1]; + } + return null; +}; + +GenericGraph.prototype.input = function() { + return DATADOC.getBoxWhikerData(); +}; + +GenericGraph.prototype.test = function(targetId) { + var plot = new GenericGraph({ + targetId : targetId, + height : 350, + width : 450, + maxBoxWidth : 25 + }); + plot.refresh(this.input()); +}; /** * Using dygraph it plots a chart. Params: targetId, labelsContainerId, args @@ -2087,338 +2087,338 @@ MacromoleculeConditionGrid.prototype.test = function(targetId) { panel.render(targetId); }; - -function ItemGraphFormatter(id, defaultFormat, selectedFormat, overFormat, draggingFormat){ - this.id = id; - this.args = new Object(); - - this.defaultFormat = new ItemFormat(defaultFormat); - - if(selectedFormat != null){ - this.selected = new ItemFormat(selectedFormat); - } - else{ - this.selected = new ItemFormat(defaultFormat); - } - - if(overFormat != null){ - this.over = new ItemFormat(overFormat); - } - else{ - this.over = new ItemFormat(defaultFormat); - } - - if(draggingFormat != null){ - this.dragging = new ItemFormat(draggingFormat); - } - else{ - this.dragging = new ItemFormat(defaultFormat); - } - - //Events - this.stateChanged = new Event(this); - - - //Attaching events - var _this = this; - this._setEvents(); -}; - -ItemGraphFormatter.prototype.getType = function(){ - return this.args.type; -}; - - -ItemGraphFormatter.prototype.toJSON = function(){ - var json = this.args; - json.defaultFormat = this.getDefault().toJSON(); - json.over = this.getOver().toJSON(); - json.selected = this.getSelected().toJSON(); - json.dragging = this.getDragging().toJSON(); - json.id = this.id; - return json; -}; - -ItemGraphFormatter.prototype.loadFromJSON = function(json){ - this.args = json; - this.defaultFormat = new ItemFormat(json.defaultFormat); - this.over = new ItemFormat(json.over); - this.selected = new ItemFormat(json.selected); - this.dragging = new ItemFormat(json.dragging); - this._setEvents(); -}; - -ItemGraphFormatter.prototype._setEvents = function(){ - //Attaching events - var _this = this; - - this.defaultFormat.changed.attach(function (sender, item){ - _this.over.setSize(_this.defaultFormat.getSize()); - _this.selected.setSize(_this.defaultFormat.getSize()); - _this.dragging.setSize(_this.defaultFormat.getSize()); - _this.stateChanged.notify(_this); - }); - - this.selected.changed.attach(function (sender, item){ - _this.stateChanged.notify(_this); - }); - - this.over.changed.attach(function (sender, item){ - _this.stateChanged.notify(_this); - }); - - this.dragging.changed.attach(function (sender, item){ - _this.stateChanged.notify(_this); - }); -}; - -/** Getters **/ -ItemGraphFormatter.prototype.getId = function(){return this.id;}; -ItemGraphFormatter.prototype.getDefault = function(){return this.defaultFormat;}; -ItemGraphFormatter.prototype.getSelected = function(){return this.selected;}; -ItemGraphFormatter.prototype.getOver = function(){return this.over;}; -ItemGraphFormatter.prototype.getDragging = function(){return this.dragging;}; - -function ItemFormat(args){ - this.defaultFormat = new Object(); - this.args = new Object(); - this.args.title = new Object(); - //Defult properties - this.args.visible = true; - this.args.hidden = false; - this.args.stroke = "#000000"; - this.args.strokeOpacity = 0.8; - this.args["stroke-width"] = 1; - this.args.fill = "#000000"; - this.args["fill-opacity"] = 1; - this.args.size = 1; - this.args.opacity = 1; - this.args.fontSize = "8"; - this.args.fontColor = "#000000"; - - /** For directed edge with arrow **/ - //this.args.arrowSize = 1; - - - if (args != null){ - if (args.visible != null){ - this.args.visible = args.visible; - } - if (args.opacity != null){ - this.args.opacity = args.opacity; - } - if (args.size != null){ - this.args.size = args.size; - } - if (args.hidden != null){ - this.args.hidden = args.hidden; - } - if (args.stroke != null){ - this.args.stroke = this._fixColor(args.stroke); - } - if (args.strokeOpacity != null){ - this.args.strokeOpacity = args.strokeOpacity; - } - if (args["stroke-width"]!=null){ - this.args["stroke-width"] = args["stroke-width"]; - } - if (args["fill-opacity"]!=null){ - this.args["fill-opacity"] = args["fill-opacity"]; - } - if (args.shape!=null){ - this.args.shape = args.shape; - } - if (args.fill!=null){ - this.args.fill = this._fixColor(args.fill); - } - - - if (args.title!=null){ - if (args.title.fontSize!=null){ - this.args.title.fontSize = args.title.fontSize; - } - if (args.title.fill!=null){ - this.args.title.fill = this._fixColor(args.title.fill); - } - } - - /** For directed edge with arrow **/ - /*if (args.arrowSize!=null){ - this.args.arrowSize = args.arrowSize; - }*/ - - } - - this.changed = new Event(); -}; - -ItemFormat.prototype._fixColor = function(color){ - var fixed = color; - if (color.indexOf("green") != -1){ - fixed = '#04B431'; - } - - if (color.indexOf("blue") != -1){ - fixed = '#045FB4'; - } - - if (color.indexOf("red") != -1){ - fixed = '#DF0101'; - } - - if (color.indexOf("black") != -1){ - fixed = '#000000'; - } - - if (color.indexOf("white") != -1){ - fixed = '#FFFFFF'; - } - - if (color.indexOf("#") == -1){ - fixed = "#" + color; - } - return fixed; -}; - -ItemFormat.prototype.toJSON = function(){ - if(this.args.strokeOpacity != null){ - this.args["stroke-opacity"] = this.args.strokeOpacity; - delete this.args.strokeOpacity; - } - -// if(this.args.strokeWidth != null){ -// this.args["stroke-width"] = this.args.strokeWidth; -// delete this.args["stroke-width"]; -// } - - if(this.args.title.fontColor != null){ - this.args.title["font-color"] = this.args.title.fontColor; - } - else{ - this.args.title["font-color"] = this.args.fontColor;//;this.args.title.fontColor; - } - - if(this.args.title.fontSize != null){ - this.args.title["font-size"] = this.args.title.fontSize;//;this.args.title.fontColor; - } - else{ - this.args.title["font-size"] = this.args.fontSize;//;this.args.title.fontColor; - } - //return this.args; - return this.args; -}; - -ItemFormat.prototype.getAttribute = function(name){return this.args[name];}; - -//Getters and Setters -ItemFormat.prototype.setVisible = function(visible){ - if (this.args.visible != visible){ - this.args.visible = visible; - this.changed.notify(this); - } -}; - -ItemFormat.prototype.getVisible = function(){return this.args.visible;}; - -ItemFormat.prototype.setHidden = function(hidden){ - if (this.args.hidden != hidden){ - this.args.hidden = hidden; - this.changed.notify(this); - } -}; - -ItemFormat.prototype.getHidden = function(){return this.args.hidden;}; - - -ItemFormat.prototype.setStroke = function(stroke){ - if (this.args.stroke != stroke){ - this.args.stroke = stroke; - this.changed.notify(this); - } -}; - -ItemFormat.prototype.getStroke = function(){return this.args.stroke;}; - -ItemFormat.prototype.setStrokeOpacity = function(strokeOpacity){ - if (this.args.strokeOpacity != strokeOpacity){ - this.args.strokeOpacity = strokeOpacity; - this.changed.notify(this); - } -}; - -ItemFormat.prototype.getStrokeOpacity = function(){return this.args["stroke-opacity"];}; - -ItemFormat.prototype.setStrokeWidth = function(strokeWidth){ - if (this.args["stroke-width"] != strokeWidth){ - this.args["stroke-width"] = strokeWidth; - this.changed.notify(this); - } -}; - - -ItemFormat.prototype.getFillOpacity = function(){return this.args["fill-opacity"];}; - -ItemFormat.prototype.setfillOpacity = function(strokeWidth){ - if (this.args["fill-opacity"] != strokeWidth){ - this.args["fill-opacity"] = strokeWidth; - this.changed.notify(this); - } -}; - - -ItemFormat.prototype.getStrokeWidth = function(){ - return this.args["stroke-width"]; -}; - -ItemFormat.prototype.setFill = function(fill){ - if (this.args.fill != fill){ - this.args.fill = fill; - this.changed.notify(this); - } -}; - -ItemFormat.prototype.getFill = function(){return this.args.fill;}; - -ItemFormat.prototype.setSize = function(size){ - if (this.args.size != size){ - this.args.size = size; - this.changed.notify(this); - } -}; - -ItemFormat.prototype.getSize = function(){return this.args.size;}; - -ItemFormat.prototype.setOpacity = function(opacity){ - if (this.args.opacity != opacity){ - this.args.opacity = opacity; - this.changed.notify(this); - } -}; - -ItemFormat.prototype.getOpacity = function(){return this.args.opacity;}; - -ItemFormat.prototype.getArrowSize = function(){return this.args.arrowSize;}; - -ItemFormat.prototype.setArrowSize = function(arrowSize){ - if (this.args.arrowSize != arrowSize){ - this.args.arrowSize = arrowSize; - this.changed.notify(this); - } -}; - -ItemFormat.prototype.getFontSize = function(){return this.args.title.fontSize;}; - -ItemFormat.prototype.setFontSize = function(fontSize){ - - if (this.args.title.fontSize != fontSize){ - this.args.title.fontSize = fontSize; - this.changed.notify(this); - } -}; - - - - + +function ItemGraphFormatter(id, defaultFormat, selectedFormat, overFormat, draggingFormat){ + this.id = id; + this.args = new Object(); + + this.defaultFormat = new ItemFormat(defaultFormat); + + if(selectedFormat != null){ + this.selected = new ItemFormat(selectedFormat); + } + else{ + this.selected = new ItemFormat(defaultFormat); + } + + if(overFormat != null){ + this.over = new ItemFormat(overFormat); + } + else{ + this.over = new ItemFormat(defaultFormat); + } + + if(draggingFormat != null){ + this.dragging = new ItemFormat(draggingFormat); + } + else{ + this.dragging = new ItemFormat(defaultFormat); + } + + //Events + this.stateChanged = new Event(this); + + + //Attaching events + var _this = this; + this._setEvents(); +}; + +ItemGraphFormatter.prototype.getType = function(){ + return this.args.type; +}; + + +ItemGraphFormatter.prototype.toJSON = function(){ + var json = this.args; + json.defaultFormat = this.getDefault().toJSON(); + json.over = this.getOver().toJSON(); + json.selected = this.getSelected().toJSON(); + json.dragging = this.getDragging().toJSON(); + json.id = this.id; + return json; +}; + +ItemGraphFormatter.prototype.loadFromJSON = function(json){ + this.args = json; + this.defaultFormat = new ItemFormat(json.defaultFormat); + this.over = new ItemFormat(json.over); + this.selected = new ItemFormat(json.selected); + this.dragging = new ItemFormat(json.dragging); + this._setEvents(); +}; + +ItemGraphFormatter.prototype._setEvents = function(){ + //Attaching events + var _this = this; + + this.defaultFormat.changed.attach(function (sender, item){ + _this.over.setSize(_this.defaultFormat.getSize()); + _this.selected.setSize(_this.defaultFormat.getSize()); + _this.dragging.setSize(_this.defaultFormat.getSize()); + _this.stateChanged.notify(_this); + }); + + this.selected.changed.attach(function (sender, item){ + _this.stateChanged.notify(_this); + }); + + this.over.changed.attach(function (sender, item){ + _this.stateChanged.notify(_this); + }); + + this.dragging.changed.attach(function (sender, item){ + _this.stateChanged.notify(_this); + }); +}; + +/** Getters **/ +ItemGraphFormatter.prototype.getId = function(){return this.id;}; +ItemGraphFormatter.prototype.getDefault = function(){return this.defaultFormat;}; +ItemGraphFormatter.prototype.getSelected = function(){return this.selected;}; +ItemGraphFormatter.prototype.getOver = function(){return this.over;}; +ItemGraphFormatter.prototype.getDragging = function(){return this.dragging;}; + +function ItemFormat(args){ + this.defaultFormat = new Object(); + this.args = new Object(); + this.args.title = new Object(); + //Defult properties + this.args.visible = true; + this.args.hidden = false; + this.args.stroke = "#000000"; + this.args.strokeOpacity = 0.8; + this.args["stroke-width"] = 1; + this.args.fill = "#000000"; + this.args["fill-opacity"] = 1; + this.args.size = 1; + this.args.opacity = 1; + this.args.fontSize = "8"; + this.args.fontColor = "#000000"; + + /** For directed edge with arrow **/ + //this.args.arrowSize = 1; + + + if (args != null){ + if (args.visible != null){ + this.args.visible = args.visible; + } + if (args.opacity != null){ + this.args.opacity = args.opacity; + } + if (args.size != null){ + this.args.size = args.size; + } + if (args.hidden != null){ + this.args.hidden = args.hidden; + } + if (args.stroke != null){ + this.args.stroke = this._fixColor(args.stroke); + } + if (args.strokeOpacity != null){ + this.args.strokeOpacity = args.strokeOpacity; + } + if (args["stroke-width"]!=null){ + this.args["stroke-width"] = args["stroke-width"]; + } + if (args["fill-opacity"]!=null){ + this.args["fill-opacity"] = args["fill-opacity"]; + } + if (args.shape!=null){ + this.args.shape = args.shape; + } + if (args.fill!=null){ + this.args.fill = this._fixColor(args.fill); + } + + + if (args.title!=null){ + if (args.title.fontSize!=null){ + this.args.title.fontSize = args.title.fontSize; + } + if (args.title.fill!=null){ + this.args.title.fill = this._fixColor(args.title.fill); + } + } + + /** For directed edge with arrow **/ + /*if (args.arrowSize!=null){ + this.args.arrowSize = args.arrowSize; + }*/ + + } + + this.changed = new Event(); +}; + +ItemFormat.prototype._fixColor = function(color){ + var fixed = color; + if (color.indexOf("green") != -1){ + fixed = '#04B431'; + } + + if (color.indexOf("blue") != -1){ + fixed = '#045FB4'; + } + + if (color.indexOf("red") != -1){ + fixed = '#DF0101'; + } + + if (color.indexOf("black") != -1){ + fixed = '#000000'; + } + + if (color.indexOf("white") != -1){ + fixed = '#FFFFFF'; + } + + if (color.indexOf("#") == -1){ + fixed = "#" + color; + } + return fixed; +}; + +ItemFormat.prototype.toJSON = function(){ + if(this.args.strokeOpacity != null){ + this.args["stroke-opacity"] = this.args.strokeOpacity; + delete this.args.strokeOpacity; + } + +// if(this.args.strokeWidth != null){ +// this.args["stroke-width"] = this.args.strokeWidth; +// delete this.args["stroke-width"]; +// } + + if(this.args.title.fontColor != null){ + this.args.title["font-color"] = this.args.title.fontColor; + } + else{ + this.args.title["font-color"] = this.args.fontColor;//;this.args.title.fontColor; + } + + if(this.args.title.fontSize != null){ + this.args.title["font-size"] = this.args.title.fontSize;//;this.args.title.fontColor; + } + else{ + this.args.title["font-size"] = this.args.fontSize;//;this.args.title.fontColor; + } + //return this.args; + return this.args; +}; + +ItemFormat.prototype.getAttribute = function(name){return this.args[name];}; + +//Getters and Setters +ItemFormat.prototype.setVisible = function(visible){ + if (this.args.visible != visible){ + this.args.visible = visible; + this.changed.notify(this); + } +}; + +ItemFormat.prototype.getVisible = function(){return this.args.visible;}; + +ItemFormat.prototype.setHidden = function(hidden){ + if (this.args.hidden != hidden){ + this.args.hidden = hidden; + this.changed.notify(this); + } +}; + +ItemFormat.prototype.getHidden = function(){return this.args.hidden;}; + + +ItemFormat.prototype.setStroke = function(stroke){ + if (this.args.stroke != stroke){ + this.args.stroke = stroke; + this.changed.notify(this); + } +}; + +ItemFormat.prototype.getStroke = function(){return this.args.stroke;}; + +ItemFormat.prototype.setStrokeOpacity = function(strokeOpacity){ + if (this.args.strokeOpacity != strokeOpacity){ + this.args.strokeOpacity = strokeOpacity; + this.changed.notify(this); + } +}; + +ItemFormat.prototype.getStrokeOpacity = function(){return this.args["stroke-opacity"];}; + +ItemFormat.prototype.setStrokeWidth = function(strokeWidth){ + if (this.args["stroke-width"] != strokeWidth){ + this.args["stroke-width"] = strokeWidth; + this.changed.notify(this); + } +}; + + +ItemFormat.prototype.getFillOpacity = function(){return this.args["fill-opacity"];}; + +ItemFormat.prototype.setfillOpacity = function(strokeWidth){ + if (this.args["fill-opacity"] != strokeWidth){ + this.args["fill-opacity"] = strokeWidth; + this.changed.notify(this); + } +}; + + +ItemFormat.prototype.getStrokeWidth = function(){ + return this.args["stroke-width"]; +}; + +ItemFormat.prototype.setFill = function(fill){ + if (this.args.fill != fill){ + this.args.fill = fill; + this.changed.notify(this); + } +}; + +ItemFormat.prototype.getFill = function(){return this.args.fill;}; + +ItemFormat.prototype.setSize = function(size){ + if (this.args.size != size){ + this.args.size = size; + this.changed.notify(this); + } +}; + +ItemFormat.prototype.getSize = function(){return this.args.size;}; + +ItemFormat.prototype.setOpacity = function(opacity){ + if (this.args.opacity != opacity){ + this.args.opacity = opacity; + this.changed.notify(this); + } +}; + +ItemFormat.prototype.getOpacity = function(){return this.args.opacity;}; + +ItemFormat.prototype.getArrowSize = function(){return this.args.arrowSize;}; + +ItemFormat.prototype.setArrowSize = function(arrowSize){ + if (this.args.arrowSize != arrowSize){ + this.args.arrowSize = arrowSize; + this.changed.notify(this); + } +}; + +ItemFormat.prototype.getFontSize = function(){return this.args.title.fontSize;}; + +ItemFormat.prototype.setFontSize = function(fontSize){ + + if (this.args.title.fontSize != fontSize){ + this.args.title.fontSize = fontSize; + this.changed.notify(this); + } +}; + + + + /** * This class executes the actions @@ -4952,2095 +4952,1101 @@ function openExperiment(experimentId) { BIOSAXS.openExperiment(experimentId); } -var SITE_CONF = { - SAMPLE_CHANGER_CONFIGURATION : { - "3":{ - "platetype3VO": {"plateTypeId":4,"name":"96 Well plate","rowCount":8,"columnCount":12,"shape":"REC"}, - "name":"96 Well plate", - "slotPositionRow":"1", - "slotPositionColumn":"3", - "storageTemperature":"0", - "sampleplateposition3VOs":[], - "experimentId":0 - }, - "2":{ - "platetype3VO":{"plateTypeId":2,"name":" 4 x ( 8 + 3 ) Block","rowCount":4,"columnCount":11,"shape":"REC"}, - "name":" 4 x ( 8 + 3 ) Block", - "slotPositionRow":"1", - "slotPositionColumn":"2", - "storageTemperature":"0", - "sampleplateposition3VOs":[],"experimentId":0 - }, - "1":{ - "platetype3VO":{"plateTypeId":1,"name":"Deep Well","rowCount":8,"columnCount":12,"shape":"REC"}, - "name":"Deep Well", - "slotPositionRow":"1", - "slotPositionColumn":"1", - "storageTemperature":"0", - "sampleplateposition3VOs":[], - "experimentId":0 - } - } +var SITE_CONF = { + SAMPLE_CHANGER_CONFIGURATION : { + "3":{ + "platetype3VO": {"plateTypeId":4,"name":"96 Well plate","rowCount":8,"columnCount":12,"shape":"REC"}, + "name":"96 Well plate", + "slotPositionRow":"1", + "slotPositionColumn":"3", + "storageTemperature":"0", + "sampleplateposition3VOs":[], + "experimentId":0 + }, + "2":{ + "platetype3VO":{"plateTypeId":2,"name":" 4 x ( 8 + 3 ) Block","rowCount":4,"columnCount":11,"shape":"REC"}, + "name":" 4 x ( 8 + 3 ) Block", + "slotPositionRow":"1", + "slotPositionColumn":"2", + "storageTemperature":"0", + "sampleplateposition3VOs":[],"experimentId":0 + }, + "1":{ + "platetype3VO":{"plateTypeId":1,"name":"Deep Well","rowCount":8,"columnCount":12,"shape":"REC"}, + "name":"Deep Well", + "slotPositionRow":"1", + "slotPositionColumn":"1", + "storageTemperature":"0", + "sampleplateposition3VOs":[], + "experimentId":0 + } + } }; - -var ISPYB_CONF = { - load : function(config) { - for (var key in config) { - ISPYB_CONF[key] = config[key]; - } - }, - SAMPLE_CHANGER_CONFIGURATION : { - "1": { - "platetype3VO": {"plateTypeId": 1, "name": "Deep Well", "rowCount": 8, "columnCount": 12, "shape": "REC"}, - "name": "Deep Well", - "slotPositionRow": "1", - "slotPositionColumn": "1", - "storageTemperature": "0", - "sampleplateposition3VOs": [], - "experimentId": 0 - } - } + +var ISPYB_CONF = { + load : function(config) { + for (var key in config) { + ISPYB_CONF[key] = config[key]; + } + }, + SAMPLE_CHANGER_CONFIGURATION : { + "1": { + "platetype3VO": {"plateTypeId": 1, "name": "Deep Well", "rowCount": 8, "columnCount": 12, "shape": "REC"}, + "name": "Deep Well", + "slotPositionRow": "1", + "slotPositionColumn": "1", + "storageTemperature": "0", + "sampleplateposition3VOs": [], + "experimentId": 0 + } + } }; -var SITE_CONF = { - -}; - -/** - * Example form - * - * @witdh - * @height - */ -function AbinitioForm(args) { - this.id = BUI.id(); - this.width = null; - this.height = null; - - if (args != null) { - if (args.width != null) { - this.width = args.width; - } - if (args.height != null) { - this.height = args.height; - } - } - - var _this = this; - /** Widgets **/ - this.abinitioGrid = new AbinitioGrid({ - width : null, - height : 200 - }); - - this.abinitioGrid.onSelected.attach(function(sender, models) { - var modelsIdList = []; - for ( var i in models) { - modelsIdList.push(models[i].modelId); - } - _this._renderPlot(_this.subtractions[0].subtractionId, modelsIdList); - _this._renderPDB(modelsIdList); - }); - - /** Dygraph Widget that plots fir files**/ - this.plotWidget = new PlotWidget({ - width : 745 / 2, - height : 300, - margin : "10 0 5 10" - }); - - /** PDB viewer **/ - this.viewer = new PDBViewer({ - width : 745 / 2, - height : 300 - }); - -} - -AbinitioForm.prototype._renderPlot = function(subtractionId, modelsIdList) { - /** Trying to plot tje subtraction and the models **/ - try { - var colors = [ "#669900", "#0000FF" ]; - this.plotWidget.refresh([], [ ], modelsIdList, [], [], colors); - } catch (e) { - console.log(e); - } -}; - -AbinitioForm.prototype._renderPDB = function(modelsIdList) { - /** Trying to plot the PDB file **/ - try { - var viz = []; - for (var i = 0; i < modelsIdList.length; i++) { - viz.push({ - modelId : modelsIdList[i], - color : "0xFF6600", - opacity : 0.8 - }); - } - this.viewer.refresh(viz); - } catch (e) { - console.log(e); - } -}; - -AbinitioForm.prototype._getItems = function() { - var _this = this; - return [ { - xtype : 'label', - forId : 'myFieldId', - text : 'INLINE HELP: To be updated', - margin : '15 0 20 10', - cls : "inline-help" - }, this.abinitioGrid.getPanel(), { - xtype : 'container', - layout : 'hbox', - items : [ this.plotWidget.getPanel(), this.viewer.getPanel() ] - } ] -}; - -AbinitioForm.prototype._getButtons = function() { - return []; -}; - -AbinitioForm.prototype.getPanel = function() { - var _this = this; - this.panel = Ext.create('Ext.form.Panel', { - width : this.width, - height : this.height, - margin : 10, - border : 1, - defaultType : 'textfield', - items : this._getItems(), - buttons : this._getButtons(), - listeners : { - afterrender : function() { - _this._populate(); +var SITE_CONF = { + +}; - } - } - }); - return this.panel; -}; +/** + * Example form + * + * @witdh + * @height + */ +function AbinitioForm(args) { + this.id = BUI.id(); + this.width = null; + this.height = null; + + if (args != null) { + if (args.width != null) { + this.width = args.width; + } + if (args.height != null) { + this.height = args.height; + } + } + + var _this = this; + /** Widgets **/ + this.abinitioGrid = new AbinitioGrid({ + width : null, + height : 200 + }); + + this.abinitioGrid.onSelected.attach(function(sender, models) { + var modelsIdList = []; + for ( var i in models) { + modelsIdList.push(models[i].modelId); + } + _this._renderPlot(_this.subtractions[0].subtractionId, modelsIdList); + _this._renderPDB(modelsIdList); + }); + + /** Dygraph Widget that plots fir files**/ + this.plotWidget = new PlotWidget({ + width : 745 / 2, + height : 300, + margin : "10 0 5 10" + }); + + /** PDB viewer **/ + this.viewer = new PDBViewer({ + width : 745 / 2, + height : 300 + }); + +} + +AbinitioForm.prototype._renderPlot = function(subtractionId, modelsIdList) { + /** Trying to plot tje subtraction and the models **/ + try { + var colors = [ "#669900", "#0000FF" ]; + this.plotWidget.refresh([], [ ], modelsIdList, [], [], colors); + } catch (e) { + console.log(e); + } +}; + +AbinitioForm.prototype._renderPDB = function(modelsIdList) { + /** Trying to plot the PDB file **/ + try { + var viz = []; + for (var i = 0; i < modelsIdList.length; i++) { + viz.push({ + modelId : modelsIdList[i], + color : "0xFF6600", + opacity : 0.8 + }); + } + this.viewer.refresh(viz); + } catch (e) { + console.log(e); + } +}; + +AbinitioForm.prototype._getItems = function() { + var _this = this; + return [ { + xtype : 'label', + forId : 'myFieldId', + text : 'INLINE HELP: To be updated', + margin : '15 0 20 10', + cls : "inline-help" + }, this.abinitioGrid.getPanel(), { + xtype : 'container', + layout : 'hbox', + items : [ this.plotWidget.getPanel(), this.viewer.getPanel() ] + } ] +}; + +AbinitioForm.prototype._getButtons = function() { + return []; +}; + +AbinitioForm.prototype.getPanel = function() { + var _this = this; + this.panel = Ext.create('Ext.form.Panel', { + width : this.width, + height : this.height, + margin : 10, + border : 1, + defaultType : 'textfield', + items : this._getItems(), + buttons : this._getButtons(), + listeners : { + afterrender : function() { + _this._populate(); + + } + } + }); + return this.panel; +}; + +/** Populates could be call when the DOM is not filled yet **/ +AbinitioForm.prototype._populate = function() { +}; + +/** It populates the form * */ +AbinitioForm.prototype.refresh = function(subtractions) { + this.subtractions = subtractions; + this.abinitioGrid.refresh(subtractions); +}; + +AbinitioForm.prototype.input = function() { + return {}; +}; + +/** It populates the form **/ +AbinitioForm.prototype.test = function(targetId) { + var macromoleculeForm = new AbinitioForm(); + var panel = macromoleculeForm.getPanel(); + panel.render(targetId); +}; -/** Populates could be call when the DOM is not filled yet **/ -AbinitioForm.prototype._populate = function() { -}; - -/** It populates the form * */ -AbinitioForm.prototype.refresh = function(subtractions) { - this.subtractions = subtractions; - this.abinitioGrid.refresh(subtractions); -}; - -AbinitioForm.prototype.input = function() { - return {}; -}; - -/** It populates the form **/ -AbinitioForm.prototype.test = function(targetId) { - var macromoleculeForm = new AbinitioForm(); - var panel = macromoleculeForm.getPanel(); - panel.render(targetId); -}; - -/** - * Example form - * - * @witdh - * @height - */ -function DataReductionForm(args) { - this.id = BUI.id(); - this.width = null; - this.height = null; - - if (args != null) { - if (args.width != null) { - this.width = args.width; - } - if (args.height != null) { - this.height = args.height; - } - } - - var _this = this; - - /** Widgets **/ - this.plotWidget = new PlotWidget({ - width : 650, - height : 490 - }); - - /** Selected frames to be displayed **/ - this.selectedItems = { - frames : [], - averages : [], - subtractions : [] - }; - -} - -DataReductionForm.prototype._parseSelectedItemsToIds = function(selectedArray) { - var ids = []; - if (selectedArray != null) { - for (var i = 0; i < selectedArray.length; i++) { - ids.push(selectedArray[i].id); - } - } - return ids; -}; - -DataReductionForm.prototype.onSelectionChanged = function(columnName, selected) { - if (selected != null) { - if (columnName == "Frames") { - this.selectedItems.frames = selected; - this.selectedItems.subtractions = []; - } - if (columnName == "Averages") { - this.selectedItems.averages = selected; - } - if (columnName == "Subtractions") { - this.selectedItems.frames = []; - this.selectedItems.subtractions = selected; - } - } - this.plotWidget.refresh(this._parseSelectedItemsToIds(this.selectedItems.frames), this._parseSelectedItemsToIds(this.selectedItems.subtractions)); -}; - -DataReductionForm.prototype._getFramesExtPanel = function(store, columnName, height) { - var _this = this; - - var selModel = Ext.create('Ext.selection.RowModel', { - allowDeselect : true, - mode : 'MULTI', - listeners : { - selectionchange : function(sm, selections) { - var selected = []; - for (var i = 0; i < selections.length; i++) { - selected.push(selections[i].raw); - } - _this.onSelectionChanged(columnName, selected); - } - } - }); - - return Ext.create('Ext.grid.Panel', { - store : store, - margin : 10, - height : height, - width : 200, - selModel : selModel, - columns : [ { - text : columnName, - dataIndex : 'fileName', - flex : 1 - } ], - viewConfig : { - } - }); -}; - -DataReductionForm.prototype._getFramesPanel = function() { - var fields = [ 'fileName', 'type', 'id' ]; - - this.framesStore = Ext.create('Ext.data.Store', { - fields : fields, - sorters : 'fileName' - }); - - this.averagesStore = Ext.create('Ext.data.Store', { - fields : fields - }); - - this.subtractionStore = Ext.create('Ext.data.Store', { - fields : fields - }); - - var gridFrames = this._getFramesExtPanel(this.framesStore, "Frames", 375); - var gridAvgs = this._getFramesExtPanel(this.averagesStore, "Averages", 125); - var subtractionAvgs = this._getFramesExtPanel(this.subtractionStore, "Subtractions", 75); - - return { - xtype : 'container', - layout : 'vbox', - items : [ gridFrames, subtractionAvgs ] - }; -}; - -DataReductionForm.prototype._getImageContainer = function(name, help) { - var html = "
" + name + "
" - return { - xtype : 'container', - layout : 'vbox', - items : [ { - html : html, - margin : "5 0 0 0", - height : 95, - width : 100 - }, { - xtype : 'label', - forId : 'myFieldId', - text : help, - margin : '5 0 0 0', - cls : "inline-help" - } ] - } -}; - -DataReductionForm.prototype._getItems = function() { - var _this = this; - return [ { - xtype : 'container', - layout : 'hbox', - items : [ - this._getFramesPanel(), - this.plotWidget.getPanel(), - { - xtype : 'panel', - width : 110, - frame : true, - margin : "10 5 5 5", - border : 0, - layout : 'vbox', - items : [ this._getImageContainer("scattering", "Scattering"), this._getImageContainer("guinier", "Guinier Region"), - this._getImageContainer("kratky", "Kratky Plot"), this._getImageContainer("gnom", "P(r) distribution") ] - } ] - } ] -}; - -DataReductionForm.prototype._getButtons = function() { - return []; -}; - -DataReductionForm.prototype.getPanel = function() { - var _this = this; - this.panel = Ext.create('Ext.form.Panel', { - width : this.width, - height : this.height, - border : 0, - items : this._getItems(), - buttons : this._getButtons(), - listeners : { - afterrender : function() { - _this._populate(); - } - } - }); - return this.panel; -}; - -/** Populates could be call when the DOM is not filled yet **/ -DataReductionForm.prototype._populate = function() { -}; - -/** It populates the form * */ -DataReductionForm.prototype.refresh = function(subtractions) { - if (subtractions != null) { - for (var i = 0; i < subtractions.length; i++) { - /** Loading frame grids **/ - var subtraction = subtractions[i]; - var averages = [ { - fileName : BUI.getFileName(subtraction.bufferAverageFilePath), - type : 'bufferAvg', - id : subtraction.subtractionId - }, { - fileName : BUI.getFileName(subtraction.sampleAverageFilePath), - type : 'sampleAvg', - id : subtraction.subtractionId - } - - ]; - this.averagesStore.loadData(averages, true); - this.subtractionStore.loadData([ { - fileName : BUI.getFileName(subtraction.substractedFilePath), - type : 'SUBTRACTION', - id : subtraction.subtractionId - } ], true); - - var frames = []; - /** Buffers **/ - if (subtraction.bufferOneDimensionalFiles != null) { - if (subtraction.bufferOneDimensionalFiles.frametolist3VOs != null) { - for (var j = 0; j < subtraction.bufferOneDimensionalFiles.frametolist3VOs.length; j++) { - var frametolist3VO = subtraction.bufferOneDimensionalFiles.frametolist3VOs[j]; - if (frametolist3VO.frame3VO != null) { - frames.push({ - fileName : BUI.getFileName(frametolist3VO.frame3VO.filePath), - type : 'BUFFER', - id : frametolist3VO.frame3VO.frameId - }); - } - } - } - } - /** Samples **/ - if (subtraction.sampleOneDimensionalFiles != null) { - if (subtraction.sampleOneDimensionalFiles.frametolist3VOs != null) { - for (var j = 0; j < subtraction.sampleOneDimensionalFiles.frametolist3VOs.length; j++) { - var frametolist3VO = subtraction.sampleOneDimensionalFiles.frametolist3VOs[j]; - if (frametolist3VO.frame3VO != null) { - frames.push({ - fileName : BUI.getFileName(frametolist3VO.frame3VO.filePath), - type : 'SAMPLE', - id : frametolist3VO.frame3VO.frameId - }); - } - } - } - } - - this.framesStore.loadData(frames, true); - - /** Loading images **/ - this._displayImage("scattering", subtraction.subtractionId); - this._displayImage("kratky", subtraction.subtractionId); - this._displayImage("guinier", subtraction.subtractionId); - this._displayImage("gnom", subtraction.subtractionId); - } - } -}; - -DataReductionForm.prototype._displayImage = function(name, subtractionId) { - var url = BUI.getURL() + '&type=' + name + '&subtractionId=' + subtractionId; - var event = "OnClick= window.open('" + url + "')"; - document.getElementById(this.id + "_" + name).innerHTML = ''; -}; - -DataReductionForm.prototype.input = function() { - return {}; -}; - -/** It populates the form **/ -DataReductionForm.prototype.test = function(targetId) { - var macromoleculeForm = new DataReductionForm(); - var panel = macromoleculeForm.getPanel(); - panel.render(targetId); -}; - -function PlotWidget(args) { - this.width = 600; - this.height = 600; - this.id = BUI.id(); - - this.linear = false; - - this.margin = "10 0 0 0"; - /** for each stat the max and minimum value when it is scaled in order to show correctly in the legend **/ - this.ranges = {}; - if (args != null) { - if (args.width != null) { - this.width = args.width; - } - if (args.height != null) { - this.height = args.height; - } - if (args.margin != null) { - this.margin = args.margin; - } - } - -} - -PlotWidget.prototype.getMenu = function() { - var _this = this; - /** Actions buttons **/ - var actions = []; - actions.push("->"); - actions.push({ - text : "Export as Image", - scope : this, - icon : '../images/save.gif', - handler : function(item, pressed) { - var largeImage = document.createElement("img"); - largeImage.style.display = 'block'; - largeImage.style.width = 200 + "px"; - largeImage.style.height = 200 + "px"; - largeImage.setAttribute('src', Dygraph.Export.asCanvas(this.dygraphObject.dygraph).toDataURL()); - window.open(Dygraph.Export.asCanvas(this.dygraphObject.dygraph).toDataURL(), 'Image', ''); - } - }); - - return actions; -}; - -/** Looks for the maximum value and then divide everything but that value **/ -PlotWidget.prototype.scaledData = function(data) { - for (var i = 0; i < data.length; i++) { - var values = this.getMaxAndMinValue(data[i]); - data[i] = this.divideValuesByMax(data[i], values.max); - this.ranges[data[i].label] = values; - } - return data; -}; - -/** Given a stat float[] and a max number it will divide each value by max **/ -PlotWidget.prototype.divideValuesByMax = function(stat, max) { - for (var j = 0; j < stat.data.length; j++) { - if (max != 0) { - stat.data[j] = Number(stat.data[j]) / max; - stat.std[j] = Number(stat.std[j]) / max; - } - } - return stat; -}; - -/** returns max value of a stat **/ -PlotWidget.prototype.getMaxAndMinValue = function(stat) { - var max = 0; - var min = stat.data[0]; - for (var j = 0; j < stat.data.length; j++) { - if (Number(stat.data[j]) > max) { - max = Number(stat.data[j]); - } - if (Number(stat.std[j]) > max) { - max = Number(stat.std[j]); - } - if (Number(stat.data[j]) < min) { - min = Number(stat.data[j]); - } - } - return { - max : Number(max), - min : Number(min) - }; -}; - -PlotWidget.prototype._renderDygraph = function(parsed, colors, labels) { - this.dygraphObject = new StdDevDyGraph(this.id, { - width : this.width - 20, - height : this.height - 40, - xlabel : "", - }); - - this.dygraphObject.draw(parsed, colors, labels); - -}; - -PlotWidget.prototype.getPanel = function() { - this.panel = Ext.create('Ext.panel.Panel', { - width : this.width, - height : this.height, - margin : this.margin, - tbar : this.getMenu(), - items : [ { - html : "", - id : this.id, - width : this.width, - height : this.height, - padding : 10, - margin : "0 0 0 -30", - border : 0 - } ] - }); - - return this.panel; -}; - -PlotWidget.prototype.getPoint = function(y, error) { - var minus = y - error; - var max = y + error; - - if (this.linear) { - return [ Math.abs(minus), y, Math.abs(max) ]; - } - if ((minus != 0) && (max != 0)) { - return [ Math.log(Math.abs(minus)), Math.log(y), Math.log(Math.abs(max)) ]; - } else { - return [ Math.log(y), Math.log(y), Math.log(y) ]; - } - -}; - -PlotWidget.prototype.getDataPlot = function(frames, subtractions, models, fits, rbms) { - var files = []; - var labels = [ "Intensity" ]; - if (frames != null) { - for (var i = 0; i < frames.length; i++) { - files.push(frames[i].data); - labels.push(frames[i].fileName); - } - } - function splitData(data, column, errorColumn, name){ - var result = [] - for (var j = 0; j < data.length; j++) { - console.log(data[j][column]); - result.push([j,data[j][column],data[j][errorColumn]]);//[0, data[i][column],0]]); - } - files.push(result); - labels.push(name); - } - - if (subtractions != null) { - for (var i = 0; i < subtractions.length; i++) { - /** For subtraction **/ - files.push(subtractions[i].subtraction.data); - labels.push(subtractions[i].subtraction.fileName); - /** For sample average **/ -// files.push(subtractions[i].sampleAvg.data); -// labels.push(subtractions[i].sampleAvg.fileName); - /** For buffer average **/ -// files.push(subtractions[i].bufferAvg.data); -// labels.push(subtractions[i].bufferAvg.fileName); - } - } - - if (models != null) { - for (var i = 0; i < models.length; i++) { - for ( var key in models[i]) { - splitData(models[i][key].fir.data, 1, 2, "Intensity"); - splitData(models[i][key].fir.data, 3, 3, "Fit"); - } - } - } - - if (fits != null) { - for (var i = 0; i < fits.length; i++) { - for ( var key in fits[i]) { - - /** adding fit file to be plotted **/ - if (fits[i][key].fit.data[0].length == 3){ - splitData(fits[i][key].fit.data, 1, 1, "Intensity"); - splitData(fits[i][key].fit.data, 2, 2, "Fit"); - } - - /** s, Iexp(s), err, Ifit(s). **/ - if (fits[i][key].fit.data[0].length == 4){ - splitData(fits[i][key].fit.data, 1, 2, "Intensity"); - splitData(fits[i][key].fit.data, 3, 3, "Fit"); - } - - if (fits[i][key].fit.data[0].length == 5){ - /** X Intensity Fit Error Residues **/ - - splitData(fits[i][key].fit.data, 1, 3, "Intensity"); - splitData(fits[i][key].fit.data, 2, 2, "Fit"); - } - } - } - } - - var dataPoints = []; - if (files.length > 0) { - for (var i = 0; i < files[0].length; i++) { - dataPoints.push([ files[0][i][0], this.getPoint(files[0][i][1], files[0][i][2]) ]); - } - if (files.length > 1) { - for (var i = 1; i < files.length; i++) { - for (var j = 0; j < dataPoints.length; j++) { - if (files[i][j] != null){ - dataPoints[j].push(this.getPoint(files[i][j][1], files[i][j][2])); - } - else{ - dataPoints[j].push([0,0,0]); - } - } - } - } - } - - return { - dataPoints : dataPoints, - labels : labels - } -}; - -PlotWidget.prototype.refresh = function(frames, subtractions, models, fits, rbms, colors) { - - var _this = this; - this.panel.setLoading("Reading Files"); - - var dataAdapter = new BiosaxsDataAdapter(); - dataAdapter.onSuccess.attach(function(sender, data) { - _this.panel.setLoading("Rendering"); - var parsed = _this.getDataPlot(data.frames, data.subtractions, data.models, data.fits, rbms); - _this._renderDygraph(parsed.dataPoints, colors, parsed.labels); - _this.panel.setLoading(false); - }); - dataAdapter.onError.attach(function(sender, data) { - _this.panel.setLoading(false); - }); - dataAdapter.getDataPlot(frames, subtractions, models, fits, rbms); -}; - -PlotWidget.prototype.input = function() { - return DATADOC.getHPLCData(); -}; - - -/** - * Fit form - * - * @witdh - * @height - */ -function FitForm(args) { - this.id = BUI.id(); - this.width = null; - this.height = null; - - if (args != null) { - if (args.width != null) { - this.width = args.width; - } - if (args.height != null) { - this.height = args.height; - } - } - - var _this = this; - - /** Widgets **/ - this.fitGrid = new FitStructureToExperimentDataGrid({ - width : null, - height : 200 - }); - - this.fitGrid.onSelected.attach(function(sender, fits) { - var modelsIdList = []; - for ( var i in fits) { - modelsIdList.push(fits[i].fitStructureToExperimentalDataId); - } - _this._renderPlot(_this.subtractions[0].subtractionId, modelsIdList); -// var adapter = new BiosaxsDataAdapter(); -// adapter.onSuccess.attach(function(sender, data) { -// -// }); -// adapter.getScatteringCurveByFrameIdsList([], [], [], ids); - }); - - - /** Dygraph Widget that plots fir files**/ - this.plotWidget = new PlotWidget({ - width : 745, - height : 300, - margin : "10 0 10 10" - }); -} - -FitForm.prototype._renderPlot = function(subtractionId, modelsIdList) { - /** Trying to plot tje subtraction and the models **/ - try { - var colors = [ "#669900", "#0000FF" ]; - - this.plotWidget.refresh([], [ ], [], modelsIdList, [], colors); - } catch (e) { - console.log(e); - } -}; - -FitForm.prototype._getItems = function() { - var _this = this; - return [ { - xtype : 'label', - forId : 'myFieldId', - text : 'INLINE HELP: To be updated', - margin : '15 0 20 10', - cls : "inline-help" - }, this.fitGrid.getPanel(), this.plotWidget.getPanel() ] - -}; - -FitForm.prototype._getButtons = function() { - return []; -}; - -FitForm.prototype.getPanel = function() { - var _this = this; - this.panel = Ext.create('Ext.form.Panel', { - width : this.width, - height : this.height, - margin : 10, - border : 1, - defaultType : 'textfield', - items : this._getItems(), - buttons : this._getButtons(), - listeners : { - afterrender : function() { - _this._populate(); - - } - } - }); - return this.panel; -}; - -/** Populates could be call when the DOM is not filled yet **/ -FitForm.prototype._populate = function() { -}; - -/** It populates the form * */ -FitForm.prototype.refresh = function(subtractions) { - this.subtractions = subtractions; - this.fitGrid.refresh(subtractions); -}; - -FitForm.prototype.input = function() { - return {}; -}; - -/** It populates the form **/ -FitForm.prototype.test = function(targetId) { - var macromoleculeForm = new FitForm(); - var panel = macromoleculeForm.getPanel(); - panel.render(targetId); -}; - -/** - * Example form - * - * @witdh - * @height - */ -function RigidBodyModelingResultForm(args) { - this.id = BUI.id(); - this.width = null; - this.height = null; - - if (args != null) { - if (args.width != null) { - this.width = args.width; - } - if (args.height != null) { - this.height = args.height; - } - } - - var _this = this; - /** Widgets **/ - this.rigidModelGrid = new RigidModelGrid({ - width : null, - height : 200 - }); - - this.rigidModelGrid.onSelected.attach(function(sender, fits){ - var ids = []; - for ( var i in fits) { - ids.push(fits[i].fitStructureToExperimentalDataId); - } - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, data){ -// debugger - - }); - - adapter.getScatteringCurveByFrameIdsList([], [], [], ids); - }); - -} - -RigidBodyModelingResultForm.prototype._getItems = function() { - var _this = this; - return [ { - xtype : 'label', - forId : 'myFieldId', - text : 'INLINE HELP: To be updated', - margin : '15 0 20 10', - cls : "inline-help" - }, this.rigidModelGrid.getPanel() ] - -}; - -RigidBodyModelingResultForm.prototype._getButtons = function() { - return []; -}; - -RigidBodyModelingResultForm.prototype.getPanel = function() { - var _this = this; - this.panel = Ext.create('Ext.form.Panel', { - width : this.width, - height : this.height, - margin : 10, - border : 1, - defaultType : 'textfield', - items : this._getItems(), - buttons : this._getButtons(), - listeners : { - afterrender : function() { - _this._populate(); - - } - } - }); - return this.panel; -}; - -/** Populates could be call when the DOM is not filled yet **/ -RigidBodyModelingResultForm.prototype._populate = function() { -}; - -/** It populates the form * */ -RigidBodyModelingResultForm.prototype.refresh = function(subtractions) { - this.rigidModelGrid.refresh(subtractions); -}; - -RigidBodyModelingResultForm.prototype.input = function() { - return {}; -}; - -/** It populates the form **/ -RigidBodyModelingResultForm.prototype.test = function(targetId) { - var macromoleculeForm = new RigidBodyModelingResultForm(); - var panel = macromoleculeForm.getPanel(); - panel.render(targetId); -}; - -/** - * Example form - * - * @witdh - * @height - */ -function SuperpositionForm(args) { - this.id = BUI.id(); - this.width = null; - this.height = null; - - if (args != null) { - if (args.width != null) { - this.width = args.width; - } - if (args.height != null) { - this.height = args.height; - } - } - - var _this = this; - - /** Widgets **/ - this.superpositionGrid = new SuperpositionGrid({ - width : null, - height : 200 - }); - - this.superpositionGrid.onSelected.attach(function(sender, superpositions) { - var ids = []; - for ( var i in superpositions) { - ids.push(superpositions[i].superpositionId); - } -// _this._renderAbinitio(ids); -// _this.aprioriPDBViewer.refresh(); - _this._renderAligned(ids); - // getAlignedPDBContentBySuperpositionList - }); - - /** PDB viewer **/ -// this.abinitioPDBViewer = new PDBViewer({ -// width : 860 / 2, -// height : 300 / 2, -// margin : 0 -// }); -// -// /** PDB viewer **/ -// this.aprioriPDBViewer = new StructurePDBViewer({ -// width : 860 / 2, -// height : 300 / 2, -// margin : 0 -// }); - - this.alignedPDBViewer = new AlignedSuperpositionPDBViewer({ - width : 860 , - height : 300 - }); - -} - -SuperpositionForm.prototype._renderAligned = function(modelsIdList) { - /** Trying to plot the PDB file **/ - try { - this.alignedPDBViewer.refresh(modelsIdList); - } catch (e) { - console.log(e); - } -}; - -SuperpositionForm.prototype._renderAbinitio = function(modelsIdList) { - /** Trying to plot the PDB file **/ - try { - var viz = []; - for (var i = 0; i < modelsIdList.length; i++) { - viz.push({ - modelId : modelsIdList[i], - color : "0xFF6600", - opacity : 0.8 - }); - } - this.abinitioPDBViewer.refresh(viz); - } catch (e) { - console.log(e); - } -}; - -SuperpositionForm.prototype._getItems = function() { - var _this = this; - return [ { - xtype : 'label', - forId : 'myFieldId', - text : 'INLINE HELP: To be updated', - margin : '15 0 20 10', - cls : "inline-help" - }, this.superpositionGrid.getPanel(), { - xtype : 'container', - layout : 'hbox', - items : [ -// { -// xtype : 'container', -// layout : 'vbox', -// items : [ this.abinitioPDBViewer.getPanel(), this.aprioriPDBViewer.getPanel() -// -// ] -// }, - - this.alignedPDBViewer.getPanel() ] - } ] - -}; - -SuperpositionForm.prototype._getButtons = function() { - return []; -}; - -SuperpositionForm.prototype.getPanel = function() { - var _this = this; - this.panel = Ext.create('Ext.form.Panel', { - width : this.width, - height : this.height, - margin : 10, - border : 1, - defaultType : 'textfield', - items : this._getItems(), - buttons : this._getButtons(), - listeners : { - afterrender : function() { - _this._populate(); - - } - } - }); - return this.panel; -}; - -/** Populates could be call when the DOM is not filled yet **/ -SuperpositionForm.prototype._populate = function() { -}; - -/** It populates the form * */ -SuperpositionForm.prototype.refresh = function(subtractions) { - this.subtractions = subtractions; - this.superpositionGrid.refresh(subtractions); -}; - -SuperpositionForm.prototype.input = function() { - return {}; -}; - -/** It populates the form **/ -SuperpositionForm.prototype.test = function(targetId) { - var macromoleculeForm = new SuperpositionForm(); - var panel = macromoleculeForm.getPanel(); - panel.render(targetId); -}; - -/** - * Example form - * - * @witdh - * @height - */ -function AssemblyForm(args) { - this.id = BUI.id(); - this.width = 700; - this.height = 500; - - if (args != null) { - if (args.width != null) { - this.width = args.width; - } - if (args.height != null) { - this.height = args.height; - } - } - - this.molarityGrid = new MolarityGrid({height : this.height - 50}); -} - -AssemblyForm.prototype._getItems = function() { - return [ { - xtype : 'label', - forId : 'myFieldId', - text : 'List of previously defined macromolecules present in the assembly. This information will be used for additional cross-checks where possible', - margin : '15 0 20 10', - cls : "inline-help" - }, this.molarityGrid.getPanel() ]; -}; - -AssemblyForm.prototype._getButtons = function() { - return []; -}; - -AssemblyForm.prototype.getPanel = function() { - this.panel = Ext.create('Ext.form.Panel', { - width : this.width, - height : this.height, - margin : 10, - border : 0, - defaultType : 'textfield', - items : this._getItems(), - buttons : this._getButtons() - }); - return this.panel; -}; - - -/** It populates the form **/ -AssemblyForm.prototype.refresh = function(macromolecule) { - this.macromolecule = macromolecule; - this.molarityGrid.refresh(macromolecule); -}; - -AssemblyForm.prototype.input = function() { - return {}; -}; - - -AssemblyForm.prototype.test = function(targetId) { - var assemblyForm = new AssemblyForm(); - - var panel = assemblyForm.getPanel(); - panel.render(targetId); -}; /** - * Edit the information of a buffer + * Example form * - * #onSaved - * #onRemoveAdditive + * @witdh + * @height */ -function BufferForm() { +function DataReductionForm(args) { + this.id = BUI.id(); + this.width = null; + this.height = null; + + if (args != null) { + if (args.width != null) { + this.width = args.width; + } + if (args.height != null) { + this.height = args.height; + } + } + var _this = this; - this.additiveGrid = new AdditiveGrid(); - this.additiveGrid.onRemoveButtonClicked.attach(function(sender, args) { - _this.onRemoveAdditive.notify(args); + /** Widgets **/ + this.plotWidget = new PlotWidget({ + width : 650, + height : 490 }); - this.onSaved = new Event(this); - this.onRemoveAdditive = new Event(this); + /** Selected frames to be displayed **/ + this.selectedItems = { + frames : [], + averages : [], + subtractions : [] + }; + } -BufferForm.prototype.getBuffer = function() { - this.buffer.name = Ext.getCmp("buffer_name").getValue(); - this.buffer.acronym = Ext.getCmp("buffer_acronym").getValue(); - this.buffer.comments = Ext.getCmp("buffer_comments").getValue(); - this.buffer.ph = Ext.getCmp("buffer_ph").getValue(); - this.buffer.composition = Ext.getCmp("buffer_composition").getValue(); - this.buffer.bufferhasadditive3VOs = this.additiveGrid.getAdditives(); - return this.buffer; +DataReductionForm.prototype._parseSelectedItemsToIds = function(selectedArray) { + var ids = []; + if (selectedArray != null) { + for (var i = 0; i < selectedArray.length; i++) { + ids.push(selectedArray[i].id); + } + } + return ids; }; -BufferForm.prototype._getTopPanel = function() { +DataReductionForm.prototype.onSelectionChanged = function(columnName, selected) { + if (selected != null) { + if (columnName == "Frames") { + this.selectedItems.frames = selected; + this.selectedItems.subtractions = []; + } + if (columnName == "Averages") { + this.selectedItems.averages = selected; + } + if (columnName == "Subtractions") { + this.selectedItems.frames = []; + this.selectedItems.subtractions = selected; + } + } + this.plotWidget.refresh(this._parseSelectedItemsToIds(this.selectedItems.frames), this._parseSelectedItemsToIds(this.selectedItems.subtractions)); +}; + +DataReductionForm.prototype._getFramesExtPanel = function(store, columnName, height) { + var _this = this; + + var selModel = Ext.create('Ext.selection.RowModel', { + allowDeselect : true, + mode : 'MULTI', + listeners : { + selectionchange : function(sm, selections) { + var selected = []; + for (var i = 0; i < selections.length; i++) { + selected.push(selections[i].raw); + } + _this.onSelectionChanged(columnName, selected); + } + } + }); + + return Ext.create('Ext.grid.Panel', { + store : store, + margin : 10, + height : height, + width : 200, + selModel : selModel, + columns : [ { + text : columnName, + dataIndex : 'fileName', + flex : 1 + } ], + viewConfig : { + } + }); +}; + +DataReductionForm.prototype._getFramesPanel = function() { + var fields = [ 'fileName', 'type', 'id' ]; + + this.framesStore = Ext.create('Ext.data.Store', { + fields : fields, + sorters : 'fileName' + }); + + this.averagesStore = Ext.create('Ext.data.Store', { + fields : fields + }); + + this.subtractionStore = Ext.create('Ext.data.Store', { + fields : fields + }); + + var gridFrames = this._getFramesExtPanel(this.framesStore, "Frames", 375); + var gridAvgs = this._getFramesExtPanel(this.averagesStore, "Averages", 125); + var subtractionAvgs = this._getFramesExtPanel(this.subtractionStore, "Subtractions", 75); + return { xtype : 'container', - layout : 'hbox', - border : 0, - frame : true, + layout : 'vbox', + items : [ gridFrames, subtractionAvgs ] + }; +}; + +DataReductionForm.prototype._getImageContainer = function(name, help) { + var html = "
" + name + "
" + return { + xtype : 'container', + layout : 'vbox', items : [ { - xtype : 'container', - layout : 'hbox', - items : [ { - xtype : 'container', - flex : 1, - border : false, - layout : 'anchor', - defaultType : 'textfield', - items : [ { - xtype : 'requiredtext', - id : 'buffer_name', - fieldLabel : 'Name', - name : 'name', - width : '200px', - value : this.buffer.name - }, { - xtype : 'requiredtext', - id : 'buffer_acronym', - fieldLabel : 'Acronym', - name : 'acronym', - width : '200px', - value : this.buffer.acronym - } ] - } ] + html : html, + margin : "5 0 0 0", + height : 95, + width : 100 }, { - xtype : 'container', - flex : 1, - layout : 'anchor', - defaultType : 'textfield', - margin : '0 0 0 10', - items : [ { - id : 'buffer_ph', - fieldLabel : 'pH', - name : 'ph', - value : this.buffer.ph, - xtype : 'numberfield', - width : 200, - minValue : 0, - maxValue : 15 - }, { - xtype : 'requiredtext', - id : 'buffer_composition', - fieldLabel : 'Composition', - name : 'composition', - width : 200, - value : this.buffer.composition - } ] + xtype : 'label', + forId : 'myFieldId', + text : help, + margin : '5 0 0 0', + cls : "inline-help" } ] - }; + } }; -BufferForm.prototype.getPanel = function(buffer) { - this.buffer = buffer; - this.panel = Ext.createWidget({ +DataReductionForm.prototype._getItems = function() { + var _this = this; + return [ { xtype : 'container', - layout : 'vbox', - style : { - padding : '10px' - }, - fieldDefaults : { - labelAlign : 'left', - labelWidth : 50 + layout : 'hbox', + items : [ + this._getFramesPanel(), + this.plotWidget.getPanel(), + { + xtype : 'panel', + width : 110, + frame : true, + margin : "10 5 5 5", + border : 0, + layout : 'vbox', + items : [ this._getImageContainer("scattering", "Scattering"), this._getImageContainer("guinier", "Guinier Region"), + this._getImageContainer("kratky", "Kratky Plot"), this._getImageContainer("gnom", "P(r) distribution") ] + } ] + } ] +}; - }, - items : [ this._getTopPanel(), { - id : 'buffer_comments', - xtype : 'textareafield', - name : 'comments', - fieldLabel : 'Comments', - width : '100%', - value : buffer.comments - }, this.additiveGrid.getPanel(buffer) ] +DataReductionForm.prototype._getButtons = function() { + return []; +}; + +DataReductionForm.prototype.getPanel = function() { + var _this = this; + this.panel = Ext.create('Ext.form.Panel', { + width : this.width, + height : this.height, + border : 0, + items : this._getItems(), + buttons : this._getButtons(), + listeners : { + afterrender : function() { + _this._populate(); + } + } }); return this.panel; }; -BufferForm.prototype.input = function() { - return { - buffer : { - "bufferId" : 422, - "proposalId" : 10, - "safetyLevelId" : null, - "name" : "B1", - "acronym" : "B1", - "ph" : null, - "composition" : null, - "bufferhasadditive3VOs" : [], - "comments" : null +/** Populates could be call when the DOM is not filled yet **/ +DataReductionForm.prototype._populate = function() { +}; + +/** It populates the form * */ +DataReductionForm.prototype.refresh = function(subtractions) { + if (subtractions != null) { + for (var i = 0; i < subtractions.length; i++) { + /** Loading frame grids **/ + var subtraction = subtractions[i]; + var averages = [ { + fileName : BUI.getFileName(subtraction.bufferAverageFilePath), + type : 'bufferAvg', + id : subtraction.subtractionId + }, { + fileName : BUI.getFileName(subtraction.sampleAverageFilePath), + type : 'sampleAvg', + id : subtraction.subtractionId + } + + ]; + this.averagesStore.loadData(averages, true); + this.subtractionStore.loadData([ { + fileName : BUI.getFileName(subtraction.substractedFilePath), + type : 'SUBTRACTION', + id : subtraction.subtractionId + } ], true); + + var frames = []; + /** Buffers **/ + if (subtraction.bufferOneDimensionalFiles != null) { + if (subtraction.bufferOneDimensionalFiles.frametolist3VOs != null) { + for (var j = 0; j < subtraction.bufferOneDimensionalFiles.frametolist3VOs.length; j++) { + var frametolist3VO = subtraction.bufferOneDimensionalFiles.frametolist3VOs[j]; + if (frametolist3VO.frame3VO != null) { + frames.push({ + fileName : BUI.getFileName(frametolist3VO.frame3VO.filePath), + type : 'BUFFER', + id : frametolist3VO.frame3VO.frameId + }); + } + } + } + } + /** Samples **/ + if (subtraction.sampleOneDimensionalFiles != null) { + if (subtraction.sampleOneDimensionalFiles.frametolist3VOs != null) { + for (var j = 0; j < subtraction.sampleOneDimensionalFiles.frametolist3VOs.length; j++) { + var frametolist3VO = subtraction.sampleOneDimensionalFiles.frametolist3VOs[j]; + if (frametolist3VO.frame3VO != null) { + frames.push({ + fileName : BUI.getFileName(frametolist3VO.frame3VO.filePath), + type : 'SAMPLE', + id : frametolist3VO.frame3VO.frameId + }); + } + } + } + } + + this.framesStore.loadData(frames, true); + + /** Loading images **/ + this._displayImage("scattering", subtraction.subtractionId); + this._displayImage("kratky", subtraction.subtractionId); + this._displayImage("guinier", subtraction.subtractionId); + this._displayImage("gnom", subtraction.subtractionId); } - }; + } }; -BufferForm.prototype.test = function(targetId) { - var bufferForm = new BufferForm(); - var panel = bufferForm.getPanel(bufferForm.input().buffer); +DataReductionForm.prototype._displayImage = function(name, subtractionId) { + var url = BUI.getURL() + '&type=' + name + '&subtractionId=' + subtractionId; + var event = "OnClick= window.open('" + url + "')"; + document.getElementById(this.id + "_" + name).innerHTML = ''; +}; + +DataReductionForm.prototype.input = function() { + return {}; +}; + +/** It populates the form **/ +DataReductionForm.prototype.test = function(targetId) { + var macromoleculeForm = new DataReductionForm(); + var panel = macromoleculeForm.getPanel(); panel.render(targetId); }; - -/** - * @showTitle - * - * #onSaved - * #onAddPlates - * #onRemovePlates - **/ -function CaseForm(args) { - this.width = 700; - this.showTitle = true; + +function PlotWidget(args) { + this.width = 600; + this.height = 600; + this.id = BUI.id(); + + this.linear = false; + + this.margin = "10 0 0 0"; + /** for each stat the max and minimum value when it is scaled in order to show correctly in the legend **/ + this.ranges = {}; if (args != null) { - if (args.showTitle != null) { - this.showTitle = args.showTitle; + if (args.width != null) { + this.width = args.width; + } + if (args.height != null) { + this.height = args.height; + } + if (args.margin != null) { + this.margin = args.margin; } } - var _this = this; - this.stockSolutionGrid = new StockSolutionGrid({ - width : this.width - 10, - minHeight : 300, - height : 300, - tbar : true, - showTitle : true, - isPackedVisible : false, - btnAddExisting : true, - btnRemoveVisible : false, - btnUnpackVisible : true - }); +} - /** When selecting existing stock solutions **/ - this.stockSolutionGrid.onStockSolutionSelected.attach(function(sender, stockSolutions) { - if (stockSolutions != null) { - for ( var i = 0; i < stockSolutions.length; i++) { - _this.saveStockSolution(stockSolutions[i]); - } +PlotWidget.prototype.getMenu = function() { + var _this = this; + /** Actions buttons **/ + var actions = []; + actions.push("->"); + actions.push({ + text : "Export as Image", + scope : this, + icon : '../images/save.gif', + handler : function(item, pressed) { + var largeImage = document.createElement("img"); + largeImage.style.display = 'block'; + largeImage.style.width = 200 + "px"; + largeImage.style.height = 200 + "px"; + largeImage.setAttribute('src', Dygraph.Export.asCanvas(this.dygraphObject.dygraph).toDataURL()); + window.open(Dygraph.Export.asCanvas(this.dygraphObject.dygraph).toDataURL(), 'Image', ''); } }); - /** it can be because it has been added a new one or removed **/ - this.stockSolutionGrid.onProposalChanged.attach(function(sender, stockSolution) { - if (stockSolution != null) { - _this.saveStockSolution(stockSolution); - } else { - BIOSAXS.proposal.onInitialized.attach(function() { - _this.stockSolutionGrid.refresh(BIOSAXS.proposal.getStockSolutionsByDewarId(_this.dewar.dewarId)); + return actions; +}; - _this.stockSolutionGrid.grid.setLoading(false); - }); - BIOSAXS.proposal.init(); +/** Looks for the maximum value and then divide everything but that value **/ +PlotWidget.prototype.scaledData = function(data) { + for (var i = 0; i < data.length; i++) { + var values = this.getMaxAndMinValue(data[i]); + data[i] = this.divideValuesByMax(data[i], values.max); + this.ranges[data[i].label] = values; + } + return data; +}; + +/** Given a stat float[] and a max number it will divide each value by max **/ +PlotWidget.prototype.divideValuesByMax = function(stat, max) { + for (var j = 0; j < stat.data.length; j++) { + if (max != 0) { + stat.data[j] = Number(stat.data[j]) / max; + stat.std[j] = Number(stat.std[j]) / max; + } + } + return stat; +}; +/** returns max value of a stat **/ +PlotWidget.prototype.getMaxAndMinValue = function(stat) { + var max = 0; + var min = stat.data[0]; + for (var j = 0; j < stat.data.length; j++) { + if (Number(stat.data[j]) > max) { + max = Number(stat.data[j]); + } + if (Number(stat.std[j]) > max) { + max = Number(stat.std[j]); + } + if (Number(stat.data[j]) < min) { + min = Number(stat.data[j]); } + } + return { + max : Number(max), + min : Number(min) + }; +}; +PlotWidget.prototype._renderDygraph = function(parsed, colors, labels) { + this.dygraphObject = new StdDevDyGraph(this.id, { + width : this.width - 20, + height : this.height - 40, + xlabel : "", }); - this.onSaved = new Event(this); - this.onAddPlates = new Event(this); - this.onRemovePlates = new Event(this); -} + this.dygraphObject.draw(parsed, colors, labels); -CaseForm.prototype.saveStockSolution = function(stockSolution) { - var _this = this; - var adapter = new BiosaxsDataAdapter(); - this.stockSolutionGrid.grid.setLoading("ISPyB: setting case to Stock solution"); - adapter.onSuccess.attach(function(sender, stockSolution) { - BIOSAXS.proposal.onInitialized.attach(function() { - _this.stockSolutionGrid.refresh(BIOSAXS.proposal.getStockSolutionsByDewarId(_this.dewar.dewarId)); - _this.stockSolutionGrid.grid.setLoading(false); - }); - BIOSAXS.proposal.init(); - }); - adapter.onError.attach(function(sender, data) { - _this.stockSolutionGrid.grid.setLoading(false); - BUI.showError(data); - }); - stockSolution.boxId = _this.dewar.dewarId; - adapter.saveStockSolution(stockSolution); }; -CaseForm.prototype.fillStores = function() { - var _this = this; - this.panel.setLoading("Loading Labcontacts from database"); - - var proposal = BUI.getProposal(); - proposal.onDataRetrieved.attach(function(sender, data) { - _this.labContactForSendingStore.loadData(data, false); - _this.labContactForReturnStore.loadData(data, false); - _this.panel.setLoading(false); +PlotWidget.prototype.getPanel = function() { + this.panel = Ext.create('Ext.panel.Panel', { + width : this.width, + height : this.height, + margin : this.margin, + tbar : this.getMenu(), + items : [ { + html : "", + id : this.id, + width : this.width, + height : this.height, + padding : 10, + margin : "0 0 0 -30", + border : 0 + } ] }); - proposal.getLabContactsByProposalId(); - -}; -CaseForm.prototype.refresh = function(dewar) { - this.setDewar(dewar); - this.stockSolutionGrid.refresh(BIOSAXS.proposal.getStockSolutionsByDewarId(dewar.dewarId)); + return this.panel; }; -CaseForm.prototype.getDewar = function() { - this.dewar.code = Ext.getCmp("dewar_code").getValue(); - this.dewar.comments = Ext.getCmp("dewar_comments").getValue(); - this.dewar.trackingNumberFromSynchrotron = Ext.getCmp("dewar_trackingNumberFromSynchrotron").getValue(); - this.dewar.trackingNumberToSynchrotron = Ext.getCmp("dewar_trackingNumberToSynchrotron").getValue(); - this.dewar.transportValue = Ext.getCmp("dewar_transportValue").getValue(); - this.dewar.storageLocation = Ext.getCmp("dewar_storageLocation").getValue(); - this.dewar.firstExperimentId = this.macromoleculeCombo.getValue(); - return this.dewar; -}; +PlotWidget.prototype.getPoint = function(y, error) { + var minus = y - error; + var max = y + error; -CaseForm.prototype.setDewar = function(dewar) { - this.dewar = dewar; - Ext.getCmp("dewar_code").setValue(this.dewar.code); - Ext.getCmp("dewar_dewarStatus").setText(new String(this.dewar.dewarStatus).toUpperCase()); - Ext.getCmp("dewar_comments").setValue(this.dewar.comments); - Ext.getCmp("dewar_trackingNumberFromSynchrotron").setValue(this.dewar.trackingNumberFromSynchrotron); - Ext.getCmp("dewar_trackingNumberToSynchrotron").setValue(this.dewar.trackingNumberToSynchrotron); - Ext.getCmp("dewar_transportValue").setValue(this.dewar.transportValue); - Ext.getCmp("dewar_storageLocation").setValue(this.dewar.storageLocation); - if (dewar.sessionVO != null) { - this.macromoleculeCombo.setValue(dewar.sessionVO.sessionId); + if (this.linear) { + return [ Math.abs(minus), y, Math.abs(max) ]; + } + if ((minus != 0) && (max != 0)) { + return [ Math.log(Math.abs(minus)), Math.log(y), Math.log(Math.abs(max)) ]; + } else { + return [ Math.log(y), Math.log(y), Math.log(y) ]; } -}; -CaseForm.prototype.getSessionCombo = function() { - this.macromoleculeCombo = BIOSAXS_COMBOMANAGER.getComboSessions(BIOSAXS.proposal.getSessions(), { - labelWidth : 100, - margin : '5 0 00 0', - width : 250 - }); - return this.macromoleculeCombo; }; -CaseForm.prototype.getInformationPanel = function() { - if (this.panel == null) { - this.informationPanel = Ext.create('Ext.form.Panel', { - width : this.width - 10, - border : 0, - items : [ { - xtype : 'container', - margin : "2 2 2 2", - collapsible : false, - defaultType : 'textfield', - layout : 'anchor', - items : [ { - xtype : 'container', - layout : 'hbox', - items : [ { - xtype : 'requiredtext', - fieldLabel : 'Code', - allowBlank : false, - name : 'code', - id : 'dewar_code', - anchor : '50%' - }, { - xtype : 'label', - margin : '0 0 0 20', - readOnly : true, - id : 'dewar_dewarStatus', - anchor : '50%' - } ] - }, this.getSessionCombo(), { - margin : '5 0 0 0', - xtype : 'textareafield', - name : 'comments', - id : 'dewar_comments', - width : this.width - 50, - fieldLabel : 'Comments' - } ] - }, { - xtype : 'fieldset', - title : 'Courier Accounts Details for Return', - collapsible : false, - layout : 'anchor', - defaults : { - anchor : '100%' - }, - items : [ { - xtype : 'container', - layout : 'hbox', - items : [ { - xtype : 'textfield', - labelWidth : 200, - width : 280, - fieldLabel : 'Track Number From Synchrotron', - id : 'dewar_trackingNumberFromSynchrotron' - }, { - xtype : 'numberfield', - width : 190, - labelWidth : 110, - margin : '0 0 0 30', - fieldLabel : 'Transport Value', - id : 'dewar_transportValue' +PlotWidget.prototype.getDataPlot = function(frames, subtractions, models, fits, rbms) { + var files = []; + var labels = [ "Intensity" ]; + if (frames != null) { + for (var i = 0; i < frames.length; i++) { + files.push(frames[i].data); + labels.push(frames[i].fileName); + } + } + function splitData(data, column, errorColumn, name){ + var result = [] + for (var j = 0; j < data.length; j++) { + console.log(data[j][column]); + result.push([j,data[j][column],data[j][errorColumn]]);//[0, data[i][column],0]]); + } + files.push(result); + labels.push(name); + } + + if (subtractions != null) { + for (var i = 0; i < subtractions.length; i++) { + /** For subtraction **/ + files.push(subtractions[i].subtraction.data); + labels.push(subtractions[i].subtraction.fileName); + /** For sample average **/ +// files.push(subtractions[i].sampleAvg.data); +// labels.push(subtractions[i].sampleAvg.fileName); + /** For buffer average **/ +// files.push(subtractions[i].bufferAvg.data); +// labels.push(subtractions[i].bufferAvg.fileName); + } + } + + if (models != null) { + for (var i = 0; i < models.length; i++) { + for ( var key in models[i]) { + splitData(models[i][key].fir.data, 1, 2, "Intensity"); + splitData(models[i][key].fir.data, 3, 3, "Fit"); + } + } + } + + if (fits != null) { + for (var i = 0; i < fits.length; i++) { + for ( var key in fits[i]) { + + /** adding fit file to be plotted **/ + if (fits[i][key].fit.data[0].length == 3){ + splitData(fits[i][key].fit.data, 1, 1, "Intensity"); + splitData(fits[i][key].fit.data, 2, 2, "Fit"); + } + + /** s, Iexp(s), err, Ifit(s). **/ + if (fits[i][key].fit.data[0].length == 4){ + splitData(fits[i][key].fit.data, 1, 2, "Intensity"); + splitData(fits[i][key].fit.data, 3, 3, "Fit"); + } + + if (fits[i][key].fit.data[0].length == 5){ + /** X Intensity Fit Error Residues **/ + + splitData(fits[i][key].fit.data, 1, 3, "Intensity"); + splitData(fits[i][key].fit.data, 2, 2, "Fit"); + } + } + } + } + + var dataPoints = []; + if (files.length > 0) { + for (var i = 0; i < files[0].length; i++) { + dataPoints.push([ files[0][i][0], this.getPoint(files[0][i][1], files[0][i][2]) ]); + } + if (files.length > 1) { + for (var i = 1; i < files.length; i++) { + for (var j = 0; j < dataPoints.length; j++) { + if (files[i][j] != null){ + dataPoints[j].push(this.getPoint(files[i][j][1], files[i][j][2])); } - - ] - }, { - xtype : 'container', - layout : 'hbox', - margin : '10 0 0 0', - items : [ - - { - xtype : 'textfield', - labelWidth : 200, - width : 280, - fieldLabel : 'Track Number To Synchrotron', - id : 'dewar_trackingNumberToSynchrotron' - }, { - xtype : 'textfield', - margin : '0 0 0 30', - width : 190, - labelWidth : 110, - fieldLabel : 'Storage Location', - id : 'dewar_storageLocation' + else{ + dataPoints[j].push([0,0,0]); } + } + } + } + } - ] - } ] - } ] - }); + return { + dataPoints : dataPoints, + labels : labels } +}; - return this.informationPanel; +PlotWidget.prototype.refresh = function(frames, subtractions, models, fits, rbms, colors) { + + var _this = this; + this.panel.setLoading("Reading Files"); + + var dataAdapter = new BiosaxsDataAdapter(); + dataAdapter.onSuccess.attach(function(sender, data) { + _this.panel.setLoading("Rendering"); + var parsed = _this.getDataPlot(data.frames, data.subtractions, data.models, data.fits, rbms); + _this._renderDygraph(parsed.dataPoints, colors, parsed.labels); + _this.panel.setLoading(false); + }); + dataAdapter.onError.attach(function(sender, data) { + _this.panel.setLoading(false); + }); + dataAdapter.getDataPlot(frames, subtractions, models, fits, rbms); }; -CaseForm.prototype.getPanel = function(dewar) { - this.dewar = dewar; - this.panel = Ext.createWidget({ - xtype : 'container', - layout : 'vbox', - width : this.width, - border : 0, - items : [ { - items : { - xtype : "container", - layout : "vbox", - margin : "5 5 5 5", - items : [ this.getInformationPanel(dewar), this.stockSolutionGrid.getPanel() ] +PlotWidget.prototype.input = function() { + return DATADOC.getHPLCData(); +}; - } - } ] + +/** + * Fit form + * + * @witdh + * @height + */ +function FitForm(args) { + this.id = BUI.id(); + this.width = null; + this.height = null; + + if (args != null) { + if (args.width != null) { + this.width = args.width; + } + if (args.height != null) { + this.height = args.height; + } + } + + var _this = this; + + /** Widgets **/ + this.fitGrid = new FitStructureToExperimentDataGrid({ + width : null, + height : 200 }); - this.refresh(dewar); - return this.panel; + this.fitGrid.onSelected.attach(function(sender, fits) { + var modelsIdList = []; + for ( var i in fits) { + modelsIdList.push(fits[i].fitStructureToExperimentalDataId); + } + _this._renderPlot(_this.subtractions[0].subtractionId, modelsIdList); +// var adapter = new BiosaxsDataAdapter(); +// adapter.onSuccess.attach(function(sender, data) { +// +// }); +// adapter.getScatteringCurveByFrameIdsList([], [], [], ids); + }); + + + /** Dygraph Widget that plots fir files**/ + this.plotWidget = new PlotWidget({ + width : 745, + height : 300, + margin : "10 0 10 10" + }); +} +FitForm.prototype._renderPlot = function(subtractionId, modelsIdList) { + /** Trying to plot tje subtraction and the models **/ + try { + var colors = [ "#669900", "#0000FF" ]; + + this.plotWidget.refresh([], [ ], [], modelsIdList, [], colors); + } catch (e) { + console.log(e); + } }; -CaseForm.prototype.input = function() { - return { - dewar : { - "dewarId" : 305861, - "code" : "ESRF-TEST", - "comments" : "comments", - "storageLocation" : "FRIDGE", - "dewarStatus" : "opened", - "timeStamp" : null, - "isStorageDewar" : null, - "barCode" : "ESRF305861", - "customsValue" : null, - "transportValue" : null, - "trackingNumberToSynchrotron" : "3333", - "trackingNumberFromSynchrotron" : "224466", - "type" : "Dewar", - "sessionVO" : { - "sessionId" : 31697, - "expSessionPk" : null, - "projectCode" : null, - "startDate" : "2012 07 21", - "endDate" : "2012 07 23", - "beamlineName" : "BM29", - "scheduled" : 1, - "nbShifts" : 2, - "comments" : null, - "beamlineOperator" : "PERNOT P", - "usedFlag" : null, - "sessionTitle" : null, - "structureDeterminations" : null, - "dewarTransport" : null, - "databackupFrance" : null, - "databackupEurope" : null, - "visit_number" : null, - "operatorSiteNumber" : "14061", - "timeStamp" : "2012 04 25" +FitForm.prototype._getItems = function() { + var _this = this; + return [ { + xtype : 'label', + forId : 'myFieldId', + text : 'INLINE HELP: To be updated', + margin : '15 0 20 10', + cls : "inline-help" + }, this.fitGrid.getPanel(), this.plotWidget.getPanel() ] + +}; + +FitForm.prototype._getButtons = function() { + return []; +}; + +FitForm.prototype.getPanel = function() { + var _this = this; + this.panel = Ext.create('Ext.form.Panel', { + width : this.width, + height : this.height, + margin : 10, + border : 1, + defaultType : 'textfield', + items : this._getItems(), + buttons : this._getButtons(), + listeners : { + afterrender : function() { + _this._populate(); + } - }, - proposal : { - "assemblies" : [], - "sessions" : [ { - "sessionId" : 31697, - "expSessionPk" : null, - "projectCode" : null, - "startDate" : "2012 07 21", - "endDate" : "2012 07 23", - "beamlineName" : "BM29", - "scheduled" : 1, - "nbShifts" : 2, - "comments" : null, - "beamlineOperator" : "PERNOT P", - "usedFlag" : null, - "sessionTitle" : null, - "structureDeterminations" : null, - "dewarTransport" : null, - "databackupFrance" : null, - "databackupEurope" : null, - "visit_number" : null, - "operatorSiteNumber" : "14061", - "timeStamp" : "2012 04 25" - } ], - "labcontacts" : [ { - "labContactId" : 787, - "personVO" : { - "personId" : 304252, - "personUUID" : null, - "familyName" : "KIM", - "givenName" : "Henry", - "title" : null, - "emailAddress" : "henry-sung.kim@ibs.fr", - "phoneNumber" : "", - "login" : "", - "passwd" : "", - "faxNumber" : "" - }, - "cardName" : "KIM-Institut de Bio", - "defaultCourrierCompany" : "22", - "courierAccount" : "", - "billingReference" : "", - "dewarAvgCustomsValue" : 0, - "dewarAvgTransportValue" : 0 - } ], - "buffers" : [ { - "bufferId" : 811, - "proposalId" : 3124, - "safetyLevelId" : null, - "name" : "EDTA", - "acronym" : "EDTA", - "ph" : null, - "composition" : "", - "bufferhasadditive3VOs" : [], - "comments" : "" - }, { - "bufferId" : 810, - "proposalId" : 3124, - "safetyLevelId" : null, - "name" : "HEPES", - "acronym" : "HEPES", - "ph" : null, - "composition" : "", - "bufferhasadditive3VOs" : [], - "comments" : "" - } ], - "shippings" : [ { - "shippingId" : 304107, - "shippingName" : "TEST", - "deliveryAgentAgentName" : null, - "deliveryAgentShippingDate" : null, - "deliveryAgentDeliveryDate" : null, - "deliveryAgentAgentCode" : null, - "deliveryAgentFlightCode" : null, - "shippingStatus" : "opened", - "timeStamp" : "2013 09 25", - "laboratoryId" : null, - "isStorageShipping" : null, - "creationDate" : "2013 09 25", - "comments" : "test", - "sendingLabContactVO" : { - "labContactId" : 787, - "personVO" : { - "personId" : 304252, - "personUUID" : null, - "familyName" : "KIM", - "givenName" : "Henry", - "title" : null, - "emailAddress" : "henry-sung.kim@ibs.fr", - "phoneNumber" : "", - "login" : "", - "passwd" : "", - "faxNumber" : "" - }, - "cardName" : "KIM-Institut de Bio", - "defaultCourrierCompany" : "22", - "courierAccount" : "", - "billingReference" : "", - "dewarAvgCustomsValue" : 0, - "dewarAvgTransportValue" : 0 - }, - "returnLabContactVO" : { - "labContactId" : 787, - "personVO" : { - "personId" : 304252, - "personUUID" : null, - "familyName" : "KIM", - "givenName" : "Henry", - "title" : null, - "emailAddress" : "henry-sung.kim@ibs.fr", - "phoneNumber" : "", - "login" : "", - "passwd" : "", - "faxNumber" : "" - }, - "cardName" : "KIM-Institut de Bio", - "defaultCourrierCompany" : "22", - "courierAccount" : "", - "billingReference" : "", - "dewarAvgCustomsValue" : 0, - "dewarAvgTransportValue" : 0 - }, - "returnCourier" : null, - "dateOfShippingToUser" : null, - "shippingType" : "DewarTracking", - "dewarVOs" : [ { - "dewarId" : 305861, - "code" : "ESRF-TEST", - "comments" : "comments", - "storageLocation" : "FRIDGE", - "dewarStatus" : "opened", - "timeStamp" : null, - "isStorageDewar" : null, - "barCode" : "ESRF305861", - "customsValue" : null, - "transportValue" : null, - "trackingNumberToSynchrotron" : "3333", - "trackingNumberFromSynchrotron" : "224466", - "type" : "Dewar", - "sessionVO" : { - "sessionId" : 31697, - "expSessionPk" : null, - "projectCode" : null, - "startDate" : "2012 07 21", - "endDate" : "2012 07 23", - "beamlineName" : "BM29", - "scheduled" : 1, - "nbShifts" : 2, - "comments" : null, - "beamlineOperator" : "PERNOT P", - "usedFlag" : null, - "sessionTitle" : null, - "structureDeterminations" : null, - "dewarTransport" : null, - "databackupFrance" : null, - "databackupEurope" : null, - "visit_number" : null, - "operatorSiteNumber" : "14061", - "timeStamp" : "2012 04 25" - } - } ] - } ], - "macromolecules" : [ { - "macromoleculeId" : 5933, - "safetylevelId" : null, - "proposalId" : 3124, - "name" : "A", - "acronym" : "A", - "molecularMass" : "", - "extintionCoefficient" : "", - "sequence" : null, - "creationDate" : null, - "comments" : "", - "macromoleculeregion3VOs" : [], - "stoichiometry3VOsForHostMacromoleculeId" : [], - "structure3VOs" : [] - } ], - "stockSolutions" : [ { - "stockSolutionId" : 6, - "proposalId" : 3124, - "macromoleculeId" : 5933, - "bufferId" : 811, - "instructionSet3VO" : null, - "boxId" : 305861, - "storageTemperature" : "20", - "volume" : "300", - "concentration" : "1.2", - "comments" : "Buffer EDTA with A", - "name" : "A_EDTA_1.2", - "samples" : [] - } ] } + }); + return this.panel; +}; - }; +/** Populates could be call when the DOM is not filled yet **/ +FitForm.prototype._populate = function() { }; -CaseForm.prototype.test = function(targetId) { - var caseForm = new CaseForm(); - BIOSAXS.proposal = new Proposal(caseForm.input().proposal); - var panel = caseForm.getPanel(caseForm.input().dewar); - panel.render(targetId); +/** It populates the form * */ +FitForm.prototype.refresh = function(subtractions) { + this.subtractions = subtractions; + this.fitGrid.refresh(subtractions); +}; + +FitForm.prototype.input = function() { + return {}; +}; + +/** It populates the form **/ +FitForm.prototype.test = function(targetId) { + var macromoleculeForm = new FitForm(); + var panel = macromoleculeForm.getPanel(); + panel.render(targetId); }; -/** - * Example form - * - * @witdh - * @height - */ -function ExampleForm(args) { - this.id = BUI.id(); - this.width = 700; - this.height = 500; - - if (args != null) { - if (args.width != null) { - this.width = args.width; - } - if (args.height != null) { - this.height = args.height; - } - } -} - -ExampleForm.prototype._getItems = function() { - return [{ - fieldLabel : 'First Name', - name : 'first', - allowBlank : false - }, { - fieldLabel : 'Last Name', - name : 'last', - allowBlank : false - } ]; -}; - -ExampleForm.prototype._getItems = function() { - return [ ]; -}; - -ExampleForm.prototype._getButtons = function() { - return [ ]; -}; - -ExampleForm.prototype.getPanel = function() { - this.panel = Ext.create('Ext.form.Panel', { - width : this.width, - height : this.height, - margin : 10, - border : 1, - defaultType : 'textfield', - items : this._getItems(), - buttons : this._getButtons() - }); - return this.panel; -}; - -/** It populates the form **/ -ExampleForm.prototype.refresh = function(macromolecule) { -}; - -function ExperimentForm(args) { +/** + * Example form + * + * @witdh + * @height + */ +function RigidBodyModelingResultForm(args) { this.id = BUI.id(); + this.width = null; + this.height = null; if (args != null) { + if (args.width != null) { + this.width = args.width; + } + if (args.height != null) { + this.height = args.height; + } } - this.onSaved = new Event(this); -} - -ExperimentForm.prototype._getItems = function(experiment) { - this.experiment = experiment; - var typeCombo = Ext.create('Ext.form.ComboBox', { - id : this.id + 'type', - fieldLabel : 'Type', - store : [ "STATIC", "CALIBRATION", "HPLC" ], - queryMode : 'local', - labelWidth : 120, - displayField : 'name', - valueField : 'name', - value : experiment.json.type, - disabled : (experiment.json.type == 'TEMPLATE') + var _this = this; + /** Widgets **/ + this.rigidModelGrid = new RigidModelGrid({ + width : null, + height : 200 }); - var items = []; - - - if (experiment.json.type == "HPLC" ){ - var typeMacromolecule = BIOSAXS_COMBOMANAGER.getComboMacromoleculeByMacromolecules(BIOSAXS.proposal.getMacromolecules(), {labelWidth : 120, width: "120px"}); - if (experiment.getHPLCMacromolecule() != null){ - typeMacromolecule.setValue(experiment.getHPLCMacromolecule().macromoleculeId); - items.push(typeMacromolecule); + this.rigidModelGrid.onSelected.attach(function(sender, fits){ + var ids = []; + for ( var i in fits) { + ids.push(fits[i].fitStructureToExperimentalDataId); } - } - - - items.push(typeCombo, { - id : this.id + 'name', - xtype : 'textfield', - fieldLabel : 'Name', - labelWidth : 120, - width : '100%', - value : experiment.json.name - }, { - id : this.id + 'comments', - xtype : 'textareafield', - name : 'comments', - fieldLabel : 'Comments', - labelWidth : 120, - height : 120, - width : '100%', - value : experiment.json.comments + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, data){ +// debugger + + }); + + adapter.getScatteringCurveByFrameIdsList([], [], [], ids); }); - return items; + +} + +RigidBodyModelingResultForm.prototype._getItems = function() { + var _this = this; + return [ { + xtype : 'label', + forId : 'myFieldId', + text : 'INLINE HELP: To be updated', + margin : '15 0 20 10', + cls : "inline-help" + }, this.rigidModelGrid.getPanel() ] + }; -ExperimentForm.prototype.getPanel = function(experiment) { + +RigidBodyModelingResultForm.prototype._getButtons = function() { + return []; +}; + +RigidBodyModelingResultForm.prototype.getPanel = function() { var _this = this; - - this.panel = Ext.createWidget({ - xtype : 'container', - layout : 'vbox', - border : 0, - style : { - padding : '10px' - }, - fieldDefaults : { - labelAlign : 'left', - labelWidth : 50 - }, - items : this._getItems(experiment) + this.panel = Ext.create('Ext.form.Panel', { + width : this.width, + height : this.height, + margin : 10, + border : 1, + defaultType : 'textfield', + items : this._getItems(), + buttons : this._getButtons(), + listeners : { + afterrender : function() { + _this._populate(); + + } + } }); return this.panel; }; -ExperimentForm.prototype.input = function() { - return new ExperimentHeaderForm().input(); +/** Populates could be call when the DOM is not filled yet **/ +RigidBodyModelingResultForm.prototype._populate = function() { }; -ExperimentForm.prototype.test = function(targetId) { - var experimentForm = new ExperimentForm(); - var panel = experimentForm.getPanel(new Experiment(experimentForm.input().experiment)); +/** It populates the form * */ +RigidBodyModelingResultForm.prototype.refresh = function(subtractions) { + this.rigidModelGrid.refresh(subtractions); +}; + +RigidBodyModelingResultForm.prototype.input = function() { + return {}; +}; + +/** It populates the form **/ +RigidBodyModelingResultForm.prototype.test = function(targetId) { + var macromoleculeForm = new RigidBodyModelingResultForm(); + var panel = macromoleculeForm.getPanel(); panel.render(targetId); }; /** - * Shows the header for the experiments changing the color and parameters depending on experiment type + * Example form * + * @witdh + * @height */ -function ExperimentHeaderForm(args) { +function SuperpositionForm(args) { this.id = BUI.id(); - this.backgroundColor = '#FFFFFF'; -} + this.width = null; + this.height = null; -ExperimentHeaderForm.prototype.getHTMLSource = function(experiment) { - var html = BUI.createFormLabel("Name :", experiment.json.name, 75, 400, this.backgroundColor); - if (experiment.json.type == "HPLC") { - if (experiment.getHPLCMacromolecule() != null){ - html = html + BUI.createFormLabel("Molecule :", experiment.getHPLCMacromolecule().acronym, 75, 400, this.backgroundColor); + if (args != null) { + if (args.width != null) { + this.width = args.width; + } + if (args.height != null) { + this.height = args.height; } } - else{ - html = html + BUI.createFormLabel("Type :", experiment.json.type, 75, 400, this.backgroundColor); - } - - html = html + BUI.createFormLabel("Date :", experiment.json.creationDate, 75, 400, this.backgroundColor); - return html; -}; -ExperimentHeaderForm.prototype.getHTMLDownload = function(experiment) { - var bgcolor = "background-color:" + this.backgroundColor + ";"; - var html = "
"; - if ((experiment.json.type == "CALIBATION") || (experiment.json.type == "STATIC")) { - html = html + " Download Source File
"; - html = html - + ""; - } - if (experiment.json.type == "TEMPLATE") { - html = html - + " Download Source File
"; - } + var _this = this; - if (experiment.json.type == "HPLC") { - html = html + " Download h5 File
"; - } + /** Widgets **/ + this.superpositionGrid = new SuperpositionGrid({ + width : null, + height : 200 + }); - return html; -}; + this.superpositionGrid.onSelected.attach(function(sender, superpositions) { + var ids = []; + for ( var i in superpositions) { + ids.push(superpositions[i].superpositionId); + } +// _this._renderAbinitio(ids); +// _this.aprioriPDBViewer.refresh(); + _this._renderAligned(ids); + // getAlignedPDBContentBySuperpositionList + }); -ExperimentHeaderForm.prototype.getTopPanel = function(experiment) { - return { - xtype : 'container', - layout : 'hbox', - items : [ { - margin : "0 0 0 0", - width : 475, - border : 0, - html : this.getHTMLSource(experiment) - }, { - margin : "0 0 0 0", - width : 475, - border : 0, - html : this.getHTMLDownload(experiment) - } ] - }; + /** PDB viewer **/ +// this.abinitioPDBViewer = new PDBViewer({ +// width : 860 / 2, +// height : 300 / 2, +// margin : 0 +// }); +// +// /** PDB viewer **/ +// this.aprioriPDBViewer = new StructurePDBViewer({ +// width : 860 / 2, +// height : 300 / 2, +// margin : 0 +// }); + + this.alignedPDBViewer = new AlignedSuperpositionPDBViewer({ + width : 860 , + height : 300 + }); + +} + +SuperpositionForm.prototype._renderAligned = function(modelsIdList) { + /** Trying to plot the PDB file **/ + try { + this.alignedPDBViewer.refresh(modelsIdList); + } catch (e) { + console.log(e); + } }; -ExperimentHeaderForm.prototype.getButton = function(experiment) { - var _this = this; - return Ext.create('Ext.Button', { - text : 'EDIT', - minWidth : '100', - margin : '10 0 0 30', - handler : function() { - var experimentWindow = new ExperimentWindow(); - experimentWindow.onSaved.attach(function(sender, data) { - _this.experiment.json.name = data.name; - _this.experiment.json.type = data.type; - _this.experiment.json.comments = data.comments; - _this.panel.remove(_this.panel.items.items[0]); - _this.panel.remove(_this.panel.items.items[0]); - _this.panel.insert(_this.getTopPanel(_this.experiment)); - _this.panel.insert(_this.getBottomPanel(_this.experiment)); +SuperpositionForm.prototype._renderAbinitio = function(modelsIdList) { + /** Trying to plot the PDB file **/ + try { + var viz = []; + for (var i = 0; i < modelsIdList.length; i++) { + viz.push({ + modelId : modelsIdList[i], + color : "0xFF6600", + opacity : 0.8 }); - experimentWindow.show(experiment); } - }); + this.abinitioPDBViewer.refresh(viz); + } catch (e) { + console.log(e); + } }; -ExperimentHeaderForm.prototype.getBottomPanel = function(experiment) { - return { +SuperpositionForm.prototype._getItems = function() { + var _this = this; + return [ { + xtype : 'label', + forId : 'myFieldId', + text : 'INLINE HELP: To be updated', + margin : '15 0 20 10', + cls : "inline-help" + }, this.superpositionGrid.getPanel(), { xtype : 'container', layout : 'hbox', - margin : '10 0 0 0', - items : [ this.getComments(experiment), this.getButton(experiment) ] - }; + items : [ +// { +// xtype : 'container', +// layout : 'vbox', +// items : [ this.abinitioPDBViewer.getPanel(), this.aprioriPDBViewer.getPanel() +// +// ] +// }, + + this.alignedPDBViewer.getPanel() ] + } ] + }; -ExperimentHeaderForm.prototype.getComments = function(experiment) { - return { - xtype : 'textareafield', - labelStyle : 'font-weight: bold;', - name : 'comments', - fieldLabel : 'Comments', - labelWidth : 70, - height : 40, - minWidth : '450', - readOnly : true, - value : experiment.json.comments - }; +SuperpositionForm.prototype._getButtons = function() { + return []; }; -ExperimentHeaderForm.prototype.getPanel = function(experiment) { - this.experiment = experiment; - - if (experiment.json.type == 'CALIBRATION') { - this.backgroundColor = '#EFFBFB'; - } - if (experiment.json.type == 'TEMPLATE') { - this.backgroundColor = '#E0F8E6'; - } +SuperpositionForm.prototype.getPanel = function() { + var _this = this; + this.panel = Ext.create('Ext.form.Panel', { + width : this.width, + height : this.height, + margin : 10, + border : 1, + defaultType : 'textfield', + items : this._getItems(), + buttons : this._getButtons(), + listeners : { + afterrender : function() { + _this._populate(); - this.panel = Ext.create('Ext.container.Container', { - frame : false, - layout : 'vbox', - padding : 5, - bodyPadding : '5 5 0 0', - width : 890, - margin : '0 0 10 0', - height : 120, - style : { - borderColor : '#99bce8', - borderStyle : 'solid', - borderWidth : '1px', - 'background-color' : this.backgroundColor - }, - fieldDefaults : { - msgTarget : 'side', - labelWidth : 100 - }, - items : [ this.getTopPanel(experiment), this.getBottomPanel(experiment) ] + } + } }); return this.panel; }; -ExperimentHeaderForm.prototype.input = function() { - return { - experiment : DATADOC.getExperiment_10() - }; +/** Populates could be call when the DOM is not filled yet **/ +SuperpositionForm.prototype._populate = function() { }; -ExperimentHeaderForm.prototype.test = function(targetId) { - var experimentHeaderForm = new ExperimentHeaderForm(); - var panel = experimentHeaderForm.getPanel(new Experiment(experimentHeaderForm.input().experiment)); - panel.render(targetId); +/** It populates the form * */ +SuperpositionForm.prototype.refresh = function(subtractions) { + this.subtractions = subtractions; + this.superpositionGrid.refresh(subtractions); +}; + +SuperpositionForm.prototype.input = function() { + return {}; +}; +/** It populates the form **/ +SuperpositionForm.prototype.test = function(targetId) { + var macromoleculeForm = new SuperpositionForm(); + var panel = macromoleculeForm.getPanel(); + panel.render(targetId); }; /** - * Macromolecule form with the general parameters of a macromolecule + * Example form * * @witdh * @height - * - * #onSave button save has been clicked - * #onClose button close has been clicked */ -function MacromoleculeForm(args) { +function AssemblyForm(args) { this.id = BUI.id(); this.width = 700; this.height = 500; @@ -7053,6751 +6059,7745 @@ function MacromoleculeForm(args) { this.height = args.height; } } - - /** Events **/ - this.onSave = new Event(this); - this.onClose = new Event(this); -} -/** Type : is the Ext type then requiredtext or textfield * */ -MacromoleculeForm.prototype._getFieldTextWithHelp = function(type, fieldLabel, fieldName, help) { - return Ext.create('Ext.container.Container', { - margin : "10 0 0 10", - items : [ { - xtype : type, - fieldLabel : fieldLabel, - name : fieldName, - id : this.id + fieldName - }, { - xtype : 'label', - forId : 'myFieldId', - text : help, - margin : "5 0 0 105", - cls : "inline-help" - } ] - }); -}; + this.molarityGrid = new MolarityGrid({height : this.height - 50}); +} -MacromoleculeForm.prototype._getNumericWithHelp = function(type, fieldLabel, fieldName, help) { - return Ext.create('Ext.container.Container', { - margin : "0 0 0 10", - items : [ { - xtype : type, - fieldLabel : fieldLabel, - name : fieldName, - id : this.id + fieldName, - decimalPrecision : 6, - width : 220 - }, { - xtype : 'label', - forId : 'myFieldId', - text : help, - margin : "5 0 0 10", - cls : "inline-help" - } ] - }); +AssemblyForm.prototype._getItems = function() { + return [ { + xtype : 'label', + forId : 'myFieldId', + text : 'List of previously defined macromolecules present in the assembly. This information will be used for additional cross-checks where possible', + margin : '15 0 20 10', + cls : "inline-help" + }, this.molarityGrid.getPanel() ]; }; -MacromoleculeForm.prototype._getButtons = function() { - var _this = this; - return [ { - text : 'Save', - handler : function() { - _this._save(); - } - },{ - text : 'Close', - handler : function() { - _this.onClose.notify(); - - } - } ]; +AssemblyForm.prototype._getButtons = function() { + return []; }; -/** It persits the macromolecule in the database **/ -MacromoleculeForm.prototype._persist = function(macromoleculeId, acronym, name, molecularMass, extintionCoefficient, comments, refractiveIndex, solventViscosity) { - - /** Checking not duplicated acronym **/ - if (((macromoleculeId == null) && (BIOSAXS.proposal.getMacromoleculeByAcronym(acronym) != null))== true){ - BUI.showError("Duplicated acronym"); - return; - } - - - if (macromoleculeId == null){ - /** new macromolecule **/ - this.macromolecule = {}; - this.macromolecule.macromoleculeId = null; - } - else{ - this.macromolecule.macromoleculeId = macromoleculeId; - } - - this.macromolecule["acronym"] = acronym; - this.macromolecule["name"] = name; - this.macromolecule["molecularMass"] = molecularMass; - this.macromolecule["extintionCoefficient"] = extintionCoefficient; - this.macromolecule["comments"] = comments; - this.macromolecule["symmetry"] = Ext.getCmp(this.id + 'comboSym').getValue(); - this.macromolecule["refractiveIndex"] = refractiveIndex; - this.macromolecule["solventViscosity"] = solventViscosity; - - var _this = this; - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, proposal) { - /** Updating the proposal global object **/ - BIOSAXS.proposal.setItems(proposal); - _this.panel.setLoading(false); - - var saved = BIOSAXS.proposal.getMacromoleculeByAcronym(acronym); - /** Refreshing to check that everything is ok **/ - _this.refresh(saved); - _this.onSave.notify(saved); +AssemblyForm.prototype.getPanel = function() { + this.panel = Ext.create('Ext.form.Panel', { + width : this.width, + height : this.height, + margin : 10, + border : 0, + defaultType : 'textfield', + items : this._getItems(), + buttons : this._getButtons() }); - - this.panel.setLoading("Saving Macromolecule") - adapter.saveMacromolecule(this.macromolecule); + return this.panel; }; -/** Save the macromolecule in the DB **/ -MacromoleculeForm.prototype._save = function() { - - var _this = this; - - var acronym = this._getField("acronym"); - var name = this._getField("name"); - var molecularMass = this._getField("molecularMass"); - var extintionCoefficient = this._getField("extintionCoefficient"); - var comments = this._getField("comments"); - - var refractiveIndex = this._getField("refractiveIndex"); - var solventViscosity = this._getField("solventViscosity"); - - /** Checking required fields **/ - if (name == "") { - BUI.showError("Name field is mandatory"); - return; - } - if (acronym == "") { - BUI.showError("Acroynm field is mandatory"); - return; - } - if (this.macromolecule != null){ - /** Checking if it is a new macromolecule **/ - if (this.macromolecule.macromoleculeId == null){ - /** Check if the acronym exists already **/ - this._persist(null, acronym, name, molecularMass, extintionCoefficient, comments, refractiveIndex, solventViscosity); - } - else{ - /** It is an update **/ - this._persist(this.macromolecule.macromoleculeId, acronym, name, molecularMass, extintionCoefficient, comments, refractiveIndex, solventViscosity); - } - } - else{ - /** It is a new macromolecule **/ - this._persist(null, acronym, name, molecularMass, extintionCoefficient, comments, refractiveIndex, solventViscosity); - } +/** It populates the form **/ +AssemblyForm.prototype.refresh = function(macromolecule) { + this.macromolecule = macromolecule; + this.molarityGrid.refresh(macromolecule); }; +AssemblyForm.prototype.input = function() { + return {}; +}; -MacromoleculeForm.prototype._getItems = function() { +AssemblyForm.prototype.test = function(targetId) { + var assemblyForm = new AssemblyForm(); + + var panel = assemblyForm.getPanel(); + panel.render(targetId); +}; +/** + * Edit the information of a buffer + * + * #onSaved + * #onRemoveAdditive + */ +function BufferForm() { var _this = this; - /** Symmetry combo box **/ - var symmetry = Ext.create('Ext.data.Store', { - fields : [ 's' ], - data : this._getSymmetries() - }); - this.symmetryComboBox = Ext.create('Ext.form.ComboBox', { - fieldLabel : 'Symmetry', - store : symmetry, - id : this.id + 'comboSym', - queryMode : 'local', - displayField : 's', - valueField : 's', - value : "P1", - margin : "0 0 0 30", - width : 220 + this.additiveGrid = new AdditiveGrid(); + this.additiveGrid.onRemoveButtonClicked.attach(function(sender, args) { + _this.onRemoveAdditive.notify(args); }); - - return [this._getFieldTextWithHelp("requiredtext", "Name", "name", "Long name. i.e: Bovine serum albumin"), - this._getFieldTextWithHelp("requiredtext", "Acronym", "acronym", "Acronym will be used in the files and analisys. i.e: BSA"), - this._getFieldTextWithHelp("textfield", "Mol. Mass (Da)", "molecularMass", "Atomic mass estimation measured in Da"), - { - xtype : 'container', - layout : 'hbox', - margin : "10 0 0 0", - items :[ - this._getNumericWithHelp("numberfield", "Extinction coef.", "extintionCoefficient", ""), - this.symmetryComboBox - ] - }, - { - xtype : 'container', - layout : 'hbox', - margin : "5 0 0 0", - items :[ - this._getNumericWithHelp("numberfield", "Refractive Index", "refractiveIndex", "How radiation propagates through the medium"), - this._getNumericWithHelp("numberfield", "Solvent Viscosity", "solventViscosity", "") - ] - }, - { - id : this.id + "comments", - xtype : 'textareafield', - name : 'comments', - margin : '35 0 0 10', - fieldLabel : 'Comments', - width : this.width - 100 - } ]; + + this.onSaved = new Event(this); + this.onRemoveAdditive = new Event(this); +} + +BufferForm.prototype.getBuffer = function() { + this.buffer.name = Ext.getCmp("buffer_name").getValue(); + this.buffer.acronym = Ext.getCmp("buffer_acronym").getValue(); + this.buffer.comments = Ext.getCmp("buffer_comments").getValue(); + this.buffer.ph = Ext.getCmp("buffer_ph").getValue(); + this.buffer.composition = Ext.getCmp("buffer_composition").getValue(); + this.buffer.bufferhasadditive3VOs = this.additiveGrid.getAdditives(); + return this.buffer; }; -MacromoleculeForm.prototype._getSymmetries = function() { - return [ { - "s" : "P1" - }, { - "s" : "P2" - }, { - "s" : "P3" - }, { - "s" : "P4" - }, { - "s" : "P5" - }, { - "s" : "P6" - }, { - "s" : "P32" - }, { - "s" : "P42" - }, { - "s" : "P52" - }, { - "s" : "P62" - }, { - "s" : "P222" - } ] +BufferForm.prototype._getTopPanel = function() { + return { + xtype : 'container', + layout : 'hbox', + border : 0, + frame : true, + items : [ { + xtype : 'container', + layout : 'hbox', + items : [ { + xtype : 'container', + flex : 1, + border : false, + layout : 'anchor', + defaultType : 'textfield', + items : [ { + xtype : 'requiredtext', + id : 'buffer_name', + fieldLabel : 'Name', + name : 'name', + width : '200px', + value : this.buffer.name + }, { + xtype : 'requiredtext', + id : 'buffer_acronym', + fieldLabel : 'Acronym', + name : 'acronym', + width : '200px', + value : this.buffer.acronym + } ] + } ] + }, { + xtype : 'container', + flex : 1, + layout : 'anchor', + defaultType : 'textfield', + margin : '0 0 0 10', + items : [ { + id : 'buffer_ph', + fieldLabel : 'pH', + name : 'ph', + value : this.buffer.ph, + xtype : 'numberfield', + width : 200, + minValue : 0, + maxValue : 15 + }, { + xtype : 'requiredtext', + id : 'buffer_composition', + fieldLabel : 'Composition', + name : 'composition', + width : 200, + value : this.buffer.composition + } ] + } ] + }; }; -MacromoleculeForm.prototype.getPanel = function() { - this.panel = Ext.create('Ext.form.Panel', { - width : this.width, - height : this.height, - margin : 10, - border : 1, - defaultType : 'textfield', - items : this._getItems(), - buttons : this._getButtons() +BufferForm.prototype.getPanel = function(buffer) { + this.buffer = buffer; + this.panel = Ext.createWidget({ + xtype : 'container', + layout : 'vbox', + style : { + padding : '10px' + }, + fieldDefaults : { + labelAlign : 'left', + labelWidth : 50 + + }, + items : [ this._getTopPanel(), { + id : 'buffer_comments', + xtype : 'textareafield', + name : 'comments', + fieldLabel : 'Comments', + width : '100%', + value : buffer.comments + }, this.additiveGrid.getPanel(buffer) ] }); return this.panel; }; - -/** Populates each text field by field name and value **/ -MacromoleculeForm.prototype._populateField = function(fieldName, value) { - if (value != null){ - Ext.getCmp(this.id + fieldName).setValue(value); - } +BufferForm.prototype.input = function() { + return { + buffer : { + "bufferId" : 422, + "proposalId" : 10, + "safetyLevelId" : null, + "name" : "B1", + "acronym" : "B1", + "ph" : null, + "composition" : null, + "bufferhasadditive3VOs" : [], + "comments" : null + } + }; }; -/** Gets the value of a textfield **/ -MacromoleculeForm.prototype._getField = function(fieldName) { - return Ext.getCmp(this.id + fieldName).getValue(); +BufferForm.prototype.test = function(targetId) { + var bufferForm = new BufferForm(); + var panel = bufferForm.getPanel(bufferForm.input().buffer); + panel.render(targetId); }; - - -/** It populates the form **/ -MacromoleculeForm.prototype.refresh = function(macromolecule) { - this.macromolecule = macromolecule; - if (macromolecule != null){ - this._populateField("name", macromolecule.name); - this._populateField("acronym", macromolecule.acronym); - this._populateField("extintionCoefficient", macromolecule.extintionCoefficient); - this._populateField("molecularMass", macromolecule.molecularMass); - this._populateField("comments", macromolecule.comments); - this._populateField("refractiveIndex", macromolecule.refractiveIndex); - this._populateField("solventViscosity", macromolecule.solventViscosity); - if (macromolecule.symmetry != null){ - Ext.getCmp(this.id + 'comboSym').setValue(macromolecule.symmetry); + +/** + * @showTitle + * + * #onSaved + * #onAddPlates + * #onRemovePlates + **/ +function CaseForm(args) { + this.width = 700; + this.showTitle = true; + if (args != null) { + if (args.showTitle != null) { + this.showTitle = args.showTitle; } } -}; - -MacromoleculeForm.prototype.input = function() { - return {}; -}; - - -/** It populates the form **/ -MacromoleculeForm.prototype.test = function(targetId) { - var macromoleculeForm = new MacromoleculeForm(); - macromoleculeForm.onClose.attach(function(sender){ - alert("Click on close"); + var _this = this; + this.stockSolutionGrid = new StockSolutionGrid({ + width : this.width - 10, + minHeight : 300, + height : 300, + tbar : true, + showTitle : true, + isPackedVisible : false, + btnAddExisting : true, + btnRemoveVisible : false, + btnUnpackVisible : true }); - var panel = macromoleculeForm.getPanel(); - panel.render(targetId); -}; + /** When selecting existing stock solutions **/ + this.stockSolutionGrid.onStockSolutionSelected.attach(function(sender, stockSolutions) { + if (stockSolutions != null) { + for ( var i = 0; i < stockSolutions.length; i++) { + _this.saveStockSolution(stockSolutions[i]); + } + } + }); - + /** it can be because it has been added a new one or removed **/ + this.stockSolutionGrid.onProposalChanged.attach(function(sender, stockSolution) { + if (stockSolution != null) { + _this.saveStockSolution(stockSolution); + } else { + BIOSAXS.proposal.onInitialized.attach(function() { + _this.stockSolutionGrid.refresh(BIOSAXS.proposal.getStockSolutionsByDewarId(_this.dewar.dewarId)); + _this.stockSolutionGrid.grid.setLoading(false); + }); + BIOSAXS.proposal.init(); -function ModelVisualizerForm(args){ - this.id =BUI.id(); - this.width = 600; - this.height = 400; - if (args!= null){ - if (args.width != null){ - this.width = args.width; - } - if (args.height != null){ - this.height = args.height; } - } -}; -ModelVisualizerForm.prototype._getFirHTML = function(modelId, width, height, type, desc) { - var html = ""; - html = html + ''; - html = html + ''; - html = html + ''; - html = html + '
dammin.' + type + '
' + desc + '
'; - return html; -}; - -ModelVisualizerForm.prototype.getItems = function(modelPanel){ - _this = this; - var height = _this.height/2 - 60; - var width = _this.width/2 - 10; - - return Ext.create('Ext.container.Container', { - layout: { - type: 'vbox', // Arrange child items vertically - }, - items: [ - modelPanel, - { - xtype : 'container', - layout: { - type: 'hbox', // Arrange child items vertically - }, - items : [{ - html : this._getFirHTML("id", width, height, "fir", "Fit of the simulated scattering curve versus a smoothed experimental data (spline interpolation)"), - height :height, - width : width, - padding: 2 - }, - { - html : this._getFirHTML("id", width, height, "fit", "Fit of the simulated scattering curve versus the experimental data."), - height : height, - width : width, - padding: 2 - }] - } - - ] }); -}; -ModelVisualizerForm.prototype.refresh = function(models){ - var input = []; -// var colors = ["008000", "F0A804", "0000FF", "800080", "C0C0C0"]; - for (var i = 0; i < models.length; i++) { - console.log(BUI.rainbow(models.length, i).replace("#", "0x")); - input.push({ - color: BUI.rainbow(models.length, i).replace("#", "0x"), - modelId: models[i].modelId, - opacity: 0.8, - radius: 3, - title: BUI.getFileNameByPath(models[i].pdbFile), - type: "SHAPEDETERMINATIONMODEL" - - }); - } - - this.panel.removeAll(); - this.panel.add( - _this.getItems( - new PDBViewer({ - width : this.width - 10, - height : (this.height/2) - 10, - title : "" - }).draw(input) - ) - ); - + this.onSaved = new Event(this); + this.onAddPlates = new Event(this); + this.onRemovePlates = new Event(this); +} + +CaseForm.prototype.saveStockSolution = function(stockSolution) { + var _this = this; var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, data) { - var splitted = data.toString().split("\n"); - var array = []; - for ( var i = 0; i < splitted.length; i++) { - var line = splitted[i].trim(); - var line_splited = line.split(/\s*[\s,]\s*/); - if (line_splited.length == 4) { - array.push([ Number(line_splited[0]), BUI.getStvArray(line_splited[1], line_splited[2]), BUI.getStvArray(line_splited[3], 0) ]); - } - } - - var id = (_this.id + "firid"); - var dygraphWidget = new StdDevDyGraph(id, { - width : (_this.width/2) - 10, - height :(_this.height/2) -110, - xlabel : 'q(nm-1)' + this.stockSolutionGrid.grid.setLoading("ISPyB: setting case to Stock solution"); + adapter.onSuccess.attach(function(sender, stockSolution) { + BIOSAXS.proposal.onInitialized.attach(function() { + _this.stockSolutionGrid.refresh(BIOSAXS.proposal.getStockSolutionsByDewarId(_this.dewar.dewarId)); + _this.stockSolutionGrid.grid.setLoading(false); }); - dygraphWidget.draw(array, [ "#FF0000", "#0000FF", "#FF00FF" ], [ 's', 'I(exp)', 'I(sim)' ]); + BIOSAXS.proposal.init(); }); - - adapter.getModelFile("FIR", models[0].modelId); - - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, data) { - var splitted = data.toString().split("\n"); - var array = []; - for ( var i = 0; i < splitted.length; i++) { - var line = splitted[i].trim(); - var line_splited = line.split(/\s*[\s,]\s*/); - if (line_splited.length == 3) { - array.push([ Number(line_splited[0]), BUI.getStvArray(line_splited[1], 0), BUI.getStvArray(line_splited[2], 0) ]); - } - } - - var id = (_this.id + "fitid"); - - var dygraphWidget = new StdDevDyGraph(id, { - width : (_this.width/2) - 10, - height : (_this.height/2) - 110, - xlabel : 'q(nm-1)' - }); - dygraphWidget.draw(array, [ "#FF0000", "#0000FF" ], [ "s", "I(exp)", "I(sim)" ]); + adapter.onError.attach(function(sender, data) { + _this.stockSolutionGrid.grid.setLoading(false); + BUI.showError(data); }); - adapter.getModelFile("FIT", models[0].modelId); + stockSolution.boxId = _this.dewar.dewarId; + adapter.saveStockSolution(stockSolution); }; -ModelVisualizerForm.prototype.getPanel = function(modelList){ - _this = this; - this.modelList = modelList; - this.panel = Ext.create('Ext.Panel', { - title: 'Results', - width: this.width, - height: this.height, - layout: { - type: 'vbox', // Arrange child items vertically -// align: 'stretch' // Each takes up full width - }, - items: [ - - ], - listeners : { - afterrender : function(grid, eOpts) { -// alert(_this.modelList) - } - } +CaseForm.prototype.fillStores = function() { + var _this = this; + this.panel.setLoading("Loading Labcontacts from database"); + + var proposal = BUI.getProposal(); + proposal.onDataRetrieved.attach(function(sender, data) { + _this.labContactForSendingStore.loadData(data, false); + _this.labContactForReturnStore.loadData(data, false); + _this.panel.setLoading(false); }); - - return this.panel; + proposal.getLabContactsByProposalId(); }; +CaseForm.prototype.refresh = function(dewar) { + this.setDewar(dewar); + this.stockSolutionGrid.refresh(BIOSAXS.proposal.getStockSolutionsByDewarId(dewar.dewarId)); +}; + +CaseForm.prototype.getDewar = function() { + this.dewar.code = Ext.getCmp("dewar_code").getValue(); + this.dewar.comments = Ext.getCmp("dewar_comments").getValue(); + this.dewar.trackingNumberFromSynchrotron = Ext.getCmp("dewar_trackingNumberFromSynchrotron").getValue(); + this.dewar.trackingNumberToSynchrotron = Ext.getCmp("dewar_trackingNumberToSynchrotron").getValue(); + this.dewar.transportValue = Ext.getCmp("dewar_transportValue").getValue(); + this.dewar.storageLocation = Ext.getCmp("dewar_storageLocation").getValue(); + this.dewar.firstExperimentId = this.macromoleculeCombo.getValue(); + return this.dewar; +}; + +CaseForm.prototype.setDewar = function(dewar) { + this.dewar = dewar; + Ext.getCmp("dewar_code").setValue(this.dewar.code); + Ext.getCmp("dewar_dewarStatus").setText(new String(this.dewar.dewarStatus).toUpperCase()); + Ext.getCmp("dewar_comments").setValue(this.dewar.comments); + Ext.getCmp("dewar_trackingNumberFromSynchrotron").setValue(this.dewar.trackingNumberFromSynchrotron); + Ext.getCmp("dewar_trackingNumberToSynchrotron").setValue(this.dewar.trackingNumberToSynchrotron); + Ext.getCmp("dewar_transportValue").setValue(this.dewar.transportValue); + Ext.getCmp("dewar_storageLocation").setValue(this.dewar.storageLocation); + if (dewar.sessionVO != null) { + this.macromoleculeCombo.setValue(dewar.sessionVO.sessionId); + } +}; + +CaseForm.prototype.getSessionCombo = function() { + this.macromoleculeCombo = BIOSAXS_COMBOMANAGER.getComboSessions(BIOSAXS.proposal.getSessions(), { + labelWidth : 100, + margin : '5 0 00 0', + width : 250 + }); + return this.macromoleculeCombo; +}; + +CaseForm.prototype.getInformationPanel = function() { + if (this.panel == null) { + this.informationPanel = Ext.create('Ext.form.Panel', { + width : this.width - 10, + border : 0, + items : [ { + xtype : 'container', + margin : "2 2 2 2", + collapsible : false, + defaultType : 'textfield', + layout : 'anchor', + items : [ { + xtype : 'container', + layout : 'hbox', + items : [ { + xtype : 'requiredtext', + fieldLabel : 'Code', + allowBlank : false, + name : 'code', + id : 'dewar_code', + anchor : '50%' + }, { + xtype : 'label', + margin : '0 0 0 20', + readOnly : true, + id : 'dewar_dewarStatus', + anchor : '50%' + } ] + }, this.getSessionCombo(), { + margin : '5 0 0 0', + xtype : 'textareafield', + name : 'comments', + id : 'dewar_comments', + width : this.width - 50, + fieldLabel : 'Comments' + } ] + }, { + xtype : 'fieldset', + title : 'Courier Accounts Details for Return', + collapsible : false, + layout : 'anchor', + defaults : { + anchor : '100%' + }, + items : [ { + xtype : 'container', + layout : 'hbox', + items : [ { + xtype : 'textfield', + labelWidth : 200, + width : 280, + fieldLabel : 'Track Number From Synchrotron', + id : 'dewar_trackingNumberFromSynchrotron' + }, { + xtype : 'numberfield', + width : 190, + labelWidth : 110, + margin : '0 0 0 30', + fieldLabel : 'Transport Value', + id : 'dewar_transportValue' + } + + ] + }, { + xtype : 'container', + layout : 'hbox', + margin : '10 0 0 0', + items : [ + + { + xtype : 'textfield', + labelWidth : 200, + width : 280, + fieldLabel : 'Track Number To Synchrotron', + id : 'dewar_trackingNumberToSynchrotron' + }, { + xtype : 'textfield', + margin : '0 0 0 30', + width : 190, + labelWidth : 110, + fieldLabel : 'Storage Location', + id : 'dewar_storageLocation' + } + + ] + } ] + } ] + }); + } + + return this.informationPanel; +}; + +CaseForm.prototype.getPanel = function(dewar) { + this.dewar = dewar; + this.panel = Ext.createWidget({ + xtype : 'container', + layout : 'vbox', + width : this.width, + border : 0, + items : [ { + items : { + xtype : "container", + layout : "vbox", + margin : "5 5 5 5", + items : [ this.getInformationPanel(dewar), this.stockSolutionGrid.getPanel() ] + + } + } ] + }); + + this.refresh(dewar); + return this.panel; + +}; + +CaseForm.prototype.input = function() { + return { + dewar : { + "dewarId" : 305861, + "code" : "ESRF-TEST", + "comments" : "comments", + "storageLocation" : "FRIDGE", + "dewarStatus" : "opened", + "timeStamp" : null, + "isStorageDewar" : null, + "barCode" : "ESRF305861", + "customsValue" : null, + "transportValue" : null, + "trackingNumberToSynchrotron" : "3333", + "trackingNumberFromSynchrotron" : "224466", + "type" : "Dewar", + "sessionVO" : { + "sessionId" : 31697, + "expSessionPk" : null, + "projectCode" : null, + "startDate" : "2012 07 21", + "endDate" : "2012 07 23", + "beamlineName" : "BM29", + "scheduled" : 1, + "nbShifts" : 2, + "comments" : null, + "beamlineOperator" : "PERNOT P", + "usedFlag" : null, + "sessionTitle" : null, + "structureDeterminations" : null, + "dewarTransport" : null, + "databackupFrance" : null, + "databackupEurope" : null, + "visit_number" : null, + "operatorSiteNumber" : "14061", + "timeStamp" : "2012 04 25" + } + }, + proposal : { + "assemblies" : [], + "sessions" : [ { + "sessionId" : 31697, + "expSessionPk" : null, + "projectCode" : null, + "startDate" : "2012 07 21", + "endDate" : "2012 07 23", + "beamlineName" : "BM29", + "scheduled" : 1, + "nbShifts" : 2, + "comments" : null, + "beamlineOperator" : "PERNOT P", + "usedFlag" : null, + "sessionTitle" : null, + "structureDeterminations" : null, + "dewarTransport" : null, + "databackupFrance" : null, + "databackupEurope" : null, + "visit_number" : null, + "operatorSiteNumber" : "14061", + "timeStamp" : "2012 04 25" + } ], + "labcontacts" : [ { + "labContactId" : 787, + "personVO" : { + "personId" : 304252, + "personUUID" : null, + "familyName" : "KIM", + "givenName" : "Henry", + "title" : null, + "emailAddress" : "henry-sung.kim@ibs.fr", + "phoneNumber" : "", + "login" : "", + "passwd" : "", + "faxNumber" : "" + }, + "cardName" : "KIM-Institut de Bio", + "defaultCourrierCompany" : "22", + "courierAccount" : "", + "billingReference" : "", + "dewarAvgCustomsValue" : 0, + "dewarAvgTransportValue" : 0 + } ], + "buffers" : [ { + "bufferId" : 811, + "proposalId" : 3124, + "safetyLevelId" : null, + "name" : "EDTA", + "acronym" : "EDTA", + "ph" : null, + "composition" : "", + "bufferhasadditive3VOs" : [], + "comments" : "" + }, { + "bufferId" : 810, + "proposalId" : 3124, + "safetyLevelId" : null, + "name" : "HEPES", + "acronym" : "HEPES", + "ph" : null, + "composition" : "", + "bufferhasadditive3VOs" : [], + "comments" : "" + } ], + "shippings" : [ { + "shippingId" : 304107, + "shippingName" : "TEST", + "deliveryAgentAgentName" : null, + "deliveryAgentShippingDate" : null, + "deliveryAgentDeliveryDate" : null, + "deliveryAgentAgentCode" : null, + "deliveryAgentFlightCode" : null, + "shippingStatus" : "opened", + "timeStamp" : "2013 09 25", + "laboratoryId" : null, + "isStorageShipping" : null, + "creationDate" : "2013 09 25", + "comments" : "test", + "sendingLabContactVO" : { + "labContactId" : 787, + "personVO" : { + "personId" : 304252, + "personUUID" : null, + "familyName" : "KIM", + "givenName" : "Henry", + "title" : null, + "emailAddress" : "henry-sung.kim@ibs.fr", + "phoneNumber" : "", + "login" : "", + "passwd" : "", + "faxNumber" : "" + }, + "cardName" : "KIM-Institut de Bio", + "defaultCourrierCompany" : "22", + "courierAccount" : "", + "billingReference" : "", + "dewarAvgCustomsValue" : 0, + "dewarAvgTransportValue" : 0 + }, + "returnLabContactVO" : { + "labContactId" : 787, + "personVO" : { + "personId" : 304252, + "personUUID" : null, + "familyName" : "KIM", + "givenName" : "Henry", + "title" : null, + "emailAddress" : "henry-sung.kim@ibs.fr", + "phoneNumber" : "", + "login" : "", + "passwd" : "", + "faxNumber" : "" + }, + "cardName" : "KIM-Institut de Bio", + "defaultCourrierCompany" : "22", + "courierAccount" : "", + "billingReference" : "", + "dewarAvgCustomsValue" : 0, + "dewarAvgTransportValue" : 0 + }, + "returnCourier" : null, + "dateOfShippingToUser" : null, + "shippingType" : "DewarTracking", + "dewarVOs" : [ { + "dewarId" : 305861, + "code" : "ESRF-TEST", + "comments" : "comments", + "storageLocation" : "FRIDGE", + "dewarStatus" : "opened", + "timeStamp" : null, + "isStorageDewar" : null, + "barCode" : "ESRF305861", + "customsValue" : null, + "transportValue" : null, + "trackingNumberToSynchrotron" : "3333", + "trackingNumberFromSynchrotron" : "224466", + "type" : "Dewar", + "sessionVO" : { + "sessionId" : 31697, + "expSessionPk" : null, + "projectCode" : null, + "startDate" : "2012 07 21", + "endDate" : "2012 07 23", + "beamlineName" : "BM29", + "scheduled" : 1, + "nbShifts" : 2, + "comments" : null, + "beamlineOperator" : "PERNOT P", + "usedFlag" : null, + "sessionTitle" : null, + "structureDeterminations" : null, + "dewarTransport" : null, + "databackupFrance" : null, + "databackupEurope" : null, + "visit_number" : null, + "operatorSiteNumber" : "14061", + "timeStamp" : "2012 04 25" + } + } ] + } ], + "macromolecules" : [ { + "macromoleculeId" : 5933, + "safetylevelId" : null, + "proposalId" : 3124, + "name" : "A", + "acronym" : "A", + "molecularMass" : "", + "extintionCoefficient" : "", + "sequence" : null, + "creationDate" : null, + "comments" : "", + "macromoleculeregion3VOs" : [], + "stoichiometry3VOsForHostMacromoleculeId" : [], + "structure3VOs" : [] + } ], + "stockSolutions" : [ { + "stockSolutionId" : 6, + "proposalId" : 3124, + "macromoleculeId" : 5933, + "bufferId" : 811, + "instructionSet3VO" : null, + "boxId" : 305861, + "storageTemperature" : "20", + "volume" : "300", + "concentration" : "1.2", + "comments" : "Buffer EDTA with A", + "name" : "A_EDTA_1.2", + "samples" : [] + } ] + } + + }; +}; + +CaseForm.prototype.test = function(targetId) { + var caseForm = new CaseForm(); + BIOSAXS.proposal = new Proposal(caseForm.input().proposal); + var panel = caseForm.getPanel(caseForm.input().dewar); + panel.render(targetId); +}; -/** - * Example form - * - * @witdh - * @height - */ -function MolarityForm(args) { - this.id = BUI.id(); - this.width = 700; - this.height = 500; - - if (args != null) { - if (args.width != null) { - this.width = args.width; - } - if (args.height != null) { - this.height = args.height; - } - } - - this.onSave = new Event(this); - this.onClose = new Event(this); -} - - -MolarityForm.prototype._getNumericWithHelp = function(type, fieldLabel, fieldName, help) { - return Ext.create('Ext.container.Container', { - margin : "10 0 0 10", - items : [ { - xtype : type, - fieldLabel : fieldLabel, - name : fieldName, - id : this.id + fieldName, - decimalPrecision : 6 - }, { - xtype : 'label', - forId : 'myFieldId', - text : help, - margin : "5 0 0 105", - cls : "inline-help" - } ] - }); -}; - - -MolarityForm.prototype._getItems = function() { - this.macromoleculeCombo = BIOSAXS_COMBOMANAGER.getComboMacromoleculeByMacromolecules(this.getMacromuleculesCandidates(this.macromolecule), { - width : 250, - labelWidth : 100, - margin : 10 - }); - - return [ { - xtype : 'container', - flex : 1, - margin : '10 0 0 10', - border : 0, - layout : 'anchor', - defaultType : 'requiredtext', - items : [ this.macromoleculeCombo, - this._getNumericWithHelp("textfield", "Ratio", "ratio", "Number in assymmetric units") - ] - } ]; -}; - -MolarityForm.prototype._persist = function() { - var _this = this; - var macromoleculeId = this.macromoleculeCombo.getValue(); - var ratio = Ext.getCmp(this.id + "ratio").getValue(); - var comments = "Not used yet"; - var dataAdapter = new BiosaxsDataAdapter(); - this.panel.setLoading("Saving"); - dataAdapter.onSuccess.attach(function(sender, args) { - _this.onSave.notify(); - }); - dataAdapter.saveStoichiometry(this.macromolecule.macromoleculeId, macromoleculeId, ratio, comments); -}; - -MolarityForm.prototype._getButtons = function() { - var _this = this; - - function onClose() { - _this.onClose.notify(); - } - - return [ { - text : 'Save', - handler : function() { - _this._persist(); - } - }, { - text : 'Cancel', - handler : function() { - onClose(); - } - } ]; -}; - -MolarityForm.prototype.getPanel = function() { - this.panel = Ext.create('Ext.form.Panel', { -// width : null, - height : this.height, - margin : 2, - border : 1, - defaultType : 'requiredtext', - items : this._getItems(), - buttons : this._getButtons() - }); - return this.panel; -}; - -/** macromolecules contains all macromolecules except this one **/ -MolarityForm.prototype.getMacromuleculesCandidates = function(macromolecule) { - var macromolecules = []; - if ( BIOSAXS.proposal.macromolecules){ - for (var i = 0; i < BIOSAXS.proposal.macromolecules.length; i++) { - var m = BIOSAXS.proposal.macromolecules[i]; - if (this.macromolecule != null){ - if (m.macromoleculeId != this.macromolecule.macromoleculeId) { - macromolecules.push(m); - } - } - } - } - return macromolecules; -}; - - -/** It populates the form **/ -MolarityForm.prototype.refresh = function(macromolecule) { - this.macromolecule = macromolecule; - -}; - - -MolarityForm.prototype.input = function() { - return {}; -}; - - -/** It populates the form **/ -MolarityForm.prototype.test = function(targetId) { - var macromoleculeForm = new MolarityForm(); - var panel = macromoleculeForm.getPanel(); - panel.render(targetId); -}; - -///** -// * -// * @witdh -// * @height -// */ -//function MacromoleculeForm(args) { -// this.id = BUI.id(); -// this.width = 700; -// this.height = 500; -// -// if (args != null) { -// if (args.width != null) { -// this.width = args.width; -// } -// if (args.height != null) { -// this.height = args.height; -// } -// } -// -// this.onClose = new Event(this); -//} -// -//MacromoleculeForm.prototype.getMacromolecule = function() { -// this.macromolecule.name = Ext.getCmp(this.panel.getItemId()).getValues().name; -// this.macromolecule.acronym = Ext.getCmp(this.panel.getItemId()).getValues().acronym; -// this.macromolecule.comments = Ext.getCmp(this.panel.getItemId()).getValues().comments; -// this.macromolecule.extintionCoefficient = Ext.getCmp(this.panel.getItemId()).getValues().extintionCoefficient; -// this.macromolecule.molecularMass = Ext.getCmp(this.panel.getItemId()).getValues().molecularMass; -// return this.macromolecule; -//}; -// -//MacromoleculeForm.prototype.setMacromolecule = function(macromolecule) { -// this.pdbStore.loadData(macromolecule.structure3VOs); -// -//}; -// -//MacromoleculeForm.prototype.getForm = function(macromolecule) { -// this.panel = Ext.createWidget('form', { -// frame : false, -// border : 0, -// padding : 15, -// width : 550, -// height : 350, -// items : [ { -// xtype : 'container', -// layout : 'hbox', -// items : [ { -// xtype : 'container', -// flex : 1, -// border : false, -// layout : 'anchor', -// defaultType : 'requiredtext', -// items : [ { -// fieldLabel : 'Name', -// name : 'name', -// anchor : '95%', -// tooltip : "Name of the macromolecule", -// value : macromolecule.name -// }, { -// fieldLabel : 'Acronym', -// name : 'acronym', -// anchor : '95%', -// value : macromolecule.acronym -// } ] -// }, { -// xtype : 'container', -// flex : 1, -// layout : 'anchor', -// defaultType : 'textfield', -// items : [ { -// xtype : 'numberfield', -// fieldLabel : 'Mol. Mass (Da)', -// name : 'molecularMass', -// value : macromolecule.molecularMass, -// decimalPrecision : 6 -// }, { -// xtype : 'numberfield', -// fieldLabel : 'Extinction coef.', -// name : 'extintionCoefficient', -// value : macromolecule.extintionCoefficient, -// decimalPrecision : 6 -// } ] -// } ] -// }, { -// xtype : 'textareafield', -// name : 'comments', -// fieldLabel : 'Comments', -// value : macromolecule.comments, -// width : this.width - 10 -// }, -// -// { -// html : BUI.getWarningHTML("The macromolecule is unsaved. Save the macromolecule to get access to other tabs"), -// margin : "150 10 10 10", -// id : this.id + "unsavedWarning", -// hidden : !(!macromolecule.macromoleculeId) -// } -// -// ] -// }); -// return this.panel; -//}; -// -//MacromoleculeForm.prototype.save = function() { -// var _this = this; -// -// var adapter = new BiosaxsDataAdapter(); -// adapter.onSuccess.attach(function(sender, proposal) { -// BIOSAXS.proposal.setItems(proposal); -// _this.panel.setLoading(false); -// -// Ext.getCmp(_this.id + "assembly").enable() -// Ext.getCmp(_this.id + "advanced").enable(); -// Ext.getCmp(_this.id + "unsavedWarning").hide(); -// }); -// -// if (this.getMacromolecule().name == "") { -// BUI.showError("Name field is mandatory"); -// return; -// } -// if (this.getMacromolecule().acronym == "") { -// BUI.showError("Acroynm field is mandatory"); -// return; -// } -// -// /** Check if acronym is unique * */ -// if (this.getMacromolecule().macromoleculeId == null) { -// if (BIOSAXS.proposal.getMacromoleculeByAcronym(this.getMacromolecule().acronym) == null) { -// this.panel.setLoading("ISPyB: Saving Macromolecule") -// adapter.saveMacromolecule(this.getMacromolecule()); -// } else { -// alert("There is already an existing macromolecule with the same acronym"); -// -// } -// } else { -// this.panel.setLoading("ISPyB: Saving Macromolecule") -// adapter.saveMacromolecule(this.getMacromolecule()); -// } -// -//}; -// -//MacromoleculeForm.prototype.getPanel = function(macromolecule) { -// var _this = this; -// this.macromolecule = macromolecule; -// return Ext.createWidget('tabpanel', { -// height : this.height, -// margin : 5, -// plain : true, -// style : { -// padding : 5 -// }, -// items : [ { -// tabConfig : { -// title : "General", -// disabled : false -// }, -// items : [ this.getForm(macromolecule) ], -// bbar : [ "->", { -// text : 'Save', -// cls : 'btn-with-border', -// style : { -// -// border : 1 -// }, -// handler : function() { -// _this.save(); -// } -// }, { -// text : 'Close', -// cls : 'btn-with-border', -// handler : function() { -// _this.onClose.notify(); -// } -// } ] -// }, { -// tabConfig : { -// id : this.id + "assembly", -// title : "Assembly", -// tooltip : 'Description of subunits present in the macromolecule', -// // hidden : (!macromolecule.macromoleculeId), -// disabled : (!macromolecule.macromoleculeId) -// }, -// items : [ this.getMolarityGrid(macromolecule) ] -// }, { -// tabConfig : { -// id : this.id + "advanced", -// title : "Advanced Modeling", -// // hidden : (!macromolecule.macromoleculeId), -// tooltip : 'Definition of the description contacts and symetries', -// disabled : (!macromolecule.macromoleculeId) -// }, -// items : [ this.getRigidBodyForm(macromolecule) ] -// } ] -// }); -//}; -// -//MacromoleculeForm.prototype.getRigidBodyForm = function(macromolecule) { -// var _this = this; -// -// // [P1, P2, P3, P4 ,P5 ,P6 ,32, P42, P52, P62] + P222 -// var symmetry = Ext.create('Ext.data.Store', { -// fields : [ 's' ], -// data : [ { -// "s" : "P1" -// }, { -// "s" : "P2" -// }, { -// "s" : "P3" -// }, { -// "s" : "P4" -// }, { -// "s" : "P5" -// }, { -// "s" : "P6" -// }, { -// "s" : "P32" -// }, { -// "s" : "P42" -// }, { -// "s" : "P52" -// }, { -// "s" : "P62" -// }, { -// "s" : "P222" -// } ] -// }); -// -// if (macromolecule.symmetry == null) { -// macromolecule.symmetry = "P1"; -// } -// var comboBox = Ext.create('Ext.form.ComboBox', { -// fieldLabel : 'Symmetry', -// store : symmetry, -// id : 'comboSym', -// queryMode : 'local', -// displayField : 's', -// valueField : 's', -// value : macromolecule.symmetry, -// margin : "10 0 0 0", -// listeners : { -// change : function(combo, newValue, oldValue, eOpts) { -// macromolecule.symmetry = newValue; -// } -// } -// }); -// -// this.rigidBodyPanel = Ext.createWidget('form', { -// frame : false, -// border : 0, -// padding : 10, -// width : 550, -// height : 400, -// items : [ { -// xtype : 'container', -// layout : 'hbox', -// items : [ { -// xtype : 'container', -// border : false, -// layout : 'hbox', -// items : [ { -// xtype : 'label', -// forId : 'myFieldId', -// text : 'Contact Desc:', -// width : 105, -// margin : '0 0 0 0' -// }, { -// xtype : 'textfield', -// hideLabel : true, -// id : "contactsDescriptionFilePath", -// margin : '0 0 0 0', -// width : 300, -// value : macromolecule.contactsDescriptionFilePath -// }, { -// text : 'Upload', -// xtype : 'button', -// margin : "0 0 0 20", -// width : 100, -// handler : function() { -// _this._openUploadDialog(macromolecule.macromoleculeId, "CONTACTS", "Upload Contact Description File"); -// } -// } ] -// } ] -// }, -// -// comboBox, { -// xtype : 'checkbox', -// margin : '10 0 0 5', -// boxLabel : "I want rigid body modeling run on this stuff", -// checked : true, -// width : 300 -// }, _this.getPDBGrid(macromolecule) ] -// }); -// return this.rigidBodyPanel; -//}; -// -//MacromoleculeForm.prototype.update = function() { -// var _this = this; -// BIOSAXS.proposal.onInitialized.attach(function() { -// if (BIOSAXS.proposal != null) { -// var macromolecules = BIOSAXS.proposal.macromolecules; -// for (var i = 0; i < macromolecules.length; i++) { -// if (macromolecules[i].macromoleculeId == _this.macromolecule.macromoleculeId) { -// _this.macromolecule = macromolecules[i]; -// _this.setMacromolecule(_this.macromolecule); -// _this.molarityStore.loadData(_this.parseMolarityData(_this.macromolecule)); -// _this.pdbGrid.setLoading(false); -// Ext.getCmp("contactsDescriptionFilePath").setValue(_this.macromolecule.contactsDescriptionFilePath) -// _this.molarityGrid.setLoading(false); -// -// } -// } -// } -// }); -// this.molarityGrid.setLoading("Updating"); -// this.pdbGrid.setLoading("Updating"); -// BIOSAXS.proposal.init(); -//}; -// -///******************************************************************************* -// * MOLARITY GRID -// ******************************************************************************/ -// -//MacromoleculeForm.prototype.parseMolarityData = function(macromolecule) { -// var data = []; -// if (macromolecule.stoichiometry != null) { -// for (var i = 0; i < macromolecule.stoichiometry.length; i++) { -// data.push({ -// ratio : macromolecule.stoichiometry[i].ratio, -// acronym : macromolecule.stoichiometry[i].macromolecule3VO.acronym, -// comments : macromolecule.stoichiometry[i].macromolecule3VO.comments, -// stoichiometryId : macromolecule.stoichiometry[i].stoichiometryId, -// name : macromolecule.stoichiometry[i].macromolecule3VO.name -// }); -// } -// } -// return data; -//}; -// -//MacromoleculeForm.prototype.getMolarityGrid = function(macromolecule) { -// var _this = this; -// -// this.molarityStore = Ext.create('Ext.data.Store', { -// fields : [ 'acronym', 'ratio', 'comments', 'stoichiometryId', 'name' ], -// data : this.parseMolarityData(macromolecule), -// sorters : { -// property : 'ratio', -// direction : 'DESC' -// } -// }); -// -// this.molarityGrid = Ext.create('Ext.grid.Panel', { -// store : this.molarityStore, -// height : 350, -// padding : 5, -// columns : [ -// -// { -// text : 'Subunit', -// columns : [ { -// text : "Acronym", -// width : 100, -// hidden : false, -// dataIndex : 'acronym', -// sortable : true -// }, { -// text : "Name", -// width : 100, -// hidden : false, -// dataIndex : 'name', -// sortable : true -// }, { -// text : "Comments", -// width : 100, -// dataIndex : 'comments', -// sortable : true -// } ] -// }, { -// text : "Number
in assymmetric units", -// width : 150, -// dataIndex : 'ratio', -// tooltip : 'Number of times this subunit is present in the macromolecule', -// sortable : true -// }, { -// id : this.id + 'MOLARITY_REMOVE', -// flex : 0.1, -// sortable : false, -// renderer : function(value, metaData, record, rowIndex, colIndex, store) { -// return BUI.getRedButton('REMOVE'); -// } -// } ], -// listeners : { -// cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { -// if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'MOLARITY_REMOVE') { -// var dataAdapter = new BiosaxsDataAdapter(); -// dataAdapter.onSuccess.attach(function() { -// _this.molarityGrid.setLoading(false); -// _this.update(); -// }); -// dataAdapter.removeStoichiometry(record.data.stoichiometryId); -// _this.molarityGrid.setLoading("Removing Structure"); -// } -// } -// }, -// buttons : [ { -// text : 'Add molarity', -// handler : function() { -// -// function onClose() { -// w.destroy(); -// _this.update(); -// } -// var w = Ext.create('Ext.window.Window', { -// title : 'Molarity', -// height : 300, -// width : 500, -// modal : true, -// buttons : [ { -// text : 'Save', -// handler : function() { -// var macromoleculeId = (_this.macromoleculeCombo.getValue()); -// var ratio = Ext.getCmp(_this.id + "ratio").getValue(); -// var comments = ""; -// var dataAdapter = new BiosaxsDataAdapter(); -// dataAdapter.onSuccess.attach(function(sender, args) { -// _this.update(); -// w.destroy(); -// }); -// dataAdapter.saveStoichiometry(_this.macromolecule.macromoleculeId, macromoleculeId, ratio, comments); -// } -// }, { -// text : 'Cancel', -// handler : function() { -// onClose(); -// } -// } ], -// items : [ _this.getMolarityForm(macromolecule) ], -// listeners : { -// onEsc : function() { -// onClose(); -// }, -// close : function() { -// onClose(); -// } -// } -// }).show(); -// } -// } ] -// }); -// return this.molarityGrid; -//}; -// -///******************************************************************************* -// * PDB GRID -// ******************************************************************************/ -// -//MacromoleculeForm.prototype._openUploadDialog = function(macromoleculeId, type, title) { -// var _this = this; -// function onClose() { -// w.destroy(); -// _this.update(); -// } -// -// var w = Ext.create('Ext.window.Window', { -// title : title, -// height : 200, -// width : 400, -// modal : true, -// buttons : [ { -// text : 'Close', -// handler : function() { -// onClose(); -// } -// } ], -// layout : 'fit', -// items : { -// html : "" -// }, -// listeners : { -// onEsc : function() { -// onClose(); -// }, -// close : function() { -// onClose(); -// } -// } -// }).show(); -//}; -// -//MacromoleculeForm.prototype._getPlugins = function() { -// var _this = this; -// var plugins = []; -// // if (this.updateRowEnabled) { -// plugins.push(Ext.create('Ext.grid.plugin.RowEditing', { -// clicksToEdit : 1, -// listeners : { -// validateedit : function(grid, e) { -// /** Comments are always updatable* */ -// e.record.raw.symmetry = e.newValues.symmetry; -// e.record.raw.multiplicity = e.newValues.multiplicity; -// -// var adapter = new BiosaxsDataAdapter(); -// adapter.onSuccess.attach(function(sender, measurement) { -// // _this.grid.setLoading(false); -// }); -// adapter.onError.attach(function() { -// alert("Error"); -// // _this.grid.setLoading(false); -// }); -// -// // _this.grid.setLoading(); -// adapter.saveStructure(e.record.raw); -// } -// } -// })); -// // } -// return plugins; -//}; -// -//MacromoleculeForm.prototype.getPDBGrid = function(macromolecule) { -// var _this = this; -// -// var data = macromolecule.structure3VOs; -// -// // /** Getting PDB from subunits **/ -// // if (macromolecule != null){ -// // if (macromolecule.stoichiometry != null){ -// // for (var i =0; i < macromolecule.stoichiometry.length; i++){ -// // var stoichiometry = macromolecule.stoichiometry[i]; -// // if (stoichiometry.macromolecule3VO != null){ -// // if (stoichiometry.macromolecule3VO.structure3VOs != null){ -// // for (var j = 0; j < stoichiometry.macromolecule3VO.structure3VOs.length; -// // j++) { -// // var structure = stoichiometry.macromolecule3VO.structure3VOs[j]; -// // structure["isSubunit"] = stoichiometry.macromolecule3VO.acronym; -// // data.push(structure) -// // } -// // } -// // } -// // } -// // } -// // } -// -// this.pdbStore = Ext.create('Ext.data.Store', { -// fields : [ 'filePath', 'structureId', 'structureType', 'symmetry', 'structureId', 'name', 'multiplicity' ], -// data : macromolecule.structure3VOs, -// groupField : 'structureType', -// sorters : { -// property : 'structureId', -// direction : 'DESC' -// } -// }); -// -// var groupingFeature = Ext.create('Ext.grid.feature.Grouping', { -// groupHeaderTpl : Ext.create('Ext.XTemplate', -// "
{name:this.formatName}
", { -// formatName : function(name) { -// return name; -// } -// }), -// hideGroupedHeader : true, -// startCollapsed : false -// }); -// -// this.pdbGrid = Ext.create('Ext.grid.Panel', { -// margin : "15 0 0 5", -// height : 250, -// store : this.pdbStore, -// plugins : _this._getPlugins(), -// buttons : [ { -// // text : 'Add PDB file', -// text : 'Add Modeling Option', -// handler : function() { -// _this._openUploadDialog(macromolecule.macromoleculeId, "PDB", 'Upload PDB File'); -// } -// } -// -// ], -// columns : [ -// { -// text : "structureId", -// flex : 0.2, -// hidden : true, -// dataIndex : 'structureId', -// sortable : true -// }, -// { -// text : "File", -// flex : 0.5, -// dataIndex : 'filePath', -// sortable : true, -// hidden : true -// }, -// { -// text : "PDB", -// flex : 0.4, -// dataIndex : 'name', -// sortable : true -// }, -// { -// text : "Symmetry", -// flex : 0.4, -// dataIndex : 'symmetry', -// sortable : true, -// editor : { -// xtype : 'combobox', -// typeAhead : true, -// triggerAction : 'all', -// selectOnTab : true, -// store : [ [ "P1", "P1" ], [ "P2", "P2" ], [ "P3", "P3" ], [ "P4", "P4" ], [ "P5", "P5" ], [ "P6", "P6" ], [ "P32", "P32" ], [ "P42", "P42" ], -// [ "P52", "P52" ], [ "P62", "P62" ], [ "P222", "P222" ] ], -// } -// }, { -// text : "Multiplicity", -// flex : 0.4, -// dataIndex : 'multiplicity', -// sortable : true, -// editor : { -// xtype : 'textfield' -// } -// -// }, { -// text : "Subunit", -// flex : 0.2, -// dataIndex : 'isSubunit', -// sortable : true, -// hidden : true -// }, { -// text : "Type", -// flex : 0.2, -// dataIndex : 'structureType', -// sortable : true, -// hidden : true -// }, -// -// { -// id : this.id + 'REMOVE', -// flex : 0.2, -// hidden : true, -// sortable : false, -// renderer : function(value, metaData, record, rowIndex, colIndex, store) { -// return BUI.getRedButton('REMOVE'); -// } -// }, ], -// -// listeners : { -// itemdblclick : function(dataview, record, item, e) { -// _this._editExperiment(record.raw.experimentId); -// }, -// cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { -// -// if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'REMOVE') { -// var dataAdapter = new BiosaxsDataAdapter(); -// dataAdapter.onSuccess.attach(function() { -// _this.pdbGrid.setLoading(false); -// _this.update(); -// }); -// dataAdapter.removeStructure(record.data.structureId); -// _this.pdbGrid.setLoading("Removing PDB file"); -// } -// -// } -// }, -// viewConfig : { -// getRowClass : function(record, rowIdx, params, store) { -// if (record.raw.isSubunit != null) { -// return "blue-row"; -// } -// } -// } -// }); -// -// return this.pdbGrid; -//}; -// -//MacromoleculeForm.prototype.getMolarityForm = function(macromolecule) { -// var _this = this; -// var data = []; -// for (var i = 0; i < BIOSAXS.proposal.macromolecules.length; i++) { -// var m = BIOSAXS.proposal.macromolecules[i]; -// if (m.macromoleculeId != macromolecule.macromoleculeId) { -// data.push(m); -// } -// -// } -// this.macromoleculeCombo = BIOSAXS_COMBOMANAGER.getComboMacromoleculeByMacromolecules(data, { -// width : 250, -// labelWidth : 100, -// margin : 10 -// }); -// -// return Ext.createWidget('form', { -// -// frame : false, -// border : 0, -// // padding : 15, -// width : 550, -// height : 350, -// items : [ { -// xtype : 'container', -// flex : 1, -// border : false, -// layout : 'anchor', -// defaultType : 'requiredtext', -// items : [ this.macromoleculeCombo, { -// xtype : 'numberfield', -// name : 'Ratio', -// id : _this.id + "ratio", -// fieldLabel : 'Number in assymmetric units', -// value : 1, -// decimalPrecision : 6, -// margin : 10 -// }, { -// xtype : 'textareafield', -// name : 'comments', -// fieldLabel : 'Comments', -// margin : 10, -// width : 400, -// value : "" -// -// } ] -// } ] -// }); -// -//}; -// -///******************************************************************************* -// * JAVASCRIPT DOC -// ******************************************************************************/ -//MacromoleculeForm.prototype.input = function() { -// return { -// macromolecule : DATADOC.getMacromolecule_10() -// }; -//}; -// -//MacromoleculeForm.prototype.test = function(targetId) { -// var macromoleculeForm = new MacromoleculeForm(); -// var panel = macromoleculeForm.getPanel(macromoleculeForm.input().macromolecule); -// panel.render(targetId); -//}; - -function ResultSummaryForm() { - this.radiationDamageThreshold = BUI.getRadiationDamageThreshold(); - this.qualityThreshold = BUI.getQualityThreshold(); - - /** Data clusters with bufers **/ - this.clusterByBuffers = false; - - /** Visualization are groupeb by concenttrations. Ex: 4.5mg/ml and 4.7mg/ml will be clusterized together **/ - this.collapseConcentrations = true; - - this.summaryHeight = 350; - this.id = BUI.id(); - - var _this = this; - - this.qualityControlResultsWidget = new QualityControlResultsWidget({ - qualityThreshold : this.radiationDamageThreshold, - radiationDamageThreshold : this.qualityThreshold - - }); - this.qualityControlResultsWidget.onQualityChanged.attach(function(sender, value) { - _this.qualityThreshold = value; - _this.thresholdsChanged(); - }); - - this.qualityControlResultsWidget.onRadiationDatamageChanged.attach(function(sender, value) { - _this.radiationDamageThreshold = value; - _this.thresholdsChanged(); - - }); - this.concentrationHTMLTableWidget = new ConcentrationHTMLTableWidget({ - height : 250, - showBufferColumns : this.clusterByBuffers - }); - - this.plot = new BoxWhiskerGraph({ - targetId : _this.id + '_boxPlot', - height : 350, - width : 450, - maxBoxWidth : 25 - }); - - this.rangePlot = new RangeWhiskerGraph({ - targetId : _this.id + '_rangePlot', - height : 350, - width : 450, - maxBoxWidth : 25 - }); -} - -ResultSummaryForm.prototype.thresholdsChanged = function() { - var parsedData = this.prepareData(this.rawData); - - var filtered = JSON.parse(JSON.stringify(parsedData)); - filtered.concentration.concentrations = []; - for ( var conc in parsedData.concentration.concentrations) { - if (parsedData.concentration.concentrations[conc].calculation.rgGnom.validNumber > 0) { - filtered.concentration.concentrations.push(parsedData.concentration.concentrations[conc]); - } - } - - this.plotWhisker(filtered); - this.plotRange(filtered); - - this.concentrationHTMLTableWidget.refresh(parsedData); -}; - -/** Given a frame object (object returned by analyzeFrames()) and depending of the threshold indicates if a datacollection has radiation damage **/ -ResultSummaryForm.prototype.hasRadiationDamage = function(frameObject) { - var bb = frameObject.bufferBeforeFramesMerged / frameObject.framesCount; - var ba = frameObject.bufferAfterFramesMerged / frameObject.framesCount; - var mol = frameObject.framesMerge / frameObject.framesCount; - return !((bb >= this.radiationDamageThreshold) && (ba >= this.radiationDamageThreshold) && (mol >= this.radiationDamageThreshold)); -}; - -/** Return (frameObject) an object with the information about the frames for a data collection**/ -ResultSummaryForm.prototype.analyzeFrames = function(dataCollectionRow) { - return { - bufferAfterFramesMerged : dataCollectionRow.bufferAfterFramesMerged, - bufferBeforeFramesMerged : dataCollectionRow.bufferBeforeFramesMerged, - framesCount : dataCollectionRow.framesCount, - framesMerge : dataCollectionRow.framesMerge - }; -}; - -ResultSummaryForm.prototype.detectDataCollectionWarnings = function(dataCollectionRow) { - var frameObject = this.analyzeFrames(dataCollectionRow); - var warnings = { - count : 0, - type : [] - }; - - if (this.hasRadiationDamage(frameObject)) { - warnings.count = warnings.count + 1; - warnings.type.push("RADIATION DAMAGE"); - } - - if (Number(dataCollectionRow.quality) < this.qualityThreshold) { - warnings.count = 1;//warnings.count + 1; - warnings.type.push("Quality <" + this.qualityThreshold); - } - return warnings; -}; - -/** Return array composed by {concentration} objects **/ -ResultSummaryForm.prototype.getConcentrations = function(data) { - var concentrationsId = {}; - - for ( var i = 0; i < data.length; i++) { - var warning = this.detectDataCollectionWarnings(data[i]); - var id = data[i].conc;// + "_" + data[i].bufferId; - if (this.clusterByBuffers) { - id = data[i].conc + "_" + data[i].bufferId; - } - - if (concentrationsId[id] == null) { - concentrationsId[id] = { - id : id, - concentration : Number(data[i].conc).toFixed(2), - bufferId : data[i].bufferId, - bufferAcronym : data[i].bufferAcronym, - rgGuinier : [], - i0Guinier : [], - rgGnom : [], - dMax : [], - quality : [], - frames : [], - frames_warning : warning.count - }; - } else { - concentrationsId[id].frames_warning = concentrationsId[id].frames_warning + warning.count; - } - - concentrationsId[id].frames.push(data[i]); - - if (warning.count == 0) { - concentrationsId[id].rgGuinier.push(data[i].rgGuinier); - concentrationsId[id].i0Guinier.push(data[i].I0); - concentrationsId[id].quality.push(data[i].quality); - concentrationsId[id].rgGnom.push(data[i].rgGnom); - concentrationsId[id].dMax.push(data[i].dmax); - } - - } - var concentrations = []; - for ( var item in concentrationsId) { - if (concentrationsId.hasOwnProperty(item)) { - concentrations.push({ - concentration : concentrationsId[item].concentration, - id : item, - bufferId : Number(concentrationsId[item].bufferId).toFixed(2), - bufferAcronym : concentrationsId[item].bufferAcronym, - rgGuinier : concentrationsId[item].rgGuinier, - /** Frames **/ - frames : concentrationsId[item].frames, - frames_warning : concentrationsId[item].frames_warning, - /** Calculation **/ - calculation : { - rgGuinier : BUI.getStandardDeviation(concentrationsId[item].rgGuinier), - i0Guinier : BUI.getStandardDeviation(concentrationsId[item].i0Guinier), - quality : BUI.getStandardDeviation(concentrationsId[item].quality), - rgGnom : BUI.getStandardDeviation(concentrationsId[item].rgGnom), - dMax : BUI.getStandardDeviation(concentrationsId[item].dMax) - - } - }); - } - } - - return { - concentrations : concentrations - }; -}; - -ResultSummaryForm.prototype.prepareData = function(data) { - /** Return array composed by {acronym, bufferId} objects **/ - function getBuffers(data) { - var buffersId = {}; - for ( var i = 0; i < data.length; i++) { - buffersId[data[i].bufferId] = data[i].acronym; - } - var buffers = []; - for ( var id in buffersId) { - if (buffersId.hasOwnProperty(id)) { - buffers.push({ - acronym : buffersId[id], - bufferId : id - }); - } - } - return buffers; - } - - /** Get a string with all the concentrations **/ - function getConcentrationString(parseConcentrations) { - var concentrations = []; - for ( var i = 0; i < parseConcentrations.concentrations.length; i++) { - concentrations.push(parseConcentrations.concentrations[i].concentration + " mg/ml "); - } - return concentrations.toString(); - } - - var parseConcentrations = this.getConcentrations(data); - - return { - dataCollectionCount : data.length, - buffers : getBuffers(data), - concentration : parseConcentrations, - concentrationLabel : getConcentrationString(parseConcentrations) - }; -}; - -ResultSummaryForm.prototype.getDataForWhisker = function(data) { - var clusters = []; - - var concentrations = {}; - var i = 0; - var conc = 0; - if (this.collapseConcentrations) { - for (i = 0; i < data.concentration.concentrations.length; i++) { - conc = data.concentration.concentrations[i]; - var concentration = Number(conc.concentration).toFixed(0); - if (concentrations[concentration] == null) { - concentrations[concentration] = {}; - concentrations[concentration].concentration = concentration; - concentrations[concentration].calculation = {}; - concentrations[concentration].calculation.rgGuinier = {}; - concentrations[concentration].calculation.rgGuinier.values = []; - concentrations[concentration].calculation.rgGuinier.values = conc.calculation.rgGuinier.values; - concentrations[concentration].calculation.rgGnom = {}; - concentrations[concentration].calculation.rgGnom.values = []; - concentrations[concentration].calculation.rgGnom.values = conc.calculation.rgGnom.values; - } else { - concentrations[concentration].calculation.rgGuinier.values = concentrations[concentration].calculation.rgGuinier.values - .concat(conc.calculation.rgGuinier.values); - concentrations[concentration].calculation.rgGnom.values = concentrations[concentration].calculation.rgGnom.values - .concat(conc.calculation.rgGnom.values); - } - } - - /** From object to array **/ - var array = []; - for ( var key in concentrations) { - if (concentrations.hasOwnProperty(key)) { - array.push(concentrations[key]); - } - } - data.concentration.concentrations = array; - } - - for (i = 0; i < data.concentration.concentrations.length; i++) { - conc = data.concentration.concentrations[i]; - clusters.push({ - name : conc.concentration + "mg/ml", - concentration : Number(conc.concentration), - x : Number(conc.concentration), - classes : [] - }); - clusters[clusters.length - 1].classes.push({ - name : "Guinier", - color : '#9A2EFE', - values : conc.calculation.rgGuinier.values - - }); - clusters[clusters.length - 1].classes.push({ - name : "P(r)", - color : '#2E64FE', - values : conc.calculation.rgGnom.values - - }); - } - return { - clusters : clusters.sort(function(a, b) { - return a.concentration - b.concentration; - }) - }; -}; - -ResultSummaryForm.prototype.getDataForWhiskerQuality = function(data) { - var clusters = []; - - for ( var i = 0; i < data.concentration.concentrations.length; i++) { - var conc = data.concentration.concentrations[i]; - clusters.push({ - name : conc.concentration + "mg/ml", - classes : [] - }); - clusters[clusters.length - 1].classes.push({ - name : "Quality", - values : conc.calculation.quality.values - - }); - } - - return { - clusters : clusters - }; -}; - -ResultSummaryForm.prototype.plotQualityWhisker = function(parsedData) { - this.qualityPlot.refresh(this.getDataForWhiskerQuality(parsedData)); -}; - -ResultSummaryForm.prototype.plotWhisker = function(parsedData) { - this.plot.refresh(this.getDataForWhisker(parsedData)); -}; - -ResultSummaryForm.prototype.plotRange = function(parsedData) { - this.rangePlot.refresh(this.getDataForWhisker(parsedData)); -}; - -ResultSummaryForm.prototype.getPanel = function(data) { - var _this = this; - _this.rawData = data; - var parsedData = this.prepareData(data); - - this.panel = Ext - .createWidget( - 'form', - { - bodyPadding : 20, - frame : false, - border : 0, - width : this.width, - height : this.summaryHeight + 1000, - items : [ { - xtype : 'container', - layout : 'hbox', - items : [ { - xtype : 'container', - flex : 1, - border : false, - layout : 'anchor', - defaultType : 'textfield', - items : [ - _this.qualityControlResultsWidget.getPanel(), - { - xtype : 'fieldset', - width : 950, - margin : "20, 0 0 0", - height : this.summaryHeight + 500, - items : [ - { - html : "
Concentration Analysis
", - border : 0, - width : 900 - - }, - { - html : this.concentrationHTMLTableWidget.getPanel(parsedData), - border : 0, - width : 900, - margin : "10, 0 0 10" - - }, - { - xtype : 'container', - layout : 'vbox', - border : 5, - items : [ - { - html : "
Rg based on Guinier and Gnom
", - border : 8, - width : 900, - margin : "20 0 0 10" - - }, { - xtype : 'container', - layout : 'hbox', - items : [ { - xtype : 'container', - layout : 'vbox', - items : [ { - html : "
", - border : 0, - width : this.plot.width, - padding : "10 0 0 20", - listeners : { - afterrender : function() { - _this.plotWhisker(parsedData); - - } - } - }, { - html : "
Concentration (mg/ml)
", - width : 450, - border : 0, - margin : "-50 0 0 0" - - } ] - }, { - xtype : 'container', - layout : 'vbox', - items : [ { - html : "
", - border : 0, - width : this.rangePlot.width, - padding : "10 0 0 10", - listeners : { - afterrender : function() { - _this.plotRange(parsedData); - } - } - }, { - html : "
Concentration (mg/ml)
", - width : 450, - border : 0, - margin : "-50 0 0 0" - - } ] - } ] - } ] - } ] - } ] - } - - ] - } ] - }); - return this.panel; - -}; - -ResultSummaryForm.prototype.input = function() { - return { - data : DATADOC.getData_3367() - }; -}; - -ResultSummaryForm.prototype.test = function(targetId) { - var resultSummaryForm = new ResultSummaryForm(); - var panel = resultSummaryForm.getPanel(resultSummaryForm.input().data); - panel.render(targetId); - -}; - -/** - * Example form - * - * @witdh - * @height - */ -function RigibBodyModelingForm(args) { - this.id = BUI.id(); - this.width = 700; - this.height = 500; - - if (args != null) { - if (args.width != null) { - this.width = args.width; - } - if (args.height != null) { - this.height = args.height; - } - } - - var _this = this; - this.rigidBodyGrid = new AprioriRigidBodyGrid(); - - this.rigidBodyGrid.onUploadFile.attach(function(sender, type, title){ - _this._openUploadDialog(_this.macromolecule.macromoleculeId, type, title); - }); - - this.rigidBodyGrid.onRemove.attach(function(sender, type, title){ - _this._update(); - }); - - this.onSave = new Event(this); - -} - -RigibBodyModelingForm.prototype._getItems = function() { - var _this = this; - - - return [ { - xtype : 'label', - forId : 'myFieldId', - text : 'Information for model fit, mixture analysis and rigid body modeling', - margin : '15 0 20 10', - cls : "inline-help" - }, - this.rigidBodyGrid.getPanel(), - { - xtype : 'label', - forId : 'myFieldId', - text : 'Distance restraints may be imposed on the model using contacts conditions file (OPTIONAL)', - margin : '25 0 5 10', - cls : "inline-help" - }, - { - xtype : 'container', - layout : 'hbox', - items : [ { - xtype : 'container', - border : false, - layout : 'hbox', - items : [ { - xtype : 'label', - forId : 'myFieldId', - text : 'Contact Description File: (Optional)', - width : 150, - margin : '10 0 0 10' - }, { - id : this.id + "contactsDescriptionFilePath", - xtype : 'textfield', - hideLabel : true, - margin : '10 0 0 0', - width : 200, - disabled: true - }, { - text : 'Upload', - xtype : 'button', - margin : "10 0 0 10", - width : 80, - handler : function() { - - _this._openUploadDialog(_this.macromolecule.macromoleculeId, "CONTACTS", "Upload Contact Description File"); - } - }, { - text : 'Remove', - id : _this.id + "_remove", - xtype : 'button', - margin : "10 0 0 10", - width : 80, - handler : function() { - _this.macromolecule.contactsDescriptionFilePath = null; - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, proposal) { - /** Updating the proposal global object **/ - BIOSAXS.proposal.setItems(proposal); - _this.panel.setLoading(false); - - var saved = BIOSAXS.proposal.getMacromoleculeByAcronym(_this.macromolecule.acronym); - /** Refreshing to check that everything is ok **/ - _this.refresh(saved); - _this.onSave.notify(saved); - }); - - _this.panel.setLoading("Saving Macromolecule") - adapter.saveMacromolecule(_this.macromolecule); - } - } ] - } ] - }, { - xtype : 'panel', - html : "Go to SASREF manual for further information", - margin : "10 0 0 160", - border : 0 - - }, - { - xtype : 'checkbox', - margin : '10 0 0 5', - boxLabel : "I want rigid body modeling run on this stuff", - checked : true, - width : 300 - } ] - -}; - -/** Because update is a jsp page we don't know if the user has uploaded a file or not then we need to refresh **/ -RigibBodyModelingForm.prototype._update = function(macromoleculeId, type, title) { - var _this = this; - BIOSAXS.proposal.onInitialized.attach(function() { - if (BIOSAXS.proposal != null) { - _this.refresh(BIOSAXS.proposal.getMacromoleculeById(_this.macromolecule.macromoleculeId)); - _this.panel.setLoading(false); - } - }); - this.panel.setLoading(); - BIOSAXS.proposal.init(); -}; - -RigibBodyModelingForm.prototype._openUploadDialog = function(macromoleculeId, type, title) { - var _this = this; - function onClose() { - w.destroy(); - _this._update(); - - } - - var w = Ext.create('Ext.window.Window', { - title : title, - height : 200, - width : 400, - modal : true, - buttons : [ { - text : 'Close', - handler : function() { - onClose(); - } - } ], - layout : 'fit', - items : { - html : "" - }, - listeners : { - onEsc : function() { - onClose(); - }, - close : function() { - onClose(); - } - } - }).show(); -}; - -RigibBodyModelingForm.prototype._getButtons = function() { - return []; -}; - -RigibBodyModelingForm.prototype.getPanel = function() { - var _this = this; - this.panel = Ext.create('Ext.form.Panel', { - width : this.width, - height : this.height, - margin : 10, - border : 1, - defaultType : 'textfield', - items : this._getItems(), - buttons : this._getButtons(), - listeners : { - afterrender : function(){ - _this._populate(); - - } - } - }); - return this.panel; -}; - -/** Populates could be call when the DOM is not filled yet **/ -RigibBodyModelingForm.prototype._populate = function() { - if (this.macromolecule != null){ - if (Ext.getCmp(this.id + "contactsDescriptionFilePath") != null){ - if (this.macromolecule.contactsDescriptionFilePath != null){ - Ext.getCmp(this.id + "contactsDescriptionFilePath").setValue(this.macromolecule.contactsDescriptionFilePath); - Ext.getCmp(this.id + "_remove").enable(); - } - else{ - Ext.getCmp(this.id + "_remove").disable(); - Ext.getCmp(this.id + "contactsDescriptionFilePath").setValue(""); - - } - } - } -}; - -/** It populates the form * */ -RigibBodyModelingForm.prototype.refresh = function(macromolecule) { - this.macromolecule = macromolecule; - this.rigidBodyGrid.refresh(macromolecule); - this._populate(); -}; - -RigibBodyModelingForm.prototype.input = function() { - return {}; -}; - - -/** It populates the form **/ -RigibBodyModelingForm.prototype.test = function(targetId) { - var macromoleculeForm = new RigibBodyModelingForm(); - var panel = macromoleculeForm.getPanel(); - panel.render(targetId); -}; - - -/** - * Same form as MX part - * - * @creationMode if true a create button appears instead of save - * @showTitle true or false - */ -function ShipmentForm(args) { - this.id = BUI.id(); - - this.creationMode = false; - this.showTitle = true; - if (args != null) { - if (args.creationMode != null) { - this.creationMode = args.creationMode; - } - if (args.showTitle != null) { - this.showTitle = args.showTitle; - } - } - - this.onSaved = new Event(this); -} - -ShipmentForm.prototype.fillStores = function() { - this.panel.setLoading("Loading Labcontacts from database"); - this.labContactForSendingStore.loadData(BIOSAXS.proposal.getLabcontacts(), false); - this.labContactForReturnStore.loadData(BIOSAXS.proposal.getLabcontacts(), false); - this.panel.setLoading(false); - if (this.shipment != null) { - this.setShipment(this.shipment); - } -}; - -ShipmentForm.prototype.draw = function(targetId) { - this.getPanel().render(targetId); -}; - -ShipmentForm.prototype.setShipment = function(shipment) { - this.shipment = shipment; - var _this = this; - Ext.getCmp(_this.id + "shippingName").setValue(shipment.json.shippingName); - Ext.getCmp(_this.id + "shippingStatus").setValue(shipment.json.shippingStatus); - Ext.getCmp(_this.id + "comments").setValue(shipment.json.comments); - if (shipment.json.sendingLabContactVO != null) { - this.labContactsSendingCombo.setValue(shipment.json.sendingLabContactVO.labContactId); - } - if (shipment.json.returnLabContactVO != null) { - this.labContactsReturnCombo.setValue(shipment.json.returnLabContactVO.labContactId); - } - -}; - -ShipmentForm.prototype._saveShipment = function() { - var _this = this; - var shippingId = null; - if (this.shipment != null) { - shippingId = this.shipment.json.shippingId; - } - var json = { - shippingId : shippingId, - name : Ext.getCmp(_this.id + "shippingName").getValue(), - status : Ext.getCmp(_this.id + "shippingStatus").getValue(), - sendingLabContactId : Ext.getCmp(_this.id + "shipmentform_sendingLabContactId").getValue(), - returnLabContactId : Ext.getCmp(_this.id + "returnLabContactId").getValue(), - returnCourier : Ext.getCmp(_this.id + "returnCourier").getValue(), - courierAccount : Ext.getCmp(_this.id + "courierAccount").getValue(), - BillingReference : Ext.getCmp(_this.id + "BillingReference").getValue(), - dewarAvgCustomsValue : Ext.getCmp(_this.id + "dewarAvgCustomsValue").getValue(), - dewarAvgTransportValue : Ext.getCmp(_this.id + "dewarAvgTransportValue").getValue(), - comments : Ext.getCmp(_this.id + "comments").getValue() - }; - - var dataAdapter = new BiosaxsDataAdapter(); - dataAdapter.onSuccess.attach(function(sender, shipment) { - window.location = BUI.getShippingURL(shipment.shippingId); - }); - - dataAdapter.onError.attach(function(sender, error) { - _this.onError.notify(error); - }); - - /** Cheking params **/ - if (json.name == "") { - BUI.showError("Name field is mandatory"); - return; - } - - if (json.sendingLabContactId == null) { - BUI.showError("Lab contact for sending field is mandatory"); - return; - } - - if (json.returnLabContactId == null) { - BUI.showError("Lab contact for return field is mandatory"); - return; - } - - dataAdapter.createShipment(json.shippingId, json.name, json.status, json.comments, json.sendingLabContactId, json.returnLabContactId, - json.returnCourier, json.courierAccount, json.BillingReference, json.dewarAvgCustomsValue, json.dewarAvgTransportValue); - -}; - -ShipmentForm.prototype.getPanel = function(shipment) { - var _this = this; - this.shipment = shipment; - var required = '*'; - var buttons = []; - - if (_this.creationMode) { - buttons.push({ - text : 'Create', - scope : this, - handler : function() { - _this._saveShipment(); - } - }); - } else { - buttons.push({ - text : 'Save', - scope : this, - handler : function() { - _this._saveShipment(); - } - }); - - } - - this.labContactForSendingStore = Ext.create('Ext.data.Store', { - fields : [ 'cardName', 'labContactId' ] - }); - - this.labContactForReturnStore = Ext.create('Ext.data.Store', { - fields : [ 'cardName', 'labContactId' ] - }); - - // Create the combo box, attached to the states data store - this.labContactsSendingCombo = Ext.create('Ext.form.ComboBox', { - id : _this.id + "shipmentform_sendingLabContactId", - fieldLabel : 'Lab contact for sending', - afterLabelTextTpl : required, - store : this.labContactForSendingStore, - queryMode : 'local', - labelWidth : 200, - displayField : 'cardName', - valueField : 'labContactId' - }); - - this.labContactsReturnCombo = Ext.create('Ext.form.ComboBox', { - id : _this.id + "returnLabContactId", - fieldLabel : 'If No, Lab-Contact for Return', - afterLabelTextTpl : required, - store : this.labContactForReturnStore, - queryMode : 'local', - labelWidth : 200, - displayField : 'cardName', - valueField : 'labContactId', - listeners : { - change : function(x, newValue) { - for ( var i = 0; i < x.getStore().data.items.length; i++) { - if (x.getStore().data.items[i].raw.labContactId == newValue) { - Ext.getCmp(_this.id + "returnCourier").setValue(x.getStore().data.items[i].raw.defaultCourrierCompany); - Ext.getCmp(_this.id + "courierAccount").setValue(x.getStore().data.items[i].raw.courierAccount); - Ext.getCmp(_this.id + "BillingReference").setValue(x.getStore().data.items[i].raw.billingReference); - Ext.getCmp(_this.id + "dewarAvgCustomsValue").setValue(x.getStore().data.items[i].raw.dewarAvgCustomsValue); - Ext.getCmp(_this.id + "dewarAvgTransportValue").setValue(x.getStore().data.items[i].raw.dewarAvgTransportValue); - } - } - } - } - }); - - if (this.panel == null) { - this.panel = Ext.create('Ext.form.Panel', { - bodyPadding : 5, - width : 600, - border : 1, - items : [ { - xtype : 'fieldset', - title : 'Details', - collapsible : false, - defaultType : 'textfield', - layout : 'anchor', - defaults : { - anchor : '100%' - }, - items : [ { - xtype : 'requiredtext', - fieldLabel : 'Shipment Label', - allowBlank : false, - name : 'shippingName', - id : _this.id + 'shippingName', - value : '', - anchor : '50%' - }, { - - xtype : 'textareafield', - name : 'comments', - id : _this.id + 'comments', - fieldLabel : 'Comments', - value : '' - }, { - fieldLabel : 'Status', - readOnly : true, - id : _this.id + 'shippingStatus', - value : 'Opened', - anchor : '50%' - } ] - }, { - xtype : 'fieldset', - title : 'Lab-Contacts', - collapsible : false, - defaultType : 'textfield', - layout : 'anchor', - defaults : { - anchor : '100%' - }, - items : [ this.labContactsSendingCombo, this.labContactsReturnCombo ] - }, { - border : 0, - html : BUI.getWarningHTML("These informations are relevant for all shipments") - }, { - xtype : 'fieldset', - title : 'Courier accounts details for return', - collapsible : false, - layout : 'anchor', - defaults : { - anchor : '100%' - }, - items : [ { - xtype : 'textfield', - labelWidth : 400, - fieldLabel : 'Courier company for return (if ESRF sends a dewar back)', - id : _this.id + 'returnCourier', - value : '' - }, { - xtype : 'textfield', - labelWidth : 400, - fieldLabel : 'Courier account', - id : _this.id + 'courierAccount', - value : '' - }, { - xtype : 'textfield', - labelWidth : 400, - fieldLabel : 'Billing reference', - id : _this.id + 'BillingReference', - value : '' - }, { - xtype : 'numberfield', - labelWidth : 400, - fieldLabel : 'Average Customs value of a dewar (Euro)', - id : _this.id + 'dewarAvgCustomsValue', - value : '' - }, { - xtype : 'numberfield', - labelWidth : 400, - fieldLabel : 'Average Transport value of a dewar (Euro)', - id : _this.id + 'dewarAvgTransportValue', - value : '' - } ] - } ], - buttons : buttons - }); - } - this.fillStores(); - if (this.showTitle) { - this.panel.setTitle('Create a new Shipment'); - } - return this.panel; -}; - -ShipmentForm.prototype.input = function() { - return { - proposal : DATADOC.getProposal_10() - - }; -}; - -ShipmentForm.prototype.test = function(targetId) { - var shipmentForm = new ShipmentForm({ - creationMode : true - - }); - BIOSAXS.proposal = new Proposal(shipmentForm.input().proposal); - shipmentForm.getPanel().render(targetId); -}; - -/** - * #onSaved - */ -function StockSolutionForm(args) { - this.id = BUI.id(); - this.actions = []; - - if (args != null) { - if (args.actions != null) { - this.actions = args.actions; - } - } - this.onSaved = new Event(this); -} - -StockSolutionForm.prototype.getStockSolution = function() { - if (this.stockSolution != null) { - this.stockSolution.concentration = Ext.getCmp(this.id + "stockSolution_concentration").getValue(); - this.stockSolution.storageTemperature = Ext.getCmp(this.id + "stockSolution_storageTemperature").getValue(); - this.stockSolution.volume = Ext.getCmp(this.id + "stockSolution_volume").getValue(); - this.stockSolution.comments = Ext.getCmp(this.id + "stockSolution_comments").getValue(); - this.stockSolution.name = Ext.getCmp(this.id + "stockSolution_name").getValue(); - this.stockSolution.bufferId = this.bufferCombo.getValue(); - - if (this.macromoleculeCombo.getValue() != null) { - this.stockSolution.macromoleculeId = this.macromoleculeCombo.getValue(); - } else { - this.stockSolution.macromolecule3VO = null; - } - - } else { - return { - concentration : Ext.getCmp(this.id + "stockSolution_concentration").getValue(), - storageTemperature : Ext.getCmp(this.id + "stockSolution_storageTemperature").getValue(), - volume : Ext.getCmp(this.id + "stockSolution_volume").getValue(), - comments : Ext.getCmp(this.id + "stockSolution_comments").getValue(), - name : Ext.getCmp(this.id + "stockSolution_name").getValue(), - bufferId : this.bufferCombo.getValue(), - macromoleculeId : this.macromoleculeCombo.getValue() - }; - } - return this.stockSolution; -}; - -StockSolutionForm.prototype.setStockSolution = function(stockSolution) { - if (stockSolution != null) { - if (stockSolution.macromoleculeId != null) { - this.macromoleculeCombo.setValue(stockSolution.macromoleculeId); - } - this.bufferCombo.setValue(stockSolution.bufferId); - Ext.getCmp(this.id + "stockSolution_concentration").setValue(this.stockSolution.concentration); - Ext.getCmp(this.id + "stockSolution_storageTemperature").setValue(this.stockSolution.storageTemperature); - Ext.getCmp(this.id + "stockSolution_volume").setValue(this.stockSolution.volume); - Ext.getCmp(this.id + "stockSolution_name").setValue(this.stockSolution.name); - Ext.getCmp(this.id + "stockSolution_comments").setValue(this.stockSolution.comments); - } -}; - -StockSolutionForm.prototype.getBufferCombo = function() { - this.bufferCombo = BIOSAXS_COMBOMANAGER.getComboBuffers(BIOSAXS.proposal.getBuffers(), { - labelWidth : 120, - margin : '0 0 10 0', - width : 220 - - }); - return this.bufferCombo; -}; - -StockSolutionForm.prototype.getMacromoleculeCombo = function() { - this.macromoleculeCombo = BIOSAXS_COMBOMANAGER.getComboMacromoleculeByMacromolecules(BIOSAXS.proposal.getMacromolecules(), { - labelWidth : 120, - margin : '0 0 10 0', - width : 220 - - }); - return this.macromoleculeCombo; -}; - -StockSolutionForm.prototype.refresh = function() { -}; - -StockSolutionForm.prototype._getTopPanel = function() { - return { - xtype : 'container', - layout : 'hbox', - border : 0, - items : [ { - xtype : 'container', - layout : 'hbox', - items : [ { - xtype : 'container', - flex : 1, - border : false, - layout : 'anchor', - defaultType : 'textfield', - items : [ - - this.getMacromoleculeCombo(), { - xtype : 'requiredtext', - id : this.id + 'stockSolution_name', - fieldLabel : 'Acronym', - labelWidth : 120, - width : 250 - }, { - xtype : 'requiredtext', - id : this.id + 'stockSolution_concentration', - fieldLabel : 'Conc. (mg/ml)', - labelWidth : 120, - width : 250 - }, - - { - id : this.id + 'stockSolution_storageTemperature', - fieldLabel : 'Storage Temp.(C)', - labelWidth : 120, - width : 250 - }, { - xtype : 'requiredtext', - id : this.id + 'stockSolution_volume', - fieldLabel : 'Volume in Well (µl)', - labelWidth : 120, - width : 250 - } ] - } ] - }, { - xtype : 'container', - flex : 1, - layout : 'anchor', - defaultType : 'textfield', - margin : '0 0 0 10', - items : [ this.getBufferCombo() ] - } ] - }; - -}; - -StockSolutionForm.prototype.getPanel = function(stockSolution) { - this.stockSolution = stockSolution; - this.panel = Ext.createWidget({ - xtype : 'container', - layout : 'vbox', - border : 0, - style : { - padding : '10px' - }, - fieldDefaults : { - labelAlign : 'left', - labelWidth : 50 - }, - items : [ this._getTopPanel(stockSolution), { - id : this.id + 'stockSolution_comments', - xtype : 'textareafield', - name : 'comments', - fieldLabel : 'Comments', - labelWidth : 120, - width : '100%' - } ] - }); - - this.setStockSolution(stockSolution); - return this.panel; -}; - -StockSolutionForm.prototype.input = function() { - return { - stock : { - "stockSolutionId" : 6, - "proposalId" : 3124, - "macromoleculeId" : 5933, - "bufferId" : 811, - "instructionSet3VO" : null, - "boxId" : 305861, - "storageTemperature" : "20", - "volume" : "300", - "concentration" : "1.2", - "comments" : "Buffer EDTA with A", - "name" : "A_EDTA_1.2", - "samples" : [], - "buffer" : "EDTA", - "macromolecule" : "A" - }, - proposal : new MeasurementGrid().input().proposal - }; -}; - -StockSolutionForm.prototype.test = function(targetId) { - var stockSolutionForm = new StockSolutionForm(); - BIOSAXS.proposal = new Proposal(stockSolutionForm.input().proposal); - var panel = stockSolutionForm.getPanel(new Shipment(stockSolutionForm.input().stock)); - panel.render(targetId); -}; - - -BoxWhiskerGraph.prototype.cleanArray = GenericGraph.prototype.cleanArray; -BoxWhiskerGraph.prototype.plotAxes = GenericGraph.prototype.plotAxes; -BoxWhiskerGraph.prototype.plotRuler = GenericGraph.prototype.plotRuler; -BoxWhiskerGraph.prototype.drawSVGVerticalText = GenericGraph.prototype.drawSVGVerticalText; -BoxWhiskerGraph.prototype.getClassColor = GenericGraph.prototype.getClassColor; -BoxWhiskerGraph.prototype.getDimensions = GenericGraph.prototype.getDimensions; -BoxWhiskerGraph.prototype.calculate = GenericGraph.prototype.calculate; -BoxWhiskerGraph.prototype.getMedian = GenericGraph.prototype.getMedian; -BoxWhiskerGraph.prototype.pointToPixel = GenericGraph.prototype.pointToPixel; -BoxWhiskerGraph.prototype.maxValueWhisker = GenericGraph.prototype.maxValueWhisker; -BoxWhiskerGraph.prototype.refresh = GenericGraph.prototype.refresh; - -/** - * Subclass of GenericGraph - * - * @maxBoxWidth - */ -function BoxWhiskerGraph(args){ - this.maxBoxWidth = 25; - - if (args == null){ - args = new Object(); - } - args["plotHorizontalByCluster"] = true; - - GenericGraph.call(this, args); - - if (args.maxBoxWidth != null){ - this.maxBoxWidth = args.maxBoxWidth; - } -}; - - - -/** -There are several different methods for calculating quartiles.[1] This calculator uses a method described by Moore and McCabe to find quartile values. -The same method also used by The TI-83 to calculate quartile values. -With this method, the first quartile is the median of the numbers below the median, the third quartile is the median of the numbers above the median. - -http://www.miniwebtool.com/quartile-calculator/ -http://www.alcula.com/calculators/statistics/box-plot/ -**/ -BoxWhiskerGraph.prototype.getQ1 = function(array){ - array = array.slice(0, array.length/2); - return this.getMedian(array); -}; - -BoxWhiskerGraph.prototype.getQ3 = function(array){ - array = array.slice((array.length + 1)/2); - return this.getMedian(array); - -}; - -BoxWhiskerGraph.prototype.getQ2 = function(array){ - return this.getMedian(array); -}; - -BoxWhiskerGraph.prototype.getBelowOutliers = function(belowLimit, array){ - var points = new Array(); - for (var i = 0; i < array.length; i++){ - if (array[i] <= belowLimit){ - points.push(array[i]); - } - } - return points; -}; - -BoxWhiskerGraph.prototype.getAboveOutliers = function(aboveLimit, array){ - var points = new Array(); - for (var i = 0; i < array.length; i++){ - if (array[i] >= aboveLimit){ - points.push(array[i]); - } - } - return points; -}; - - -BoxWhiskerGraph.prototype.minValueWhisker = function(belowLimit, q1, array){ - var points = new Array(); - - for (var i = 0; i < array.length; i++){ - if ((array[i] < q1) && (array[i]) > belowLimit){ - points.push(array[i]); - } - } - - if (points.length > 0){ - points.sort(function(a, b){return a - b;}); - return points[0]; - } - return null; -}; - -BoxWhiskerGraph.prototype.isNumber = function(value){ - if (value=="") return false; - - var d = parseInt(value); - if (!isNaN(d)) return true; else return false; - -}; - -BoxWhiskerGraph.prototype.plotBoxWhisker = function(boxProperties){ - if (this.maxBoxWidth != null){ - if (boxProperties.width > this.maxBoxWidth){ - boxProperties.x = boxProperties.x + (boxProperties.width/2) - (this.maxBoxWidth/2); - boxProperties.width = this.maxBoxWidth; - } - - } - - if (this.plotPoints){ - for(var i = 0; i < boxProperties.values.length; i++){ - var value = boxProperties.values[i]; - var toPixel = this.pointToPixel(value, boxProperties); - SVG.drawCircle(boxProperties.x + (boxProperties.width/2) + 2, toPixel, this.pointRadius, this.svg, [["fill", "green"], ["fill-opacity", this.fillOpacityPoint],['stroke-opacity', this.strokeOpacityPoint], ["stroke", "black"]]); - } - } - - - var boxColor = this.getClassColor(boxProperties.name); - var result = this.calculate(boxProperties.values); - /** Q1 **/ - - if (this.isNumber(result.Q1)){ - SVG.drawLine(boxProperties.x, this.pointToPixel(result.Q1, boxProperties), boxProperties.x + boxProperties.width, this.pointToPixel(result.Q1, boxProperties), this.svg, [["stroke", boxColor]]); - } - /** Q2 **/ - if (this.isNumber(result.Q2)){ - SVG.drawLine(boxProperties.x, this.pointToPixel(result.Q2, boxProperties), boxProperties.x + boxProperties.width, this.pointToPixel(result.Q2, boxProperties), this.svg, [["stroke", "blue"], ["stroke-width", "2"]]); - } - - /** Q3 **/ - if (this.isNumber(result.Q3)){ - SVG.drawLine(boxProperties.x, this.pointToPixel(result.Q3, boxProperties), boxProperties.x + boxProperties.width, this.pointToPixel(result.Q3, boxProperties), this.svg, [["stroke", boxColor]]); - } - - /** Concenting Q1 and Q3 **/ - if (this.isNumber(result.Q1)&&this.isNumber(result.Q3)){ - SVG.drawLine(boxProperties.x, this.pointToPixel(result.Q1, boxProperties), boxProperties.x, this.pointToPixel(result.Q3, boxProperties), this.svg, [["stroke", boxColor]]); - SVG.drawLine(boxProperties.x + boxProperties.width, this.pointToPixel(result.Q1, boxProperties), boxProperties.x + boxProperties.width, this.pointToPixel(result.Q3, boxProperties), this.svg, [["stroke", boxColor]]); - } - - /** min-whisker **/ - if (result["min-whisker"] != null){ - if (this.isNumber(result.Q1)){ - SVG.drawLine(boxProperties.x + (boxProperties.width/2) + 2, this.pointToPixel(result.Q1, boxProperties), boxProperties.x + (boxProperties.width/2) + 2, this.pointToPixel(result["min-whisker"], boxProperties), this.svg, [["stroke", boxColor]]); - } - SVG.drawLine(boxProperties.x,this.pointToPixel(result["min-whisker"], boxProperties), boxProperties.x + boxProperties.width, this.pointToPixel(result["min-whisker"], boxProperties) , this.svg, [["stroke", boxColor]]); - - } - - /** max-whisker **/ - if (result["max-whisker"] != null){ - if (this.isNumber(result.Q3)){ - SVG.drawLine(boxProperties.x + (boxProperties.width/2) + 2, this.pointToPixel(result.Q3, boxProperties), boxProperties.x + (boxProperties.width/2) + 2, this.pointToPixel(result["max-whisker"], boxProperties), this.svg, [["stroke", boxColor]]); - } - SVG.drawLine(boxProperties.x , this.pointToPixel(result["max-whisker"], boxProperties), boxProperties.x + boxProperties.width, this.pointToPixel(result["max-whisker"], boxProperties), this.svg, [["stroke", boxColor]]); - - } - - /** outliners **/ - if (result["above-outliers"] != null){ - for (var point in result["above-outliers"]){ - SVG.drawCircle(boxProperties.x + (boxProperties.width/2) + 2, this.pointToPixel(result["above-outliers"][point], boxProperties), this.pointRadius, this.svg, [["fill", "red"], ["fill-opacity", "1"]]); - //SVG.drawText(boxProperties.x + (boxProperties.width/2) - 2, this.pointToPixel(result["above-outliers"][point], boxProperties) + 2, "x",this.svg, [["fill", "black"], ["font-size", "x-small"]]); - } - } - if (result["below-outliers"] != null){ - for (var point in result["below-outliers"]){ - SVG.drawCircle(boxProperties.x + (boxProperties.width/2) + 2, this.pointToPixel(result["below-outliers"][point], boxProperties), this.pointRadius, this.svg, [["fill", "red"], ["fill-opacity", "1"]]); - //SVG.drawText(boxProperties.x + (boxProperties.width/2) - 2, this.pointToPixel(result["below-outliers"][point], boxProperties) + 2, "x",this.svg, [["fill", "black"], ["font-size", "x-small"]]); - } - } -}; - - - -BoxWhiskerGraph.prototype.plotClusterTitles = function(properties){ - /** Cluster Titles **/ - var posX = this.left + this.rulerWidth; - var data = this.data; - for(var i = 0; i < data.clusters.length; i++ ){ - /** title for the clusters **/ - posX = posX + this.interClustersSpace; - - /** Drawing title of classes **/ - var cluster = data.clusters[i]; - var posXClasses = posX; - for(var j = 0; j < cluster.classes.length; j++){ - //SVG.drawRectangle(posXClasses, this.height - (this.bottom + this.clusterTitleHeight + this.rulerHeight), properties.classWidth, this.rulerHeight, this.svg, [["fill", "green"]]); -// SVG.drawText(posXClasses + 1/6*properties.classWidth, this.height - (this.bottom + this.clusterTitleHeight + (this.rulerHeight*1/4)), cluster.classes[j].name, this.svg, [["style", "font-size:xx-small"]]); - var color = cluster.classes[j].color; - this.drawSVGVerticalText(posXClasses + 1/2*(properties.classWidth), this.height - (this.bottom - this.clusterTitleHeight + (this.rulerHeight)), cluster.classes[j].name , [["style", "font-size:x-small;"], ["fill", color]]); - - // SVG.drawRectangle(posXClasses, this.height - (this.bottom + this.clusterTitleHeight), properties.classWidth, 2, this.svg, [["fill", "black"]]); - posXClasses = posXClasses + properties.classWidth + this.interClassesSpace; - } - - var clusterTitleSpace = (data.clusters[i].classes.length * properties.classWidth) + ((data.clusters[i].classes.length - 1) * this.interClassesSpace); - //SVG.drawRectangle(posX, this.height - (this.bottom + this.clusterTitleHeight), clusterTitleSpace, this.clusterTitleHeight, this.svg, [["fill", "pink"]]); - SVG.drawRectangle(posX, this.height - (this.clusterTitleHeight+this.bottom), clusterTitleSpace, 1, this.svg, []); - -// var transform = ["translate", "(" + posX + 1/3*clusterTitleSpace +", " + this.height - (this.bottom + (this.clusterTitleHeight*1/4)) +")"], ["transform", "rotate(180)"]; -// var transform = ["transform", "translate(" + (posX + 1/3*clusterTitleSpace) +", " + (this.height - (this.bottom + (this.clusterTitleHeight*1/4))) + "), rotate(-90) "]; -// this.drawSVGVerticalText(posX + 1/3*clusterTitleSpace, this.height - (this.bottom + (this.clusterTitleHeight*1/4)),data.clusters[i].name, [["style", "font-size:x-small"]]); - SVG.drawText(posX , this.height - (this.bottom + (this.clusterTitleHeight*1/4)),data.clusters[i].name ,this.svg, [["style", "font-size:x-small"]]); - posX = posX + clusterTitleSpace; - } -}; - -BoxWhiskerGraph.prototype.plotWhisters = function(data, properties){ - var colors = ["yellow", "orange", "green"]; - var posX = this.left + this.rulerWidth; - for(var i = 0; i < data.clusters.length; i++ ){ - var cluster = data.clusters[i]; - /** inter cluster space **/ - //SVG.drawRectangle(posX, this.top, this.interClustersSpace, this.height - this.top - this.bottom - this.clusterTitleHeight - this.rulerHeight, this.svg, [["fill", "black"]]); - posX = posX + this.interClustersSpace; - - for (var j = 0; j < cluster.classes.length; j ++){ - - //SVG.drawRectangle(posX, this.top, properties.classWidth, this.height - this.top - this.bottom - this.clusterTitleHeight - this.rulerHeight, this.svg, [["fill", colors[j]]]); - this.plotBoxWhisker( - { - name : cluster.classes[j].name, - values : cluster.classes[j].values, - minPoint : properties.minPoint, - maxPoint : properties.maxPoint, - x : posX, - y : this.top, - width : properties.classWidth, - height : this.height - this.top - this.bottom - this.clusterTitleHeight - this.rulerHeight - - - } - ); - posX = posX + properties.classWidth; - //SVG.drawRectangle(posX, this.top, this.interClassesSpace, this.height - this.top - this.bottom - this.clusterTitleHeight - this.rulerHeight, this.svg, [["fill", "black"]]); - if (j < cluster.classes.length - 1){ - posX = posX + this.interClassesSpace; - } - } - } -}; - -BoxWhiskerGraph.prototype.draw = function(targetId, data){ - this.targetId = targetId; - var properties = (this.getDimensions(data)); - this.svg = SVG.createSVGCanvas(document.getElementById(this.targetId), [["width", this.width], ["height", this.height]]); - this.plotAxes(properties); - this.plotClusterTitles(properties); - this.plotWhisters(data,properties); -}; - -BoxWhiskerGraph.prototype.input = function(){ - return DATADOC.getBoxWhikerData(); -}; - -BoxWhiskerGraph.prototype.test = function(targetId){ - var plot = new BoxWhiskerGraph( - { - targetId : targetId, - height : 350, - width : 450, - maxBoxWidth : 25 - } - ); - plot.refresh(this.input()); -}; - - - - - - - -function DataSet(){ - this.json = null; - -}; - - -DataSet.prototype.loadFromJSON = function(json){ - if(json != null) { - if(this.validate(json)) { - this.json = json; - } - } -}; - - -DataSet.prototype.toJSON = function(json){ - return this.json; -}; - - -/** Abstract method to be override on childs classes **/ -DataSet.prototype.validate = function(json){ - if (true){ - return true; - } - /*else{ - throw "Data validation failed"; - }*/ -}; - - - - -Edge.prototype.getName = GraphItem.prototype.getName; -Edge.prototype.setName = GraphItem.prototype.setName; -Edge.prototype.getId = GraphItem.prototype.getId; - -function Edge(id, name, nodeSource, nodeTarget, args){ - GraphItem.prototype.constructor.call(this, id, name, args); - - this.sourceNode = nodeSource; - this.targetNode = nodeTarget; - -}; - -Edge.prototype.toJSON = function(){ - return {"index": this.id,"sourceIndex":this.sourceNode.getId(),"targetIndex":this.targetNode.getId(),"args":this.args}; -}; - -Edge.prototype.getNodeSource = function(){ - return this.sourceNode; -}; - -Edge.prototype.getNodeTarget = function(){ - return this.targetNode; -}; - -Edge.prototype.remove = function(){ - //Remove edge object in the nodes - this.getNodeSource().removeEdge(this); - this.getNodeTarget().removeEdge(this); - - this.deleted.notify(this); -}; - - -EdgeGraphFormatter.prototype.setProperties = ItemGraphFormatter.prototype.setProperties; -EdgeGraphFormatter.prototype.getDefault = ItemGraphFormatter.prototype.getDefault; -EdgeGraphFormatter.prototype.getSelected = ItemGraphFormatter.prototype.getSelected; -EdgeGraphFormatter.prototype.getOver = ItemGraphFormatter.prototype.getOver; -EdgeGraphFormatter.prototype.getDragging = ItemGraphFormatter.prototype.getDragging; -EdgeGraphFormatter.prototype.getId = ItemGraphFormatter.prototype.getId; -EdgeGraphFormatter.prototype.toJSON = ItemGraphFormatter.prototype.toJSON; -EdgeGraphFormatter.prototype.loadFromJSON = ItemGraphFormatter.prototype.loadFromJSON; -EdgeGraphFormatter.prototype._setEvents = ItemGraphFormatter.prototype._setEvents; - -function EdgeGraphFormatter(id, defaultFormat, selectedFormat, overFormat, dragginFormat){ - ItemGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, dragginFormat); - - this.getDefault().args.type = "EdgeGraphFormatter"; -}; - - - - -LineEdgeGraphFormatter.prototype.setProperties = EdgeGraphFormatter.prototype.setProperties; -LineEdgeGraphFormatter.prototype.getDefault = EdgeGraphFormatter.prototype.getDefault; -LineEdgeGraphFormatter.prototype.getSelected = EdgeGraphFormatter.prototype.getSelected; -LineEdgeGraphFormatter.prototype.getOver = EdgeGraphFormatter.prototype.getOver; -LineEdgeGraphFormatter.prototype.getDragging = EdgeGraphFormatter.prototype.getDragging; -LineEdgeGraphFormatter.prototype.getId = EdgeGraphFormatter.prototype.getId; -LineEdgeGraphFormatter.prototype.toJSON = EdgeGraphFormatter.prototype.toJSON; -LineEdgeGraphFormatter.prototype.loadFromJSON = EdgeGraphFormatter.prototype.loadFromJSON; -LineEdgeGraphFormatter.prototype._setEvents = EdgeGraphFormatter.prototype._setEvents; - -function LineEdgeGraphFormatter(id, defaultFormat, selectedFormat, overFormat, dragginFormat){ - EdgeGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, dragginFormat); - this.args.type = "LineEdgeGraphFormatter"; -}; - -/** DIRECTED **/ -DirectedLineEdgeGraphFormatter.prototype.setProperties = EdgeGraphFormatter.prototype.setProperties; -DirectedLineEdgeGraphFormatter.prototype.getDefault = EdgeGraphFormatter.prototype.getDefault; -DirectedLineEdgeGraphFormatter.prototype.getSelected = EdgeGraphFormatter.prototype.getSelected; -DirectedLineEdgeGraphFormatter.prototype.getOver = EdgeGraphFormatter.prototype.getOver; -DirectedLineEdgeGraphFormatter.prototype.getDragging = EdgeGraphFormatter.prototype.getDragging; -DirectedLineEdgeGraphFormatter.prototype.getId = EdgeGraphFormatter.prototype.getId; -DirectedLineEdgeGraphFormatter.prototype.toJSON = EdgeGraphFormatter.prototype.toJSON; -DirectedLineEdgeGraphFormatter.prototype.loadFromJSON = EdgeGraphFormatter.prototype.loadFromJSON; -DirectedLineEdgeGraphFormatter.prototype._setEvents = EdgeGraphFormatter.prototype._setEvents; - -function DirectedLineEdgeGraphFormatter(id, defaultFormat, selectedFormat, overFormat, dragginFormat){ - EdgeGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, dragginFormat); - this.args.type = "DirectedLineEdgeGraphFormatter"; - this.args.arrowSize = "3"; -}; -DirectedLineEdgeGraphFormatter.prototype.getArrowSize = function(){ - return this.args.arrowSize; -}; - -OdirectedLineEdgeGraphFormatter.prototype.setProperties = EdgeGraphFormatter.prototype.setProperties; -OdirectedLineEdgeGraphFormatter.prototype.getDefault = EdgeGraphFormatter.prototype.getDefault; -OdirectedLineEdgeGraphFormatter.prototype.getSelected = EdgeGraphFormatter.prototype.getSelected; -OdirectedLineEdgeGraphFormatter.prototype.getOver = EdgeGraphFormatter.prototype.getOver; -OdirectedLineEdgeGraphFormatter.prototype.getDragging = EdgeGraphFormatter.prototype.getDragging; -OdirectedLineEdgeGraphFormatter.prototype.getId = EdgeGraphFormatter.prototype.getId; -OdirectedLineEdgeGraphFormatter.prototype.toJSON = EdgeGraphFormatter.prototype.toJSON; -OdirectedLineEdgeGraphFormatter.prototype.loadFromJSON = EdgeGraphFormatter.prototype.loadFromJSON; -OdirectedLineEdgeGraphFormatter.prototype._setEvents = EdgeGraphFormatter.prototype._setEvents; - -function OdirectedLineEdgeGraphFormatter(id, defaultFormat, selectedFormat, overFormat, dragginFormat){ - EdgeGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, dragginFormat); - this.args.type = "DirectedLineEdgeGraphFormatter"; - this.args.arrowSize = "3"; -}; - -OdirectedLineEdgeGraphFormatter.prototype.getArrowSize = function(){ - return this.args.arrowSize; -}; - - -/** CUT (inhibition) **/ -CutDirectedLineEdgeGraphFormatter.prototype.setProperties = EdgeGraphFormatter.prototype.setProperties; -CutDirectedLineEdgeGraphFormatter.prototype.getDefault = EdgeGraphFormatter.prototype.getDefault; -CutDirectedLineEdgeGraphFormatter.prototype.getSelected = EdgeGraphFormatter.prototype.getSelected; -CutDirectedLineEdgeGraphFormatter.prototype.getOver = EdgeGraphFormatter.prototype.getOver; -CutDirectedLineEdgeGraphFormatter.prototype.getDragging = EdgeGraphFormatter.prototype.getDragging; -CutDirectedLineEdgeGraphFormatter.prototype.getId = EdgeGraphFormatter.prototype.getId; -CutDirectedLineEdgeGraphFormatter.prototype.toJSON = EdgeGraphFormatter.prototype.toJSON; -CutDirectedLineEdgeGraphFormatter.prototype.loadFromJSON = EdgeGraphFormatter.prototype.loadFromJSON; -CutDirectedLineEdgeGraphFormatter.prototype._setEvents = EdgeGraphFormatter.prototype._setEvents; - -function CutDirectedLineEdgeGraphFormatter(id, defaultFormat, selectedFormat, overFormat, dragginFormat){ - EdgeGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, dragginFormat); - this.args.type = "CutDirectedLineEdgeGraphFormatter"; -}; - - - - -BezierEdgeGraphFormatter.prototype.setProperties = EdgeGraphFormatter.prototype.setProperties; -BezierEdgeGraphFormatter.prototype.setProperties = EdgeGraphFormatter.prototype.setProperties; -BezierEdgeGraphFormatter.prototype.getDefault = EdgeGraphFormatter.prototype.getDefault; -BezierEdgeGraphFormatter.prototype.getSelected = EdgeGraphFormatter.prototype.getSelected; -BezierEdgeGraphFormatter.prototype.getOver = EdgeGraphFormatter.prototype.getOver; -BezierEdgeGraphFormatter.prototype.getDragging = EdgeGraphFormatter.prototype.getDragging; -BezierEdgeGraphFormatter.prototype.getId = EdgeGraphFormatter.prototype.getId; -BezierEdgeGraphFormatter.prototype.toJSON = EdgeGraphFormatter.prototype.toJSON; -BezierEdgeGraphFormatter.prototype.loadFromJSON = EdgeGraphFormatter.prototype.loadFromJSON; -BezierEdgeGraphFormatter.prototype._setEvents = EdgeGraphFormatter.prototype._setEvents; - -function BezierEdgeGraphFormatter(id, defaultFormat, selectedFormat, overFormat, dragginFormat){ - EdgeGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, dragginFormat); - this.args.type = "BezierEdgeGraphFormatter"; -}; - - - -DotDirectedLineEdgeGraphFormatter.prototype.setProperties = EdgeGraphFormatter.prototype.setProperties; -DotDirectedLineEdgeGraphFormatter.prototype.getDefault = EdgeGraphFormatter.prototype.getDefault; -DotDirectedLineEdgeGraphFormatter.prototype.getSelected = EdgeGraphFormatter.prototype.getSelected; -DotDirectedLineEdgeGraphFormatter.prototype.getOver = EdgeGraphFormatter.prototype.getOver; -DotDirectedLineEdgeGraphFormatter.prototype.getDragging = EdgeGraphFormatter.prototype.getDragging; -DotDirectedLineEdgeGraphFormatter.prototype.getId = EdgeGraphFormatter.prototype.getId; -DotDirectedLineEdgeGraphFormatter.prototype.toJSON = EdgeGraphFormatter.prototype.toJSON; -DotDirectedLineEdgeGraphFormatter.prototype.loadFromJSON = EdgeGraphFormatter.prototype.loadFromJSON; -DotDirectedLineEdgeGraphFormatter.prototype._setEvents = EdgeGraphFormatter.prototype._setEvents; - -function DotDirectedLineEdgeGraphFormatter(id, defaultFormat, selectedFormat, overFormat, dragginFormat){ - EdgeGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, dragginFormat); - this.args.type = "DotDirectedLineEdgeGraphFormatter"; -}; - - -OdotDirectedLineEdgeGraphFormatter.prototype.setProperties = EdgeGraphFormatter.prototype.setProperties; -OdotDirectedLineEdgeGraphFormatter.prototype.getDefault = EdgeGraphFormatter.prototype.getDefault; -OdotDirectedLineEdgeGraphFormatter.prototype.getSelected = EdgeGraphFormatter.prototype.getSelected; -OdotDirectedLineEdgeGraphFormatter.prototype.getOver = EdgeGraphFormatter.prototype.getOver; -OdotDirectedLineEdgeGraphFormatter.prototype.getDragging = EdgeGraphFormatter.prototype.getDragging; -OdotDirectedLineEdgeGraphFormatter.prototype.getId = EdgeGraphFormatter.prototype.getId; -OdotDirectedLineEdgeGraphFormatter.prototype.toJSON = EdgeGraphFormatter.prototype.toJSON; -OdotDirectedLineEdgeGraphFormatter.prototype.loadFromJSON = EdgeGraphFormatter.prototype.loadFromJSON; -OdotDirectedLineEdgeGraphFormatter.prototype._setEvents = EdgeGraphFormatter.prototype._setEvents; - -function OdotDirectedLineEdgeGraphFormatter(id, defaultFormat, selectedFormat, overFormat, dragginFormat){ - EdgeGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, dragginFormat); - this.args.type = "OdotDirectedLineEdgeGraphFormatter"; -}; - - - -function GraphDataset(){ - DataSet.prototype.constructor.call(this); - this.edges = new Object(); - this.vertices = new Object(); - this.verticesIndex = new Object(); - - //Events - this.newVertex = new Event(this); - this.vertexNameChanged = new Event(this); - this.vertexDeleted = new Event(this); - - this.newEdge = new Event(this); - this.edgeNameChanged = new Event(this); - this.edgeDeleted = new Event(this); - - this.json = new Object(); - this.json.vertices = new Array(); - this.json.edges = new Array(); - this.json.relations = new Array(); -}; - -GraphDataset.prototype.loadFromJSON = DataSet.prototype.loadFromJSON; -GraphDataset.prototype.toJSON = DataSet.prototype.toJSON; -GraphDataset.prototype.validate = DataSet.prototype.validate; - -/** Devuelve el numero de edges incidentes sobre el nodo con mas edges **/ -GraphDataset.prototype.getMaxClass = function(){ - var maxClassNode = 0; - for ( var node in this.vertices) { - if (this.vertices[node].getEdgesCount() > maxClassNode){ - maxClassNode = this.vertices[node].getEdgesCount(); - } - } - return maxClassNode; -}; - -/** Devuelve el numero de edges incidentes sobre el nodo con mas edges **/ -GraphDataset.prototype.getMinClass = function(){ - var minClassNode = Math.min(); - for ( var node in this.vertices) { - if (this.vertices[node].getEdgesCount() < minClassNode){ - minClassNode = this.vertices[node].getEdgesCount(); - } - } - return minClassNode; -}; - -GraphDataset.prototype.getVertexByName = function(nodeName){ - var results = new Array(); - - for (var vertexId in this.verticesIndex[nodeName]){ - var vertexByid = this.getVertexById(this.verticesIndex[nodeName][vertexId]); - results.push(vertexByid); - //* añadido nuevo porque fallaba el anterior codigo - return vertexByid - } - - if (results <= 1){ - return this.getVertexById(this.verticesIndex[nodeName]); - } - else{ - return results; - } -}; - -GraphDataset.prototype.getVertexById = function(id){ - return this.vertices[id]; -}; - -GraphDataset.prototype.toSIF = function(){ - var sifDataAdapter = new SifFileDataAdapter(); - return sifDataAdapter.toSIF(this); -}; - -GraphDataset.prototype.toSIFID = function(){ - var sifDataAdapter = new SifFileDataAdapter(); - return sifDataAdapter.toSIFID(this); -}; - -GraphDataset.prototype.toDOT = function(){ - var dotFileDataAdapter = new DotFileDataAdapter(); - return dotFileDataAdapter.toDOT(this); -}; - -GraphDataset.prototype.toDOTID = function(){ - var dotFileDataAdapter = new DotFileDataAdapter(); - return dotFileDataAdapter.toDOTID(this); -}; - -GraphDataset.prototype._addNode = function(nodeName, args){ - return new Vertex(this._getVerticesCount()-1, nodeName, args); -}; - -GraphDataset.prototype.addNode = function(nodeName, args){ - this.json.vertices.push(nodeName); - this._addVerticesIndex(nodeName, this._getVerticesCount() - 1); - var vertex = this._addNode(nodeName, args); - this.vertices[this._getVerticesCount()-1] = vertex; - this._setNodeEvents(vertex); - this.newVertex.notify(vertex); -}; - -GraphDataset.prototype._addVerticesIndex = function(nodeName, id){ - if (this.verticesIndex[nodeName] == null){ - this.verticesIndex[nodeName] = new Array(); - } - this.verticesIndex[nodeName].push(id); -}; - -GraphDataset.prototype.addEdge = function(edgeName, nodeSourceId, nodeTargetId, args){ - this.json.edges.push(edgeName); - var nodeSource = this.getVertexById(nodeSourceId); - var nodeTarget = this.getVertexById(nodeTargetId); - var index = this.getEdgesCount() - 1; - this.edges[index] = new Edge(index, edgeName, nodeSource, nodeTarget, args); - this.json.relations.push({"index": index, "sourceIndex": nodeSourceId, "targetIndex": nodeTargetId, "args": args }); - - nodeSource.addEdge(this.edges[index]); - nodeTarget.addEdge(this.edges[index]); - this._setEdgeEvents(this.edges[index]); - this.newEdge.notify(this.edges[index]); -}; - -GraphDataset.prototype.getVertices = function(){ - return this.vertices; -}; - -GraphDataset.prototype.getEdges = function(){ - return this.edges; -}; - -GraphDataset.prototype.getEdgeById = function(edgeId){ - return this.edges[edgeId]; -}; - -GraphDataset.prototype._getVerticesCount = function(){ - return this.json.vertices.length; -}; - - -GraphDataset.prototype.getVerticesCount = function(){ - var count = 0; - for ( var vertex in this.getVertices()) { - count ++; - } - return count; -}; - - -GraphDataset.prototype.getEdgesCount = function(){ - return this.json.edges.length; -}; - -GraphDataset.prototype.init = function(){ - this.edges = new Object(); - this.vertices = new Object(); -}; - -GraphDataset.prototype._setNodeEvents = function(node){ - var _this = this; - //NODE EVENTS - node.deleted.attach(function (sender, node){ - _this._removeNode(node); - }); - - node.nameChanged.attach(function (sender, args){ - var item = args.item; - var newName = item.name; - var indexes = _this.verticesIndex[args.previousName]; - for(var i = 0; i < indexes.length; i++){ - if(indexes[i] == item.id) - indexes.splice(i,1); - } - if(indexes.length == 0){ - delete _this.verticesIndex[args.previousName]; - } - _this._addVerticesIndex(newName, item.id); - _this.json.vertices[item.id] = newName; - _this.vertexNameChanged.notify(args); - }); -}; - -GraphDataset.prototype._setEdgeEvents = function(edge){ - var _this = this; - //EDGE EVENTS - edge.nameChanged.attach(function (sender, edge){ - _this.edgeNameChanged.notify(edge); - - }); - - edge.deleted.attach(function (sender, edge){ - _this._removeEdge(edge); - }); -}; - -GraphDataset.prototype._connectVerticesByName = function(nodeNameSource, nodeNameTarget){ - var source = this.getVertexByName(nodeNameSource); - var target = this.getVertexByName(nodeNameTarget); - - if ((source != null)&&(target!=null)){ - this.addEdge(source.getName() +"_" + target.getName(), source.getId(), target.getId(), {}); - } - else{ - if (source == null){ - console.log("No encontrado: " + nodeNameSource) - } - if (target == null){ - console.log("No encontrado: " + nodeNameTarget) - } - } -}; - -GraphDataset.prototype.loadFromJSON = function(json){ - var json = json; - this.init(); - this.json = new Object(); - this.json.vertices = new Array(); - this.json.edges = new Array(); - this.json.relations = new Array(); - - for ( var i = 0; i < json.nodes.length; i++) { - if (json.nodes[i] != null){ - var name = json.nodes[i]; - this.addNode(name); - } - else{ - this.json.vertices.push(null); - } - } - - for ( var i = 0; i < json.edges.length; i++) { - if (json.edges[i] != null){ - if (json.relations[i] != null){ - var name = json.edges[i]; - this.addEdge(name, json.relations[i].sourceIndex, json.relations[i].targetIndex, json.relations[i].args); - } - } - else{ - this.json.edges.push(null); - this.json.relations.push(null); - } - } -}; - -GraphDataset.prototype.prettyPrint = function(){ - for ( var node in this.vertices) { - console.log(this.vertices[node].getName() ); - for ( var j = 0; j < this.vertices[node].getEdgesIn().length; j++) { - console.log(" --> " + this.vertices[node].getEdgesIn()[j].getNodeTarget().getName() ); - } - } -}; - -GraphDataset.prototype._removeEdge = function(edge){ - this.json.edges[edge.getId()] = null; - this.json.relations[edge.getId()] = null; - - delete this.edges[edge.getId()]; - this.edgeDeleted.notify(edge); - - -}; - -GraphDataset.prototype._removeNode = function(node){ - this.json.vertices[node.getId()] = null; - delete this.vertices[node.getId()]; - this.vertexDeleted.notify(node); -}; - -GraphDataset.prototype.toJSON = function(){ - var json = new Object(); - var nodes = new Array(); - json.nodes = this.json.vertices; //nodes; - json.edges = this.json.edges; //edges; - json.relations = this.json.relations; - return json; -}; - -GraphDataset.prototype.clone = function(){ - var dsDataset = new GraphDataset(); - dsDataset.loadFromJSON(this.toJSON()); - return dsDataset; -}; -//GraphDataset.prototype.test = function(){ -// this.loadFromJSON(this.toJSON()); -//}; - -function labels(){ - var names = new Array(); - - var dataset = interactomeViewer.graphEditorWidget.dataset; - var layout = interactomeViewer.graphEditorWidget.layout; - - for ( var vertexId in interactomeViewer.graphEditorWidget.dataset.getVertices()) { - names.push(interactomeViewer.graphEditorWidget.dataset.getVertexById(vertexId).getName()); - } - - var sorted = (names.sort()); - console.log(sorted) - var distance = 0.01; - var altura = 0.6; - for ( var i = 0; i < names.length; i++) { - var id =dataset.getVertexByName(names[i]).getId(); - - layout.getNodeById(id).setCoordenates(distance, altura); - - - distance = parseFloat(distance) + parseFloat(0.03); - - altura = parseFloat(altura) + parseFloat(0.02); - - if (parseFloat(altura) == 0.9800000000000003){ - - altura = 0.6; - distance = distance - 0.51; - } - - } - - -}; - -function GraphItem(id, name, args){ - this.id = id; - this.name = name; - this.type = "NONE"; - - this.args = new Object(); - - - if (args!=null){ - this.args = args; - if (args.type !=null){ - this.type = args.type; - } - } - - //Events - this.nameChanged = new Event(this); - this.deleted = new Event(this); -} - -GraphItem.prototype.getName = function(){ - return this.name; -}; - -GraphItem.prototype.getId = function(){ - return this.id; -}; - -GraphItem.prototype.setName = function(name){ - var oldName = this.getName(); - this.name = name; - this.nameChanged.notify({"item": this, "previousName" : oldName}); -}; - - - - - -function LayoutDataset(){ - this.dataset = null; - this.vertices = new Object(); - this.changed = new Event(this); - - - this.args = new Object(); - - //RANDOM, CIRCLE - this.args.type = "CIRCLE"; -}; - -LayoutDataset.prototype.loadFromJSON = function(dataset, json){ - var _this = this; - this.vertices = new Object(); - this.dataset = dataset; //new GraphDataset(); - for ( var vertex in json) { - this.vertices[vertex] = new NodeLayout(vertex, json[vertex].x, json[vertex].y); - this.vertices[vertex].changed.attach(function (sender, item){ - _this.changed.notify(item); - }); - } - this._attachDatasetEvents(); -}; - - -LayoutDataset.prototype.toJSON = function(){ - var serialize = new Object(); - for ( var vertex in this.vertices) { - serialize[vertex] = new Object(); - serialize[vertex].x = this.vertices[vertex].x; - serialize[vertex].y = this.vertices[vertex].y; - } - serialize.dataset = new Object(); - serialize.dataset =this.dataset.toJSON(); - return serialize; -}; - -LayoutDataset.prototype.dataBind = function(graphDataset){ - this.dataset = graphDataset; - this._attachDatasetEvents(); - this._calculateLayout(); -}; - -LayoutDataset.prototype._removeVertex = function(vertexId){ - delete this.vertices[vertexId]; -}; - -LayoutDataset.prototype._attachDatasetEvents = function(){ - var _this = this; - - this.dataset.vertexDeleted.attach(function (sender, item){ - _this._removeVertex(item.getId()); - }); - - this.dataset.newVertex.attach(function (sender, item){ - _this.vertices[item.getId()] = new NodeLayout(item.getId(), 0.5, 0.5); - _this.vertices[item.getId()].changed.attach(function (sender, item){ - _this.changed.notify(item); - }); - }); -}; - -LayoutDataset.prototype.getType = function(){ - return this.args.type; -}; - -LayoutDataset.prototype._calculateLayoutVertices = function(type, count){ - - if (type == "CIRCLE"){ - var radius = 0.4; - var centerX = 0.5; - var centerY = 0.5; - var verticesCoordinates = new Array(); - for(var i = 0; i < count; i++){ - x = centerX + radius * Math.sin(i * 2 * Math.PI/count); - y = centerY + radius * Math.cos(i * 2 * Math.PI/count); - verticesCoordinates.push({'x':x,'y':y}); - } - return verticesCoordinates; - } -}; - - -LayoutDataset.prototype._calculateLayout = function(){ - var _this = this; - if (this.getType() == "RANDOM"){ - for ( var vertex in this.dataset.getVertices()) { - if (this.vertices[vertex] == null){ - this.vertices[vertex] = new NodeLayout(vertex, 0, 0); - } - this.vertices[vertex].setCoordinates(Math.random(), Math.random()); - this.vertices[vertex].changed.attach(function (sender, item){ - _this.changed.notify(item); - }); - } - } - - if ( this.getType() == "CIRCLE"){ - - var count = this.dataset._getVerticesCount(); - var verticesCoordinates = this._calculateLayoutVertices(this.getType(), count); - - var aux = 0; - for ( var vertex in this.dataset.getVertices()) { - if (this.vertices[vertex] == null){ - this.vertices[vertex] = new NodeLayout(vertex, 0, 0); - } - this.vertices[vertex].setCoordinates(verticesCoordinates[aux].x, verticesCoordinates[aux].y);//{"x":, "y":}; - aux++; - this.vertices[vertex].changed.attach(function (sender, item){ - _this.changed.notify(item); - }); - } - } - - - if (this.getType() == "SQUARE"){ - - var count = this.dataset._getVerticesCount(); - var xMin = 0.1; - var xMax = 0.9; - var yMin = 0.1; - var yMax = 0.9; - - var rows = Math.sqrt(count); - var step = (xMax - xMin) / rows; - - var verticesCoordinates = new Array(); - for(var i = 0; i < rows; i ++){ - for ( var j = 0; j < rows; j++) { - x = i * step + xMin; - y = j * step + yMin; - verticesCoordinates.push({'x':x,'y':y}); - } - } - - var aux = 0; - for ( var vertex in this.dataset.getVertices()) { - if (this.vertices[vertex] == null){ - this.vertices[vertex] = new NodeLayout(vertex, 0, 0); - } - this.vertices[vertex].setCoordinates(verticesCoordinates[aux].x, verticesCoordinates[aux].y);//{"x":, "y":}; - aux++; - this.vertices[vertex].changed.attach(function (sender, item){ - _this.changed.notify(item); - }); - } - } - -}; - -LayoutDataset.prototype.getNodeById = function(id){ - return this.vertices[id]; -}; - -LayoutDataset.prototype.getVerticesByArea = function(x1, y1, x2, y2){ - var vertices = new Array(); - for ( var vertex in this.dataset.getVertices()) { - if ((this.vertices[vertex].x >= x1)&&(this.vertices[vertex].x <= x2)){ - if ((this.vertices[vertex].y >= y1)&&(this.vertices[vertex].y <= y2)){ - vertices.push(this.vertices[vertex]); - } - } - } - return vertices; -}; - - - - -LayoutDataset.prototype.getLayout = function(type){ - - if (type == "CIRCLE"){ - this.args.type = "CIRCLE"; - this._calculateLayout(); - return; - } - - if (type == "SQUARE"){ - this.args.type = "SQUARE"; - this._calculateLayout(); - return; - } - - if (type == "RANDOM"){ - this.args.type = "RANDOM"; - this._calculateLayout(); - return; - } - - - var dotText = this.dataset.toDOTID(); - var url = "http://bioinfo.cipf.es/utils/ws/rest/network/layout/"+type+".coords"; - var _this = this; - - $.ajax({ - async: true, - type: "POST", - url: url, - dataType: "text", - data: { - dot :dotText - }, - cache: false, - success: function(data){ - var response = JSON.parse(data); - for ( var vertexId in response) { - _this.vertices[vertexId].setCoordinates(response[vertexId].x, response[vertexId].y); - } - } - }); - -// $.ajax({ -// async: true, -// type: "POST", -// url: url, -// dataType: "script", -// data: { -// dot :dotText -// }, -// cache: false, -// success: function(data){ -// var response = JSON.parse(data); -// for ( var vertexId in response) { -// _this.vertices[vertexId].setCoordinates(response[vertexId].x, response[vertexId].y); -// } -// } -// }); - -}; - -function NodeLayout(id, x, y, args){ - this.id = id; - this.x = x; - this.y = y; - this.changed = new Event(this); -}; - -NodeLayout.prototype.getId = function(id){ - return this.id; -}; - -NodeLayout.prototype.setCoordinates = function(x, y){ - this.x = x; - this.y = y; - this.changed.notify(this); -}; - - - -function NetworkDataSetFormatter(vertexFormatProperties, defaultEdgeProperties, args){ - DataSet.prototype.constructor.call(this); - - this.args = new Object(); - - this.vertices = new Object(); - this.edges = new Object(); - this.dataset = null; - this.maxClass = 0; - this.minClass = 0; - - /** Coordenates with default Setting */ - this.args.width = 1100; - this.args.height = 500; - - /** Optional parameters */ - this.args.backgroundColor = "#FFFFFF"; - this.args.backgroundImage = null; - this.args.backgroundImageHeight = null; - this.args.backgroundImageWidth = null; - this.args.backgroundImageX = null; - this.args.backgroundImageY = null; - - - this.args.balanceNodes = false; - this.args.nodesMaxSize = 3; - this.args.nodesMinSize = 0.5; - - - /** Zoom **/ - this.args.zoomScale = 1; - this.args.zoomScaleStepFactor = 0.4; - - if (args != null){ - if (args.backgroundImage != null){ - this.args.backgroundImage = args.backgroundImage; - } - - if (args.width != null){ - this.args.width = args.width; - } - - if (args.height != null){ - this.args.height = args.height; - this.args.svgHeight = args.height; - } - - if (args.svgHeight != null){ - this.args.svgHeight = args.svgHeight; - } - - if (args.backgroundColor != null){ - this.args.backgroundColor = args.backgroundColor; - } - - if(args.balanceNodes != null){ - this.args.balanceNodes = args.balanceNodes; - } - - if(args.nodesMaxSize != null){ - this.args.nodesMaxSize = args.nodesMaxSize; - } - if(args.nodesMinSize != null){ - this.args.nodesMinSize = args.nodesMinSize; - } - } - - this.args.defaultEdgeProperties = {"fill":"#000000", "radius":"1", "stroke":"#000000", "size":1, "title":{"fontSize":10, "fontColor":"#000000"}}; - this.args.vertexFormatProperties = { "fill":"#000000", "stroke":"#000000", "stroke-opacity":"#000000"}; - - if (vertexFormatProperties!= null){ - this.args.vertexFormatProperties = vertexFormatProperties; - } - if (defaultEdgeProperties!= null){ - this.args.defaultEdgeProperties = defaultEdgeProperties; - } - - /** Events **/ - this.changed = new Event(this); - this.edgeChanged = new Event(this); - this.resized = new Event(this); - this.backgroundImageChanged= new Event(this); - this.backgroundColorChanged= new Event(this); -}; - -NetworkDataSetFormatter.prototype.loadFromJSON = function(dataset, json){ - this.args = new Object(); - this.vertices = new Object(); - this.args = json; - this._setDataset(dataset); - var _this = this; - - for ( var vertex in json.vertices) { - this.addVertex(vertex, json.vertices[vertex]); - } - - for ( var edgeId in json.edges) { - this.addEdge(edgeId, json.edges[edgeId]); - } -}; - - -NetworkDataSetFormatter.prototype.toJSON = function(){ - - -// this.args.vertices = new Object(); -// this.args.edges = new Object(); -// -// for ( var vertex in this.vertices) { -// this.args.vertices[vertex] = this.getVertexById(vertex).toJSON(); -// } -// for ( var edge in this.edges) { -// this.args.edges[edge] = this.getEdgeById(edge).toJSON(); -// } -// -// return (this.args); - - - var serialize = new Object(); - serialize = JSON.parse(JSON.stringify(this.args)); - serialize.vertices = new Object(); - serialize.edges = new Object(); - - for ( var vertex in this.vertices) { - serialize.vertices[vertex] = this.getVertexById(vertex).toJSON(); - } - for ( var edge in this.edges) { - serialize.edges[edge] = this.getEdgeById(edge).toJSON(); - } - - return (serialize); -}; - - - -NetworkDataSetFormatter.prototype._getNodeSize = function(nodeId){ - if (this.isVerticesBalanced()){ - var total = this.maxClass - this.minClass; - if (total == 0){ total = 1;} - var nodeConnection = this.dataset.getVertexById(nodeId).getEdges().length; - return ((nodeConnection*this.args.nodesMaxSize)/total) + this.args.nodesMinSize; - } - return this.getVertexById(nodeId).getDefault().getSize(); -}; - -NetworkDataSetFormatter.prototype._recalculateSize = function(){ - if (this.isVerticesBalanced()){ - this.maxClass = this.dataset.getMaxClass(); - this.minClass = this.dataset.getMinClass(); - for ( var vertexIdAll in this.vertices) { - var size = this._getNodeSize(vertexIdAll); - this.vertices[vertexIdAll].getDefault().setSize(size); - this.vertices[vertexIdAll].getSelected().setSize(size); - this.vertices[vertexIdAll].getOver().setSize(size); - } - } -}; - - -NetworkDataSetFormatter.prototype.addVertex = function(vertexId, json){ - - - if (json == null){ - this.vertices[vertexId] = new CircleVertexGraphFormatter(vertexId, this.args.vertexFormatProperties.defaultFormat, this.args.vertexFormatProperties.selected, this.args.vertexFormatProperties.over, this.args.vertexFormatProperties.dragging); - } - else{ - - /** Cargo los attributos desde el json **/ - if (json.type == null){ - this.vertices[vertexId] = new CircleVertexGraphFormatter(vertexId, this.args.vertexFormatProperties.defaultFormat, this.args.vertexFormatProperties.selected, this.args.vertexFormatProperties.over, this.args.vertexFormatProperties.dragging); - } - - if ((json.type == "SquareVertexGraphFormatter")||(json.type == "SquareVertexNetworkFormatter")){ - this.vertices[vertexId] = new SquareVertexGraphFormatter(vertexId); - this.vertices[vertexId].loadFromJSON(json); - } - - if ((json.type == "CircleVertexGraphFormatter")||(json.type == "CircleVertexNetworkFormatter")){ - this.vertices[vertexId] = new CircleVertexGraphFormatter(vertexId); - this.vertices[vertexId].loadFromJSON(json); - } - - if ((json.type == "EllipseVertexGraphFormatter")||(json.type == "EllipseVertexNetworkFormatter")){ - this.vertices[vertexId] = new EllipseVertexGraphFormatter(vertexId); - this.vertices[vertexId].loadFromJSON(json); - } - - if ((json.type == "RectangleVertexGraphFormatter")||(json.type == "RectangleVertexNetworkFormatter")){ - this.vertices[vertexId] = new RectangleVertexGraphFormatter(vertexId); - this.vertices[vertexId].loadFromJSON(json); - } - - if ((json.type == "RoundedVertexGraphFormatter")||(json.type == "RoundedVertexNetworkFormatter")){ - this.vertices[vertexId] = new RoundedVertexGraphFormatter(vertexId); - this.vertices[vertexId].loadFromJSON(json); - } - - } - - - var _this = this; - this.vertices[vertexId].stateChanged.attach(function (sender, item){ - _this.changed.notify(item); - }); - - var size = this._getNodeSize(vertexId); - this.vertices[vertexId].defaultFormat.args.size = size; - this.vertices[vertexId].selected.args.size = size; - this.vertices[vertexId].over.args.size = size; - -}; - -NetworkDataSetFormatter.prototype.addEdge = function(edgeId, json){ - - /** Es un edge nuevo que le doy los atributos por defecto **/ - if (json == null){ - if (this.dataset.getEdgeById(edgeId).getNodeSource().getId() == this.dataset.getEdgeById(edgeId).getNodeTarget().getId()){ - this.edges[edgeId] = new BezierEdgeGraphFormatter(edgeId, this.args.defaultEdgeProperties.defaultFormat, this.args.defaultEdgeProperties.selected, this.args.defaultEdgeProperties.over, this.args.defaultEdgeProperties.dragging); - }else{ - this.edges[edgeId] = new DirectedLineEdgeGraphFormatter(edgeId, this.args.defaultEdgeProperties.defaultFormat, this.args.defaultEdgeProperties.selected, this.args.defaultEdgeProperties.over, this.args.defaultEdgeProperties.dragging); - } - } - else{ - /** Cargo los attributos desde el json **/ - if (json.type == null){ - this.edges[edgeId] = new LineEdgeGraphFormatter(edgeId, this.args.defaultEdgeProperties.defaultFormat, this.args.defaultEdgeProperties.selected, this.args.defaultEdgeProperties.over, this.args.defaultEdgeProperties.dragging); - } - - if ((json.type == "LineEdgeGraphFormatter")||(json.type == "LineEdgeNetworkFormatter")){ - this.edges[edgeId] = new LineEdgeGraphFormatter(edgeId); - this.edges[edgeId].loadFromJSON(json); - } - if ((json.type == "DirectedLineEdgeGraphFormatter")||(json.type == "DirectedLineEdgeNetworkFormatter")){ - this.edges[edgeId] = new DirectedLineEdgeGraphFormatter(edgeId); - this.edges[edgeId].loadFromJSON(json); - } - - if ((json.type == "BezierEdgeGraphFormatter")||(json.type == "BezierEdgeNetworkFormatter")){ - this.edges[edgeId] = new BezierEdgeGraphFormatter(edgeId); - this.edges[edgeId].loadFromJSON(json); - } - - - if ((json.type == "CutDirectedLineEdgeGraphFormatter")||(json.type == "CutDirectedLineEdgeNetworkFormatter")){ - this.edges[edgeId] = new CutDirectedLineEdgeGraphFormatter(edgeId); - this.edges[edgeId].loadFromJSON(json); - } - - if ((json.type == "DotDirectedLineEdgeGraphFormatter")||(json.type == "DotDirectedLineEdgeNetworkFormatter")){ - this.edges[edgeId] = new DotDirectedLineEdgeGraphFormatter(edgeId); - this.edges[edgeId].loadFromJSON(json); - } - if ((json.type == "OdotDirectedLineEdgeGraphFormatter")||(json.type == "OdotDirectedLineEdgeNetworkFormatter")){ - this.edges[edgeId] = new OdotDirectedLineEdgeGraphFormatter(edgeId); - this.edges[edgeId].loadFromJSON(json); - } - - if ((json.type == "OdirectedLineEdgeGraphFormatter")||(json.type == "OdirectedLineEdgeNetworkFormatter")){ - this.edges[edgeId] = new OdirectedLineEdgeGraphFormatter(edgeId); - this.edges[edgeId].loadFromJSON(json); - } - } - - var _this = this; - this.edges[edgeId].stateChanged.attach(function (sender, item){ - _this.edgeChanged.notify(item); - }); - - this._recalculateSize(); -}; - -NetworkDataSetFormatter.prototype._setDataset = function(dataset){ - this.dataset = dataset; - this.maxClass = dataset.getMaxClass(); - this.minClass = dataset.getMinClass(); - this._attachDatasetEvents(); -}; - -NetworkDataSetFormatter.prototype.changeEdgeType = function(edgeId, type){ - if ((type == "LineEdgeGraphFormatter")||(type == "LineEdgeNetworkFormatter")){ - this.edges[edgeId] = new LineEdgeGraphFormatter(edgeId, this.args.defaultEdgeProperties.defaultFormat, this.args.defaultEdgeProperties.selected, this.args.defaultEdgeProperties.over, this.args.defaultEdgeProperties.dragging); - - } - if ((type == "DirectedLineEdgeGraphFormatter")||(type == "DirectedLineEdgeNetworkFormatter")){ - this.edges[edgeId] = new DirectedLineEdgeGraphFormatter(edgeId, this.args.defaultEdgeProperties.defaultFormat, this.args.defaultEdgeProperties.selected, this.args.defaultEdgeProperties.over, this.args.defaultEdgeProperties.dragging); - } - - if ((type == "CutDirectedLineEdgeGraphFormatter")||(type == "CutDirectedLineEdgeNetworkFormatter")){ - this.edges[edgeId] = new CutDirectedLineEdgeGraphFormatter(edgeId, this.args.defaultEdgeProperties.defaultFormat, this.args.defaultEdgeProperties.selected, this.args.defaultEdgeProperties.over, this.args.defaultEdgeProperties.dragging); - } - - if ((type == "DotDirectedLineEdgeGraphFormatter")||(type == "DotDirectedLineEdgeNetworkFormatter")){ - this.edges[edgeId] = new DotDirectedLineEdgeGraphFormatter(edgeId, this.args.defaultEdgeProperties.defaultFormat, this.args.defaultEdgeProperties.selected, this.args.defaultEdgeProperties.over, this.args.defaultEdgeProperties.dragging); - } - - if ((type == "OdotDirectedLineEdgeGraphFormatter")||(type == "OdotDirectedLineEdgeNetworkFormatter")){ - this.edges[edgeId] = new OdotDirectedLineEdgeGraphFormatter(edgeId, this.args.defaultEdgeProperties.defaultFormat, this.args.defaultEdgeProperties.selected, this.args.defaultEdgeProperties.over, this.args.defaultEdgeProperties.dragging); - } - - if ((type == "OdirectedLineEdgeGraphFormatter")||(type == "OdirectedLineEdgeNetworkFormatter")){ - this.edges[edgeId] = new OdirectedLineEdgeGraphFormatter(edgeId, this.args.defaultEdgeProperties.defaultFormat, this.args.defaultEdgeProperties.selected, this.args.defaultEdgeProperties.over, this.args.defaultEdgeProperties.dragging); - } - - - - var _this = this; - this.edges[edgeId].stateChanged.attach(function (sender, item){ - _this.edgeChanged.notify(item); - }); - _this.edgeChanged.notify(this.edges[edgeId]); -}; -/* -classe = "SquareNetworkNodeFormatter"; -} -if (value == "circle"){ - classe = "CircleNetworkNodeFormatter"; - **/ -NetworkDataSetFormatter.prototype.changeNodeType = function(vertexId, type){ - var defaultFormat = JSON.parse(JSON.stringify(this.vertices[vertexId].getDefault())); - var selectedFormat = JSON.parse(JSON.stringify(this.vertices[vertexId].getSelected())); - var defaultFormat = JSON.parse(JSON.stringify(this.vertices[vertexId].getDefault())); - var defaultFormat = JSON.parse(JSON.stringify(this.vertices[vertexId].getDefault())); - - if ((type == "SquareVertexGraphFormatter")||(type == "SquareVertexNetworkFormatter")){ - this.vertices[vertexId] = new SquareVertexGraphFormatter(vertexId,defaultFormat, this.args.vertexFormatProperties.selected, this.args.vertexFormatProperties.over, this.args.vertexFormatProperties.dragging); - } - - if ((type == "CircleVertexGraphFormatter")||(type == "CircleVertexNetworkFormatter")){ - this.vertices[vertexId] = new CircleVertexGraphFormatter(vertexId, defaultFormat, this.args.vertexFormatProperties.selected, this.args.vertexFormatProperties.over, this.args.vertexFormatProperties.dragging); - } - - if ((type == "EllipseVertexGraphFormatter")||(type == "EllipseVertexNetworkFormatter")){ - this.vertices[vertexId] = new EllipseVertexGraphFormatter(vertexId, defaultFormat, this.args.vertexFormatProperties.selected, this.args.vertexFormatProperties.over, this.args.vertexFormatProperties.dragging); - } - - if ((type == "RectangleVertexGraphFormatter")||(type == "RectangleVertexNetworkFormatter")){ - this.vertices[vertexId] = new RectangleVertexGraphFormatter(vertexId, defaultFormat, this.args.vertexFormatProperties.selected, this.args.vertexFormatProperties.over, this.args.vertexFormatProperties.dragging); - } - - if ((type == "RoundedVertexGraphFormatter")||(type == "RoundedVertexNetworkFormatter")){ - this.vertices[vertexId] = new RoundedVertexGraphFormatter(vertexId, defaultFormat, this.args.vertexFormatProperties.selected, this.args.vertexFormatProperties.over, this.args.vertexFormatProperties.dragging); - } - - if ((type == "OctagonVertexGraphFormatter")||(type == "OctagonVertexNetworkhFormatter")){ - this.vertices[vertexId] = new OctagonVertexGraphFormatter(vertexId, defaultFormat, this.args.vertexFormatProperties.selected, this.args.vertexFormatProperties.over, this.args.vertexFormatProperties.dragging); - } - - - var _this = this; - this.vertices[vertexId].stateChanged.attach(function (sender, item){ - _this.changed.notify(item); - }); - _this.changed.notify(this.vertices[vertexId]); -}; - - - -NetworkDataSetFormatter.prototype.dataBind = function(networkDataSet){ - var _this = this; - this._setDataset(networkDataSet); - - for ( var vertex in this.dataset.getVertices()) { - this.addVertex(vertex); - } - - for ( var edge in this.dataset.getEdges()) { - this.addEdge(edge); - } -}; - -NetworkDataSetFormatter.prototype._removeEdge = function(edgeId){ - delete this.edges[edgeId]; -}; - -NetworkDataSetFormatter.prototype._removeVertex = function(vertexId){ - delete this.vertices[vertexId]; -}; - -NetworkDataSetFormatter.prototype._attachDatasetEvents = function(id){ - var _this = this; - this.dataset.vertexDeleted.attach(function (sender, item){ - _this._removeVertex(item.getId()); - }); - - this.dataset.edgeDeleted.attach(function (sender, item){ - _this._removeEdge(item.getId()); - }); - - this.dataset.newVertex.attach(function (sender, item){ - _this.addVertex(item.getId()); - }); - - this.dataset.newEdge.attach(function (sender, item){ - _this.addEdge(item.getId()); - }); -}; - - -NetworkDataSetFormatter.prototype.getVertexById = function(id){ - return this.vertices[id]; -}; - -NetworkDataSetFormatter.prototype.getEdgeById = function(id){ - return this.edges[id]; -}; - -NetworkDataSetFormatter.prototype.makeLabelsBigger = function(){ -for ( var vertexId in this.vertices) { - var fontSize = this.vertices[vertexId].getDefault().getFontSize() + 2; - this.vertices[vertexId].getDefault().setFontSize(fontSize); - } -}; - -NetworkDataSetFormatter.prototype.makeLabelsSmaller = function(){ - for ( var vertexId in this.vertices) { - var fontSize = this.vertices[vertexId].getDefault().getFontSize() - 2; - this.vertices[vertexId].getDefault().setFontSize(fontSize); - } -}; - - -NetworkDataSetFormatter.prototype.resize = function(width, height){ - this.args.width = width; - this.args.height = height; - this.resized.notify(); -}; - -/** ZOOM GETTERS **/ -NetworkDataSetFormatter.prototype.getZoomScaleStepFactor = function(){return this.args.zoomScaleStepFactor;}; -NetworkDataSetFormatter.prototype.setZoomScaleStepFactor = function(scaleFactor){this.args.zoomScaleStepFactor = scaleFactor;}; -NetworkDataSetFormatter.prototype.getZoomScale = function(){return this.args.zoomScale;}; -NetworkDataSetFormatter.prototype.setZoomScale = function(scale){this.args.zoomScale = scale;}; - -NetworkDataSetFormatter.prototype.getNodesMaxSize = function(){return this.args.nodesMaxSize;}; -NetworkDataSetFormatter.prototype.getNodesMinSize = function(){return this.args.nodesMinSize;}; - -/** SIZE SETTERS AND GETTERS **/ -NetworkDataSetFormatter.prototype.isVerticesBalanced = function(){return this.args.balanceNodes;}; -NetworkDataSetFormatter.prototype.getWidth = function(){return this.args.width;}; -NetworkDataSetFormatter.prototype.getHeight = function(){return this.args.height;}; - -/** OPTIONAL PARAMETERS GETTERS AND SETTERS **/ -NetworkDataSetFormatter.prototype.getBackgroundImage = function(){return this.args.backgroundImage;}; -NetworkDataSetFormatter.prototype.setBackgroundImage = function(value){this.args.backgroundImage = value; this.backgroundImageChanged.notify(this);}; - - -NetworkDataSetFormatter.prototype.getBackgroundImageWidth = function(){return this.args.backgroundImageWidth;}; -NetworkDataSetFormatter.prototype.setBackgroundImageWidth = function(value){this.args.backgroundImageWidth = value; this.backgroundImageChanged.notify(this);}; - -NetworkDataSetFormatter.prototype.getBackgroundImageHeight = function(){return this.args.backgroundImageHeight;}; -NetworkDataSetFormatter.prototype.setBackgroundImageHeight = function(value){this.args.backgroundImageHeight = value; this.backgroundImageChanged.notify(this);}; - -NetworkDataSetFormatter.prototype.getBackgroundImageX = function(){return this.args.backgroundImageX;}; -NetworkDataSetFormatter.prototype.setBackgroundImageX = function(value){this.args.backgroundImageX = value; this.backgroundImageChanged.notify(this);}; - -NetworkDataSetFormatter.prototype.getBackgroundImageY = function(){return this.args.backgroundImageX;}; -NetworkDataSetFormatter.prototype.setBackgroundImageY = function(value){this.args.backgroundImageY = value; this.backgroundImageChanged.notify(this);}; - - - -NetworkDataSetFormatter.prototype.getBackgroundColor = function(){return this.args.backgroundColor;}; -NetworkDataSetFormatter.prototype.setBackgroundColor = function(value){this.args.backgroundColor = value; this.backgroundColorChanged.notify(this);}; - -NetworkDataSetFormatter.prototype.getWidth = function(){return this.args.width;}; -NetworkDataSetFormatter.prototype.setWidth = function(value){this.args.width = value;}; - -NetworkDataSetFormatter.prototype.getHeight = function(){return this.args.height;}; -NetworkDataSetFormatter.prototype.setHeight = function(value){this.args.height = value;}; - - - - -Vertex.prototype.getName = GraphItem.prototype.getName; -Vertex.prototype.setName = GraphItem.prototype.setName; -Vertex.prototype.getId = GraphItem.prototype.getId; - -function Vertex(id, name, args){ - GraphItem.prototype.constructor.call(this, id, name, args); - this.edgesIn= new Array(); - this.edgesOut= new Array(); -}; - -Vertex.prototype.getEdges = function(){ - return this.edgesIn.concat(this.edgesOut); -}; - -Vertex.prototype.getEdgesCount = function(){ - return this.getEdges().length; -}; - -Vertex.prototype.getEdgesIn = function(){ - return this.edgesIn; -}; - -Vertex.prototype.getEdgesOut = function(){ - return this.edgesOut; -}; - -Vertex.prototype.addEdge = function(edge){ - if (edge.getNodeSource().getId() == this.id){ - this.edgesIn.push(edge); - } - else{ - this.edgesOut.push(edge); - } -}; - -Vertex.prototype.removeEdge = function(edge){ - for ( var i = 0; i < this.getEdgesIn().length; i++) { - var edgeIn = this.edgesIn[i]; - if (edgeIn.getId() == edge.getId()){ - this.edgesIn.splice(i, 1); - } - } - for ( var i = 0; i < this.getEdgesOut().length; i++) { - var edgeIn = this.edgesOut[i]; - if (edgeIn.getId() == edge.getId()){ - this.edgesOut.splice(i, 1); - } - } -}; - -Vertex.prototype.remove = function(){ - var edges = this.getEdges(); - for ( var i = 0; i < edges.length; i++) { - var edge = edges[i]; - edge.remove(); - } - this.deleted.notify(this); -}; - - - - - -VertexGraphFormatter.prototype.getDefault = ItemGraphFormatter.prototype.getDefault; -VertexGraphFormatter.prototype.getSelected = ItemGraphFormatter.prototype.getSelected; -VertexGraphFormatter.prototype.getOver = ItemGraphFormatter.prototype.getOver; -VertexGraphFormatter.prototype.getDragging = ItemGraphFormatter.prototype.getDragging; -VertexGraphFormatter.prototype.getId = ItemGraphFormatter.prototype.getId; -VertexGraphFormatter.prototype.toJSON = ItemGraphFormatter.prototype.toJSON; -VertexGraphFormatter.prototype.loadFromJSON = ItemGraphFormatter.prototype.loadFromJSON; -VertexGraphFormatter.prototype._setEvents = ItemGraphFormatter.prototype._setEvents; - - -function VertexGraphFormatter(defaultFormat, selectedFormat, overFormat, draggingFormat){ - ItemGraphFormatter.prototype.constructor.call(this, defaultFormat, selectedFormat, overFormat, draggingFormat); -}; - - -CircleVertexGraphFormatter.prototype.getDefault = VertexGraphFormatter.prototype.getDefault; -CircleVertexGraphFormatter.prototype.getSelected = VertexGraphFormatter.prototype.getSelected; -CircleVertexGraphFormatter.prototype.getOver = VertexGraphFormatter.prototype.getOver; -CircleVertexGraphFormatter.prototype.getDragging = VertexGraphFormatter.prototype.getDragging; -CircleVertexGraphFormatter.prototype.getId = VertexGraphFormatter.prototype.getId; -CircleVertexGraphFormatter.prototype.toJSON = VertexGraphFormatter.prototype.toJSON; -CircleVertexGraphFormatter.prototype.loadFromJSON = VertexGraphFormatter.prototype.loadFromJSON; -CircleVertexGraphFormatter.prototype._setEvents = VertexGraphFormatter.prototype._setEvents; - -function CircleVertexGraphFormatter(id, defaultFormat, selectedFormat, overFormat, draggingFormat){ - VertexGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, draggingFormat); - this.args.type = "CircleVertexGraphFormatter"; - if (defaultFormat != null){ - if (defaultFormat.radius != null){ - this.defaultFormat.args.radius = defaultFormat.radius; - } - } - - if (selectedFormat != null){ - if (selectedFormat.radius != null){ - this.selected.args.radius = selectedFormat.radius; - } - } - - if (overFormat != null){ - if (overFormat.radius != null){ - this.over.args.radius = overFormat.radius; - } - } - - if (draggingFormat != null){ - if (draggingFormat.radius != null){ - this.dragging.args.draggingFormat = draggingFormat.radius; - } - } -}; - - - -SquareVertexGraphFormatter.prototype.getDefault = VertexGraphFormatter.prototype.getDefault; -SquareVertexGraphFormatter.prototype.getSelected = VertexGraphFormatter.prototype.getSelected; -SquareVertexGraphFormatter.prototype.getOver = VertexGraphFormatter.prototype.getOver; -SquareVertexGraphFormatter.prototype.getDragging = VertexGraphFormatter.prototype.getDragging; -SquareVertexGraphFormatter.prototype.getId = VertexGraphFormatter.prototype.getId; -SquareVertexGraphFormatter.prototype.toJSON = VertexGraphFormatter.prototype.toJSON; -SquareVertexGraphFormatter.prototype.loadFromJSON = VertexGraphFormatter.prototype.loadFromJSON; -SquareVertexGraphFormatter.prototype._setEvents = VertexGraphFormatter.prototype._setEvents; - -function SquareVertexGraphFormatter(id, defaultFormat, selectedFormat, overFormat, draggingFormat){ - VertexGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, draggingFormat); - this.args.type = "SquareVertexGraphFormatter"; - if (defaultFormat != null){ - if (defaultFormat.radius != null){ - this.defaultFormat.args.radius = defaultFormat.radius; - } - } - - if (selectedFormat != null){ - if (selectedFormat.radius != null){ - this.selected.args.radius = selectedFormat.radius; - } - } - - if (overFormat != null){ - if (overFormat.radius != null){ - this.over.args.radius = overFormat.radius; - } - } - - if (draggingFormat != null){ - if (draggingFormat.radius != null){ - this.dragging.args.draggingFormat = draggingFormat.radius; - } - } -}; - - -EllipseVertexGraphFormatter.prototype.getDefault = VertexGraphFormatter.prototype.getDefault; -EllipseVertexGraphFormatter.prototype.getSelected = VertexGraphFormatter.prototype.getSelected; -EllipseVertexGraphFormatter.prototype.getOver = VertexGraphFormatter.prototype.getOver; -EllipseVertexGraphFormatter.prototype.getDragging = VertexGraphFormatter.prototype.getDragging; -EllipseVertexGraphFormatter.prototype.getId = VertexGraphFormatter.prototype.getId; -EllipseVertexGraphFormatter.prototype.toJSON = VertexGraphFormatter.prototype.toJSON; -EllipseVertexGraphFormatter.prototype.loadFromJSON = VertexGraphFormatter.prototype.loadFromJSON; -EllipseVertexGraphFormatter.prototype._setEvents = VertexGraphFormatter.prototype._setEvents; - -function EllipseVertexGraphFormatter(id, defaultFormat, selectedFormat, overFormat, draggingFormat){ - VertexGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, draggingFormat); - this.args.type = "EllipseVertexGraphFormatter"; - if (defaultFormat != null){ - if (defaultFormat.radius != null){ - this.defaultFormat.args.radius = defaultFormat.radius; - } - } - - if (selectedFormat != null){ - if (selectedFormat.radius != null){ - this.selected.args.radius = selectedFormat.radius; - } - } - - if (overFormat != null){ - if (overFormat.radius != null){ - this.over.args.radius = overFormat.radius; - } - } - - if (draggingFormat != null){ - if (draggingFormat.radius != null){ - this.dragging.args.draggingFormat = draggingFormat.radius; - } - } -}; - - - -RectangleVertexGraphFormatter.prototype.getDefault = ItemGraphFormatter.prototype.getDefault; -RectangleVertexGraphFormatter.prototype.getSelected = ItemGraphFormatter.prototype.getSelected; -RectangleVertexGraphFormatter.prototype.getOver = ItemGraphFormatter.prototype.getOver; -RectangleVertexGraphFormatter.prototype.getDragging = ItemGraphFormatter.prototype.getDragging; -RectangleVertexGraphFormatter.prototype.getId = ItemGraphFormatter.prototype.getId; -RectangleVertexGraphFormatter.prototype.toJSON = ItemGraphFormatter.prototype.toJSON; -RectangleVertexGraphFormatter.prototype.loadFromJSON = ItemGraphFormatter.prototype.loadFromJSON; -RectangleVertexGraphFormatter.prototype._setEvents = ItemGraphFormatter.prototype._setEvents; - -function RectangleVertexGraphFormatter(id, defaultFormat, selectedFormat, overFormat, draggingFormat){ - VertexGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, draggingFormat); - this.args.type = "RectangleVertexGraphFormatter"; - if (defaultFormat != null){ - if (defaultFormat.radius != null){ - this.defaultFormat.args.radius = defaultFormat.radius; - } - } - - if (selectedFormat != null){ - if (selectedFormat.radius != null){ - this.selected.args.radius = selectedFormat.radius; - } - } - - if (overFormat != null){ - if (overFormat.radius != null){ - this.over.args.radius = overFormat.radius; - } - } - - if (draggingFormat != null){ - if (draggingFormat.radius != null){ - this.dragging.args.draggingFormat = draggingFormat.radius; - } - } -}; - - - -RoundedVertexGraphFormatter.prototype.getDefault = ItemGraphFormatter.prototype.getDefault; -RoundedVertexGraphFormatter.prototype.getSelected = ItemGraphFormatter.prototype.getSelected; -RoundedVertexGraphFormatter.prototype.getOver = ItemGraphFormatter.prototype.getOver; -RoundedVertexGraphFormatter.prototype.getDragging = ItemGraphFormatter.prototype.getDragging; -RoundedVertexGraphFormatter.prototype.getId = ItemGraphFormatter.prototype.getId; -RoundedVertexGraphFormatter.prototype.toJSON = ItemGraphFormatter.prototype.toJSON; -RoundedVertexGraphFormatter.prototype.loadFromJSON = ItemGraphFormatter.prototype.loadFromJSON; -RoundedVertexGraphFormatter.prototype._setEvents = ItemGraphFormatter.prototype._setEvents; - -function RoundedVertexGraphFormatter(id, defaultFormat, selectedFormat, overFormat, draggingFormat){ - VertexGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, draggingFormat); - this.args.type = "RoundedVertexGraphFormatter"; - if (defaultFormat != null){ - if (defaultFormat.radius != null){ - this.defaultFormat.args.radius = defaultFormat.radius; - } - } - - if (selectedFormat != null){ - if (selectedFormat.radius != null){ - this.selected.args.radius = selectedFormat.radius; - } - } - - if (overFormat != null){ - if (overFormat.radius != null){ - this.over.args.radius = overFormat.radius; - } - } - - if (draggingFormat != null){ - if (draggingFormat.radius != null){ - this.dragging.args.draggingFormat = draggingFormat.radius; - } - } -}; - - - -OctagonVertexGraphFormatter.prototype.getDefault = ItemGraphFormatter.prototype.getDefault; -OctagonVertexGraphFormatter.prototype.getSelected = ItemGraphFormatter.prototype.getSelected; -OctagonVertexGraphFormatter.prototype.getOver = ItemGraphFormatter.prototype.getOver; -OctagonVertexGraphFormatter.prototype.getDragging = ItemGraphFormatter.prototype.getDragging; -OctagonVertexGraphFormatter.prototype.getId = ItemGraphFormatter.prototype.getId; -OctagonVertexGraphFormatter.prototype.toJSON = ItemGraphFormatter.prototype.toJSON; -OctagonVertexGraphFormatter.prototype.loadFromJSON = ItemGraphFormatter.prototype.loadFromJSON; -OctagonVertexGraphFormatter.prototype._setEvents = ItemGraphFormatter.prototype._setEvents; - -function OctagonVertexGraphFormatter(id, defaultFormat, selectedFormat, overFormat, draggingFormat){ - VertexGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, draggingFormat); - this.args.type = "OctagonVertexGraphFormatter"; - if (defaultFormat != null){ - if (defaultFormat.radius != null){ - this.defaultFormat.args.radius = defaultFormat.radius; - } - } - - if (selectedFormat != null){ - if (selectedFormat.radius != null){ - this.selected.args.radius = selectedFormat.radius; - } - } - - if (overFormat != null){ - if (overFormat.radius != null){ - this.over.args.radius = overFormat.radius; - } - } - - if (draggingFormat != null){ - if (draggingFormat.radius != null){ - this.dragging.args.draggingFormat = draggingFormat.radius; - } - } -}; - - - -var Colors = new function() -{ - this.hashColor = []; - this.getColorByScoreArrayValue = function (arrayScore) - { - var array = new Array(); - - for (var i = 0; i< arrayScore.length; i++) - { - - var color = this.getColorByScoreValue(arrayScore[i]) - array.push( color); - - } - return array; - }; - - this.getHexStringByScoreArrayValue = function (arrayScore) - { - var arrayColor = this.getColorByScoreArrayValue(arrayScore); - var arrayHex = new Array(); - for (var i = 0; i< arrayColor.length; i++) - { - arrayHex.push( arrayColor[i].HexString()); - } - return arrayHex; - }; - - this.getColorByScoreValue = function (score) - { - - var truncate = score.toString().substr(0,4); - if (this.hashColor[truncate]!=null) - { - return this.hashColor[truncate]; - } - - - if(isNaN(score)) { - return Colors.ColorFromRGB(0,0,0); - } - var value; - - var from, to; - if(score < 0.5) { - from = Colors.ColorFromRGB(0,0,255); - to = Colors.ColorFromRGB(255,255,255); - value = (score * 2); - } else { - from = Colors.ColorFromRGB(255,255,255); - to = Colors.ColorFromRGB(255,0,0); - value = (score * 2) - 1; - } - - var x = value; - var y = 1.0 - value; - var color = Colors.ColorFromRGB(y * from.Red() + x * to.Red(), y * from.Green() + x * to.Green(), y * from.Blue() + x * to.Blue()); - - this.hashColor[truncate] = color; - - return color; - }; - - this.ColorFromHSV = function(hue, sat, val) - { - var color = new Color(); - color.SetHSV(hue,sat,val); - return color; - }; - - this.ColorFromRGB = function(r, g, b) - { - var color = new Color(); - color.SetRGB(r,g,b); - return color; - }; - - this.ColorFromHex = function(hexStr) - { - var color = new Color(); - color.SetHexString(hexStr); - return color; - }; - - function Color() { - //Stored as values between 0 and 1 - var red = 0; - var green = 0; - var blue = 0; - - //Stored as values between 0 and 360 - var hue = 0; - - //Strored as values between 0 and 1 - var saturation = 0; - var value = 0; - - this.SetRGB = function(r, g, b) - { - red = r/255.0; - green = g/255.0; - blue = b/255.0; - calculateHSV(); - }; - - this.Red = function() { return Math.round(red*255); }; - - this.Green = function() { return Math.round(green*255); }; - - this.Blue = function() { return Math.round(blue*255); }; - - this.SetHSV = function(h, s, v) - { - hue = h; - saturation = s; - value = v; - calculateRGB(); - }; - - this.Hue = function() - { return hue; }; - - this.Saturation = function() - { return saturation; }; - - this.Value = function() - { return value; }; - - this.SetHexString = function(hexString) - { - if(hexString == null || typeof(hexString) != "string") - { - this.SetRGB(0,0,0); - return; - } - - if (hexString.substr(0, 1) == '#') - hexString = hexString.substr(1); - - if(hexString.length != 6) - { - this.SetRGB(0,0,0); - return; - } - - var r = parseInt(hexString.substr(0, 2), 16); - var g = parseInt(hexString.substr(2, 2), 16); - var b = parseInt(hexString.substr(4, 2), 16); - if (isNaN(r) || isNaN(g) || isNaN(b)) - { - this.SetRGB(0,0,0); - return; - } - - this.SetRGB(r,g,b); - }; - - this.HexString = function() - { - - var rStr = this.Red().toString(16); - if (rStr.length == 1) - rStr = '0' + rStr; - var gStr = this.Green().toString(16); - if (gStr.length == 1) - gStr = '0' + gStr; - var bStr = this.Blue().toString(16); - if (bStr.length == 1) - bStr = '0' + bStr; - return ('#' + rStr + gStr + bStr).toUpperCase(); - }; - - this.Complement = function() - { - var newHue = (hue >= 180) ? hue - 180 : hue + 180; - var newVal = (value * (saturation - 1) + 1); - var newSat = (value*saturation) / newVal; - var newColor = new Color(); - newColor.SetHSV(newHue, newSat, newVal); - return newColor; - } ; - - function calculateHSV() - { - var max = Math.max(Math.max(red, green), blue); - var min = Math.min(Math.min(red, green), blue); - - value = max; - - saturation = 0; - if(max != 0) - saturation = 1 - min/max; - - hue = 0; - if(min == max) - return; - - var delta = (max - min); - if (red == max) - hue = (green - blue) / delta; - else if (green == max) - hue = 2 + ((blue - red) / delta); - else - hue = 4 + ((red - green) / delta); - hue = hue * 60; - if(hue < 0) - hue += 360; - } - - function calculateRGB() - { - red = value; - green = value; - blue = value; - - if(value == 0 || saturation == 0) - return; - - var tHue = (hue / 60); - var i = Math.floor(tHue); - var f = tHue - i; - var p = value * (1 - saturation); - var q = value * (1 - saturation * f); - var t = value * (1 - saturation * (1 - f)); - switch(i) - { - case 0: - red = value; green = t; blue = p; - break; - case 1: - red = q; green = value; blue = p; - break; - case 2: - red = p; green = value; blue = t; - break; - case 3: - red = p; green = q; blue = value; - break; - case 4: - red = t; green = p; blue = value; - break; - default: - red = value; green = p; blue = q; - break; - } - } - } -} -(); -/* - * Clase gestiona la insercción, eliminación de los elementos en el DOM - * - * Vital hacerla SIEMPRE compatible hacia atrás - * - * Last update: 28-10-2010 - * - */ - - -var DOM = {}; - -DOM.createNewElement = function(type, nodeParent, attributes) -{ - - var node = document.createElement(type); - for (var i=0; i0) - { - parent.removeChild(parent.childNodes[0]); - - } -}; - -DOM.select = function(targetID) -{ - return document.getElementById(targetID); -// return $("#"+targetID); -}; -var Geometry = -{ - - /** From tow points obtains the angles formed with the cartesian side **/ - getAngleBetweenTwoPoints : function(x1, y1, x2, y2){ - //var m = (y1 - y2)/ (x1 - x2); - return Math.atan2(y2-y1, x2-x1); - }, - - getAdjacentSideOfRectangleRight : function(angle, hypotenuse){ - return Math.abs(Math.cos(angle)*hypotenuse); - }, - - getOppositeSideOfRectangleRight : function(angle, hypotenuse){ - return Math.abs(Math.sin(angle)*hypotenuse); - }, - - toDegree: function(radian){ - return radian*180/Math.PI; - - } - - -}; - -var SVG = -{ - svgns : 'http://www.w3.org/2000/svg', - xlinkns : "http://www.w3.org/1999/xlink", - - createSVGCanvas: function(parentNode, attributes) - { - - attributes.push( ['xmlns', SVG.svgns], ['xmlns:xlink', 'http://www.w3.org/1999/xlink']); - var svg = document.createElementNS("http://www.w3.org/2000/svg", "svg"); - this._setProperties(svg, attributes); - parentNode.appendChild( svg); - return svg; - - }, - - createRectangle : function (x, y, width, height, attributes){ - var rect = document.createElementNS(this.svgns, "rect"); - rect.setAttribute("x",x); - rect.setAttribute("y",y); - rect.setAttribute("width",width); - rect.setAttribute("height",height); - SVG._setProperties(rect, attributes); - return rect; - }, - - drawImage64 : function (x, y, width, height, base64, svgNode, attributes) { - var node = SVG.createImage64(x, y, width, height, base64, attributes); - svgNode.appendChild(node); - return node; - }, - - createImage64 : function (x, y, width, height, base64, attributes) { - var img = document.createElementNS(this.svgns, "image"); - img.setAttribute("x",x); - img.setAttribute("y",y); - img.setAttribute("width",width); - img.setAttribute("height",height); - img.setAttribute("xlink:href",base64); - SVG._setProperties(img, attributes); - return img; - }, - - createLine: function (x1, y1, x2, y2, attributes){ - var line = document.createElementNS(this.svgns,"line"); - line.setAttribute("x1",x1); - line.setAttribute("y1",y1); - line.setAttribute("x2", x2); - line.setAttribute("y2", y2); - SVG._setProperties(line, attributes); - return line; - }, - - createClip: function (id, nodeToClip, attributes){ - var clip = document.createElementNS(this.svgns,"clipPath"); - clip.setAttribute("id",id); - clip.appendChild(nodeToClip); - return clip; - }, - - drawClip : function (id, nodeToClip, svgNode) { - var node = SVG.createClip(id, nodeToClip); - svgNode.appendChild(node); - return node; - }, - - drawRectangle : function (cx, cy, width, height, svgNode, attributes) { - try{ - var node = SVG.createRectangle(cx, cy, width, height, attributes); - svgNode.appendChild(node); - } - catch(e){ - - console.log("-------------------- "); - console.log("Error on drawRectangle " + e); - console.log(attributes); - console.log("-------------------- "); - } - return node; - }, - - createEllipse : function (x, y, rx, ry, attributes){ - var rect = document.createElementNS(this.svgns, "ellipse"); - rect.setAttribute("cx",x); - rect.setAttribute("cy",y); - rect.setAttribute("rx",rx); - rect.setAttribute("ry",ry); - SVG._setProperties(rect, attributes); - return rect; - }, - - drawEllipse : function (cx, cy, rx, ry, svgNode, attributes) { - var node = SVG.createEllipse(cx, cy, rx, ry, attributes); - svgNode.appendChild(node); - return node; - }, - - drawImage : function (x, y, canvasSVG, attributes) { - var image = document.createElementNS(this.svgns, "image"); - image.setAttribute("x",x); - image.setAttribute("y",y); - canvasSVG.appendChild(image); - SVG._setProperties(image, attributes); - }, - - drawLine : function (x1, y1, x2, y2, nodeSVG, attributes) { - try{ - var line = SVG.createLine(x1, y1, x2, y2, attributes); - nodeSVG.appendChild(line); - }catch(e){ - } - return line; - }, - - - drawPath: function (d, nodeSVG, attributes) { - var path = SVG.createPath(d, attributes); - nodeSVG.appendChild(path); - return path; - }, - - createPoligon : function (points, attributes){ - var poligon = document.createElementNS(this.svgns, "polygon"); - poligon.setAttribute("points",points); - SVG._setProperties(poligon, attributes); - return poligon; - }, - - drawPoligon : function (points, canvasSVG, attributes){ - var poligon = SVG.createPoligon(points, attributes); - canvasSVG.appendChild(poligon); - return poligon; - }, - // - - createPath : function (d, attributes){ - var path = document.createElementNS(this.svgns, "path"); - path.setAttribute("d",d); - SVG._setProperties(path, attributes); - return path; - }, - - drawCircle : function (x, y, radio, canvasSVG, attributes) { - - var newText = document.createElementNS(this.svgns,"circle"); - newText.setAttribute("cx",x); - newText.setAttribute("cy",y); - newText.setAttribute("r",radio); - - canvasSVG.appendChild(newText); - attributes["cursor"] = "pointer"; - this._setProperties(newText, attributes); - return newText; - }, - - - _setProperties: function(node, attributes) - { - if (attributes instanceof Array){ - for (var i=0; i< attributes.length; i++) - { - node.setAttribute(attributes[i][0], attributes[i][1]); - } - } - else{ - for ( var key in attributes){ - node.setAttribute(key, attributes[key]); - } - } - }, - -/* drawPath: function(pointsArray, canvasSVG, attributes){ - var path = document.createElementNS(this.svgns,"polyline"); - path.setAttribute ('id', id); - - var d= pointsArray[0].x+ " "+ pointsArray[0].y; - for (var i=1; i< pointsArray.length; i++) - { - d=d+" "+pointsArray[i].x+" "+pointsArray[i].y; - } - path.setAttribute ('points', d); - canvasSVG.appendChild(path); - },*/ - - createText : function (x, y, text, attributes) { - var node = document.createElementNS(this.svgns,"text"); - node.setAttributeNS(null , "x",x); - node.setAttributeNS(null, "y",y); - - var textNode = document.createTextNode(text); - node.appendChild(textNode); - - this._setProperties(node, attributes); - return node; - }, - - drawText : function (x, y, text, canvasSVG, attributes) { - var text = SVG.createText(x, y, text, attributes); - canvasSVG.appendChild(text); - return text; - }, - - - - drawGroup: function(svgNode, attributes) - { - var group = SVG.createGroup(attributes); - svgNode.appendChild(group); - return group; - }, - - createGroup: function(attributes){ - var group = document.createElementNS(this.svgns,"g"); - this._setProperties(group, attributes); - return group; - } - -}; - - - -var CanvasToSVG = { - - convert: function(sourceCanvas, targetSVG, x, y, id, attributes) { - - var img = this._convert(sourceCanvas, targetSVG, x, y, id); - - for (var i=0; i< attributes.length; i++) - { - img.setAttribute(attributes[i][0], attributes[i][1]); - } - }, - - _convert: function(sourceCanvas, targetSVG, x, y, id) { - var svgNS = "http://www.w3.org/2000/svg"; - var xlinkNS = "http://www.w3.org/1999/xlink"; - // get base64 encoded png from Canvas - var image = sourceCanvas.toDataURL(); - - // must be careful with the namespaces - var svgimg = document.createElementNS(svgNS, "image"); - - svgimg.setAttribute('id', id); - - //svgimg.setAttribute('class', class); - //svgimg.setAttribute('xlink:href', image); - svgimg.setAttributeNS(xlinkNS, 'xlink:href', image); - - - - - svgimg.setAttribute('x', x ? x : 0); - svgimg.setAttribute('y', y ? y : 0); - svgimg.setAttribute('width', sourceCanvas.width); - svgimg.setAttribute('height', sourceCanvas.height); - //svgimg.setAttribute('cursor', 'pointer'); - svgimg.imageData = image; - - targetSVG.appendChild(svgimg); - return svgimg; - }, - - importSVG: function(sourceSVG, targetCanvas) { - svg_xml = sourceSVG;//(new XMLSerializer()).serializeToString(sourceSVG); - var ctx = targetCanvas.getContext('2d'); - - var img = new Image(); - img.src = "data:image/svg+xml;base64," + btoa(svg_xml); -// img.onload = function() { - ctx.drawImage(img, 0, 0); -// }; - } - -}; -/* -Graph.prototype.importSVG = function(sourceSVG, targetCanvas) { - sourceSVG = this._svg; - targetCanvas = document.createElementNS('canvas'); - // https://developer.mozilla.org/en/XMLSerializer - svg_xml = (new XMLSerializer()).serializeToString(sourceSVG); - var ctx = targetCanvas.getContext('2d'); - // this is just a JavaScript (HTML) image - var img = new Image(); - // http://en.wikipedia.org/wiki/SVG#Native_support - // https://developer.mozilla.org/en/DOM/window.btoa - img.src = "data:image/svg+xml;base64," + btoa(svg_xml); - img.onload = function() { - // after this, Canvas’ origin-clean is DIRTY - ctx.drawImage(img, 0, 0); - } -}; -*/ - -/* - -Normalizacion de datos para dibujar colores -Issues: - No sé como debería llamarse esta libreria - No sé si ya existe una funciçon en javascript que lo haga - - -*/ - - -var Normalizer = new function() -{ - this.normalizeArray = function (arrayData) - { - - return this.standardizeArray(this.normal(arrayData)); - -// var result = this._getMaxAndMin(arrayData); -// var min =result[0]; -// var max = result[1]; -// -// -// //los hacemos todos positivos -// for (var i = 0; i< arrayData.length; i++) -// { -// arrayData[i]= Math.abs(min) + parseFloat(arrayData[i]); -// } -// -// var result = this._getMaxAndMin(arrayData); -// var min =result[0]; -// var max = result[1]; -// -// -// var resultArray = new Array(); -// for (var i = 0; i< arrayData.length; i++) -// { -// resultArray.push(arrayData[i]*1/max); -// } -// return resultArray; - }; - - this.normal = function(arrayData){ - var mean = this._getMean(arrayData); - var deviation = this._getStdDeviation(arrayData); - var result = this._getMaxAndMin(arrayData); - var min = result[0]; - var max = result[1]; - - var resultArray = new Array(); - for (var i = 0; i< arrayData.length; i++) { - if (deviation!=0){ - resultArray.push((arrayData[i]-mean)/deviation); - }else{ - resultArray.push(arrayData[i]); - } - } - return resultArray; - }; - - this.standardizeArray = function(arrayData) - { - var result = this._getMaxAndMin(arrayData); - var min = result[0]; - var max = result[1]; - - var offset = ( min <= 0 ) ? Math.abs(min) : (-1 * min); - var resultArray = new Array(); - for (var i = 0; i< arrayData.length; i++) { - if(max + offset!=0){ - resultArray.push((arrayData[i] + offset) / (max + offset)); - }else{ - resultArray.push(arrayData[i]+offset); - } - } - return resultArray; - }; - - - this._getMean = function(arrayData) { - var sum = 0; - for (var i = 0; i< arrayData.length; i++) { - sum = sum + parseFloat(arrayData[i]); - } - return sum/arrayData.length; - }; - - this._getStdDeviation = function(arrayData) { - var mean = this._getMean(arrayData); - var acum = 0.0; - for(var i=0; i max) max = parseFloat(arrayData[i]); - } - - return [min, max]; - }; -}; -function GraphCanvas(componentID, targetNode, args) { - this.args = {}; - /** target */ - this.targetID = targetNode.id; - - /** id manage */ - this.id = componentID; - this.args.idGraph = this.id + "main"; - this.args.idBackgroundNode = this.id + "background"; - - this.args.idEdgesGraph = this.id + "edges"; - this.args.idNodesGraph = this.id + "vertices"; - this.args.idLabelGraph = this.id + "label"; - this.args.idBackground = this.id + "background"; - - /** Objects Graph **/ - this.dataset = null; - this.formatter = null; - this.layout = null; - - /** Drawing **/ - this.circleDefaultRadius = 2; - this.squareDefaultSide = this.circleDefaultRadius * 1.5; - - /** Directed Arrow **/ - this.arrowDefaultSize = this.circleDefaultRadius; - - /** Groups **/ - this.GraphGroup = null; - this.GraphNodeGroup = null; - this.GraphLabelGroup = null; - this.GraphBackground = null; - - /** SETTINGS FLAGS **/ - this.args.draggingCanvasEnabled = false; //Flag to set if the canvas can be dragged - this.args.multipleSelectionEnabled = false; - this.args.interactive = false; - this.args.labeled = false; - this.args.linkEnabled = false; - - /** If numberEdge > maxNumberEdgesMoving then only it will move edges when mouse up **/ - this.args.maxNumberEdgesMoving = 3; - this.args.maxNumberEdgesFiringEvents = 50; - - /** Linking edges **/ - this.args.linking = false; - this.linkStartX = 0; - this.linkStartY = 0; - this.linkSVGNode = null; - this.linkNodeSource = null; - this.linkNodeTarget = null; - - /** Dragging Control **/ - this.draggingElement = null; - this.dragging = false; - this.nMouseOffsetX = 0; - this.nMouseOffsetY = 0; - this.dragStartX = 0; - this.dragStartY = 0; - this.desplazamientoX = 0; - this.desplazamientoY = 0; - - /** Selection Control **/ - this.selecting = false; - this.selectorX = null; - this.selectorY = null; - this.selectorSVGNode = null; - - /** Node status **/ - this.args.isVertexSelected = {}; - this.args.selectedVertices = []; - - /** Edges status **/ - this.args.isEdgeSelected = {}; - //this.args.selectedEdges = []; - - if (args != null) { - if (args.multipleSelectionEnabled != null) { - this.args.multipleSelectionEnabled = args.multipleSelectionEnabled; - this.args.draggingCanvasEnabled = !(this.args.multipleSelectionEnabled); - } - if (args.draggingCanvasEnabled != null) { - this.args.draggingCanvasEnabled = args.draggingCanvasEnabled; - this.args.multipleSelectionEnabled = !(this.args.draggingCanvasEnabled); - } - if (args.interactive != null) { - this.args.interactive = args.interactive; - } - if (args.labeled != null) { - this.args.labeled = args.labeled; - } - - } - - /** Hashmap with the svg node labels **/ - this.svgLabels = {}; - - /** EVENTS **/ - this.onVertexOut = new Event(this); - this.onVertexOver = new Event(this); - this.onVertexSelect = new Event(this); - this.onEdgeSelect = new Event(this); - this.onCanvasClicked = new Event(this); - this.onVertexUp = new Event(this); -} - -GraphCanvas.prototype.showLabels = function(value) { - this.args.labeled = value; - this.removeLabels(); - if (value) { - this.renderLabels(); - } -}; - -GraphCanvas.prototype.getSelectedVertices = function() { - return this.args.selectedVertices; -}; - -GraphCanvas.prototype.getSelectedEdges = function() { - var selected = []; - for ( var selectedEdge in this.args.isEdgeSelected) { - selected.push(selectedEdge); - } - return selected; -}; - -GraphCanvas.prototype.createSVGDom = function(targetID, id, width, height, backgroundColor) { - var container = document.getElementById(targetID); - this._svg = SVG.createSVGCanvas(container, [ - [ "style", "background-color:" + backgroundColor + ";" ], [ "id", id ], [ "dragx", 0 ], [ "dragy", 0 ], - [ "height", this.getFormatter().getHeight() ], [ "width", this.getFormatter().getWidth() ] ]); - return this._svg; -}; - -/** MULTIPLE SELECTION **/ -GraphCanvas.prototype.isMultipleSelectionEnabled = function() { - return this.args.multipleSelectionEnabled; -}; - -GraphCanvas.prototype.setMultipleSelection = function(value) { - this.args.multipleSelectionEnabled = value; - this.args.draggingCanvasEnabled = (!value); -}; - -GraphCanvas.prototype.setSelecting = function(value) { - this.selecting = value; -}; - -/** linking **/ -GraphCanvas.prototype.setLinking = function(value) { - this.args.linkEnabled = value; - this.selecting = !value; - this.dragging = !value; -}; - -/** CANVAS MOVING **/ -GraphCanvas.prototype.setDraggingCanvas = function(value) { - this.args.draggingCanvasEnabled = value; - this.args.multipleSelectionEnabled = !value; -}; - -GraphCanvas.prototype.isDraggingCanvasEnabled = function() { - return this.args.draggingCanvasEnabled; -}; -/** ZOOM **/ -GraphCanvas.prototype.getScale = function() { - return this.getFormatter().getZoomScale(); -}; - -GraphCanvas.prototype.setScale = function(scale) { - var graphNode = document.getElementById(this.args.idGraph); - graphNode.setAttribute("transform", graphNode.getAttribute("transform").replace("scale(" + this.getScale() + ")", "scale(" + scale + ")")); - this.getFormatter().setZoomScale(scale); -}; - -GraphCanvas.prototype.zoomIn = function() { - this.setScale(this.getScale() + this.getFormatter().getZoomScaleStepFactor()); -}; - -GraphCanvas.prototype.zoomOut = function() { - this.setScale(this.getScale() - this.getFormatter().getZoomScaleStepFactor()); - -}; - -/** SVG COORDENATES **/ -GraphCanvas.prototype.getSVGCoordenates = function(evt) { - var p = this._svg.createSVGPoint(); - p.x = evt.clientX; - p.y = evt.clientY; - - var m = this._svg.getScreenCTM(document.documentElement); - p = p.matrixTransform(m.inverse()); - return p; -}; - -/** SVG EVENTS **/ -GraphCanvas.prototype.mouseClick = function(event) { - if (event.button == 0) { - if (!this.args.interactive) { - return; - } - - if (this.isVertex(event.target)) { - this.clickNode(this.getVertexIdFromSVGId(event.target.id)); - } - /** Como el evento mouseClick viene despues del mouse up es aqui donde manejo el tema de deseccionar los elementos que estoy dragging **/ - if (this.dragging) { - this.dragging = false; - } - } -}; - -GraphCanvas.prototype.mouseMove = function(evt) { - if (this.selecting) { - this.clearLabels(); - - var width = (this.getSVGCoordenates(evt).x - this.selectorX); - var height = (this.getSVGCoordenates(evt).y - this.selectorY); - if ((width > 0) && (height > 0)) { - this.displaySelection(this.selectorX, this.selectorY, width, height); - } - if ((width > 0) && (height < 0)) { - this.displaySelection(this.selectorX, this.getSVGCoordenates(evt).y, width, Math.abs(height)); - } - if ((width < 0) && (height < 0)) { - this.displaySelection(this.getSVGCoordenates(evt).x, this.getSVGCoordenates(evt).y, Math.abs(width), Math.abs(height)); - } - if ((width < 0) && (height > 0)) { - this.displaySelection(this.selectorX + width, this.selectorY, Math.abs(width), Math.abs(height)); - } - - var x1 = (parseFloat(this.selectorSVGNode.getAttribute("x")) - DOM.select(this.id).getAttribute("dragx")) / this.getFormatter().getWidth(); - var y1 = (parseFloat(this.selectorSVGNode.getAttribute("y")) - DOM.select(this.id).getAttribute("dragy")) / this.getFormatter().getHeight(); - var x2 = (x1 + parseFloat(this.selectorSVGNode.getAttribute("width") / this.getFormatter().getWidth())); - var y2 = (y1 + parseFloat(this.selectorSVGNode.getAttribute("height") / this.getFormatter().getHeight())); - - this.deselectNodes(this.getLayout()); - var verticesSelected = this.getLayout().getVerticesByArea(x1 / this.getFormatter().getZoomScale(), y1 / this.getFormatter().getZoomScale(), - x2 / this.getFormatter().getZoomScale(), y2 / this.getFormatter().getZoomScale()); - for ( var i = 0; i < verticesSelected.length; i++) { - this.selectNode(verticesSelected[i].getId()); - this.renderLabel(verticesSelected[i].getId()); - } - - } - var p = null; - if (this.args.linking) { - p = this.getSVGCoordenates(evt); - if (this.linkSVGNode != null) { - this.linkSVGNode.setAttribute("x2", p.x - 2); - this.linkSVGNode.setAttribute("y2", p.y - 2); - } - } - - if (this.dragging) { - p = this.getSVGCoordenates(evt); - p.x -= this.nMouseOffsetX; - p.y -= this.nMouseOffsetY; - this.desplazamientoX = (this.getSVGCoordenates(evt).x - this.dragStartX);// + parseFloat(DOM.select(this.id).getAttribute("dragx")); - this.desplazamientoY = (this.getSVGCoordenates(evt).y - this.dragStartY);// + parseFloat(DOM.select(this.id).getAttribute("dragy")); - - if (this.draggingElement != null) { - /** Click sobre el recct del banground que provoca que mueva todo el canvas **/ - if (this.isNodeCanvas(this.draggingElement)) { - - p = this.getSVGCoordenates(evt); - p.x = this.desplazamientoX; - p.y = this.desplazamientoY; - - this.draggingElement.setAttribute("dragx", p.x); - this.draggingElement.setAttribute("dragy", p.y); - this.draggingElement = document.getElementById(this.args.idGraph); - this.draggingElement.setAttribute("transform", "translate(" + p.x + "," + p.y + "), scale(" + this.getScale() + ")"); - - DOM.select(this.id).setAttribute("dragx", p.x); - DOM.select(this.id).setAttribute("dragy", p.y); - - if (this.NodeSVGbackgroundImage != null) { - this.NodeSVGbackgroundImage.setAttribute("dragx", p.x); - this.NodeSVGbackgroundImage.setAttribute("dragy", p.y); - } - } else { - if (this.isVertex(this.draggingElement)) { - this.selectNode(this.getVertexIdFromSVGId(this.draggingElement.id)); - this.desplazamientoX = this.desplazamientoX / this.getFormatter().getZoomScale(); - this.desplazamientoY = this.desplazamientoY / this.getFormatter().getZoomScale(); - this.moveSelectedNodes(this.desplazamientoX, this.desplazamientoY); - - this.dragStartX = this.getSVGCoordenates(evt).x; - this.dragStartY = this.getSVGCoordenates(evt).y; - } else { - if (this.isNodeBackground(this.draggingElement)) { - - this.draggingElement.setAttribute("dragx", p.x); - this.draggingElement.setAttribute("dragy", p.y); - this.draggingElement = document.getElementById(this.args.idGraph); - this.draggingElement.setAttribute("transform", "translate(" + p.x + "," + p.y + "), scale(" + this.getScale() + ")"); - } else { - this.draggingElement.setAttribute("transform", "translate(" + p.x + "," + p.y + ")"); - } - } - } - } - } -}; - -GraphCanvas.prototype.moveSelectedNodes = function(offsetX, offsetY) { - for ( var i = 0; i < this.getSelectedVertices().length; i++) { - - var nodeId = this.getSelectedVertices()[i]; - var svgNodeId = this.getSVGNodeId(nodeId); - - var x = parseFloat(DOM.select(svgNodeId).getAttribute("dragx")) + parseFloat(offsetX);// - parseFloat(DOM.select(this.id).getAttribute("dragx")); - var y = parseFloat(DOM.select(svgNodeId).getAttribute("dragy")) + parseFloat(offsetY);// + parseFloat(DOM.select(this.id).getAttribute("dragy")); - - this._movingNode(DOM.select(svgNodeId), x, y); - } -}; - -GraphCanvas.prototype.mouseDown = function(evt) { - if (event.button == 0) { - - /** if !no interactive mouse events do anything **/ - if (!this.args.interactive) { - return; - } - - var p = this.getSVGCoordenates(evt); - - /** When click on canvas or background deselect all **/ - if (this.isNodeCanvas(evt.target) || this.isNodeBackground(evt.target)) { - this.deselectNodes(); - this.deselectEdges(); - this.onCanvasClicked.notify(); - } - - /** if I am linking vertices **/ - if (this.args.linkEnabled) { - - if (!this.args.linking) { - this.args.linking = true; - if (this.isVertex(evt.target)) { - this.linkStartX = p.x; - this.linkStartY = p.y; - this.linkSVGNode = SVG.drawLine(p.x, p.y, p.x, p.y, this._svg, { - "stroke" : "#FF0000" - }); - this.linkNodeSource = this.getVertexIdFromSVGId(evt.target.id); - } - } else { - this.linkNodeTarget = this.getVertexIdFromSVGId(evt.target.id); - this.args.linking = false; - this.args.linkEnabled = false; - if (this.isVertex(evt.target)) { - this.getDataset().addEdge(this.linkNodeSource + "_" + this.linkNodeTarget, this.linkNodeSource, this.linkNodeTarget, {}); - } - this.linkSVGNode.parentNode.removeChild(this.linkSVGNode); - } - return; - } - - /** Id is a vertex or the canvas **/ - if (this.isVertex(evt.target) || this.isNodeCanvas(evt.target) || this.isNodeBackground(evt.target)) { - this._startDragging(evt); - } - /** if i is edge **/ - if (this.isEdge(evt.target)) { - this.selectEdge(this.getEdgeIdFromSVGId(evt.target.getAttribute("id"))); - } - - if (this.args.multipleSelectionEnabled) { - if (!this.dragging) { - this.setSelecting(true); - this.selectorX = p.x; - this.selectorY = p.y; - this.displaySelection(p.x, p.y, 1, 1); - } - } - - } - if (event.button == 1) { - this.setLinking(false); - this.setMultipleSelection(false); - this.selecting = false; - - /** Id is a vertex or the canvas **/ - if (this.isVertex(evt.target) || this.isNodeCanvas(evt.target) || this.isNodeBackground(evt.target)) { - this._startDragging(evt); - } - } -}; - -GraphCanvas.prototype.mouseUp = function(event) { - if (!this.args.interactive) { - return; - } - - if (this.dragging) { - this._stopDragging(event); - if (this.isVertex(event.target)) { - var vertexId = this.getVertexIdFromSVGId(event.target.id); - if (this.getDataset().getVertexById(vertexId).getEdges().length >= this.args.maxNumberEdgesMoving) { - this.moveEdge(vertexId); - } - } - } - - if (this.selecting) { - this.setSelecting(false); - - var x1 = (parseFloat(this.selectorSVGNode.getAttribute("x")) - DOM.select(this.id).getAttribute("dragx")) / this.getFormatter().getWidth(); - var y1 = (parseFloat(this.selectorSVGNode.getAttribute("y")) - DOM.select(this.id).getAttribute("dragy")) / this.getFormatter().getHeight(); - var x2 = (x1 + parseFloat(this.selectorSVGNode.getAttribute("width") / this.formatter.getWidth())); - var y2 = (y1 + parseFloat(this.selectorSVGNode.getAttribute("height") / this.formatter.getHeight())); - - var verticesSelected = this.getLayout().getVerticesByArea(x1 / this.getFormatter().getZoomScale, y1 / this.getFormatter().getZoomScale, - x2 / this.getFormatter().getZoomScale, y2 / this.getFormatter().getZoomScale); - - for ( var i = 0; i < verticesSelected.length; i++) { - this.selectNode(verticesSelected[i].getId()); - } - - if (this.selectorSVGNode != null) { - this._svg.removeChild(this.selectorSVGNode); - } - - if (this.args.labeled) { - this.clearLabels(); - this.renderLabels(); - } - - this.selectorSVGNode = null; - // this.renderLabels(); - } -}; - -/** SELECTION **/ -GraphCanvas.prototype.displaySelection = function(x, y, width, height) { - if (this.selectorSVGNode != null) { - this.selectorSVGNode.setAttribute("x", x); - this.selectorSVGNode.setAttribute("y", y); - this.selectorSVGNode.setAttribute("width", width); - this.selectorSVGNode.setAttribute("height", height); - } else { - this.selectorSVGNode = SVG.drawRectangle(x, y, width, height, this._svg, { - "fill" : "red", - "stroke" : "black", - "opacity" : "0.2", - "stroke-opacity" : "1" - }); - } -}; - -/** DRAGGING **/ -GraphCanvas.prototype._startDragging = function(evt) { - if (!this.isDraggingCanvasEnabled()) { - if (this.isNodeCanvas(evt.target)) { - this.draggingElement = null; - } - } - - if (this.isVertex(evt.target) || (this.isNodeBackground(evt.target) && (this.isDraggingCanvasEnabled()))|| (this.isNodeCanvas(evt.target) && (this.isDraggingCanvasEnabled()))) { - this.clearLabels(); - this.draggingElement = evt.target; - this.dragging = true; - var p = this.getSVGCoordenates(evt); - - this.nMouseOffsetX = p.x - parseInt(evt.target.getAttribute("dragx")); - this.nMouseOffsetY = p.y - parseInt(evt.target.getAttribute("dragy")); - - if (this.isVertex(evt.target)) { - this.dragStartX = parseInt(this.draggingElement.getAttribute("dragx")) * this.getFormatter().getZoomScale() - + parseFloat(DOM.select(this.id).getAttribute("dragx")); - this.dragStartY = parseInt(this.draggingElement.getAttribute("dragy")) * this.getFormatter().getZoomScale() - + parseFloat(DOM.select(this.id).getAttribute("dragy")); - } else { - this.dragStartX = p.x - parseInt(this.draggingElement.getAttribute("dragx"));// + parseFloat(DOM.select(this.id).getAttribute("dragx")); - this.dragStartY = p.y - parseInt(this.draggingElement.getAttribute("dragy"));// + parseFloat(DOM.select(this.id).getAttribute("dragy")); - } - } -}; - -GraphCanvas.prototype._stopDragging = function(event) { - this.nMouseOffsetX = 0; - this.nMouseOffsetX = 0; - /** despues del evento up viene el evento click entonces acabo el dragging en el mouseclick **/ - this.dragging = false; - this.draggingElement = null; - this.renderLabels(); - - this.setLinking(false); - this.setMultipleSelection(true); - this.selecting = false; - -}; - -/** Move the edges of the vertex with the vertexId indicado **/ -GraphCanvas.prototype.moveEdge = function(vertexId) { - var x = this.getLayout().getNodeById(vertexId).x * this.getFormatter().getWidth(); - var y = this.getLayout().getNodeById(vertexId).y * this.getFormatter().getHeight(); - - /** Moving edges out **/ - for ( var i = 0; i < this.getDataset().getVertexById(vertexId).getEdgesOut().length; i++) { - var edgeId = this.getDataset().getVertexById(vertexId).getEdgesOut()[i].getId(); - var svgEdgeId = this.getSVGEdgeId(edgeId); - var edgeFormatter = this.getFormatter().getEdgeById(edgeId); - if (edgeFormatter instanceof LineEdgeGraphFormatter) { - DOM.select(svgEdgeId + "_shadow").setAttribute("x2", x); - DOM.select(svgEdgeId + "_shadow").setAttribute("y2", y); - DOM.select(svgEdgeId).setAttribute("x2", x); - DOM.select(svgEdgeId).setAttribute("y2", y); - } - - if ((edgeFormatter instanceof DirectedLineEdgeGraphFormatter) || (edgeFormatter instanceof OdirectedLineEdgeGraphFormatter) - || (edgeFormatter instanceof OdotDirectedLineEdgeGraphFormatter) || (edgeFormatter instanceof DotDirectedLineEdgeGraphFormatter) - || (edgeFormatter instanceof CutDirectedLineEdgeGraphFormatter)) { - this.removeEdge(edgeId); - this.renderEdge(edgeId); - } - } - - /** Moving edges in **/ - for ( var i = 0; i < this.getDataset().getVertexById(vertexId).getEdgesIn().length; i++) { - var edgeId = this.getDataset().getVertexById(vertexId).getEdgesIn()[i].getId(); - var svgEdgeId = this.getSVGEdgeId(edgeId); - var edgeFormatter = this.getFormatter().getEdgeById(edgeId); - if (edgeFormatter instanceof LineEdgeGraphFormatter) { - DOM.select(svgEdgeId).setAttribute("x1", x); - DOM.select(svgEdgeId).setAttribute("y1", y); - DOM.select(svgEdgeId + "_shadow").setAttribute("x1", x); - DOM.select(svgEdgeId + "_shadow").setAttribute("y1", y); - } - - if ((edgeFormatter instanceof DirectedLineEdgeGraphFormatter) || (edgeFormatter instanceof OdirectedLineEdgeGraphFormatter) - || (edgeFormatter instanceof OdotDirectedLineEdgeGraphFormatter) || (edgeFormatter instanceof DotDirectedLineEdgeGraphFormatter) - || (edgeFormatter instanceof CutDirectedLineEdgeGraphFormatter)) { - this.removeEdge(edgeId); - this.renderEdge(edgeId); - } - - if (edgeFormatter instanceof BezierEdgeGraphFormatter) { - var radius = this.getFormatter().getVertexById(vertexId).getDefault().getSize() * this.getFormatter().getNodesMaxSize(); - var d = this.calculateCoordenatesBezier(radius, x, y); - DOM.select(svgEdgeId).setAttribute("d", d); - } - } -}; - -GraphCanvas.prototype.moveNode = function(vertexId) { - var x = this.getLayout().getNodeById(vertexId).x * this.getFormatter().getWidth(); - var y = this.getLayout().getNodeById(vertexId).y * this.getFormatter().getHeight(); - var svgNodeElement = DOM.select(this.getSVGNodeId(vertexId)); - - svgNodeElement.setAttribute("dragx", x); - svgNodeElement.setAttribute("dragy", y); - svgNodeElement.setAttribute("transform", "translate(" + x + "," + y + ")"); - - if (this.getDataset().getVertexById(vertexId).getEdges().length < this.args.maxNumberEdgesMoving) { - this.moveEdge(vertexId); - } -}; - -GraphCanvas.prototype._movingNode = function(svgNodeElement, x, y) { - var vertexId = this.getVertexIdFromSVGId(svgNodeElement.getAttribute("id")); - this.getLayout().getNodeById(vertexId).setCoordinates(x / this.getFormatter().getWidth(), y / this.getFormatter().getHeight()); - this.desplazamientoX = 0; - this.desplazamientoY = 0; - this.removeLabel(vertexId); - this.renderLabel(vertexId); -}; - -/** INIT **/ -GraphCanvas.prototype.init = function() { - - this._svg = this.createSVGDom(this.targetID, this.id, this.getFormatter().getWidth(), this.getFormatter().getHeight(), this.getFormatter() - .getBackgroundColor()); - this.GraphGroup = SVG.drawGroup(this._svg, [ [ "id", this.args.idGraph ], [ "transform", "translate(0,0), scale(1)" ] ]); - this.GraphBackground = SVG.drawGroup(this.GraphGroup, [ [ "id", this.args.idBackground ] ]); - this.GraphEdgeGroup = SVG.drawGroup(this.GraphGroup, [ [ "id", this.args.idEdgesGraph ] ]); - this.GraphNodeGroup = SVG.drawGroup(this.GraphGroup, [ [ "id", this.args.idNodesGraph ] ]); - this.GraphLabelGroup = SVG.drawGroup(this.GraphGroup, [ [ "id", this.args.idLabelGraph ] ]); - - if ((this.getFormatter().getBackgroundImage() != null) && (this.getFormatter().getBackgroundImage() != "")) { - this.setBackgroundImage(this.getFormatter().getBackgroundImage()); - } - /** SVG Events listener */ - var _this = this; - this._svg.addEventListener("click", function(event) { - _this.mouseClick(event); - }, false); - this._svg.addEventListener("mousemove", function(event) { - _this.mouseMove(event, _this); - }, false); - this._svg.addEventListener("mousedown", function(event) { - _this.mouseDown(event, _this); - }, false); - this._svg.addEventListener("mouseup", function(event) { - _this.mouseUp(event, _this); - }, false); -}; - -/* - GraphCanvas.prototype.backgroungToSVG = function(){ - var _this = this; - var canvas = document.createElement('canvas'); - canvas.setAttribute("id", "canvas"); - canvas.width = this.formatter.getWidth(); - canvas.height = this.formatter.getHeight(); - - this._svg.parentNode.parentNode.appendChild(canvas); - var ctx = document.getElementById('canvas').getContext('2d'); - var img = new Image(); - - img.src = this.formatter.getBackgroundImage(); - ctx.drawImage(img,0,0 ,_this.formatter.getWidth(), _this.formatter.getHeight()); - - - img.onload = function() { - canvas.parentNode.removeChild(canvas); - } - - this.NodeSVGbackgroundImage.setAttribute("xlink:href", document.getElementById("canvas").toDataURL()); - this.NodeSVGbackgroundImage.removeAttribute("href"); - - // - - };*/ - -GraphCanvas.prototype.setBackgroundImage = function() { - if (this.NodeSVGbackgroundImage != null) { - this.NodeSVGbackgroundImage.parentNode.removeChild(this.NodeSVGbackgroundImage); - } - $('#' + this.targetID).svg(); - $('#' + this.targetID).svg("get"); - - $('#' + this.targetID).svg("get")._svg = document.getElementById(this.id); - - var svg = $('#' + this.targetID).svg("get"); - this.NodeSVGbackgroundImage = svg.image(0, 0, this.getFormatter().getWidth(), this.getFormatter().getHeight(), this.getFormatter() - .getBackgroundImage()); - this.NodeSVGbackgroundImage.setAttribute("id", this.args.idBackgroundNode); - - this.NodeSVGbackgroundImage.setAttribute("x", 0); - this.NodeSVGbackgroundImage.setAttribute("y", 0); - - this.NodeSVGbackgroundImage.setAttribute("dragx", 0); - this.NodeSVGbackgroundImage.setAttribute("dragy", 0); - - if (this.getFormatter().args.backgroundImageHeight != null) { - this.NodeSVGbackgroundImage.setAttribute("height", this.getFormatter().args.backgroundImageHeight); - } - if (this.getFormatter().args.backgroundImageWidth != null) { - this.NodeSVGbackgroundImage.setAttribute("width", this.getFormatter().args.backgroundImageWidth); - } - - if (this.getFormatter().args.backgroundImageX != null) { - this.NodeSVGbackgroundImage.setAttribute("x", this.getFormatter().args.backgroundImageX); - } - if (this.getFormatter().args.backgroundImageY != null) { - this.NodeSVGbackgroundImage.setAttribute("y", this.getFormatter().args.backgroundImageY); - } - - this.GraphBackground.appendChild(this.NodeSVGbackgroundImage); - this.NodeSVGbackgroundImage.removeAttribute("href"); - this.NodeSVGbackgroundImage.setAttribute("xlink:href", this.getFormatter().getBackgroundImage()); -}; - -GraphCanvas.prototype.removeBackgroundImage = function() { - if (this.NodeSVGbackgroundImage != null) { - this.NodeSVGbackgroundImage.parentNode.removeChild(this.NodeSVGbackgroundImage); - } -}; - -GraphCanvas.prototype._setBackgroundColor = function(color) { - var attributes = [ [ "fill", color ] ]; - SVG.drawRectangle(0, 0, this.getFormatter().getWidth(), this.getFormatter().getHeight(), this.GraphBackground, attributes); -}; - -/** Serialize **/ -GraphCanvas.prototype.toJSON = function() { - var json = {}; - json.dataset = {}; - json.formatter = {}; - json.layout = {}; - json.dataset = this.getDataset().toJSON(); - json.formatter = this.getFormatter().toJSON(); - json.layout = this.getLayout().toJSON(); - return json; -}; - -GraphCanvas.prototype.toHTML = function() { - //this.backgroungToSVG(); - var html = this._svg.parentElement.innerHTML; - - var start = html.indexOf("") + 6; - - return html.substr(start, end); -}; - -/** DRAW **/ -GraphCanvas.prototype.draw = function(graphdataset, graphformatter, graphlayout) { - this.setDataset(graphdataset); - this.setFormatter(graphformatter); - this.setLayout(graphlayout); - - var _this = this; - this.getFormatter().changed.attach(function(sender, item) { - _this.removeNode(item.getId()); - _this.renderNode(item.getId()); - if (_this.args.labeled) { - _this.removeLabel(item.getId()); - _this.renderLabel(item.getId()); - } - - }); - //TODO - this.getFormatter().edgeChanged.attach(function(sender, item) { - _this.removeEdge(item.getId()); - _this.renderEdge(item.getId()); - }); - - this.getFormatter().resized.attach(function(sender, item) { - _this.resize(_this.getFormatter().getWidth(), _this.getFormatter().getHeight()); - }); - - this.getFormatter().backgroundImageChanged.attach(function(sender, item) { - _this.setBackgroundImage(_this.getFormatter().getBackgroundImage()); - }); - - this.getFormatter().backgroundColorChanged.attach(function(sender, item) { - _this._setBackgroundColor(_this.getFormatter().getBackgroundColor()); - }); - - this.getLayout().changed.attach(function(sender, item) { - _this.moveNode(item.getId()); - _this.moveEdge(item.getId()); - if (_this.args.labeled) { - _this.removeLabel(item.getId()); - _this.renderLabel(item.getId()); - } - }); - - this.getDataset().newVertex.attach(function(sender, item) { - - _this.renderNode(item.getId()); - if (_this.args.labeled) { - _this.renderLabel(item.getId()); - } - }); - - this.getDataset().newEdge.attach(function(sender, item) { - _this.renderEdge(item.getId()); - }); - - this.getDataset().vertexDeleted.attach(function(sender, item) { - _this.removeNode(item.getId()); - if (_this.args.labeled) { - _this.removeLabel(item.getId()); - } - }); - - this.getDataset().edgeDeleted.attach(function(sender, item) { - _this.removeEdge(item.getId()); - }); - - this.getDataset().vertexNameChanged.attach(function(sender, args) { - if (_this.args.labeled) { - _this.removeLabel(args.item.getId()); - _this.removeLabel(args.item.getId()); - _this.renderLabel(args.item.getId()); - } - }); - this.init(); - this.render(); -}; - -GraphCanvas.prototype.render = function() { - for ( var id in this.getDataset().getVertices()) { - this.renderNode(id); - } - this.renderLabels(); - this.renderEdges(); -}; - -GraphCanvas.prototype.renderLabels = function() { - if (this.args.labeled) { - for ( var id in this.getDataset().getVertices()) { - this.renderLabel(id); - } - } -}; - -GraphCanvas.prototype.removeLabels = function() { - for ( var id in this.getDataset().getVertices()) { - this.removeLabel(id); - } -}; - -/** Utilities method for nodes **/ -GraphCanvas.prototype.isNodeCanvas = function(node) { - return ((node.id == this.args.idGraph) || (node.id == this.id)); -}; - -GraphCanvas.prototype.isNodeBackground = function(node) { - return ((node.id == this.args.idBackgroundNode)); -}; - -GraphCanvas.prototype.isVertex = function(node) { - if (node.getAttribute("id") != null) { - if (node.getAttribute("id").indexOf("_v_") != -1) { - return true; - } - } - return false; -}; - -GraphCanvas.prototype.isLabel = function(node) { - if (node.getAttribute("id") != null) { - if (node.getAttribute("id").indexOf("_l_") != -1) { - return true; - } - } - return false; -}; - -GraphCanvas.prototype.isEdge = function(node) { - if (node.getAttribute("id") != null) { - if (node.getAttribute("id").indexOf("_e_") != -1) { - return true; - } - } - return false; -}; - -/** Resize **/ -GraphCanvas.prototype.resize = function(width, height) { - // this._svg.setAttribute("width", width); - // this._svg.setAttribute("height", height); - if (this.NodeSVGbackgroundImage != null) { - this.NodeSVGbackgroundImage.setAttribute("width", width); - this.NodeSVGbackgroundImage.setAttribute("height", height); - } - - this._svg.setAttribute("width", width); - this._svg.setAttribute("height", height); - - this.clearCanvas(); - this.render(); -}; - -GraphCanvas.prototype.clearCanvas = function() { - DOM.removeChilds(this.GraphEdgeGroup.getAttribute("id")); - DOM.removeChilds(this.GraphNodeGroup.getAttribute("id")); - this.clearLabels(); -}; - -GraphCanvas.prototype.clearLabels = function() { - DOM.removeChilds(this.GraphLabelGroup.getAttribute("id")); -}; - -/** ID'S converter **/ -GraphCanvas.prototype.getSVGNodeId = function(nodeId) { - return this.id + "_v_" + nodeId; -}; - -GraphCanvas.prototype.getSVGEdgeId = function(edgeId) { - return this.id + "_e_" + edgeId; -}; - -GraphCanvas.prototype.getSVGArrowEdgeId = function(edgeId) { - return this.id + "_arrow_" + edgeId; -}; - -GraphCanvas.prototype.getSVGLabelId = function(edgeId) { - return this.id + "_l_" + edgeId; -}; - -GraphCanvas.prototype.blinkVertexById = function(vertexId) { - $("#" + this.getSVGNodeId(vertexId)).fadeIn().fadeOut().fadeIn().fadeOut().fadeIn().fadeOut(); -}; - -GraphCanvas.prototype.getVertexIdFromSVGId = function(svgVertexId) { - return svgVertexId.replace(this.id, "").replace("_v_", ""); -}; - -GraphCanvas.prototype.getEdgeIdFromSVGId = function(svgEdgeId) { - return svgEdgeId.replace(this.id, "").replace("_e_", ""); -}; - -/** VERTEX **/ -GraphCanvas.prototype.getVertexById = function(id) { - return document.getElementById(this.getSVGNodeId(id)); -}; - -GraphCanvas.prototype.renderNodes = function() { - for ( var id in this.getDataset().getVertices()) { - this.renderNode(id); - } -}; - -GraphCanvas.prototype.overNode = function(nodeId) { - if (!this.args.interactive) { - return; - } - /** If selected we don't change the format **/ - if (this.args.isVertexSelected[nodeId] == null) { - var args = this.getFormatter().getVertexById(nodeId).getOver(); - args.args["cursor"] = 'pointer'; - this.changeVertexFormat(nodeId, args); - } -}; - -GraphCanvas.prototype.outNode = function(nodeId) { - if (!this.args.interactive) { - return; - } - - /** If selected we don't change the format **/ - if (this.args.isVertexSelected[nodeId] == null) { - this.changeVertexFormat(nodeId, this.getFormatter().getVertexById(nodeId).getDefault()); - } -}; - -GraphCanvas.prototype.overLabel = function(nodeId) { - this.overNode(nodeId); - // this.svgLabels[nodeId].setAttribute("cursor", "pointer"); -}; - -GraphCanvas.prototype.outLabel = function(nodeId) { - this.outNode(nodeId); - // this.svgLabels[nodeId].setAttribute("cursor", ""); -}; - -GraphCanvas.prototype.clickNode = function(nodeId) { - if (!this.args.interactive) { - return; - } - - /** si el evento se dispara oprque estaba dragging entonces no activo nada **/ - if (this.args.isVertexSelected[nodeId] == null) { - this.selectNode(nodeId); - } else { - this.deselectNode(nodeId); - } -}; - -GraphCanvas.prototype.selectNode = function(nodeId) { - for ( var i = 0; i < this.args.selectedVertices.length; i++) { - var format = this.getFormatter().getVertexById(nodeId).getSelected(); - format.opacity = 0.2; - this.changeVertexFormat(nodeId, this.getFormatter().getVertexById(nodeId).getSelected()); - } - - if (this.args.isVertexSelected[nodeId] == null) { - var format = this.getFormatter().getVertexById(nodeId).getSelected(); - format.opacity = 1; - this.changeVertexFormat(nodeId, this.getFormatter().getVertexById(nodeId).getSelected()); - this.args.selectedVertices.push(nodeId); - this.args.isVertexSelected[nodeId] = this.args.selectedVertices.length - 1; - this.onVertexSelect.notify(nodeId); - } -}; - -GraphCanvas.prototype.selectAllEdges = function() { - this.deselectNodes(); - this.deselectEdges(); - - for ( var edgesId in this.getDataset().edges) { - this.selectEdge(edgesId); - } -}; - -GraphCanvas.prototype.selectAllNodes = function() { - this.deselectNodes(); - this.deselectEdges(); - - for ( var vertexId in this.getDataset().vertices) { - this.selectNode(vertexId); - } -}; - -GraphCanvas.prototype.selectAll = function() { - this.deselectNodes(); - this.deselectEdges(); - - for ( var vertexId in this.getDataset().vertices) { - this.selectNode(vertexId); - } - - for ( var edgesId in this.getDataset().edges) { - this.selectEdge(edgesId); - } -}; - -GraphCanvas.prototype.selectEdge = function(edgeId) { - if (this.args.isEdgeSelected[edgeId] == null) { - this.changeEdgeFormat(edgeId, this.getFormatter().getEdgeById(edgeId).getSelected()); - //this.args.selectedEdges.push(edgeId); - this.args.isEdgeSelected[edgeId] = true; //this.args.selectedEdges.length - 1; - this.onEdgeSelect.notify(edgeId); - } -}; - -GraphCanvas.prototype.selectEdges = function(edges) { - - for ( var i = 0; i < edges.length; i++) { - this.selectEdge(edges[i]); - } -}; - -GraphCanvas.prototype.deselectNode = function(nodeId) { - if (this.args.isVertexSelected[nodeId] != null) { - this.changeVertexFormat(nodeId, this.getFormatter().getVertexById(nodeId).getDefault()); - this.args.selectedVertices.splice(this.args.isVertexSelected[nodeId], 1); - var index = this.args.isVertexSelected[nodeId]; - delete this.args.isVertexSelected[nodeId]; - - for ( var vertex in this.args.isVertexSelected) { - if (this.args.isVertexSelected[vertex] > index) { - this.args.isVertexSelected[vertex] = this.args.isVertexSelected[vertex] - 1; - } - } - } -}; - -GraphCanvas.prototype.deselectNodes = function() { - var selected = JSON.parse(JSON.stringify(this.getSelectedVertices())); - for ( var i = 0; i < selected.length; i++) { - this.deselectNode(selected[i]); - } -}; -GraphCanvas.prototype.selectNodes = function(idNodes) { - - for ( var i = 0; i < idNodes.length; i++) { - this.selectNode(idNodes[i]); - } - - // for ( var vertex in this.args.isVertexSelected) { - // if (this.args.isVertexSelected[vertex] > index){ - // this.args.isVertexSelected[vertex] = this.args.isVertexSelected[vertex] - 1; - // } - // } - -}; - -GraphCanvas.prototype.changeVertexFormat = function(nodeId, format) { - var svgNode = DOM.select(this.getSVGNodeId(nodeId)); - if (svgNode != null) { - var properties = format.toJSON(); - for ( var item in properties) { - svgNode.setAttribute(item, properties[item]); - } - - if (this.getFormatter().getVertexById(nodeId) instanceof CircleVertexGraphFormatter) { - var transform = "translate(" + svgNode.getAttribute("dragx") + "," + svgNode.getAttribute("dragy") + "), scale(" + format.getSize() + ")"; - svgNode.setAttribute("transform", transform); - } - } -}; - -GraphCanvas.prototype.renderLabel = function(nodeId) { - var x = Math.ceil(this.getLayout().getNodeById(nodeId).x * this.getFormatter().getWidth()); - var y = Math.ceil(this.getLayout().getNodeById(nodeId).y * this.getFormatter().getHeight()); - - var svgAttributesNode = JSON.parse(JSON.stringify(this.getFormatter().getVertexById(nodeId).getDefault().toJSON().title)); - svgAttributesNode.id = this.getSVGLabelId(this.getDataset().getVertexById(nodeId).getId()); - svgAttributesNode.dx = (-1) * (this.getDataset().getVertexById(nodeId).getName().length * svgAttributesNode["font-size"]) / 4 - 4; - - svgAttributesNode.dy = parseFloat((this.getFormatter().getVertexById(nodeId).getDefault().getSize())) - + parseFloat(svgAttributesNode["font-size"]) + parseFloat(this.getFormatter().getVertexById(nodeId).getDefault().getStrokeWidth()) - 4; - svgAttributesNode.dragx = Math.ceil(this.getLayout().getNodeById(nodeId).x * this.getFormatter().getWidth()); - - var gragy = parseFloat(this.getFormatter().getVertexById(nodeId).getDefault().getSize()) - + Math.ceil(this.getLayout().getNodeById(nodeId).y * this.getFormatter().getHeight()); - svgAttributesNode.dragy = gragy; - svgAttributesNode.transform = "translate(" + svgAttributesNode.dragx + "," + svgAttributesNode.dragy + ")";//, scale("+this.formatter.getVertexById(nodeId).getDefault().getSize()+")"; - - var nodeSVG = SVG.drawText(0, 0, this.getDataset().getVertexById(nodeId).getName(), this.GraphLabelGroup, svgAttributesNode); - - this.svgLabels[nodeId] = nodeSVG; - - /** Events for the SVG node **/ - var _this = this; - if (nodeSVG != null) { - nodeSVG.addEventListener("mouseover", function() { - _this.overLabel(nodeId); - }, false); - nodeSVG.addEventListener("mouseout", function() { - _this.outLabel(nodeId); - }, false); - } - -}; - -GraphCanvas.prototype.removeLabel = function(labelId) { - if (DOM.select(this.getSVGLabelId(labelId)) != null) { - DOM.select(this.getSVGLabelId(labelId)).parentNode.removeChild(DOM.select(this.getSVGLabelId(labelId))); - } -}; - -GraphCanvas.prototype.renderNode = function(nodeId) { - var svgAttributesNode = JSON.parse(JSON.stringify(this.getFormatter().getVertexById(nodeId).getDefault().toJSON())); - svgAttributesNode.dragx = Math.ceil(this.getLayout().getNodeById(nodeId).x * this.getFormatter().getWidth()); - svgAttributesNode.dragy = Math.ceil(this.getLayout().getNodeById(nodeId).y * this.getFormatter().getHeight()); - svgAttributesNode.transform = "translate(" + svgAttributesNode.dragx + "," + svgAttributesNode.dragy + ")"; - svgAttributesNode.id = this.getSVGNodeId(nodeId); - /*svgAttributesNode["stroke-width"] = 3 ; - svgAttributesNode["stroke-opacity"] = 1 ; - svgAttributesNode["fill-opacity"] = svgAttributesNode["opacity"] ; - svgAttributesNode["opacity"] = 1 ;*/ - this.circleDefaultRadius = this.getFormatter().getVertexById(nodeId).getDefault().getSize(); - var nodeSVG; - - if (this.getFormatter().getVertexById(nodeId) instanceof CircleVertexGraphFormatter) { - nodeSVG = SVG.drawCircle(0, 0, this.circleDefaultRadius, this.GraphNodeGroup, svgAttributesNode); - } - - if (this.getFormatter().getVertexById(nodeId) instanceof SquareVertexGraphFormatter) { - //nodeSVG = SVG.drawRectangle(0 - (this.circleDefaultRadius) ,0 - (this.formatter.getVertexById(nodeId).getDefault().getSize()) , (this.getFormatter().getVertexById(nodeId).getDefault().getSize()*2), (this.getFormatter().getVertexById(nodeId).getDefault().getSize()*2), this.GraphNodeGroup, svgAttributesNode); - nodeSVG = SVG.drawRectangle(0 - (this.circleDefaultRadius), 0 - (this.circleDefaultRadius), (this.circleDefaultRadius * 2), - (this.circleDefaultRadius * 2), this.GraphNodeGroup, svgAttributesNode); - } - - if (this.getFormatter().getVertexById(nodeId) instanceof EllipseVertexGraphFormatter) { - nodeSVG = SVG.drawEllipse(0, 0, this.circleDefaultRadius * 1.5, this.circleDefaultRadius, this.GraphNodeGroup, svgAttributesNode); - } - - if (this.getFormatter().getVertexById(nodeId) instanceof RectangleVertexGraphFormatter) { - //nodeSVG = SVG.drawRectangle(0 - (this.circleDefaultRadius) ,0 - ((this.circleDefaultRadius*2)/2) , (this.circleDefaultRadius*2), (this.circleDefaultRadius), this.GraphNodeGroup, svgAttributesNode); - nodeSVG = SVG.drawRectangle(0 - (this.circleDefaultRadius * 1.5), 0 - (this.circleDefaultRadius), (this.circleDefaultRadius * 2 * 1.5), - (this.circleDefaultRadius * 2), this.GraphNodeGroup, svgAttributesNode); - - } - - if (this.getFormatter().getVertexById(nodeId) instanceof RoundedVertexGraphFormatter) { - svgAttributesNode.ry = 2;// this.formatter.getVertexById(nodeId).getDefault().getSize()/4; - svgAttributesNode.rx = 2;// this.formatter.getVertexById(nodeId).getDefault().getSize()/4; - nodeSVG = SVG.drawRectangle(0 - (this.circleDefaultRadius * 1.5), 0 - (this.circleDefaultRadius), (this.circleDefaultRadius * 2 * 1.5), - (this.circleDefaultRadius * 2), this.GraphNodeGroup, svgAttributesNode); - } - - // - - if (this.getFormatter().getVertexById(nodeId) instanceof OctagonVertexGraphFormatter) { - svgAttributesNode.ry = 2; - svgAttributesNode.rx = 2; - nodeSVG = SVG.drawRectangle(0 - (this.circleDefaultRadius * 1.5), 0 - (this.circleDefaultRadius), (this.circleDefaultRadius * 2 * 1.5), - (this.circleDefaultRadius * 2), this.GraphNodeGroup, svgAttributesNode); - } - - nodeSVG.internalId = nodeId; - // - var _this = this; - - /** Events for the SVG node **/ - if (nodeSVG != null) { - nodeSVG.addEventListener("mouseover", function() { - _this.onVertexOver.notify(nodeId); - _this.overNode(nodeId); - }, false); - nodeSVG.addEventListener("mouseout", function() { - _this.onVertexOut.notify(nodeId); - _this.outNode(nodeId); - }, false); - //nodeSVG.addEventListener("click", function(){_this.clickNode(nodeId);}, false); - // - nodeSVG.addEventListener("mouseup", function() { - _this.onVertexUp.notify(nodeId); - }, false); - } -}; - -GraphCanvas.prototype.removeNode = function(nodeId) { - DOM.select(this.getSVGNodeId(nodeId)).parentNode.removeChild(DOM.select(this.getSVGNodeId(nodeId))); - if (this.args.labeled) { - this.removeLabel(nodeId); - } -}; - -/** REMOVING **/ -GraphCanvas.prototype.removeSelected = function() { - /** El orden importa **/ - this.removeSelectedEdges(); - this.removeSelectedNode(); - -}; - -GraphCanvas.prototype.removeSelectedNode = function() { - var selected = JSON.parse(JSON.stringify(this.getSelectedVertices())); - this.deselectNodes(); - var sorted = selected.sort(function(a, b) { - return a - b - }); - for ( var i = 0; i < sorted.length; i++) { - if (this.getDataset().getVertexById(sorted[i]) != null) { - this.getDataset().getVertexById(sorted[i]).remove(); - } - } -}; - -/** EDGES **/ -GraphCanvas.prototype.removeEdge = function(edgeId) { - if (DOM.select(this.getSVGEdgeId(edgeId)) != null) { - DOM.select(this.getSVGEdgeId(edgeId)).parentNode.removeChild(DOM.select(this.getSVGEdgeId(edgeId))); - } - - if (DOM.select(this.getSVGEdgeId(edgeId) + "_shadow") != null) { - DOM.select(this.getSVGEdgeId(edgeId) + "_shadow").parentNode.removeChild(DOM.select(this.getSVGEdgeId(edgeId) + "_shadow")); - } - - if (DOM.select(this.getSVGArrowEdgeId(edgeId)) != null) { - DOM.select(this.getSVGArrowEdgeId(edgeId)).parentNode.removeChild(DOM.select(this.getSVGArrowEdgeId(edgeId))); - } -}; - -GraphCanvas.prototype.overEdge = function(edgeId) { - if ((!this.args.interactive) || this.dragging || this.selecting) { - return; - } - - /** If selected we don't change the format **/ - if (this.args.isEdgeSelected[edgeId] == null) { - var format = this.getFormatter().getEdgeById(edgeId).getOver(); - format.args["cursor"] = "pointer"; - this.changeEdgeFormat(edgeId, format); - } -}; - -GraphCanvas.prototype.outEdge = function(edgeId) { - if (!this.args.interactive) { - return; - } - - /** If selected we don't change the format **/ - if (this.args.isEdgeSelected[edgeId] == null) { - this.changeEdgeFormat(edgeId, this.getFormatter().getEdgeById(edgeId).getDefault()); - } -}; - -GraphCanvas.prototype.changeEdgeFormat = function(edgeId, format) { - var svgEdge = DOM.select(this.getSVGEdgeId(edgeId) + "_shadow"); - if (svgEdge != null) { - var properties = format.toJSON(); - for ( var item in properties) { - svgEdge.setAttribute(item, properties[item]); - } - } -}; - -GraphCanvas.prototype.deselectEdge = function(edgeID) { - if (this.args.isEdgeSelected[edgeID] != null) { - this.changeEdgeFormat(edgeID, this.getFormatter().getEdgeById(edgeID).getDefault()); - var index = this.args.isEdgeSelected[edgeID]; - delete this.args.isEdgeSelected[edgeID]; - } -}; - -GraphCanvas.prototype.deselectEdges = function() { - var selected = JSON.parse(JSON.stringify(this.getSelectedEdges())); - for ( var i = 0; i < selected.length; i++) { - this.deselectEdge(selected[i]); - } -}; - -GraphCanvas.prototype.removeSelectedEdges = function() { - var selected = JSON.parse(JSON.stringify(this.getSelectedEdges())); - this.deselectEdges(); - for ( var i = 0; i < selected.length; i++) { - if (this.getDataset().getEdgeById(selected[i]) != null) { - this.getDataset().getEdgeById(selected[i]).remove(); - } - } -}; - -GraphCanvas.prototype.renderEdge = function(edgeId) { - var svgAttributesEdge = this.getFormatter().getEdgeById(edgeId).getDefault().toJSON(); - var edge = this.getDataset().getEdgeById(edgeId); - - var svgNodeTarget = this.getVertexById(edge.getNodeTarget().getId()); - var svgNodeSource = this.getVertexById(edge.getNodeSource().getId()); - svgAttributesEdge.id = this.getSVGEdgeId(edge.getId()) + "_shadow"; - - var svgEdge = null; - - if (this.getFormatter().getEdgeById(edgeId) instanceof LineEdgeGraphFormatter) { - var coordenateSourceX = svgNodeSource.getAttribute("dragx"); - var coordenateSourceY = svgNodeSource.getAttribute("dragy"); - var coordenateTargetX = svgNodeTarget.getAttribute("dragx"); - var coordenateTargetY = svgNodeTarget.getAttribute("dragy"); - - SVG.drawLine(coordenateSourceX, coordenateSourceY, coordenateTargetX, coordenateTargetY, this.GraphEdgeGroup, svgAttributesEdge); - var attributesShadow = {}; - attributesShadow.id = this.getSVGEdgeId(edge.getId()); - attributesShadow["stroke-opacity"] = 0; - attributesShadow["stroke-width"] = 4; - attributesShadow["stroke"] = "black"; - svgEdge = SVG.drawLine(svgNodeSource.getAttribute("dragx"), svgNodeSource.getAttribute("dragy"), svgNodeTarget.getAttribute("dragx"), - svgNodeTarget.getAttribute("dragy"), this.GraphEdgeGroup, attributesShadow); - } - - if (this.getFormatter().getEdgeById(edgeId) instanceof BezierEdgeGraphFormatter) { - var nodeId = edge.getNodeTarget().getId(); - var nodeSize = this.formatter.getVertexById(nodeId).getDefault().getSize() * this.getFormatter().getNodesMaxSize(); - svgAttributesEdge.fill = "none"; - svgAttributesEdge.id = this.getSVGEdgeId(edgeId); - var d = this.calculateCoordenatesBezier(nodeSize, svgNodeSource.getAttribute("dragx"), svgNodeSource.getAttribute("dragy")); - svgEdge = SVG.drawPath(d, this.GraphEdgeGroup, svgAttributesEdge); - } - ; - - if ((this.getFormatter().getEdgeById(edgeId) instanceof DirectedLineEdgeGraphFormatter) - || (this.getFormatter().getEdgeById(edgeId) instanceof CutDirectedLineEdgeGraphFormatter) - || (this.getFormatter().getEdgeById(edgeId) instanceof DotDirectedLineEdgeGraphFormatter) - || (this.getFormatter().getEdgeById(edgeId) instanceof OdotDirectedLineEdgeGraphFormatter) - || (this.getFormatter().getEdgeById(edgeId) instanceof OdirectedLineEdgeGraphFormatter)) { - var coordenateSourceX = svgNodeSource.getAttribute("dragx"); - var coordenateSourceY = svgNodeSource.getAttribute("dragy"); - var coordenateTargetX = svgNodeTarget.getAttribute("dragx"); - var coordenateTargetY = svgNodeTarget.getAttribute("dragy"); - - var offset = parseFloat(this.getFormatter().getVertexById(this.getDataset().getEdgeById(edgeId).getNodeTarget().getId()).getDefault() - .getSize() - * this.circleDefaultRadius); - var point = this._calculateEdgePointerPosition(coordenateSourceX, coordenateSourceY, coordenateTargetX, coordenateTargetY, offset); - coordenateTargetX = point.x; - coordenateTargetY = point.y; - SVG.drawLine(coordenateSourceX, coordenateSourceY, coordenateTargetX, coordenateTargetY, this.GraphEdgeGroup, svgAttributesEdge); - - var attributesShadow = {}; - attributesShadow.id = this.getSVGEdgeId(edge.getId()); - attributesShadow["stroke-opacity"] = 0; - attributesShadow["stroke-width"] = 4; - attributesShadow["stroke"] = "black"; - svgEdge = SVG.drawLine(coordenateSourceX, coordenateSourceY, coordenateTargetX, coordenateTargetY, this.GraphEdgeGroup, attributesShadow); - } - - if (this.getFormatter().getEdgeById(edgeId) instanceof DirectedLineEdgeGraphFormatter - || (this.getFormatter().getEdgeById(edgeId) instanceof OdirectedLineEdgeGraphFormatter)) { - var coordenateSourceX = svgNodeSource.getAttribute("dragx"); - var coordenateSourceY = svgNodeSource.getAttribute("dragy"); - var coordenateTargetX = svgNodeTarget.getAttribute("dragx"); - var coordenateTargetY = svgNodeTarget.getAttribute("dragy"); - - var point = this._calculateEdgePointerPosition(coordenateSourceX, coordenateSourceY, coordenateTargetX, coordenateTargetY, offset); - coordenateTargetX = point.x; - coordenateTargetY = point.y; - - var angle = Geometry.toDegree(point.angle) + 90; - this.arrowDefaultSize = this.getFormatter().getEdgeById(edgeId).getArrowSize(); //getDefault().getArrowSize(); - var d = "-" + this.arrowDefaultSize + ",0 0,-" + parseFloat(this.arrowDefaultSize) * 2 + " " + this.arrowDefaultSize + ",0"; - - var attributes; - - if (this.getFormatter().getEdgeById(edgeId) instanceof DirectedLineEdgeGraphFormatter) { - attributes = [ - [ "fill", this.getFormatter().getEdgeById(edgeId).getDefault().getStroke() ], - [ "stroke", this.getFormatter().getEdgeById(edgeId).getDefault().getStroke() ], [ "id", this.getSVGArrowEdgeId(edgeId) ] ]; - } else { - attributes = [ - [ "fill", "#FFFFFF" ], [ "stroke", this.getFormatter().getEdgeById(edgeId).getDefault().getStroke() ], - [ "id", this.getSVGArrowEdgeId(edgeId) ] ]; - } - - var flechaSVGNode = SVG.drawPoligon(d, this.GraphEdgeGroup, attributes);//, ["transform", "rotate("+angle+"), translate(0,0)"]]); - flechaSVGNode.setAttribute("transform", " translate(" + coordenateTargetX + ", " + coordenateTargetY + "), rotate(" + angle + ")"); - } - ; - - if (this.getFormatter().getEdgeById(edgeId) instanceof CutDirectedLineEdgeGraphFormatter) { - var coordenateSourceX = svgNodeSource.getAttribute("dragx"); - var coordenateSourceY = svgNodeSource.getAttribute("dragy"); - var coordenateTargetX = svgNodeTarget.getAttribute("dragx"); - var coordenateTargetY = svgNodeTarget.getAttribute("dragy"); - - var point = this._calculateEdgePointerPosition(coordenateSourceX, coordenateSourceY, coordenateTargetX, coordenateTargetY, offset); - coordenateTargetX = point.x; - coordenateTargetY = point.y; - - var angle = Geometry.toDegree(point.angle) + 90; - - //this.arrowDefaultSize = 2; //getDefault().getArrowSize(); - var d = "-4,0 4,0 4,-2 -4,-2"; - - var flechaSVGNode = SVG.drawPoligon(d, this.GraphEdgeGroup, [ - [ "fill", this.getFormatter().getEdgeById(edgeId).getDefault().getStroke() ], - [ "stroke", this.getFormatter().getEdgeById(edgeId).getDefault().getStroke() ], [ "id", this.getSVGArrowEdgeId(edgeId) ] ]);//, ["transform", "rotate("+angle+"), translate(0,0)"]]); - flechaSVGNode.setAttribute("transform", " translate(" + coordenateTargetX + ", " + coordenateTargetY + "), rotate(" + angle + ")"); - } - ; - - if ((this.getFormatter().getEdgeById(edgeId) instanceof DotDirectedLineEdgeGraphFormatter) - || (this.getFormatter().getEdgeById(edgeId) instanceof OdotDirectedLineEdgeGraphFormatter)) { - var coordenateSourceX = svgNodeSource.getAttribute("dragx"); - var coordenateSourceY = svgNodeSource.getAttribute("dragy"); - var coordenateTargetX = svgNodeTarget.getAttribute("dragx"); - var coordenateTargetY = svgNodeTarget.getAttribute("dragy"); - var point = this._calculateEdgePointerPosition(coordenateSourceX, coordenateSourceY, coordenateTargetX, coordenateTargetY, offset); - coordenateTargetX = point.x; - coordenateTargetY = point.y; - var angle = Geometry.toDegree(point.angle) + 90; - // this.arrowDefaultSize = this.formatter.getEdgeById(edgeId).getArrowSize(); //getDefault().getArrowSize(); - var attributes = []; - if (this.getFormatter().getEdgeById(edgeId) instanceof OdotDirectedLineEdgeGraphFormatter) { - attributes = [ - [ "fill", "#FFFFFF" ], [ "stroke", this.getFormatter().getEdgeById(edgeId).getDefault().getStroke() ], - [ "id", this.getSVGArrowEdgeId(edgeId) ] ]; - } else { - attributes = [ - [ "fill", this.getFormatter().getEdgeById(edgeId).getDefault().getStroke() ], - [ "stroke", this.getFormatter().getEdgeById(edgeId).getDefault().getStroke() ], [ "id", this.getSVGArrowEdgeId(edgeId) ] ]; - } - var flechaSVGNode = SVG.drawCircle(0, 0, 4, this.GraphEdgeGroup, attributes); - flechaSVGNode.setAttribute("transform", " translate(" + coordenateTargetX + ", " + coordenateTargetY + "), rotate(" + angle + ")"); - } - ; - - var _this = this; - /** Events for the SVG edge **/ - if (svgEdge != null) { - if (this.getDataset().getEdgesCount() < this.args.maxNumberEdgesFiringEvents) { - svgEdge.addEventListener("mouseover", function() { - _this.overEdge(edgeId); - }, false); - svgEdge.addEventListener("mouseout", function() { - _this.outEdge(edgeId); - }, false); - } - } -}; - -GraphCanvas.prototype._calculateEdgePointerPosition = function(sourceX, sourceY, targetX, targetY, radius) { - var angle = Geometry.getAngleBetweenTwoPoints(sourceX, sourceY, targetX, targetY); - - /** Suponiendo el node source que este a la derecha **/ - if ((targetX - sourceX) < 0) { - var b = Geometry.getAdjacentSideOfRectangleRight(angle, radius); - targetX = parseFloat(targetX) + parseFloat(b); - arrowX = parseFloat(targetX) + parseFloat(b) + this.arrowDefaultSize / 2; - } else { - var b = Geometry.getAdjacentSideOfRectangleRight(angle, radius); - targetX = parseFloat(targetX) - parseFloat(b); - arrowX = parseFloat(targetX) - parseFloat(b) - this.arrowDefaultSize / 2; - } - - /** Suponiendo el node source que este a la arriba **/ - if ((targetY - sourceY) > 0) { - var a = Geometry.getOppositeSideOfRectangleRight(angle, radius); - targetY = parseFloat(targetY) - parseFloat(a); - arrowY = parseFloat(targetY) - parseFloat(a) - this.arrowDefaultSize / 2; - } else { - var a = Geometry.getOppositeSideOfRectangleRight(angle, radius); - targetY = parseFloat(targetY) + parseFloat(a); - arrowY = parseFloat(targetY) + parseFloat(a) - this.arrowDefaultSize / 2; - - } - - return { - "x" : arrowX, - "y" : arrowY, - "angle" : angle - }; -}; - -GraphCanvas.prototype.calculateCoordenatesBezier = function(nodeSize, x1, y1) { - var x11 = x1 - (nodeSize / 2); - var y11 = y1 - (nodeSize / 2); - - var x12 = parseFloat(x1) + parseFloat(nodeSize / 2); - var y12 = y1 - (nodeSize / 2); - - var curvePointX = (x12 - x11) / 2 + x11; - var curvePointY = y1 - (nodeSize * 2); - var d = "M" + x11 + "," + y11 + " T" + curvePointX + "," + curvePointY + " " + x12 + "," + y12; - return d; - -}; - -GraphCanvas.prototype.renderEdges = function() { - for ( var edge in this.getDataset().getEdges()) { - this.renderEdge(this.getDataset().getEdgeById(edge).getId()); - - } -}; - -GraphCanvas.prototype.getLastSelectedNode = function() { - var node = null; - if (this.getSelectedVertices().length > 0) { - var nodeId = this.getSelectedVertices()[this.getSelectedVertices().length - 1]; - node = this.getDataset().getVertexById(nodeId); - } - return node; -}; -/* - GraphCanvas.prototype.getNodeByNameAndIndex = function(node, index){ - var nodeId = this.getDataset().verticesIndex[node][index]; - var nodeItem = this.getDataset().getVertexById(nodeId); - return nodeItem; - }; - */ - -GraphCanvas.prototype.setDataset = function(dataset) { - this.dataset = dataset; -}; - -GraphCanvas.prototype.setFormatter = function(formatter) { - this.formatter = formatter; -}; - -GraphCanvas.prototype.setLayout = function(layout) { - this.layout = layout; -}; - -/** API **/ -GraphCanvas.prototype.getDataset = function() { - return this.dataset; -}; - -GraphCanvas.prototype.getFormatter = function() { - return this.formatter; -}; - -GraphCanvas.prototype.getLayout = function() { - return this.layout; -}; - -/** API DATASET **/ -GraphCanvas.prototype.addVertex = function(name, args) { - this.getDataset().addNode(name, args); -}; - -GraphCanvas.prototype.removeVertex = function(vertexId) { - this.getDataset().getVertexById(vertexId).remove(); -}; - -GraphCanvas.prototype.addEdge = function(edgeName, nodeSourceId, nodeTargetId, args) { - this.getDataset().addEdge(edgeName, nodeSourceId, nodeTargetId, args); -}; -/* - GraphCanvas.prototype.removeEdge = function(edgeId){ - this.getDataset().getEdgeById(edgeId).remove(); - }; - */ - -/** API FORMATTER **/ -GraphCanvas.prototype.getWidth = function() { - return this.getFormatter().getWidth(); -}; - -GraphCanvas.prototype.getHeight = function() { - return this.getFormatter().getHeight(); -}; - -GraphCanvas.prototype.getBackgroundImage = function() { - return this.getFormatter().getBackgroundImage(); -}; +/** + * Example form + * + * @witdh + * @height + */ +function ExampleForm(args) { + this.id = BUI.id(); + this.width = 700; + this.height = 500; + + if (args != null) { + if (args.width != null) { + this.width = args.width; + } + if (args.height != null) { + this.height = args.height; + } + } +} + +ExampleForm.prototype._getItems = function() { + return [{ + fieldLabel : 'First Name', + name : 'first', + allowBlank : false + }, { + fieldLabel : 'Last Name', + name : 'last', + allowBlank : false + } ]; +}; + +ExampleForm.prototype._getItems = function() { + return [ ]; +}; + +ExampleForm.prototype._getButtons = function() { + return [ ]; +}; + +ExampleForm.prototype.getPanel = function() { + this.panel = Ext.create('Ext.form.Panel', { + width : this.width, + height : this.height, + margin : 10, + border : 1, + defaultType : 'textfield', + items : this._getItems(), + buttons : this._getButtons() + }); + return this.panel; +}; + +/** It populates the form **/ +ExampleForm.prototype.refresh = function(macromolecule) { +}; -//GraphCanvas.prototype.setBackgroundImage = function(value){ -// this.getFormatter().setBackgroundImage(value); -//}; +function ExperimentForm(args) { + this.id = BUI.id(); + + if (args != null) { + } + + this.onSaved = new Event(this); +} + +ExperimentForm.prototype._getItems = function(experiment) { + this.experiment = experiment; + var typeCombo = Ext.create('Ext.form.ComboBox', { + id : this.id + 'type', + fieldLabel : 'Type', + store : [ "STATIC", "CALIBRATION", "HPLC" ], + queryMode : 'local', + labelWidth : 120, + displayField : 'name', + valueField : 'name', + value : experiment.json.type, + disabled : (experiment.json.type == 'TEMPLATE') + }); + + var items = []; + + + if (experiment.json.type == "HPLC" ){ + var typeMacromolecule = BIOSAXS_COMBOMANAGER.getComboMacromoleculeByMacromolecules(BIOSAXS.proposal.getMacromolecules(), {labelWidth : 120, width: "120px"}); + if (experiment.getHPLCMacromolecule() != null){ + typeMacromolecule.setValue(experiment.getHPLCMacromolecule().macromoleculeId); + items.push(typeMacromolecule); + } + } + + + items.push(typeCombo, { + id : this.id + 'name', + xtype : 'textfield', + fieldLabel : 'Name', + labelWidth : 120, + width : '100%', + value : experiment.json.name + }, { + id : this.id + 'comments', + xtype : 'textareafield', + name : 'comments', + fieldLabel : 'Comments', + labelWidth : 120, + height : 120, + width : '100%', + value : experiment.json.comments + }); + return items; +}; +ExperimentForm.prototype.getPanel = function(experiment) { + var _this = this; + + this.panel = Ext.createWidget({ + xtype : 'container', + layout : 'vbox', + border : 0, + style : { + padding : '10px' + }, + fieldDefaults : { + labelAlign : 'left', + labelWidth : 50 + }, + items : this._getItems(experiment) + }); + return this.panel; +}; + +ExperimentForm.prototype.input = function() { + return new ExperimentHeaderForm().input(); +}; + +ExperimentForm.prototype.test = function(targetId) { + var experimentForm = new ExperimentForm(); + var panel = experimentForm.getPanel(new Experiment(experimentForm.input().experiment)); + panel.render(targetId); +}; -GraphCanvas.prototype.getBackgroundColor = function() { - return this.getFormatter().getBackgroundColor(); -}; +/** + * Shows the header for the experiments changing the color and parameters depending on experiment type + * + */ +function ExperimentHeaderForm(args) { + this.id = BUI.id(); + this.backgroundColor = '#FFFFFF'; +} + +ExperimentHeaderForm.prototype.getHTMLSource = function(experiment) { + var html = BUI.createFormLabel("Name :", experiment.json.name, 75, 400, this.backgroundColor); + if (experiment.json.type == "HPLC") { + if (experiment.getHPLCMacromolecule() != null){ + html = html + BUI.createFormLabel("Molecule :", experiment.getHPLCMacromolecule().acronym, 75, 400, this.backgroundColor); + } + } + else{ + html = html + BUI.createFormLabel("Type :", experiment.json.type, 75, 400, this.backgroundColor); + } + + html = html + BUI.createFormLabel("Date :", experiment.json.creationDate, 75, 400, this.backgroundColor); + return html; +}; + +ExperimentHeaderForm.prototype.getHTMLDownload = function(experiment) { + var bgcolor = "background-color:" + this.backgroundColor + ";"; + var html = "
"; + if ((experiment.json.type == "CALIBATION") || (experiment.json.type == "STATIC")) { + html = html + " Download Source File
"; + html = html + + ""; + } + if (experiment.json.type == "TEMPLATE") { + html = html + + " Download Source File
"; + } + + if (experiment.json.type == "HPLC") { + html = html + " Download h5 File"; + } + + return html; +}; + +ExperimentHeaderForm.prototype.getTopPanel = function(experiment) { + return { + xtype : 'container', + layout : 'hbox', + items : [ { + margin : "0 0 0 0", + width : 475, + border : 0, + html : this.getHTMLSource(experiment) + }, { + margin : "0 0 0 0", + width : 475, + border : 0, + html : this.getHTMLDownload(experiment) + } ] + }; +}; + +ExperimentHeaderForm.prototype.getButton = function(experiment) { + var _this = this; + return Ext.create('Ext.Button', { + text : 'EDIT', + minWidth : '100', + margin : '10 0 0 30', + handler : function() { + var experimentWindow = new ExperimentWindow(); + experimentWindow.onSaved.attach(function(sender, data) { + _this.experiment.json.name = data.name; + _this.experiment.json.type = data.type; + _this.experiment.json.comments = data.comments; + _this.panel.remove(_this.panel.items.items[0]); + _this.panel.remove(_this.panel.items.items[0]); + _this.panel.insert(_this.getTopPanel(_this.experiment)); + _this.panel.insert(_this.getBottomPanel(_this.experiment)); + }); + experimentWindow.show(experiment); + } + }); +}; + +ExperimentHeaderForm.prototype.getBottomPanel = function(experiment) { + return { + xtype : 'container', + layout : 'hbox', + margin : '10 0 0 0', + items : [ this.getComments(experiment), this.getButton(experiment) ] + }; +}; + +ExperimentHeaderForm.prototype.getComments = function(experiment) { + return { + xtype : 'textareafield', + labelStyle : 'font-weight: bold;', + name : 'comments', + fieldLabel : 'Comments', + labelWidth : 70, + height : 40, + minWidth : '450', + readOnly : true, + value : experiment.json.comments + }; +}; + +ExperimentHeaderForm.prototype.getPanel = function(experiment) { + this.experiment = experiment; + + if (experiment.json.type == 'CALIBRATION') { + this.backgroundColor = '#EFFBFB'; + } + if (experiment.json.type == 'TEMPLATE') { + this.backgroundColor = '#E0F8E6'; + } + + this.panel = Ext.create('Ext.container.Container', { + frame : false, + layout : 'vbox', + padding : 5, + bodyPadding : '5 5 0 0', + width : 890, + margin : '0 0 10 0', + height : 120, + style : { + borderColor : '#99bce8', + borderStyle : 'solid', + borderWidth : '1px', + 'background-color' : this.backgroundColor + }, + fieldDefaults : { + msgTarget : 'side', + labelWidth : 100 + }, + items : [ this.getTopPanel(experiment), this.getBottomPanel(experiment) ] + }); + return this.panel; +}; + +ExperimentHeaderForm.prototype.input = function() { + return { + experiment : DATADOC.getExperiment_10() + }; +}; + +ExperimentHeaderForm.prototype.test = function(targetId) { + var experimentHeaderForm = new ExperimentHeaderForm(); + var panel = experimentHeaderForm.getPanel(new Experiment(experimentHeaderForm.input().experiment)); + panel.render(targetId); + +}; -GraphCanvas.prototype.setBackgroundColor = function() { - this.getFormatter().setBackgroundColor(value); -}; +/** + * Macromolecule form with the general parameters of a macromolecule + * + * @witdh + * @height + * + * #onSave button save has been clicked + * #onClose button close has been clicked + */ +function MacromoleculeForm(args) { + this.id = BUI.id(); + this.width = 700; + this.height = 500; + + if (args != null) { + if (args.width != null) { + this.width = args.width; + } + if (args.height != null) { + this.height = args.height; + } + } + + /** Events **/ + this.onSave = new Event(this); + this.onClose = new Event(this); +} + +/** Type : is the Ext type then requiredtext or textfield * */ +MacromoleculeForm.prototype._getFieldTextWithHelp = function(type, fieldLabel, fieldName, help) { + return Ext.create('Ext.container.Container', { + margin : "10 0 0 10", + items : [ { + xtype : type, + fieldLabel : fieldLabel, + name : fieldName, + id : this.id + fieldName + }, { + xtype : 'label', + forId : 'myFieldId', + text : help, + margin : "5 0 0 105", + cls : "inline-help" + } ] + }); +}; + +MacromoleculeForm.prototype._getNumericWithHelp = function(type, fieldLabel, fieldName, help) { + return Ext.create('Ext.container.Container', { + margin : "0 0 0 10", + items : [ { + xtype : type, + fieldLabel : fieldLabel, + name : fieldName, + id : this.id + fieldName, + decimalPrecision : 6, + width : 220 + }, { + xtype : 'label', + forId : 'myFieldId', + text : help, + margin : "5 0 0 10", + cls : "inline-help" + } ] + }); +}; + +MacromoleculeForm.prototype._getButtons = function() { + var _this = this; + return [ { + text : 'Save', + handler : function() { + _this._save(); + } + },{ + text : 'Close', + handler : function() { + _this.onClose.notify(); + + } + } ]; +}; + +/** It persits the macromolecule in the database **/ +MacromoleculeForm.prototype._persist = function(macromoleculeId, acronym, name, molecularMass, extintionCoefficient, comments, refractiveIndex, solventViscosity) { + + /** Checking not duplicated acronym **/ + if (((macromoleculeId == null) && (BIOSAXS.proposal.getMacromoleculeByAcronym(acronym) != null))== true){ + BUI.showError("Duplicated acronym"); + return; + } + + + if (macromoleculeId == null){ + /** new macromolecule **/ + this.macromolecule = {}; + this.macromolecule.macromoleculeId = null; + } + else{ + this.macromolecule.macromoleculeId = macromoleculeId; + } + + this.macromolecule["acronym"] = acronym; + this.macromolecule["name"] = name; + this.macromolecule["molecularMass"] = molecularMass; + this.macromolecule["extintionCoefficient"] = extintionCoefficient; + this.macromolecule["comments"] = comments; + this.macromolecule["symmetry"] = Ext.getCmp(this.id + 'comboSym').getValue(); + this.macromolecule["refractiveIndex"] = refractiveIndex; + this.macromolecule["solventViscosity"] = solventViscosity; + + var _this = this; + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, proposal) { + /** Updating the proposal global object **/ + BIOSAXS.proposal.setItems(proposal); + _this.panel.setLoading(false); + + var saved = BIOSAXS.proposal.getMacromoleculeByAcronym(acronym); + /** Refreshing to check that everything is ok **/ + _this.refresh(saved); + _this.onSave.notify(saved); + }); + + this.panel.setLoading("Saving Macromolecule") + adapter.saveMacromolecule(this.macromolecule); +}; + +/** Save the macromolecule in the DB **/ +MacromoleculeForm.prototype._save = function() { + + var _this = this; + + var acronym = this._getField("acronym"); + var name = this._getField("name"); + var molecularMass = this._getField("molecularMass"); + var extintionCoefficient = this._getField("extintionCoefficient"); + var comments = this._getField("comments"); + + var refractiveIndex = this._getField("refractiveIndex"); + var solventViscosity = this._getField("solventViscosity"); + + /** Checking required fields **/ + if (name == "") { + BUI.showError("Name field is mandatory"); + return; + } + if (acronym == "") { + BUI.showError("Acroynm field is mandatory"); + return; + } + + if (this.macromolecule != null){ + /** Checking if it is a new macromolecule **/ + if (this.macromolecule.macromoleculeId == null){ + /** Check if the acronym exists already **/ + this._persist(null, acronym, name, molecularMass, extintionCoefficient, comments, refractiveIndex, solventViscosity); + } + else{ + /** It is an update **/ + this._persist(this.macromolecule.macromoleculeId, acronym, name, molecularMass, extintionCoefficient, comments, refractiveIndex, solventViscosity); + } + } + else{ + /** It is a new macromolecule **/ + this._persist(null, acronym, name, molecularMass, extintionCoefficient, comments, refractiveIndex, solventViscosity); + } +}; + + + +MacromoleculeForm.prototype._getItems = function() { + var _this = this; + /** Symmetry combo box **/ + var symmetry = Ext.create('Ext.data.Store', { + fields : [ 's' ], + data : this._getSymmetries() + }); + + this.symmetryComboBox = Ext.create('Ext.form.ComboBox', { + fieldLabel : 'Symmetry', + store : symmetry, + id : this.id + 'comboSym', + queryMode : 'local', + displayField : 's', + valueField : 's', + value : "P1", + margin : "0 0 0 30", + width : 220 + }); + + return [this._getFieldTextWithHelp("requiredtext", "Name", "name", "Long name. i.e: Bovine serum albumin"), + this._getFieldTextWithHelp("requiredtext", "Acronym", "acronym", "Acronym will be used in the files and analisys. i.e: BSA"), + this._getFieldTextWithHelp("textfield", "Mol. Mass (Da)", "molecularMass", "Atomic mass estimation measured in Da"), + { + xtype : 'container', + layout : 'hbox', + margin : "10 0 0 0", + items :[ + this._getNumericWithHelp("numberfield", "Extinction coef.", "extintionCoefficient", ""), + this.symmetryComboBox + ] + }, + { + xtype : 'container', + layout : 'hbox', + margin : "5 0 0 0", + items :[ + this._getNumericWithHelp("numberfield", "Refractive Index", "refractiveIndex", "How radiation propagates through the medium"), + this._getNumericWithHelp("numberfield", "Solvent Viscosity", "solventViscosity", "") + ] + }, + { + id : this.id + "comments", + xtype : 'textareafield', + name : 'comments', + margin : '35 0 0 10', + fieldLabel : 'Comments', + width : this.width - 100 + } ]; +}; + +MacromoleculeForm.prototype._getSymmetries = function() { + return [ { + "s" : "P1" + }, { + "s" : "P2" + }, { + "s" : "P3" + }, { + "s" : "P4" + }, { + "s" : "P5" + }, { + "s" : "P6" + }, { + "s" : "P32" + }, { + "s" : "P42" + }, { + "s" : "P52" + }, { + "s" : "P62" + }, { + "s" : "P222" + } ] +}; + +MacromoleculeForm.prototype.getPanel = function() { + this.panel = Ext.create('Ext.form.Panel', { + width : this.width, + height : this.height, + margin : 10, + border : 1, + defaultType : 'textfield', + items : this._getItems(), + buttons : this._getButtons() + }); + return this.panel; +}; + + +/** Populates each text field by field name and value **/ +MacromoleculeForm.prototype._populateField = function(fieldName, value) { + if (value != null){ + Ext.getCmp(this.id + fieldName).setValue(value); + } +}; + +/** Gets the value of a textfield **/ +MacromoleculeForm.prototype._getField = function(fieldName) { + return Ext.getCmp(this.id + fieldName).getValue(); +}; + + +/** It populates the form **/ +MacromoleculeForm.prototype.refresh = function(macromolecule) { + this.macromolecule = macromolecule; + if (macromolecule != null){ + this._populateField("name", macromolecule.name); + this._populateField("acronym", macromolecule.acronym); + this._populateField("extintionCoefficient", macromolecule.extintionCoefficient); + this._populateField("molecularMass", macromolecule.molecularMass); + this._populateField("comments", macromolecule.comments); + this._populateField("refractiveIndex", macromolecule.refractiveIndex); + this._populateField("solventViscosity", macromolecule.solventViscosity); + if (macromolecule.symmetry != null){ + Ext.getCmp(this.id + 'comboSym').setValue(macromolecule.symmetry); + } + } +}; + + +MacromoleculeForm.prototype.input = function() { + return {}; +}; + + +/** It populates the form **/ +MacromoleculeForm.prototype.test = function(targetId) { + var macromoleculeForm = new MacromoleculeForm(); + macromoleculeForm.onClose.attach(function(sender){ + alert("Click on close"); + }); + var panel = macromoleculeForm.getPanel(); + panel.render(targetId); +}; + + -//GraphCanvas.prototype.setEdgeFill = function(edgeId, value){ -// this.getFormatter().getEdgeById(edgeId).getDefault().setFill(value); -//}; -// -//GraphCanvas.prototype.getEdgeFill = function(edgeId){ -// return this.getFormatter().getEdgeById(edgeId).getDefault().getFill(); -//}; + + +function ModelVisualizerForm(args){ + this.id =BUI.id(); + this.width = 600; + this.height = 400; + if (args!= null){ + if (args.width != null){ + this.width = args.width; + } + if (args.height != null){ + this.height = args.height; + } + } +}; + +ModelVisualizerForm.prototype._getFirHTML = function(modelId, width, height, type, desc) { + var html = ""; + html = html + ''; + html = html + ''; + html = html + ''; + html = html + '
dammin.' + type + '
' + desc + '
'; + return html; +}; + +ModelVisualizerForm.prototype.getItems = function(modelPanel){ + _this = this; + var height = _this.height/2 - 60; + var width = _this.width/2 - 10; + + return Ext.create('Ext.container.Container', { + layout: { + type: 'vbox', // Arrange child items vertically + }, + items: [ + modelPanel, + { + xtype : 'container', + layout: { + type: 'hbox', // Arrange child items vertically + }, + items : [{ + html : this._getFirHTML("id", width, height, "fir", "Fit of the simulated scattering curve versus a smoothed experimental data (spline interpolation)"), + height :height, + width : width, + padding: 2 + }, + { + html : this._getFirHTML("id", width, height, "fit", "Fit of the simulated scattering curve versus the experimental data."), + height : height, + width : width, + padding: 2 + }] + } + + ] + }); +}; + +ModelVisualizerForm.prototype.refresh = function(models){ + var input = []; +// var colors = ["008000", "F0A804", "0000FF", "800080", "C0C0C0"]; + for (var i = 0; i < models.length; i++) { + console.log(BUI.rainbow(models.length, i).replace("#", "0x")); + input.push({ + color: BUI.rainbow(models.length, i).replace("#", "0x"), + modelId: models[i].modelId, + opacity: 0.8, + radius: 3, + title: BUI.getFileNameByPath(models[i].pdbFile), + type: "SHAPEDETERMINATIONMODEL" + + }); + } + + this.panel.removeAll(); + this.panel.add( + _this.getItems( + new PDBViewer({ + width : this.width - 10, + height : (this.height/2) - 10, + title : "" + }).draw(input) + ) + ); + + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, data) { + var splitted = data.toString().split("\n"); + var array = []; + for ( var i = 0; i < splitted.length; i++) { + var line = splitted[i].trim(); + var line_splited = line.split(/\s*[\s,]\s*/); + if (line_splited.length == 4) { + array.push([ Number(line_splited[0]), BUI.getStvArray(line_splited[1], line_splited[2]), BUI.getStvArray(line_splited[3], 0) ]); + } + } + + var id = (_this.id + "firid"); + var dygraphWidget = new StdDevDyGraph(id, { + width : (_this.width/2) - 10, + height :(_this.height/2) -110, + xlabel : 'q(nm-1)' + }); + dygraphWidget.draw(array, [ "#FF0000", "#0000FF", "#FF00FF" ], [ 's', 'I(exp)', 'I(sim)' ]); + }); + + adapter.getModelFile("FIR", models[0].modelId); + + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, data) { + var splitted = data.toString().split("\n"); + var array = []; + for ( var i = 0; i < splitted.length; i++) { + var line = splitted[i].trim(); + var line_splited = line.split(/\s*[\s,]\s*/); + if (line_splited.length == 3) { + array.push([ Number(line_splited[0]), BUI.getStvArray(line_splited[1], 0), BUI.getStvArray(line_splited[2], 0) ]); + } + } + + var id = (_this.id + "fitid"); + + var dygraphWidget = new StdDevDyGraph(id, { + width : (_this.width/2) - 10, + height : (_this.height/2) - 110, + xlabel : 'q(nm-1)' + }); + dygraphWidget.draw(array, [ "#FF0000", "#0000FF" ], [ "s", "I(exp)", "I(sim)" ]); + }); + adapter.getModelFile("FIT", models[0].modelId); +}; + +ModelVisualizerForm.prototype.getPanel = function(modelList){ + _this = this; + this.modelList = modelList; + this.panel = Ext.create('Ext.Panel', { + title: 'Results', + width: this.width, + height: this.height, + layout: { + type: 'vbox', // Arrange child items vertically +// align: 'stretch' // Each takes up full width + }, + items: [ + + ], + listeners : { + afterrender : function(grid, eOpts) { +// alert(_this.modelList) + } + } + }); + + return this.panel; + +}; + -/** VERTICES FORMATTER **/ -GraphCanvas.prototype.setVertexSize = function(vertexId, value) { - this.getFormatter().getVertexById(vertexId).getDefault().setSize(value); -}; +/** + * Example form + * + * @witdh + * @height + */ +function MolarityForm(args) { + this.id = BUI.id(); + this.width = 700; + this.height = 500; + + if (args != null) { + if (args.width != null) { + this.width = args.width; + } + if (args.height != null) { + this.height = args.height; + } + } + + this.onSave = new Event(this); + this.onClose = new Event(this); +} + + +MolarityForm.prototype._getNumericWithHelp = function(type, fieldLabel, fieldName, help) { + return Ext.create('Ext.container.Container', { + margin : "10 0 0 10", + items : [ { + xtype : type, + fieldLabel : fieldLabel, + name : fieldName, + id : this.id + fieldName, + decimalPrecision : 6 + }, { + xtype : 'label', + forId : 'myFieldId', + text : help, + margin : "5 0 0 105", + cls : "inline-help" + } ] + }); +}; + + +MolarityForm.prototype._getItems = function() { + this.macromoleculeCombo = BIOSAXS_COMBOMANAGER.getComboMacromoleculeByMacromolecules(this.getMacromuleculesCandidates(this.macromolecule), { + width : 250, + labelWidth : 100, + margin : 10 + }); + + return [ { + xtype : 'container', + flex : 1, + margin : '10 0 0 10', + border : 0, + layout : 'anchor', + defaultType : 'requiredtext', + items : [ this.macromoleculeCombo, + this._getNumericWithHelp("textfield", "Ratio", "ratio", "Number in assymmetric units") + ] + } ]; +}; + +MolarityForm.prototype._persist = function() { + var _this = this; + var macromoleculeId = this.macromoleculeCombo.getValue(); + var ratio = Ext.getCmp(this.id + "ratio").getValue(); + var comments = "Not used yet"; + var dataAdapter = new BiosaxsDataAdapter(); + this.panel.setLoading("Saving"); + dataAdapter.onSuccess.attach(function(sender, args) { + _this.onSave.notify(); + }); + dataAdapter.saveStoichiometry(this.macromolecule.macromoleculeId, macromoleculeId, ratio, comments); +}; + +MolarityForm.prototype._getButtons = function() { + var _this = this; + + function onClose() { + _this.onClose.notify(); + } + + return [ { + text : 'Save', + handler : function() { + _this._persist(); + } + }, { + text : 'Cancel', + handler : function() { + onClose(); + } + } ]; +}; + +MolarityForm.prototype.getPanel = function() { + this.panel = Ext.create('Ext.form.Panel', { +// width : null, + height : this.height, + margin : 2, + border : 1, + defaultType : 'requiredtext', + items : this._getItems(), + buttons : this._getButtons() + }); + return this.panel; +}; + +/** macromolecules contains all macromolecules except this one **/ +MolarityForm.prototype.getMacromuleculesCandidates = function(macromolecule) { + var macromolecules = []; + if ( BIOSAXS.proposal.macromolecules){ + for (var i = 0; i < BIOSAXS.proposal.macromolecules.length; i++) { + var m = BIOSAXS.proposal.macromolecules[i]; + if (this.macromolecule != null){ + if (m.macromoleculeId != this.macromolecule.macromoleculeId) { + macromolecules.push(m); + } + } + } + } + return macromolecules; +}; + + +/** It populates the form **/ +MolarityForm.prototype.refresh = function(macromolecule) { + this.macromolecule = macromolecule; + +}; + + +MolarityForm.prototype.input = function() { + return {}; +}; + + +/** It populates the form **/ +MolarityForm.prototype.test = function(targetId) { + var macromoleculeForm = new MolarityForm(); + var panel = macromoleculeForm.getPanel(); + panel.render(targetId); +}; -GraphCanvas.prototype.getVertexSize = function(vertexId) { - return this.getFormatter().getVertexById(vertexId).getDefault().getSize(); -}; +///** +// * +// * @witdh +// * @height +// */ +//function MacromoleculeForm(args) { +// this.id = BUI.id(); +// this.width = 700; +// this.height = 500; +// +// if (args != null) { +// if (args.width != null) { +// this.width = args.width; +// } +// if (args.height != null) { +// this.height = args.height; +// } +// } +// +// this.onClose = new Event(this); +//} +// +//MacromoleculeForm.prototype.getMacromolecule = function() { +// this.macromolecule.name = Ext.getCmp(this.panel.getItemId()).getValues().name; +// this.macromolecule.acronym = Ext.getCmp(this.panel.getItemId()).getValues().acronym; +// this.macromolecule.comments = Ext.getCmp(this.panel.getItemId()).getValues().comments; +// this.macromolecule.extintionCoefficient = Ext.getCmp(this.panel.getItemId()).getValues().extintionCoefficient; +// this.macromolecule.molecularMass = Ext.getCmp(this.panel.getItemId()).getValues().molecularMass; +// return this.macromolecule; +//}; +// +//MacromoleculeForm.prototype.setMacromolecule = function(macromolecule) { +// this.pdbStore.loadData(macromolecule.structure3VOs); +// +//}; +// +//MacromoleculeForm.prototype.getForm = function(macromolecule) { +// this.panel = Ext.createWidget('form', { +// frame : false, +// border : 0, +// padding : 15, +// width : 550, +// height : 350, +// items : [ { +// xtype : 'container', +// layout : 'hbox', +// items : [ { +// xtype : 'container', +// flex : 1, +// border : false, +// layout : 'anchor', +// defaultType : 'requiredtext', +// items : [ { +// fieldLabel : 'Name', +// name : 'name', +// anchor : '95%', +// tooltip : "Name of the macromolecule", +// value : macromolecule.name +// }, { +// fieldLabel : 'Acronym', +// name : 'acronym', +// anchor : '95%', +// value : macromolecule.acronym +// } ] +// }, { +// xtype : 'container', +// flex : 1, +// layout : 'anchor', +// defaultType : 'textfield', +// items : [ { +// xtype : 'numberfield', +// fieldLabel : 'Mol. Mass (Da)', +// name : 'molecularMass', +// value : macromolecule.molecularMass, +// decimalPrecision : 6 +// }, { +// xtype : 'numberfield', +// fieldLabel : 'Extinction coef.', +// name : 'extintionCoefficient', +// value : macromolecule.extintionCoefficient, +// decimalPrecision : 6 +// } ] +// } ] +// }, { +// xtype : 'textareafield', +// name : 'comments', +// fieldLabel : 'Comments', +// value : macromolecule.comments, +// width : this.width - 10 +// }, +// +// { +// html : BUI.getWarningHTML("The macromolecule is unsaved. Save the macromolecule to get access to other tabs"), +// margin : "150 10 10 10", +// id : this.id + "unsavedWarning", +// hidden : !(!macromolecule.macromoleculeId) +// } +// +// ] +// }); +// return this.panel; +//}; +// +//MacromoleculeForm.prototype.save = function() { +// var _this = this; +// +// var adapter = new BiosaxsDataAdapter(); +// adapter.onSuccess.attach(function(sender, proposal) { +// BIOSAXS.proposal.setItems(proposal); +// _this.panel.setLoading(false); +// +// Ext.getCmp(_this.id + "assembly").enable() +// Ext.getCmp(_this.id + "advanced").enable(); +// Ext.getCmp(_this.id + "unsavedWarning").hide(); +// }); +// +// if (this.getMacromolecule().name == "") { +// BUI.showError("Name field is mandatory"); +// return; +// } +// if (this.getMacromolecule().acronym == "") { +// BUI.showError("Acroynm field is mandatory"); +// return; +// } +// +// /** Check if acronym is unique * */ +// if (this.getMacromolecule().macromoleculeId == null) { +// if (BIOSAXS.proposal.getMacromoleculeByAcronym(this.getMacromolecule().acronym) == null) { +// this.panel.setLoading("ISPyB: Saving Macromolecule") +// adapter.saveMacromolecule(this.getMacromolecule()); +// } else { +// alert("There is already an existing macromolecule with the same acronym"); +// +// } +// } else { +// this.panel.setLoading("ISPyB: Saving Macromolecule") +// adapter.saveMacromolecule(this.getMacromolecule()); +// } +// +//}; +// +//MacromoleculeForm.prototype.getPanel = function(macromolecule) { +// var _this = this; +// this.macromolecule = macromolecule; +// return Ext.createWidget('tabpanel', { +// height : this.height, +// margin : 5, +// plain : true, +// style : { +// padding : 5 +// }, +// items : [ { +// tabConfig : { +// title : "General", +// disabled : false +// }, +// items : [ this.getForm(macromolecule) ], +// bbar : [ "->", { +// text : 'Save', +// cls : 'btn-with-border', +// style : { +// +// border : 1 +// }, +// handler : function() { +// _this.save(); +// } +// }, { +// text : 'Close', +// cls : 'btn-with-border', +// handler : function() { +// _this.onClose.notify(); +// } +// } ] +// }, { +// tabConfig : { +// id : this.id + "assembly", +// title : "Assembly", +// tooltip : 'Description of subunits present in the macromolecule', +// // hidden : (!macromolecule.macromoleculeId), +// disabled : (!macromolecule.macromoleculeId) +// }, +// items : [ this.getMolarityGrid(macromolecule) ] +// }, { +// tabConfig : { +// id : this.id + "advanced", +// title : "Advanced Modeling", +// // hidden : (!macromolecule.macromoleculeId), +// tooltip : 'Definition of the description contacts and symetries', +// disabled : (!macromolecule.macromoleculeId) +// }, +// items : [ this.getRigidBodyForm(macromolecule) ] +// } ] +// }); +//}; +// +//MacromoleculeForm.prototype.getRigidBodyForm = function(macromolecule) { +// var _this = this; +// +// // [P1, P2, P3, P4 ,P5 ,P6 ,32, P42, P52, P62] + P222 +// var symmetry = Ext.create('Ext.data.Store', { +// fields : [ 's' ], +// data : [ { +// "s" : "P1" +// }, { +// "s" : "P2" +// }, { +// "s" : "P3" +// }, { +// "s" : "P4" +// }, { +// "s" : "P5" +// }, { +// "s" : "P6" +// }, { +// "s" : "P32" +// }, { +// "s" : "P42" +// }, { +// "s" : "P52" +// }, { +// "s" : "P62" +// }, { +// "s" : "P222" +// } ] +// }); +// +// if (macromolecule.symmetry == null) { +// macromolecule.symmetry = "P1"; +// } +// var comboBox = Ext.create('Ext.form.ComboBox', { +// fieldLabel : 'Symmetry', +// store : symmetry, +// id : 'comboSym', +// queryMode : 'local', +// displayField : 's', +// valueField : 's', +// value : macromolecule.symmetry, +// margin : "10 0 0 0", +// listeners : { +// change : function(combo, newValue, oldValue, eOpts) { +// macromolecule.symmetry = newValue; +// } +// } +// }); +// +// this.rigidBodyPanel = Ext.createWidget('form', { +// frame : false, +// border : 0, +// padding : 10, +// width : 550, +// height : 400, +// items : [ { +// xtype : 'container', +// layout : 'hbox', +// items : [ { +// xtype : 'container', +// border : false, +// layout : 'hbox', +// items : [ { +// xtype : 'label', +// forId : 'myFieldId', +// text : 'Contact Desc:', +// width : 105, +// margin : '0 0 0 0' +// }, { +// xtype : 'textfield', +// hideLabel : true, +// id : "contactsDescriptionFilePath", +// margin : '0 0 0 0', +// width : 300, +// value : macromolecule.contactsDescriptionFilePath +// }, { +// text : 'Upload', +// xtype : 'button', +// margin : "0 0 0 20", +// width : 100, +// handler : function() { +// _this._openUploadDialog(macromolecule.macromoleculeId, "CONTACTS", "Upload Contact Description File"); +// } +// } ] +// } ] +// }, +// +// comboBox, { +// xtype : 'checkbox', +// margin : '10 0 0 5', +// boxLabel : "I want rigid body modeling run on this stuff", +// checked : true, +// width : 300 +// }, _this.getPDBGrid(macromolecule) ] +// }); +// return this.rigidBodyPanel; +//}; +// +//MacromoleculeForm.prototype.update = function() { +// var _this = this; +// BIOSAXS.proposal.onInitialized.attach(function() { +// if (BIOSAXS.proposal != null) { +// var macromolecules = BIOSAXS.proposal.macromolecules; +// for (var i = 0; i < macromolecules.length; i++) { +// if (macromolecules[i].macromoleculeId == _this.macromolecule.macromoleculeId) { +// _this.macromolecule = macromolecules[i]; +// _this.setMacromolecule(_this.macromolecule); +// _this.molarityStore.loadData(_this.parseMolarityData(_this.macromolecule)); +// _this.pdbGrid.setLoading(false); +// Ext.getCmp("contactsDescriptionFilePath").setValue(_this.macromolecule.contactsDescriptionFilePath) +// _this.molarityGrid.setLoading(false); +// +// } +// } +// } +// }); +// this.molarityGrid.setLoading("Updating"); +// this.pdbGrid.setLoading("Updating"); +// BIOSAXS.proposal.init(); +//}; +// +///******************************************************************************* +// * MOLARITY GRID +// ******************************************************************************/ +// +//MacromoleculeForm.prototype.parseMolarityData = function(macromolecule) { +// var data = []; +// if (macromolecule.stoichiometry != null) { +// for (var i = 0; i < macromolecule.stoichiometry.length; i++) { +// data.push({ +// ratio : macromolecule.stoichiometry[i].ratio, +// acronym : macromolecule.stoichiometry[i].macromolecule3VO.acronym, +// comments : macromolecule.stoichiometry[i].macromolecule3VO.comments, +// stoichiometryId : macromolecule.stoichiometry[i].stoichiometryId, +// name : macromolecule.stoichiometry[i].macromolecule3VO.name +// }); +// } +// } +// return data; +//}; +// +//MacromoleculeForm.prototype.getMolarityGrid = function(macromolecule) { +// var _this = this; +// +// this.molarityStore = Ext.create('Ext.data.Store', { +// fields : [ 'acronym', 'ratio', 'comments', 'stoichiometryId', 'name' ], +// data : this.parseMolarityData(macromolecule), +// sorters : { +// property : 'ratio', +// direction : 'DESC' +// } +// }); +// +// this.molarityGrid = Ext.create('Ext.grid.Panel', { +// store : this.molarityStore, +// height : 350, +// padding : 5, +// columns : [ +// +// { +// text : 'Subunit', +// columns : [ { +// text : "Acronym", +// width : 100, +// hidden : false, +// dataIndex : 'acronym', +// sortable : true +// }, { +// text : "Name", +// width : 100, +// hidden : false, +// dataIndex : 'name', +// sortable : true +// }, { +// text : "Comments", +// width : 100, +// dataIndex : 'comments', +// sortable : true +// } ] +// }, { +// text : "Number
in assymmetric units", +// width : 150, +// dataIndex : 'ratio', +// tooltip : 'Number of times this subunit is present in the macromolecule', +// sortable : true +// }, { +// id : this.id + 'MOLARITY_REMOVE', +// flex : 0.1, +// sortable : false, +// renderer : function(value, metaData, record, rowIndex, colIndex, store) { +// return BUI.getRedButton('REMOVE'); +// } +// } ], +// listeners : { +// cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { +// if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'MOLARITY_REMOVE') { +// var dataAdapter = new BiosaxsDataAdapter(); +// dataAdapter.onSuccess.attach(function() { +// _this.molarityGrid.setLoading(false); +// _this.update(); +// }); +// dataAdapter.removeStoichiometry(record.data.stoichiometryId); +// _this.molarityGrid.setLoading("Removing Structure"); +// } +// } +// }, +// buttons : [ { +// text : 'Add molarity', +// handler : function() { +// +// function onClose() { +// w.destroy(); +// _this.update(); +// } +// var w = Ext.create('Ext.window.Window', { +// title : 'Molarity', +// height : 300, +// width : 500, +// modal : true, +// buttons : [ { +// text : 'Save', +// handler : function() { +// var macromoleculeId = (_this.macromoleculeCombo.getValue()); +// var ratio = Ext.getCmp(_this.id + "ratio").getValue(); +// var comments = ""; +// var dataAdapter = new BiosaxsDataAdapter(); +// dataAdapter.onSuccess.attach(function(sender, args) { +// _this.update(); +// w.destroy(); +// }); +// dataAdapter.saveStoichiometry(_this.macromolecule.macromoleculeId, macromoleculeId, ratio, comments); +// } +// }, { +// text : 'Cancel', +// handler : function() { +// onClose(); +// } +// } ], +// items : [ _this.getMolarityForm(macromolecule) ], +// listeners : { +// onEsc : function() { +// onClose(); +// }, +// close : function() { +// onClose(); +// } +// } +// }).show(); +// } +// } ] +// }); +// return this.molarityGrid; +//}; +// +///******************************************************************************* +// * PDB GRID +// ******************************************************************************/ +// +//MacromoleculeForm.prototype._openUploadDialog = function(macromoleculeId, type, title) { +// var _this = this; +// function onClose() { +// w.destroy(); +// _this.update(); +// } +// +// var w = Ext.create('Ext.window.Window', { +// title : title, +// height : 200, +// width : 400, +// modal : true, +// buttons : [ { +// text : 'Close', +// handler : function() { +// onClose(); +// } +// } ], +// layout : 'fit', +// items : { +// html : "" +// }, +// listeners : { +// onEsc : function() { +// onClose(); +// }, +// close : function() { +// onClose(); +// } +// } +// }).show(); +//}; +// +//MacromoleculeForm.prototype._getPlugins = function() { +// var _this = this; +// var plugins = []; +// // if (this.updateRowEnabled) { +// plugins.push(Ext.create('Ext.grid.plugin.RowEditing', { +// clicksToEdit : 1, +// listeners : { +// validateedit : function(grid, e) { +// /** Comments are always updatable* */ +// e.record.raw.symmetry = e.newValues.symmetry; +// e.record.raw.multiplicity = e.newValues.multiplicity; +// +// var adapter = new BiosaxsDataAdapter(); +// adapter.onSuccess.attach(function(sender, measurement) { +// // _this.grid.setLoading(false); +// }); +// adapter.onError.attach(function() { +// alert("Error"); +// // _this.grid.setLoading(false); +// }); +// +// // _this.grid.setLoading(); +// adapter.saveStructure(e.record.raw); +// } +// } +// })); +// // } +// return plugins; +//}; +// +//MacromoleculeForm.prototype.getPDBGrid = function(macromolecule) { +// var _this = this; +// +// var data = macromolecule.structure3VOs; +// +// // /** Getting PDB from subunits **/ +// // if (macromolecule != null){ +// // if (macromolecule.stoichiometry != null){ +// // for (var i =0; i < macromolecule.stoichiometry.length; i++){ +// // var stoichiometry = macromolecule.stoichiometry[i]; +// // if (stoichiometry.macromolecule3VO != null){ +// // if (stoichiometry.macromolecule3VO.structure3VOs != null){ +// // for (var j = 0; j < stoichiometry.macromolecule3VO.structure3VOs.length; +// // j++) { +// // var structure = stoichiometry.macromolecule3VO.structure3VOs[j]; +// // structure["isSubunit"] = stoichiometry.macromolecule3VO.acronym; +// // data.push(structure) +// // } +// // } +// // } +// // } +// // } +// // } +// +// this.pdbStore = Ext.create('Ext.data.Store', { +// fields : [ 'filePath', 'structureId', 'structureType', 'symmetry', 'structureId', 'name', 'multiplicity' ], +// data : macromolecule.structure3VOs, +// groupField : 'structureType', +// sorters : { +// property : 'structureId', +// direction : 'DESC' +// } +// }); +// +// var groupingFeature = Ext.create('Ext.grid.feature.Grouping', { +// groupHeaderTpl : Ext.create('Ext.XTemplate', +// "
{name:this.formatName}
", { +// formatName : function(name) { +// return name; +// } +// }), +// hideGroupedHeader : true, +// startCollapsed : false +// }); +// +// this.pdbGrid = Ext.create('Ext.grid.Panel', { +// margin : "15 0 0 5", +// height : 250, +// store : this.pdbStore, +// plugins : _this._getPlugins(), +// buttons : [ { +// // text : 'Add PDB file', +// text : 'Add Modeling Option', +// handler : function() { +// _this._openUploadDialog(macromolecule.macromoleculeId, "PDB", 'Upload PDB File'); +// } +// } +// +// ], +// columns : [ +// { +// text : "structureId", +// flex : 0.2, +// hidden : true, +// dataIndex : 'structureId', +// sortable : true +// }, +// { +// text : "File", +// flex : 0.5, +// dataIndex : 'filePath', +// sortable : true, +// hidden : true +// }, +// { +// text : "PDB", +// flex : 0.4, +// dataIndex : 'name', +// sortable : true +// }, +// { +// text : "Symmetry", +// flex : 0.4, +// dataIndex : 'symmetry', +// sortable : true, +// editor : { +// xtype : 'combobox', +// typeAhead : true, +// triggerAction : 'all', +// selectOnTab : true, +// store : [ [ "P1", "P1" ], [ "P2", "P2" ], [ "P3", "P3" ], [ "P4", "P4" ], [ "P5", "P5" ], [ "P6", "P6" ], [ "P32", "P32" ], [ "P42", "P42" ], +// [ "P52", "P52" ], [ "P62", "P62" ], [ "P222", "P222" ] ], +// } +// }, { +// text : "Multiplicity", +// flex : 0.4, +// dataIndex : 'multiplicity', +// sortable : true, +// editor : { +// xtype : 'textfield' +// } +// +// }, { +// text : "Subunit", +// flex : 0.2, +// dataIndex : 'isSubunit', +// sortable : true, +// hidden : true +// }, { +// text : "Type", +// flex : 0.2, +// dataIndex : 'structureType', +// sortable : true, +// hidden : true +// }, +// +// { +// id : this.id + 'REMOVE', +// flex : 0.2, +// hidden : true, +// sortable : false, +// renderer : function(value, metaData, record, rowIndex, colIndex, store) { +// return BUI.getRedButton('REMOVE'); +// } +// }, ], +// +// listeners : { +// itemdblclick : function(dataview, record, item, e) { +// _this._editExperiment(record.raw.experimentId); +// }, +// cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { +// +// if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'REMOVE') { +// var dataAdapter = new BiosaxsDataAdapter(); +// dataAdapter.onSuccess.attach(function() { +// _this.pdbGrid.setLoading(false); +// _this.update(); +// }); +// dataAdapter.removeStructure(record.data.structureId); +// _this.pdbGrid.setLoading("Removing PDB file"); +// } +// +// } +// }, +// viewConfig : { +// getRowClass : function(record, rowIdx, params, store) { +// if (record.raw.isSubunit != null) { +// return "blue-row"; +// } +// } +// } +// }); +// +// return this.pdbGrid; +//}; +// +//MacromoleculeForm.prototype.getMolarityForm = function(macromolecule) { +// var _this = this; +// var data = []; +// for (var i = 0; i < BIOSAXS.proposal.macromolecules.length; i++) { +// var m = BIOSAXS.proposal.macromolecules[i]; +// if (m.macromoleculeId != macromolecule.macromoleculeId) { +// data.push(m); +// } +// +// } +// this.macromoleculeCombo = BIOSAXS_COMBOMANAGER.getComboMacromoleculeByMacromolecules(data, { +// width : 250, +// labelWidth : 100, +// margin : 10 +// }); +// +// return Ext.createWidget('form', { +// +// frame : false, +// border : 0, +// // padding : 15, +// width : 550, +// height : 350, +// items : [ { +// xtype : 'container', +// flex : 1, +// border : false, +// layout : 'anchor', +// defaultType : 'requiredtext', +// items : [ this.macromoleculeCombo, { +// xtype : 'numberfield', +// name : 'Ratio', +// id : _this.id + "ratio", +// fieldLabel : 'Number in assymmetric units', +// value : 1, +// decimalPrecision : 6, +// margin : 10 +// }, { +// xtype : 'textareafield', +// name : 'comments', +// fieldLabel : 'Comments', +// margin : 10, +// width : 400, +// value : "" +// +// } ] +// } ] +// }); +// +//}; +// +///******************************************************************************* +// * JAVASCRIPT DOC +// ******************************************************************************/ +//MacromoleculeForm.prototype.input = function() { +// return { +// macromolecule : DATADOC.getMacromolecule_10() +// }; +//}; +// +//MacromoleculeForm.prototype.test = function(targetId) { +// var macromoleculeForm = new MacromoleculeForm(); +// var panel = macromoleculeForm.getPanel(macromoleculeForm.input().macromolecule); +// panel.render(targetId); +//}; -GraphCanvas.prototype.setVertexStroke = function(vertexId, value) { - this.getFormatter().getVertexById(vertexId).getDefault().setStroke(value); -}; +function ResultSummaryForm() { + this.radiationDamageThreshold = BUI.getRadiationDamageThreshold(); + this.qualityThreshold = BUI.getQualityThreshold(); + + /** Data clusters with bufers **/ + this.clusterByBuffers = false; + + /** Visualization are groupeb by concenttrations. Ex: 4.5mg/ml and 4.7mg/ml will be clusterized together **/ + this.collapseConcentrations = true; + + this.summaryHeight = 350; + this.id = BUI.id(); + + var _this = this; + + this.qualityControlResultsWidget = new QualityControlResultsWidget({ + qualityThreshold : this.radiationDamageThreshold, + radiationDamageThreshold : this.qualityThreshold + + }); + this.qualityControlResultsWidget.onQualityChanged.attach(function(sender, value) { + _this.qualityThreshold = value; + _this.thresholdsChanged(); + }); + + this.qualityControlResultsWidget.onRadiationDatamageChanged.attach(function(sender, value) { + _this.radiationDamageThreshold = value; + _this.thresholdsChanged(); + + }); + this.concentrationHTMLTableWidget = new ConcentrationHTMLTableWidget({ + height : 250, + showBufferColumns : this.clusterByBuffers + }); + + this.plot = new BoxWhiskerGraph({ + targetId : _this.id + '_boxPlot', + height : 350, + width : 450, + maxBoxWidth : 25 + }); + + this.rangePlot = new RangeWhiskerGraph({ + targetId : _this.id + '_rangePlot', + height : 350, + width : 450, + maxBoxWidth : 25 + }); +} + +ResultSummaryForm.prototype.thresholdsChanged = function() { + var parsedData = this.prepareData(this.rawData); + + var filtered = JSON.parse(JSON.stringify(parsedData)); + filtered.concentration.concentrations = []; + for ( var conc in parsedData.concentration.concentrations) { + if (parsedData.concentration.concentrations[conc].calculation.rgGnom.validNumber > 0) { + filtered.concentration.concentrations.push(parsedData.concentration.concentrations[conc]); + } + } + + this.plotWhisker(filtered); + this.plotRange(filtered); + + this.concentrationHTMLTableWidget.refresh(parsedData); +}; + +/** Given a frame object (object returned by analyzeFrames()) and depending of the threshold indicates if a datacollection has radiation damage **/ +ResultSummaryForm.prototype.hasRadiationDamage = function(frameObject) { + var bb = frameObject.bufferBeforeFramesMerged / frameObject.framesCount; + var ba = frameObject.bufferAfterFramesMerged / frameObject.framesCount; + var mol = frameObject.framesMerge / frameObject.framesCount; + return !((bb >= this.radiationDamageThreshold) && (ba >= this.radiationDamageThreshold) && (mol >= this.radiationDamageThreshold)); +}; + +/** Return (frameObject) an object with the information about the frames for a data collection**/ +ResultSummaryForm.prototype.analyzeFrames = function(dataCollectionRow) { + return { + bufferAfterFramesMerged : dataCollectionRow.bufferAfterFramesMerged, + bufferBeforeFramesMerged : dataCollectionRow.bufferBeforeFramesMerged, + framesCount : dataCollectionRow.framesCount, + framesMerge : dataCollectionRow.framesMerge + }; +}; + +ResultSummaryForm.prototype.detectDataCollectionWarnings = function(dataCollectionRow) { + var frameObject = this.analyzeFrames(dataCollectionRow); + var warnings = { + count : 0, + type : [] + }; + + if (this.hasRadiationDamage(frameObject)) { + warnings.count = warnings.count + 1; + warnings.type.push("RADIATION DAMAGE"); + } + + if (Number(dataCollectionRow.quality) < this.qualityThreshold) { + warnings.count = 1;//warnings.count + 1; + warnings.type.push("Quality <" + this.qualityThreshold); + } + return warnings; +}; + +/** Return array composed by {concentration} objects **/ +ResultSummaryForm.prototype.getConcentrations = function(data) { + var concentrationsId = {}; + + for ( var i = 0; i < data.length; i++) { + var warning = this.detectDataCollectionWarnings(data[i]); + var id = data[i].conc;// + "_" + data[i].bufferId; + if (this.clusterByBuffers) { + id = data[i].conc + "_" + data[i].bufferId; + } + + if (concentrationsId[id] == null) { + concentrationsId[id] = { + id : id, + concentration : Number(data[i].conc).toFixed(2), + bufferId : data[i].bufferId, + bufferAcronym : data[i].bufferAcronym, + rgGuinier : [], + i0Guinier : [], + rgGnom : [], + dMax : [], + quality : [], + frames : [], + frames_warning : warning.count + }; + } else { + concentrationsId[id].frames_warning = concentrationsId[id].frames_warning + warning.count; + } + + concentrationsId[id].frames.push(data[i]); + + if (warning.count == 0) { + concentrationsId[id].rgGuinier.push(data[i].rgGuinier); + concentrationsId[id].i0Guinier.push(data[i].I0); + concentrationsId[id].quality.push(data[i].quality); + concentrationsId[id].rgGnom.push(data[i].rgGnom); + concentrationsId[id].dMax.push(data[i].dmax); + } + + } + var concentrations = []; + for ( var item in concentrationsId) { + if (concentrationsId.hasOwnProperty(item)) { + concentrations.push({ + concentration : concentrationsId[item].concentration, + id : item, + bufferId : Number(concentrationsId[item].bufferId).toFixed(2), + bufferAcronym : concentrationsId[item].bufferAcronym, + rgGuinier : concentrationsId[item].rgGuinier, + /** Frames **/ + frames : concentrationsId[item].frames, + frames_warning : concentrationsId[item].frames_warning, + /** Calculation **/ + calculation : { + rgGuinier : BUI.getStandardDeviation(concentrationsId[item].rgGuinier), + i0Guinier : BUI.getStandardDeviation(concentrationsId[item].i0Guinier), + quality : BUI.getStandardDeviation(concentrationsId[item].quality), + rgGnom : BUI.getStandardDeviation(concentrationsId[item].rgGnom), + dMax : BUI.getStandardDeviation(concentrationsId[item].dMax) + + } + }); + } + } + + return { + concentrations : concentrations + }; +}; + +ResultSummaryForm.prototype.prepareData = function(data) { + /** Return array composed by {acronym, bufferId} objects **/ + function getBuffers(data) { + var buffersId = {}; + for ( var i = 0; i < data.length; i++) { + buffersId[data[i].bufferId] = data[i].acronym; + } + var buffers = []; + for ( var id in buffersId) { + if (buffersId.hasOwnProperty(id)) { + buffers.push({ + acronym : buffersId[id], + bufferId : id + }); + } + } + return buffers; + } + + /** Get a string with all the concentrations **/ + function getConcentrationString(parseConcentrations) { + var concentrations = []; + for ( var i = 0; i < parseConcentrations.concentrations.length; i++) { + concentrations.push(parseConcentrations.concentrations[i].concentration + " mg/ml "); + } + return concentrations.toString(); + } + + var parseConcentrations = this.getConcentrations(data); + + return { + dataCollectionCount : data.length, + buffers : getBuffers(data), + concentration : parseConcentrations, + concentrationLabel : getConcentrationString(parseConcentrations) + }; +}; + +ResultSummaryForm.prototype.getDataForWhisker = function(data) { + var clusters = []; + + var concentrations = {}; + var i = 0; + var conc = 0; + if (this.collapseConcentrations) { + for (i = 0; i < data.concentration.concentrations.length; i++) { + conc = data.concentration.concentrations[i]; + var concentration = Number(conc.concentration).toFixed(0); + if (concentrations[concentration] == null) { + concentrations[concentration] = {}; + concentrations[concentration].concentration = concentration; + concentrations[concentration].calculation = {}; + concentrations[concentration].calculation.rgGuinier = {}; + concentrations[concentration].calculation.rgGuinier.values = []; + concentrations[concentration].calculation.rgGuinier.values = conc.calculation.rgGuinier.values; + concentrations[concentration].calculation.rgGnom = {}; + concentrations[concentration].calculation.rgGnom.values = []; + concentrations[concentration].calculation.rgGnom.values = conc.calculation.rgGnom.values; + } else { + concentrations[concentration].calculation.rgGuinier.values = concentrations[concentration].calculation.rgGuinier.values + .concat(conc.calculation.rgGuinier.values); + concentrations[concentration].calculation.rgGnom.values = concentrations[concentration].calculation.rgGnom.values + .concat(conc.calculation.rgGnom.values); + } + } + + /** From object to array **/ + var array = []; + for ( var key in concentrations) { + if (concentrations.hasOwnProperty(key)) { + array.push(concentrations[key]); + } + } + data.concentration.concentrations = array; + } + + for (i = 0; i < data.concentration.concentrations.length; i++) { + conc = data.concentration.concentrations[i]; + clusters.push({ + name : conc.concentration + "mg/ml", + concentration : Number(conc.concentration), + x : Number(conc.concentration), + classes : [] + }); + clusters[clusters.length - 1].classes.push({ + name : "Guinier", + color : '#9A2EFE', + values : conc.calculation.rgGuinier.values + + }); + clusters[clusters.length - 1].classes.push({ + name : "P(r)", + color : '#2E64FE', + values : conc.calculation.rgGnom.values + + }); + } + return { + clusters : clusters.sort(function(a, b) { + return a.concentration - b.concentration; + }) + }; +}; + +ResultSummaryForm.prototype.getDataForWhiskerQuality = function(data) { + var clusters = []; + + for ( var i = 0; i < data.concentration.concentrations.length; i++) { + var conc = data.concentration.concentrations[i]; + clusters.push({ + name : conc.concentration + "mg/ml", + classes : [] + }); + clusters[clusters.length - 1].classes.push({ + name : "Quality", + values : conc.calculation.quality.values + + }); + } + + return { + clusters : clusters + }; +}; + +ResultSummaryForm.prototype.plotQualityWhisker = function(parsedData) { + this.qualityPlot.refresh(this.getDataForWhiskerQuality(parsedData)); +}; + +ResultSummaryForm.prototype.plotWhisker = function(parsedData) { + this.plot.refresh(this.getDataForWhisker(parsedData)); +}; + +ResultSummaryForm.prototype.plotRange = function(parsedData) { + this.rangePlot.refresh(this.getDataForWhisker(parsedData)); +}; + +ResultSummaryForm.prototype.getPanel = function(data) { + var _this = this; + _this.rawData = data; + var parsedData = this.prepareData(data); + + this.panel = Ext + .createWidget( + 'form', + { + bodyPadding : 20, + frame : false, + border : 0, + width : this.width, + height : this.summaryHeight + 1000, + items : [ { + xtype : 'container', + layout : 'hbox', + items : [ { + xtype : 'container', + flex : 1, + border : false, + layout : 'anchor', + defaultType : 'textfield', + items : [ + _this.qualityControlResultsWidget.getPanel(), + { + xtype : 'fieldset', + width : 950, + margin : "20, 0 0 0", + height : this.summaryHeight + 500, + items : [ + { + html : "
Concentration Analysis
", + border : 0, + width : 900 + + }, + { + html : this.concentrationHTMLTableWidget.getPanel(parsedData), + border : 0, + width : 900, + margin : "10, 0 0 10" + + }, + { + xtype : 'container', + layout : 'vbox', + border : 5, + items : [ + { + html : "
Rg based on Guinier and Gnom
", + border : 8, + width : 900, + margin : "20 0 0 10" + + }, { + xtype : 'container', + layout : 'hbox', + items : [ { + xtype : 'container', + layout : 'vbox', + items : [ { + html : "
", + border : 0, + width : this.plot.width, + padding : "10 0 0 20", + listeners : { + afterrender : function() { + _this.plotWhisker(parsedData); + + } + } + }, { + html : "
Concentration (mg/ml)
", + width : 450, + border : 0, + margin : "-50 0 0 0" + + } ] + }, { + xtype : 'container', + layout : 'vbox', + items : [ { + html : "
", + border : 0, + width : this.rangePlot.width, + padding : "10 0 0 10", + listeners : { + afterrender : function() { + _this.plotRange(parsedData); + } + } + }, { + html : "
Concentration (mg/ml)
", + width : 450, + border : 0, + margin : "-50 0 0 0" + + } ] + } ] + } ] + } ] + } ] + } + + ] + } ] + }); + return this.panel; + +}; + +ResultSummaryForm.prototype.input = function() { + return { + data : DATADOC.getData_3367() + }; +}; + +ResultSummaryForm.prototype.test = function(targetId) { + var resultSummaryForm = new ResultSummaryForm(); + var panel = resultSummaryForm.getPanel(resultSummaryForm.input().data); + panel.render(targetId); + +}; -GraphCanvas.prototype.getVertexStroke = function(vertexId) { - return this.getFormatter().getVertexById(vertexId).getDefault().getStroke(); -}; +/** + * Example form + * + * @witdh + * @height + */ +function RigibBodyModelingForm(args) { + this.id = BUI.id(); + this.width = 700; + this.height = 500; + + if (args != null) { + if (args.width != null) { + this.width = args.width; + } + if (args.height != null) { + this.height = args.height; + } + } + + var _this = this; + this.rigidBodyGrid = new AprioriRigidBodyGrid(); + + this.rigidBodyGrid.onUploadFile.attach(function(sender, type, title){ + _this._openUploadDialog(_this.macromolecule.macromoleculeId, type, title); + }); + + this.rigidBodyGrid.onRemove.attach(function(sender, type, title){ + _this._update(); + }); + + this.onSave = new Event(this); + +} + +RigibBodyModelingForm.prototype._getItems = function() { + var _this = this; + + + return [ { + xtype : 'label', + forId : 'myFieldId', + text : 'Information for model fit, mixture analysis and rigid body modeling', + margin : '15 0 20 10', + cls : "inline-help" + }, + this.rigidBodyGrid.getPanel(), + { + xtype : 'label', + forId : 'myFieldId', + text : 'Distance restraints may be imposed on the model using contacts conditions file (OPTIONAL)', + margin : '25 0 5 10', + cls : "inline-help" + }, + { + xtype : 'container', + layout : 'hbox', + items : [ { + xtype : 'container', + border : false, + layout : 'hbox', + items : [ { + xtype : 'label', + forId : 'myFieldId', + text : 'Contact Description File: (Optional)', + width : 150, + margin : '10 0 0 10' + }, { + id : this.id + "contactsDescriptionFilePath", + xtype : 'textfield', + hideLabel : true, + margin : '10 0 0 0', + width : 200, + disabled: true + }, { + text : 'Upload', + xtype : 'button', + margin : "10 0 0 10", + width : 80, + handler : function() { + + _this._openUploadDialog(_this.macromolecule.macromoleculeId, "CONTACTS", "Upload Contact Description File"); + } + }, { + text : 'Remove', + id : _this.id + "_remove", + xtype : 'button', + margin : "10 0 0 10", + width : 80, + handler : function() { + _this.macromolecule.contactsDescriptionFilePath = null; + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, proposal) { + /** Updating the proposal global object **/ + BIOSAXS.proposal.setItems(proposal); + _this.panel.setLoading(false); + + var saved = BIOSAXS.proposal.getMacromoleculeByAcronym(_this.macromolecule.acronym); + /** Refreshing to check that everything is ok **/ + _this.refresh(saved); + _this.onSave.notify(saved); + }); + + _this.panel.setLoading("Saving Macromolecule") + adapter.saveMacromolecule(_this.macromolecule); + } + } ] + } ] + }, { + xtype : 'panel', + html : "Go to SASREF manual for further information", + margin : "10 0 0 160", + border : 0 + + }, + { + xtype : 'checkbox', + margin : '10 0 0 5', + boxLabel : "I want rigid body modeling run on this stuff", + checked : true, + width : 300 + } ] + +}; + +/** Because update is a jsp page we don't know if the user has uploaded a file or not then we need to refresh **/ +RigibBodyModelingForm.prototype._update = function(macromoleculeId, type, title) { + var _this = this; + BIOSAXS.proposal.onInitialized.attach(function() { + if (BIOSAXS.proposal != null) { + _this.refresh(BIOSAXS.proposal.getMacromoleculeById(_this.macromolecule.macromoleculeId)); + _this.panel.setLoading(false); + } + }); + this.panel.setLoading(); + BIOSAXS.proposal.init(); +}; + +RigibBodyModelingForm.prototype._openUploadDialog = function(macromoleculeId, type, title) { + var _this = this; + function onClose() { + w.destroy(); + _this._update(); + + } + + var w = Ext.create('Ext.window.Window', { + title : title, + height : 200, + width : 400, + modal : true, + buttons : [ { + text : 'Close', + handler : function() { + onClose(); + } + } ], + layout : 'fit', + items : { + html : "" + }, + listeners : { + onEsc : function() { + onClose(); + }, + close : function() { + onClose(); + } + } + }).show(); +}; + +RigibBodyModelingForm.prototype._getButtons = function() { + return []; +}; + +RigibBodyModelingForm.prototype.getPanel = function() { + var _this = this; + this.panel = Ext.create('Ext.form.Panel', { + width : this.width, + height : this.height, + margin : 10, + border : 1, + defaultType : 'textfield', + items : this._getItems(), + buttons : this._getButtons(), + listeners : { + afterrender : function(){ + _this._populate(); + + } + } + }); + return this.panel; +}; + +/** Populates could be call when the DOM is not filled yet **/ +RigibBodyModelingForm.prototype._populate = function() { + if (this.macromolecule != null){ + if (Ext.getCmp(this.id + "contactsDescriptionFilePath") != null){ + if (this.macromolecule.contactsDescriptionFilePath != null){ + Ext.getCmp(this.id + "contactsDescriptionFilePath").setValue(this.macromolecule.contactsDescriptionFilePath); + Ext.getCmp(this.id + "_remove").enable(); + } + else{ + Ext.getCmp(this.id + "_remove").disable(); + Ext.getCmp(this.id + "contactsDescriptionFilePath").setValue(""); + + } + } + } +}; + +/** It populates the form * */ +RigibBodyModelingForm.prototype.refresh = function(macromolecule) { + this.macromolecule = macromolecule; + this.rigidBodyGrid.refresh(macromolecule); + this._populate(); +}; + +RigibBodyModelingForm.prototype.input = function() { + return {}; +}; + + +/** It populates the form **/ +RigibBodyModelingForm.prototype.test = function(targetId) { + var macromoleculeForm = new RigibBodyModelingForm(); + var panel = macromoleculeForm.getPanel(); + panel.render(targetId); +}; + -GraphCanvas.prototype.setVertexStrokeOpacity = function(vertexId, value) { - this.getFormatter().getVertexById(vertexId).getDefault().setStrokeOpacity(value); -}; +/** + * Same form as MX part + * + * @creationMode if true a create button appears instead of save + * @showTitle true or false + */ +function ShipmentForm(args) { + this.id = BUI.id(); + + this.creationMode = false; + this.showTitle = true; + if (args != null) { + if (args.creationMode != null) { + this.creationMode = args.creationMode; + } + if (args.showTitle != null) { + this.showTitle = args.showTitle; + } + } + + this.onSaved = new Event(this); +} + +ShipmentForm.prototype.fillStores = function() { + this.panel.setLoading("Loading Labcontacts from database"); + this.labContactForSendingStore.loadData(BIOSAXS.proposal.getLabcontacts(), false); + this.labContactForReturnStore.loadData(BIOSAXS.proposal.getLabcontacts(), false); + this.panel.setLoading(false); + if (this.shipment != null) { + this.setShipment(this.shipment); + } +}; + +ShipmentForm.prototype.draw = function(targetId) { + this.getPanel().render(targetId); +}; + +ShipmentForm.prototype.setShipment = function(shipment) { + this.shipment = shipment; + var _this = this; + Ext.getCmp(_this.id + "shippingName").setValue(shipment.json.shippingName); + Ext.getCmp(_this.id + "shippingStatus").setValue(shipment.json.shippingStatus); + Ext.getCmp(_this.id + "comments").setValue(shipment.json.comments); + if (shipment.json.sendingLabContactVO != null) { + this.labContactsSendingCombo.setValue(shipment.json.sendingLabContactVO.labContactId); + } + if (shipment.json.returnLabContactVO != null) { + this.labContactsReturnCombo.setValue(shipment.json.returnLabContactVO.labContactId); + } + +}; + +ShipmentForm.prototype._saveShipment = function() { + var _this = this; + var shippingId = null; + if (this.shipment != null) { + shippingId = this.shipment.json.shippingId; + } + var json = { + shippingId : shippingId, + name : Ext.getCmp(_this.id + "shippingName").getValue(), + status : Ext.getCmp(_this.id + "shippingStatus").getValue(), + sendingLabContactId : Ext.getCmp(_this.id + "shipmentform_sendingLabContactId").getValue(), + returnLabContactId : Ext.getCmp(_this.id + "returnLabContactId").getValue(), + returnCourier : Ext.getCmp(_this.id + "returnCourier").getValue(), + courierAccount : Ext.getCmp(_this.id + "courierAccount").getValue(), + BillingReference : Ext.getCmp(_this.id + "BillingReference").getValue(), + dewarAvgCustomsValue : Ext.getCmp(_this.id + "dewarAvgCustomsValue").getValue(), + dewarAvgTransportValue : Ext.getCmp(_this.id + "dewarAvgTransportValue").getValue(), + comments : Ext.getCmp(_this.id + "comments").getValue() + }; + + var dataAdapter = new BiosaxsDataAdapter(); + dataAdapter.onSuccess.attach(function(sender, shipment) { + window.location = BUI.getShippingURL(shipment.shippingId); + }); + + dataAdapter.onError.attach(function(sender, error) { + _this.onError.notify(error); + }); + + /** Cheking params **/ + if (json.name == "") { + BUI.showError("Name field is mandatory"); + return; + } + + if (json.sendingLabContactId == null) { + BUI.showError("Lab contact for sending field is mandatory"); + return; + } + + if (json.returnLabContactId == null) { + BUI.showError("Lab contact for return field is mandatory"); + return; + } + + dataAdapter.createShipment(json.shippingId, json.name, json.status, json.comments, json.sendingLabContactId, json.returnLabContactId, + json.returnCourier, json.courierAccount, json.BillingReference, json.dewarAvgCustomsValue, json.dewarAvgTransportValue); + +}; + +ShipmentForm.prototype.getPanel = function(shipment) { + var _this = this; + this.shipment = shipment; + var required = '*'; + var buttons = []; + + if (_this.creationMode) { + buttons.push({ + text : 'Create', + scope : this, + handler : function() { + _this._saveShipment(); + } + }); + } else { + buttons.push({ + text : 'Save', + scope : this, + handler : function() { + _this._saveShipment(); + } + }); + + } + + this.labContactForSendingStore = Ext.create('Ext.data.Store', { + fields : [ 'cardName', 'labContactId' ] + }); + + this.labContactForReturnStore = Ext.create('Ext.data.Store', { + fields : [ 'cardName', 'labContactId' ] + }); + + // Create the combo box, attached to the states data store + this.labContactsSendingCombo = Ext.create('Ext.form.ComboBox', { + id : _this.id + "shipmentform_sendingLabContactId", + fieldLabel : 'Lab contact for sending', + afterLabelTextTpl : required, + store : this.labContactForSendingStore, + queryMode : 'local', + labelWidth : 200, + displayField : 'cardName', + valueField : 'labContactId' + }); + + this.labContactsReturnCombo = Ext.create('Ext.form.ComboBox', { + id : _this.id + "returnLabContactId", + fieldLabel : 'If No, Lab-Contact for Return', + afterLabelTextTpl : required, + store : this.labContactForReturnStore, + queryMode : 'local', + labelWidth : 200, + displayField : 'cardName', + valueField : 'labContactId', + listeners : { + change : function(x, newValue) { + for ( var i = 0; i < x.getStore().data.items.length; i++) { + if (x.getStore().data.items[i].raw.labContactId == newValue) { + Ext.getCmp(_this.id + "returnCourier").setValue(x.getStore().data.items[i].raw.defaultCourrierCompany); + Ext.getCmp(_this.id + "courierAccount").setValue(x.getStore().data.items[i].raw.courierAccount); + Ext.getCmp(_this.id + "BillingReference").setValue(x.getStore().data.items[i].raw.billingReference); + Ext.getCmp(_this.id + "dewarAvgCustomsValue").setValue(x.getStore().data.items[i].raw.dewarAvgCustomsValue); + Ext.getCmp(_this.id + "dewarAvgTransportValue").setValue(x.getStore().data.items[i].raw.dewarAvgTransportValue); + } + } + } + } + }); + + if (this.panel == null) { + this.panel = Ext.create('Ext.form.Panel', { + bodyPadding : 5, + width : 600, + border : 1, + items : [ { + xtype : 'fieldset', + title : 'Details', + collapsible : false, + defaultType : 'textfield', + layout : 'anchor', + defaults : { + anchor : '100%' + }, + items : [ { + xtype : 'requiredtext', + fieldLabel : 'Shipment Label', + allowBlank : false, + name : 'shippingName', + id : _this.id + 'shippingName', + value : '', + anchor : '50%' + }, { + + xtype : 'textareafield', + name : 'comments', + id : _this.id + 'comments', + fieldLabel : 'Comments', + value : '' + }, { + fieldLabel : 'Status', + readOnly : true, + id : _this.id + 'shippingStatus', + value : 'Opened', + anchor : '50%' + } ] + }, { + xtype : 'fieldset', + title : 'Lab-Contacts', + collapsible : false, + defaultType : 'textfield', + layout : 'anchor', + defaults : { + anchor : '100%' + }, + items : [ this.labContactsSendingCombo, this.labContactsReturnCombo ] + }, { + border : 0, + html : BUI.getWarningHTML("These informations are relevant for all shipments") + }, { + xtype : 'fieldset', + title : 'Courier accounts details for return', + collapsible : false, + layout : 'anchor', + defaults : { + anchor : '100%' + }, + items : [ { + xtype : 'textfield', + labelWidth : 400, + fieldLabel : 'Courier company for return (if ESRF sends a dewar back)', + id : _this.id + 'returnCourier', + value : '' + }, { + xtype : 'textfield', + labelWidth : 400, + fieldLabel : 'Courier account', + id : _this.id + 'courierAccount', + value : '' + }, { + xtype : 'textfield', + labelWidth : 400, + fieldLabel : 'Billing reference', + id : _this.id + 'BillingReference', + value : '' + }, { + xtype : 'numberfield', + labelWidth : 400, + fieldLabel : 'Average Customs value of a dewar (Euro)', + id : _this.id + 'dewarAvgCustomsValue', + value : '' + }, { + xtype : 'numberfield', + labelWidth : 400, + fieldLabel : 'Average Transport value of a dewar (Euro)', + id : _this.id + 'dewarAvgTransportValue', + value : '' + } ] + } ], + buttons : buttons + }); + } + this.fillStores(); + if (this.showTitle) { + this.panel.setTitle('Create a new Shipment'); + } + return this.panel; +}; + +ShipmentForm.prototype.input = function() { + return { + proposal : DATADOC.getProposal_10() + + }; +}; + +ShipmentForm.prototype.test = function(targetId) { + var shipmentForm = new ShipmentForm({ + creationMode : true + + }); + BIOSAXS.proposal = new Proposal(shipmentForm.input().proposal); + shipmentForm.getPanel().render(targetId); +}; -GraphCanvas.prototype.getVertexStrokeOpacity = function(vertexId) { - return this.getFormatter().getVertexById(vertexId).getDefault().getStrokeOpacity(); -}; +/** + * #onSaved + */ +function StockSolutionForm(args) { + this.id = BUI.id(); + this.actions = []; + + if (args != null) { + if (args.actions != null) { + this.actions = args.actions; + } + } + this.onSaved = new Event(this); +} + +StockSolutionForm.prototype.getStockSolution = function() { + if (this.stockSolution != null) { + this.stockSolution.concentration = Ext.getCmp(this.id + "stockSolution_concentration").getValue(); + this.stockSolution.storageTemperature = Ext.getCmp(this.id + "stockSolution_storageTemperature").getValue(); + this.stockSolution.volume = Ext.getCmp(this.id + "stockSolution_volume").getValue(); + this.stockSolution.comments = Ext.getCmp(this.id + "stockSolution_comments").getValue(); + this.stockSolution.name = Ext.getCmp(this.id + "stockSolution_name").getValue(); + this.stockSolution.bufferId = this.bufferCombo.getValue(); + + if (this.macromoleculeCombo.getValue() != null) { + this.stockSolution.macromoleculeId = this.macromoleculeCombo.getValue(); + } else { + this.stockSolution.macromolecule3VO = null; + } + + } else { + return { + concentration : Ext.getCmp(this.id + "stockSolution_concentration").getValue(), + storageTemperature : Ext.getCmp(this.id + "stockSolution_storageTemperature").getValue(), + volume : Ext.getCmp(this.id + "stockSolution_volume").getValue(), + comments : Ext.getCmp(this.id + "stockSolution_comments").getValue(), + name : Ext.getCmp(this.id + "stockSolution_name").getValue(), + bufferId : this.bufferCombo.getValue(), + macromoleculeId : this.macromoleculeCombo.getValue() + }; + } + return this.stockSolution; +}; + +StockSolutionForm.prototype.setStockSolution = function(stockSolution) { + if (stockSolution != null) { + if (stockSolution.macromoleculeId != null) { + this.macromoleculeCombo.setValue(stockSolution.macromoleculeId); + } + this.bufferCombo.setValue(stockSolution.bufferId); + Ext.getCmp(this.id + "stockSolution_concentration").setValue(this.stockSolution.concentration); + Ext.getCmp(this.id + "stockSolution_storageTemperature").setValue(this.stockSolution.storageTemperature); + Ext.getCmp(this.id + "stockSolution_volume").setValue(this.stockSolution.volume); + Ext.getCmp(this.id + "stockSolution_name").setValue(this.stockSolution.name); + Ext.getCmp(this.id + "stockSolution_comments").setValue(this.stockSolution.comments); + } +}; + +StockSolutionForm.prototype.getBufferCombo = function() { + this.bufferCombo = BIOSAXS_COMBOMANAGER.getComboBuffers(BIOSAXS.proposal.getBuffers(), { + labelWidth : 120, + margin : '0 0 10 0', + width : 220 + + }); + return this.bufferCombo; +}; + +StockSolutionForm.prototype.getMacromoleculeCombo = function() { + this.macromoleculeCombo = BIOSAXS_COMBOMANAGER.getComboMacromoleculeByMacromolecules(BIOSAXS.proposal.getMacromolecules(), { + labelWidth : 120, + margin : '0 0 10 0', + width : 220 + + }); + return this.macromoleculeCombo; +}; + +StockSolutionForm.prototype.refresh = function() { +}; + +StockSolutionForm.prototype._getTopPanel = function() { + return { + xtype : 'container', + layout : 'hbox', + border : 0, + items : [ { + xtype : 'container', + layout : 'hbox', + items : [ { + xtype : 'container', + flex : 1, + border : false, + layout : 'anchor', + defaultType : 'textfield', + items : [ + + this.getMacromoleculeCombo(), { + xtype : 'requiredtext', + id : this.id + 'stockSolution_name', + fieldLabel : 'Acronym', + labelWidth : 120, + width : 250 + }, { + xtype : 'requiredtext', + id : this.id + 'stockSolution_concentration', + fieldLabel : 'Conc. (mg/ml)', + labelWidth : 120, + width : 250 + }, + + { + id : this.id + 'stockSolution_storageTemperature', + fieldLabel : 'Storage Temp.(C)', + labelWidth : 120, + width : 250 + }, { + xtype : 'requiredtext', + id : this.id + 'stockSolution_volume', + fieldLabel : 'Volume in Well (µl)', + labelWidth : 120, + width : 250 + } ] + } ] + }, { + xtype : 'container', + flex : 1, + layout : 'anchor', + defaultType : 'textfield', + margin : '0 0 0 10', + items : [ this.getBufferCombo() ] + } ] + }; + +}; + +StockSolutionForm.prototype.getPanel = function(stockSolution) { + this.stockSolution = stockSolution; + this.panel = Ext.createWidget({ + xtype : 'container', + layout : 'vbox', + border : 0, + style : { + padding : '10px' + }, + fieldDefaults : { + labelAlign : 'left', + labelWidth : 50 + }, + items : [ this._getTopPanel(stockSolution), { + id : this.id + 'stockSolution_comments', + xtype : 'textareafield', + name : 'comments', + fieldLabel : 'Comments', + labelWidth : 120, + width : '100%' + } ] + }); + + this.setStockSolution(stockSolution); + return this.panel; +}; + +StockSolutionForm.prototype.input = function() { + return { + stock : { + "stockSolutionId" : 6, + "proposalId" : 3124, + "macromoleculeId" : 5933, + "bufferId" : 811, + "instructionSet3VO" : null, + "boxId" : 305861, + "storageTemperature" : "20", + "volume" : "300", + "concentration" : "1.2", + "comments" : "Buffer EDTA with A", + "name" : "A_EDTA_1.2", + "samples" : [], + "buffer" : "EDTA", + "macromolecule" : "A" + }, + proposal : new MeasurementGrid().input().proposal + }; +}; + +StockSolutionForm.prototype.test = function(targetId) { + var stockSolutionForm = new StockSolutionForm(); + BIOSAXS.proposal = new Proposal(stockSolutionForm.input().proposal); + var panel = stockSolutionForm.getPanel(new Shipment(stockSolutionForm.input().stock)); + panel.render(targetId); +}; -GraphCanvas.prototype.setVertexOpacity = function(vertexId, value) { - this.getFormatter().getVertexById(vertexId).getDefault().setOpacity(value); -}; + +BoxWhiskerGraph.prototype.cleanArray = GenericGraph.prototype.cleanArray; +BoxWhiskerGraph.prototype.plotAxes = GenericGraph.prototype.plotAxes; +BoxWhiskerGraph.prototype.plotRuler = GenericGraph.prototype.plotRuler; +BoxWhiskerGraph.prototype.drawSVGVerticalText = GenericGraph.prototype.drawSVGVerticalText; +BoxWhiskerGraph.prototype.getClassColor = GenericGraph.prototype.getClassColor; +BoxWhiskerGraph.prototype.getDimensions = GenericGraph.prototype.getDimensions; +BoxWhiskerGraph.prototype.calculate = GenericGraph.prototype.calculate; +BoxWhiskerGraph.prototype.getMedian = GenericGraph.prototype.getMedian; +BoxWhiskerGraph.prototype.pointToPixel = GenericGraph.prototype.pointToPixel; +BoxWhiskerGraph.prototype.maxValueWhisker = GenericGraph.prototype.maxValueWhisker; +BoxWhiskerGraph.prototype.refresh = GenericGraph.prototype.refresh; + +/** + * Subclass of GenericGraph + * + * @maxBoxWidth + */ +function BoxWhiskerGraph(args){ + this.maxBoxWidth = 25; + + if (args == null){ + args = new Object(); + } + args["plotHorizontalByCluster"] = true; + + GenericGraph.call(this, args); + + if (args.maxBoxWidth != null){ + this.maxBoxWidth = args.maxBoxWidth; + } +}; + + + +/** +There are several different methods for calculating quartiles.[1] This calculator uses a method described by Moore and McCabe to find quartile values. +The same method also used by The TI-83 to calculate quartile values. +With this method, the first quartile is the median of the numbers below the median, the third quartile is the median of the numbers above the median. + +http://www.miniwebtool.com/quartile-calculator/ +http://www.alcula.com/calculators/statistics/box-plot/ +**/ +BoxWhiskerGraph.prototype.getQ1 = function(array){ + array = array.slice(0, array.length/2); + return this.getMedian(array); +}; + +BoxWhiskerGraph.prototype.getQ3 = function(array){ + array = array.slice((array.length + 1)/2); + return this.getMedian(array); + +}; + +BoxWhiskerGraph.prototype.getQ2 = function(array){ + return this.getMedian(array); +}; + +BoxWhiskerGraph.prototype.getBelowOutliers = function(belowLimit, array){ + var points = new Array(); + for (var i = 0; i < array.length; i++){ + if (array[i] <= belowLimit){ + points.push(array[i]); + } + } + return points; +}; + +BoxWhiskerGraph.prototype.getAboveOutliers = function(aboveLimit, array){ + var points = new Array(); + for (var i = 0; i < array.length; i++){ + if (array[i] >= aboveLimit){ + points.push(array[i]); + } + } + return points; +}; + + +BoxWhiskerGraph.prototype.minValueWhisker = function(belowLimit, q1, array){ + var points = new Array(); + + for (var i = 0; i < array.length; i++){ + if ((array[i] < q1) && (array[i]) > belowLimit){ + points.push(array[i]); + } + } + + if (points.length > 0){ + points.sort(function(a, b){return a - b;}); + return points[0]; + } + return null; +}; + +BoxWhiskerGraph.prototype.isNumber = function(value){ + if (value=="") return false; + + var d = parseInt(value); + if (!isNaN(d)) return true; else return false; + +}; + +BoxWhiskerGraph.prototype.plotBoxWhisker = function(boxProperties){ + if (this.maxBoxWidth != null){ + if (boxProperties.width > this.maxBoxWidth){ + boxProperties.x = boxProperties.x + (boxProperties.width/2) - (this.maxBoxWidth/2); + boxProperties.width = this.maxBoxWidth; + } + + } + + if (this.plotPoints){ + for(var i = 0; i < boxProperties.values.length; i++){ + var value = boxProperties.values[i]; + var toPixel = this.pointToPixel(value, boxProperties); + SVG.drawCircle(boxProperties.x + (boxProperties.width/2) + 2, toPixel, this.pointRadius, this.svg, [["fill", "green"], ["fill-opacity", this.fillOpacityPoint],['stroke-opacity', this.strokeOpacityPoint], ["stroke", "black"]]); + } + } + + + var boxColor = this.getClassColor(boxProperties.name); + var result = this.calculate(boxProperties.values); + /** Q1 **/ + + if (this.isNumber(result.Q1)){ + SVG.drawLine(boxProperties.x, this.pointToPixel(result.Q1, boxProperties), boxProperties.x + boxProperties.width, this.pointToPixel(result.Q1, boxProperties), this.svg, [["stroke", boxColor]]); + } + /** Q2 **/ + if (this.isNumber(result.Q2)){ + SVG.drawLine(boxProperties.x, this.pointToPixel(result.Q2, boxProperties), boxProperties.x + boxProperties.width, this.pointToPixel(result.Q2, boxProperties), this.svg, [["stroke", "blue"], ["stroke-width", "2"]]); + } + + /** Q3 **/ + if (this.isNumber(result.Q3)){ + SVG.drawLine(boxProperties.x, this.pointToPixel(result.Q3, boxProperties), boxProperties.x + boxProperties.width, this.pointToPixel(result.Q3, boxProperties), this.svg, [["stroke", boxColor]]); + } + + /** Concenting Q1 and Q3 **/ + if (this.isNumber(result.Q1)&&this.isNumber(result.Q3)){ + SVG.drawLine(boxProperties.x, this.pointToPixel(result.Q1, boxProperties), boxProperties.x, this.pointToPixel(result.Q3, boxProperties), this.svg, [["stroke", boxColor]]); + SVG.drawLine(boxProperties.x + boxProperties.width, this.pointToPixel(result.Q1, boxProperties), boxProperties.x + boxProperties.width, this.pointToPixel(result.Q3, boxProperties), this.svg, [["stroke", boxColor]]); + } + + /** min-whisker **/ + if (result["min-whisker"] != null){ + if (this.isNumber(result.Q1)){ + SVG.drawLine(boxProperties.x + (boxProperties.width/2) + 2, this.pointToPixel(result.Q1, boxProperties), boxProperties.x + (boxProperties.width/2) + 2, this.pointToPixel(result["min-whisker"], boxProperties), this.svg, [["stroke", boxColor]]); + } + SVG.drawLine(boxProperties.x,this.pointToPixel(result["min-whisker"], boxProperties), boxProperties.x + boxProperties.width, this.pointToPixel(result["min-whisker"], boxProperties) , this.svg, [["stroke", boxColor]]); + + } + + /** max-whisker **/ + if (result["max-whisker"] != null){ + if (this.isNumber(result.Q3)){ + SVG.drawLine(boxProperties.x + (boxProperties.width/2) + 2, this.pointToPixel(result.Q3, boxProperties), boxProperties.x + (boxProperties.width/2) + 2, this.pointToPixel(result["max-whisker"], boxProperties), this.svg, [["stroke", boxColor]]); + } + SVG.drawLine(boxProperties.x , this.pointToPixel(result["max-whisker"], boxProperties), boxProperties.x + boxProperties.width, this.pointToPixel(result["max-whisker"], boxProperties), this.svg, [["stroke", boxColor]]); + + } + + /** outliners **/ + if (result["above-outliers"] != null){ + for (var point in result["above-outliers"]){ + SVG.drawCircle(boxProperties.x + (boxProperties.width/2) + 2, this.pointToPixel(result["above-outliers"][point], boxProperties), this.pointRadius, this.svg, [["fill", "red"], ["fill-opacity", "1"]]); + //SVG.drawText(boxProperties.x + (boxProperties.width/2) - 2, this.pointToPixel(result["above-outliers"][point], boxProperties) + 2, "x",this.svg, [["fill", "black"], ["font-size", "x-small"]]); + } + } + if (result["below-outliers"] != null){ + for (var point in result["below-outliers"]){ + SVG.drawCircle(boxProperties.x + (boxProperties.width/2) + 2, this.pointToPixel(result["below-outliers"][point], boxProperties), this.pointRadius, this.svg, [["fill", "red"], ["fill-opacity", "1"]]); + //SVG.drawText(boxProperties.x + (boxProperties.width/2) - 2, this.pointToPixel(result["below-outliers"][point], boxProperties) + 2, "x",this.svg, [["fill", "black"], ["font-size", "x-small"]]); + } + } +}; + + + +BoxWhiskerGraph.prototype.plotClusterTitles = function(properties){ + /** Cluster Titles **/ + var posX = this.left + this.rulerWidth; + var data = this.data; + for(var i = 0; i < data.clusters.length; i++ ){ + /** title for the clusters **/ + posX = posX + this.interClustersSpace; + + /** Drawing title of classes **/ + var cluster = data.clusters[i]; + var posXClasses = posX; + for(var j = 0; j < cluster.classes.length; j++){ + //SVG.drawRectangle(posXClasses, this.height - (this.bottom + this.clusterTitleHeight + this.rulerHeight), properties.classWidth, this.rulerHeight, this.svg, [["fill", "green"]]); +// SVG.drawText(posXClasses + 1/6*properties.classWidth, this.height - (this.bottom + this.clusterTitleHeight + (this.rulerHeight*1/4)), cluster.classes[j].name, this.svg, [["style", "font-size:xx-small"]]); + var color = cluster.classes[j].color; + this.drawSVGVerticalText(posXClasses + 1/2*(properties.classWidth), this.height - (this.bottom - this.clusterTitleHeight + (this.rulerHeight)), cluster.classes[j].name , [["style", "font-size:x-small;"], ["fill", color]]); + + // SVG.drawRectangle(posXClasses, this.height - (this.bottom + this.clusterTitleHeight), properties.classWidth, 2, this.svg, [["fill", "black"]]); + posXClasses = posXClasses + properties.classWidth + this.interClassesSpace; + } + + var clusterTitleSpace = (data.clusters[i].classes.length * properties.classWidth) + ((data.clusters[i].classes.length - 1) * this.interClassesSpace); + //SVG.drawRectangle(posX, this.height - (this.bottom + this.clusterTitleHeight), clusterTitleSpace, this.clusterTitleHeight, this.svg, [["fill", "pink"]]); + SVG.drawRectangle(posX, this.height - (this.clusterTitleHeight+this.bottom), clusterTitleSpace, 1, this.svg, []); + +// var transform = ["translate", "(" + posX + 1/3*clusterTitleSpace +", " + this.height - (this.bottom + (this.clusterTitleHeight*1/4)) +")"], ["transform", "rotate(180)"]; +// var transform = ["transform", "translate(" + (posX + 1/3*clusterTitleSpace) +", " + (this.height - (this.bottom + (this.clusterTitleHeight*1/4))) + "), rotate(-90) "]; +// this.drawSVGVerticalText(posX + 1/3*clusterTitleSpace, this.height - (this.bottom + (this.clusterTitleHeight*1/4)),data.clusters[i].name, [["style", "font-size:x-small"]]); + SVG.drawText(posX , this.height - (this.bottom + (this.clusterTitleHeight*1/4)),data.clusters[i].name ,this.svg, [["style", "font-size:x-small"]]); + posX = posX + clusterTitleSpace; + } +}; + +BoxWhiskerGraph.prototype.plotWhisters = function(data, properties){ + var colors = ["yellow", "orange", "green"]; + var posX = this.left + this.rulerWidth; + for(var i = 0; i < data.clusters.length; i++ ){ + var cluster = data.clusters[i]; + /** inter cluster space **/ + //SVG.drawRectangle(posX, this.top, this.interClustersSpace, this.height - this.top - this.bottom - this.clusterTitleHeight - this.rulerHeight, this.svg, [["fill", "black"]]); + posX = posX + this.interClustersSpace; + + for (var j = 0; j < cluster.classes.length; j ++){ + + //SVG.drawRectangle(posX, this.top, properties.classWidth, this.height - this.top - this.bottom - this.clusterTitleHeight - this.rulerHeight, this.svg, [["fill", colors[j]]]); + this.plotBoxWhisker( + { + name : cluster.classes[j].name, + values : cluster.classes[j].values, + minPoint : properties.minPoint, + maxPoint : properties.maxPoint, + x : posX, + y : this.top, + width : properties.classWidth, + height : this.height - this.top - this.bottom - this.clusterTitleHeight - this.rulerHeight + + + } + ); + posX = posX + properties.classWidth; + //SVG.drawRectangle(posX, this.top, this.interClassesSpace, this.height - this.top - this.bottom - this.clusterTitleHeight - this.rulerHeight, this.svg, [["fill", "black"]]); + if (j < cluster.classes.length - 1){ + posX = posX + this.interClassesSpace; + } + } + } +}; + +BoxWhiskerGraph.prototype.draw = function(targetId, data){ + this.targetId = targetId; + var properties = (this.getDimensions(data)); + this.svg = SVG.createSVGCanvas(document.getElementById(this.targetId), [["width", this.width], ["height", this.height]]); + this.plotAxes(properties); + this.plotClusterTitles(properties); + this.plotWhisters(data,properties); +}; + +BoxWhiskerGraph.prototype.input = function(){ + return DATADOC.getBoxWhikerData(); +}; + +BoxWhiskerGraph.prototype.test = function(targetId){ + var plot = new BoxWhiskerGraph( + { + targetId : targetId, + height : 350, + width : 450, + maxBoxWidth : 25 + } + ); + plot.refresh(this.input()); +}; + + + + + -GraphCanvas.prototype.getVertexOpacity = function(vertexId) { - return this.getFormatter().getVertexById(vertexId).getDefault().getOpacity(); -}; + +function DataSet(){ + this.json = null; + +}; + + +DataSet.prototype.loadFromJSON = function(json){ + if(json != null) { + if(this.validate(json)) { + this.json = json; + } + } +}; + + +DataSet.prototype.toJSON = function(json){ + return this.json; +}; + + +/** Abstract method to be override on childs classes **/ +DataSet.prototype.validate = function(json){ + if (true){ + return true; + } + /*else{ + throw "Data validation failed"; + }*/ +}; + + -GraphCanvas.prototype.setVertexFill = function(vertexId, color) { - this.getFormatter().getVertexById(vertexId).getDefault().setFill(color); -}; + +Edge.prototype.getName = GraphItem.prototype.getName; +Edge.prototype.setName = GraphItem.prototype.setName; +Edge.prototype.getId = GraphItem.prototype.getId; + +function Edge(id, name, nodeSource, nodeTarget, args){ + GraphItem.prototype.constructor.call(this, id, name, args); + + this.sourceNode = nodeSource; + this.targetNode = nodeTarget; + +}; + +Edge.prototype.toJSON = function(){ + return {"index": this.id,"sourceIndex":this.sourceNode.getId(),"targetIndex":this.targetNode.getId(),"args":this.args}; +}; + +Edge.prototype.getNodeSource = function(){ + return this.sourceNode; +}; + +Edge.prototype.getNodeTarget = function(){ + return this.targetNode; +}; + +Edge.prototype.remove = function(){ + //Remove edge object in the nodes + this.getNodeSource().removeEdge(this); + this.getNodeTarget().removeEdge(this); + + this.deleted.notify(this); +}; -GraphCanvas.prototype.getVertexFill = function(vertexId) { - return this.getFormatter().getVertexById(vertexId).getDefault().getFill(); -}; + +EdgeGraphFormatter.prototype.setProperties = ItemGraphFormatter.prototype.setProperties; +EdgeGraphFormatter.prototype.getDefault = ItemGraphFormatter.prototype.getDefault; +EdgeGraphFormatter.prototype.getSelected = ItemGraphFormatter.prototype.getSelected; +EdgeGraphFormatter.prototype.getOver = ItemGraphFormatter.prototype.getOver; +EdgeGraphFormatter.prototype.getDragging = ItemGraphFormatter.prototype.getDragging; +EdgeGraphFormatter.prototype.getId = ItemGraphFormatter.prototype.getId; +EdgeGraphFormatter.prototype.toJSON = ItemGraphFormatter.prototype.toJSON; +EdgeGraphFormatter.prototype.loadFromJSON = ItemGraphFormatter.prototype.loadFromJSON; +EdgeGraphFormatter.prototype._setEvents = ItemGraphFormatter.prototype._setEvents; + +function EdgeGraphFormatter(id, defaultFormat, selectedFormat, overFormat, dragginFormat){ + ItemGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, dragginFormat); + + this.getDefault().args.type = "EdgeGraphFormatter"; +}; + + + + +LineEdgeGraphFormatter.prototype.setProperties = EdgeGraphFormatter.prototype.setProperties; +LineEdgeGraphFormatter.prototype.getDefault = EdgeGraphFormatter.prototype.getDefault; +LineEdgeGraphFormatter.prototype.getSelected = EdgeGraphFormatter.prototype.getSelected; +LineEdgeGraphFormatter.prototype.getOver = EdgeGraphFormatter.prototype.getOver; +LineEdgeGraphFormatter.prototype.getDragging = EdgeGraphFormatter.prototype.getDragging; +LineEdgeGraphFormatter.prototype.getId = EdgeGraphFormatter.prototype.getId; +LineEdgeGraphFormatter.prototype.toJSON = EdgeGraphFormatter.prototype.toJSON; +LineEdgeGraphFormatter.prototype.loadFromJSON = EdgeGraphFormatter.prototype.loadFromJSON; +LineEdgeGraphFormatter.prototype._setEvents = EdgeGraphFormatter.prototype._setEvents; + +function LineEdgeGraphFormatter(id, defaultFormat, selectedFormat, overFormat, dragginFormat){ + EdgeGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, dragginFormat); + this.args.type = "LineEdgeGraphFormatter"; +}; + +/** DIRECTED **/ +DirectedLineEdgeGraphFormatter.prototype.setProperties = EdgeGraphFormatter.prototype.setProperties; +DirectedLineEdgeGraphFormatter.prototype.getDefault = EdgeGraphFormatter.prototype.getDefault; +DirectedLineEdgeGraphFormatter.prototype.getSelected = EdgeGraphFormatter.prototype.getSelected; +DirectedLineEdgeGraphFormatter.prototype.getOver = EdgeGraphFormatter.prototype.getOver; +DirectedLineEdgeGraphFormatter.prototype.getDragging = EdgeGraphFormatter.prototype.getDragging; +DirectedLineEdgeGraphFormatter.prototype.getId = EdgeGraphFormatter.prototype.getId; +DirectedLineEdgeGraphFormatter.prototype.toJSON = EdgeGraphFormatter.prototype.toJSON; +DirectedLineEdgeGraphFormatter.prototype.loadFromJSON = EdgeGraphFormatter.prototype.loadFromJSON; +DirectedLineEdgeGraphFormatter.prototype._setEvents = EdgeGraphFormatter.prototype._setEvents; + +function DirectedLineEdgeGraphFormatter(id, defaultFormat, selectedFormat, overFormat, dragginFormat){ + EdgeGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, dragginFormat); + this.args.type = "DirectedLineEdgeGraphFormatter"; + this.args.arrowSize = "3"; +}; +DirectedLineEdgeGraphFormatter.prototype.getArrowSize = function(){ + return this.args.arrowSize; +}; + +OdirectedLineEdgeGraphFormatter.prototype.setProperties = EdgeGraphFormatter.prototype.setProperties; +OdirectedLineEdgeGraphFormatter.prototype.getDefault = EdgeGraphFormatter.prototype.getDefault; +OdirectedLineEdgeGraphFormatter.prototype.getSelected = EdgeGraphFormatter.prototype.getSelected; +OdirectedLineEdgeGraphFormatter.prototype.getOver = EdgeGraphFormatter.prototype.getOver; +OdirectedLineEdgeGraphFormatter.prototype.getDragging = EdgeGraphFormatter.prototype.getDragging; +OdirectedLineEdgeGraphFormatter.prototype.getId = EdgeGraphFormatter.prototype.getId; +OdirectedLineEdgeGraphFormatter.prototype.toJSON = EdgeGraphFormatter.prototype.toJSON; +OdirectedLineEdgeGraphFormatter.prototype.loadFromJSON = EdgeGraphFormatter.prototype.loadFromJSON; +OdirectedLineEdgeGraphFormatter.prototype._setEvents = EdgeGraphFormatter.prototype._setEvents; + +function OdirectedLineEdgeGraphFormatter(id, defaultFormat, selectedFormat, overFormat, dragginFormat){ + EdgeGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, dragginFormat); + this.args.type = "DirectedLineEdgeGraphFormatter"; + this.args.arrowSize = "3"; +}; + +OdirectedLineEdgeGraphFormatter.prototype.getArrowSize = function(){ + return this.args.arrowSize; +}; + + +/** CUT (inhibition) **/ +CutDirectedLineEdgeGraphFormatter.prototype.setProperties = EdgeGraphFormatter.prototype.setProperties; +CutDirectedLineEdgeGraphFormatter.prototype.getDefault = EdgeGraphFormatter.prototype.getDefault; +CutDirectedLineEdgeGraphFormatter.prototype.getSelected = EdgeGraphFormatter.prototype.getSelected; +CutDirectedLineEdgeGraphFormatter.prototype.getOver = EdgeGraphFormatter.prototype.getOver; +CutDirectedLineEdgeGraphFormatter.prototype.getDragging = EdgeGraphFormatter.prototype.getDragging; +CutDirectedLineEdgeGraphFormatter.prototype.getId = EdgeGraphFormatter.prototype.getId; +CutDirectedLineEdgeGraphFormatter.prototype.toJSON = EdgeGraphFormatter.prototype.toJSON; +CutDirectedLineEdgeGraphFormatter.prototype.loadFromJSON = EdgeGraphFormatter.prototype.loadFromJSON; +CutDirectedLineEdgeGraphFormatter.prototype._setEvents = EdgeGraphFormatter.prototype._setEvents; + +function CutDirectedLineEdgeGraphFormatter(id, defaultFormat, selectedFormat, overFormat, dragginFormat){ + EdgeGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, dragginFormat); + this.args.type = "CutDirectedLineEdgeGraphFormatter"; +}; + + + + +BezierEdgeGraphFormatter.prototype.setProperties = EdgeGraphFormatter.prototype.setProperties; +BezierEdgeGraphFormatter.prototype.setProperties = EdgeGraphFormatter.prototype.setProperties; +BezierEdgeGraphFormatter.prototype.getDefault = EdgeGraphFormatter.prototype.getDefault; +BezierEdgeGraphFormatter.prototype.getSelected = EdgeGraphFormatter.prototype.getSelected; +BezierEdgeGraphFormatter.prototype.getOver = EdgeGraphFormatter.prototype.getOver; +BezierEdgeGraphFormatter.prototype.getDragging = EdgeGraphFormatter.prototype.getDragging; +BezierEdgeGraphFormatter.prototype.getId = EdgeGraphFormatter.prototype.getId; +BezierEdgeGraphFormatter.prototype.toJSON = EdgeGraphFormatter.prototype.toJSON; +BezierEdgeGraphFormatter.prototype.loadFromJSON = EdgeGraphFormatter.prototype.loadFromJSON; +BezierEdgeGraphFormatter.prototype._setEvents = EdgeGraphFormatter.prototype._setEvents; + +function BezierEdgeGraphFormatter(id, defaultFormat, selectedFormat, overFormat, dragginFormat){ + EdgeGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, dragginFormat); + this.args.type = "BezierEdgeGraphFormatter"; +}; + + + +DotDirectedLineEdgeGraphFormatter.prototype.setProperties = EdgeGraphFormatter.prototype.setProperties; +DotDirectedLineEdgeGraphFormatter.prototype.getDefault = EdgeGraphFormatter.prototype.getDefault; +DotDirectedLineEdgeGraphFormatter.prototype.getSelected = EdgeGraphFormatter.prototype.getSelected; +DotDirectedLineEdgeGraphFormatter.prototype.getOver = EdgeGraphFormatter.prototype.getOver; +DotDirectedLineEdgeGraphFormatter.prototype.getDragging = EdgeGraphFormatter.prototype.getDragging; +DotDirectedLineEdgeGraphFormatter.prototype.getId = EdgeGraphFormatter.prototype.getId; +DotDirectedLineEdgeGraphFormatter.prototype.toJSON = EdgeGraphFormatter.prototype.toJSON; +DotDirectedLineEdgeGraphFormatter.prototype.loadFromJSON = EdgeGraphFormatter.prototype.loadFromJSON; +DotDirectedLineEdgeGraphFormatter.prototype._setEvents = EdgeGraphFormatter.prototype._setEvents; + +function DotDirectedLineEdgeGraphFormatter(id, defaultFormat, selectedFormat, overFormat, dragginFormat){ + EdgeGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, dragginFormat); + this.args.type = "DotDirectedLineEdgeGraphFormatter"; +}; + + +OdotDirectedLineEdgeGraphFormatter.prototype.setProperties = EdgeGraphFormatter.prototype.setProperties; +OdotDirectedLineEdgeGraphFormatter.prototype.getDefault = EdgeGraphFormatter.prototype.getDefault; +OdotDirectedLineEdgeGraphFormatter.prototype.getSelected = EdgeGraphFormatter.prototype.getSelected; +OdotDirectedLineEdgeGraphFormatter.prototype.getOver = EdgeGraphFormatter.prototype.getOver; +OdotDirectedLineEdgeGraphFormatter.prototype.getDragging = EdgeGraphFormatter.prototype.getDragging; +OdotDirectedLineEdgeGraphFormatter.prototype.getId = EdgeGraphFormatter.prototype.getId; +OdotDirectedLineEdgeGraphFormatter.prototype.toJSON = EdgeGraphFormatter.prototype.toJSON; +OdotDirectedLineEdgeGraphFormatter.prototype.loadFromJSON = EdgeGraphFormatter.prototype.loadFromJSON; +OdotDirectedLineEdgeGraphFormatter.prototype._setEvents = EdgeGraphFormatter.prototype._setEvents; + +function OdotDirectedLineEdgeGraphFormatter(id, defaultFormat, selectedFormat, overFormat, dragginFormat){ + EdgeGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, dragginFormat); + this.args.type = "OdotDirectedLineEdgeGraphFormatter"; +}; + + -/** EDGES FORMATTER **/ -GraphCanvas.prototype.setEdgeSize = function(edgeId, value) { - this.getFormatter().getEdgeById(edgeId).getDefault().setSize(value); -}; +function GraphDataset(){ + DataSet.prototype.constructor.call(this); + this.edges = new Object(); + this.vertices = new Object(); + this.verticesIndex = new Object(); + + //Events + this.newVertex = new Event(this); + this.vertexNameChanged = new Event(this); + this.vertexDeleted = new Event(this); + + this.newEdge = new Event(this); + this.edgeNameChanged = new Event(this); + this.edgeDeleted = new Event(this); + + this.json = new Object(); + this.json.vertices = new Array(); + this.json.edges = new Array(); + this.json.relations = new Array(); +}; + +GraphDataset.prototype.loadFromJSON = DataSet.prototype.loadFromJSON; +GraphDataset.prototype.toJSON = DataSet.prototype.toJSON; +GraphDataset.prototype.validate = DataSet.prototype.validate; + +/** Devuelve el numero de edges incidentes sobre el nodo con mas edges **/ +GraphDataset.prototype.getMaxClass = function(){ + var maxClassNode = 0; + for ( var node in this.vertices) { + if (this.vertices[node].getEdgesCount() > maxClassNode){ + maxClassNode = this.vertices[node].getEdgesCount(); + } + } + return maxClassNode; +}; + +/** Devuelve el numero de edges incidentes sobre el nodo con mas edges **/ +GraphDataset.prototype.getMinClass = function(){ + var minClassNode = Math.min(); + for ( var node in this.vertices) { + if (this.vertices[node].getEdgesCount() < minClassNode){ + minClassNode = this.vertices[node].getEdgesCount(); + } + } + return minClassNode; +}; + +GraphDataset.prototype.getVertexByName = function(nodeName){ + var results = new Array(); + + for (var vertexId in this.verticesIndex[nodeName]){ + var vertexByid = this.getVertexById(this.verticesIndex[nodeName][vertexId]); + results.push(vertexByid); + //* añadido nuevo porque fallaba el anterior codigo + return vertexByid + } + + if (results <= 1){ + return this.getVertexById(this.verticesIndex[nodeName]); + } + else{ + return results; + } +}; + +GraphDataset.prototype.getVertexById = function(id){ + return this.vertices[id]; +}; + +GraphDataset.prototype.toSIF = function(){ + var sifDataAdapter = new SifFileDataAdapter(); + return sifDataAdapter.toSIF(this); +}; + +GraphDataset.prototype.toSIFID = function(){ + var sifDataAdapter = new SifFileDataAdapter(); + return sifDataAdapter.toSIFID(this); +}; + +GraphDataset.prototype.toDOT = function(){ + var dotFileDataAdapter = new DotFileDataAdapter(); + return dotFileDataAdapter.toDOT(this); +}; + +GraphDataset.prototype.toDOTID = function(){ + var dotFileDataAdapter = new DotFileDataAdapter(); + return dotFileDataAdapter.toDOTID(this); +}; + +GraphDataset.prototype._addNode = function(nodeName, args){ + return new Vertex(this._getVerticesCount()-1, nodeName, args); +}; + +GraphDataset.prototype.addNode = function(nodeName, args){ + this.json.vertices.push(nodeName); + this._addVerticesIndex(nodeName, this._getVerticesCount() - 1); + var vertex = this._addNode(nodeName, args); + this.vertices[this._getVerticesCount()-1] = vertex; + this._setNodeEvents(vertex); + this.newVertex.notify(vertex); +}; + +GraphDataset.prototype._addVerticesIndex = function(nodeName, id){ + if (this.verticesIndex[nodeName] == null){ + this.verticesIndex[nodeName] = new Array(); + } + this.verticesIndex[nodeName].push(id); +}; + +GraphDataset.prototype.addEdge = function(edgeName, nodeSourceId, nodeTargetId, args){ + this.json.edges.push(edgeName); + var nodeSource = this.getVertexById(nodeSourceId); + var nodeTarget = this.getVertexById(nodeTargetId); + var index = this.getEdgesCount() - 1; + this.edges[index] = new Edge(index, edgeName, nodeSource, nodeTarget, args); + this.json.relations.push({"index": index, "sourceIndex": nodeSourceId, "targetIndex": nodeTargetId, "args": args }); + + nodeSource.addEdge(this.edges[index]); + nodeTarget.addEdge(this.edges[index]); + this._setEdgeEvents(this.edges[index]); + this.newEdge.notify(this.edges[index]); +}; + +GraphDataset.prototype.getVertices = function(){ + return this.vertices; +}; + +GraphDataset.prototype.getEdges = function(){ + return this.edges; +}; + +GraphDataset.prototype.getEdgeById = function(edgeId){ + return this.edges[edgeId]; +}; + +GraphDataset.prototype._getVerticesCount = function(){ + return this.json.vertices.length; +}; + + +GraphDataset.prototype.getVerticesCount = function(){ + var count = 0; + for ( var vertex in this.getVertices()) { + count ++; + } + return count; +}; + + +GraphDataset.prototype.getEdgesCount = function(){ + return this.json.edges.length; +}; + +GraphDataset.prototype.init = function(){ + this.edges = new Object(); + this.vertices = new Object(); +}; + +GraphDataset.prototype._setNodeEvents = function(node){ + var _this = this; + //NODE EVENTS + node.deleted.attach(function (sender, node){ + _this._removeNode(node); + }); + + node.nameChanged.attach(function (sender, args){ + var item = args.item; + var newName = item.name; + var indexes = _this.verticesIndex[args.previousName]; + for(var i = 0; i < indexes.length; i++){ + if(indexes[i] == item.id) + indexes.splice(i,1); + } + if(indexes.length == 0){ + delete _this.verticesIndex[args.previousName]; + } + _this._addVerticesIndex(newName, item.id); + _this.json.vertices[item.id] = newName; + _this.vertexNameChanged.notify(args); + }); +}; + +GraphDataset.prototype._setEdgeEvents = function(edge){ + var _this = this; + //EDGE EVENTS + edge.nameChanged.attach(function (sender, edge){ + _this.edgeNameChanged.notify(edge); + + }); + + edge.deleted.attach(function (sender, edge){ + _this._removeEdge(edge); + }); +}; + +GraphDataset.prototype._connectVerticesByName = function(nodeNameSource, nodeNameTarget){ + var source = this.getVertexByName(nodeNameSource); + var target = this.getVertexByName(nodeNameTarget); + + if ((source != null)&&(target!=null)){ + this.addEdge(source.getName() +"_" + target.getName(), source.getId(), target.getId(), {}); + } + else{ + if (source == null){ + console.log("No encontrado: " + nodeNameSource) + } + if (target == null){ + console.log("No encontrado: " + nodeNameTarget) + } + } +}; + +GraphDataset.prototype.loadFromJSON = function(json){ + var json = json; + this.init(); + this.json = new Object(); + this.json.vertices = new Array(); + this.json.edges = new Array(); + this.json.relations = new Array(); + + for ( var i = 0; i < json.nodes.length; i++) { + if (json.nodes[i] != null){ + var name = json.nodes[i]; + this.addNode(name); + } + else{ + this.json.vertices.push(null); + } + } + + for ( var i = 0; i < json.edges.length; i++) { + if (json.edges[i] != null){ + if (json.relations[i] != null){ + var name = json.edges[i]; + this.addEdge(name, json.relations[i].sourceIndex, json.relations[i].targetIndex, json.relations[i].args); + } + } + else{ + this.json.edges.push(null); + this.json.relations.push(null); + } + } +}; + +GraphDataset.prototype.prettyPrint = function(){ + for ( var node in this.vertices) { + console.log(this.vertices[node].getName() ); + for ( var j = 0; j < this.vertices[node].getEdgesIn().length; j++) { + console.log(" --> " + this.vertices[node].getEdgesIn()[j].getNodeTarget().getName() ); + } + } +}; + +GraphDataset.prototype._removeEdge = function(edge){ + this.json.edges[edge.getId()] = null; + this.json.relations[edge.getId()] = null; + + delete this.edges[edge.getId()]; + this.edgeDeleted.notify(edge); + + +}; + +GraphDataset.prototype._removeNode = function(node){ + this.json.vertices[node.getId()] = null; + delete this.vertices[node.getId()]; + this.vertexDeleted.notify(node); +}; + +GraphDataset.prototype.toJSON = function(){ + var json = new Object(); + var nodes = new Array(); + json.nodes = this.json.vertices; //nodes; + json.edges = this.json.edges; //edges; + json.relations = this.json.relations; + return json; +}; + +GraphDataset.prototype.clone = function(){ + var dsDataset = new GraphDataset(); + dsDataset.loadFromJSON(this.toJSON()); + return dsDataset; +}; +//GraphDataset.prototype.test = function(){ +// this.loadFromJSON(this.toJSON()); +//}; + +function labels(){ + var names = new Array(); + + var dataset = interactomeViewer.graphEditorWidget.dataset; + var layout = interactomeViewer.graphEditorWidget.layout; + + for ( var vertexId in interactomeViewer.graphEditorWidget.dataset.getVertices()) { + names.push(interactomeViewer.graphEditorWidget.dataset.getVertexById(vertexId).getName()); + } + + var sorted = (names.sort()); + console.log(sorted) + var distance = 0.01; + var altura = 0.6; + for ( var i = 0; i < names.length; i++) { + var id =dataset.getVertexByName(names[i]).getId(); + + layout.getNodeById(id).setCoordenates(distance, altura); + + + distance = parseFloat(distance) + parseFloat(0.03); + + altura = parseFloat(altura) + parseFloat(0.02); + + if (parseFloat(altura) == 0.9800000000000003){ + + altura = 0.6; + distance = distance - 0.51; + } + + } + + +}; -GraphCanvas.prototype.getEdgeSize = function(edgeId) { - return this.getFormatter().getEdgeById(edgeId).getDefault().getSize(); -}; +function GraphItem(id, name, args){ + this.id = id; + this.name = name; + this.type = "NONE"; + + this.args = new Object(); + + + if (args!=null){ + this.args = args; + if (args.type !=null){ + this.type = args.type; + } + } + + //Events + this.nameChanged = new Event(this); + this.deleted = new Event(this); +} + +GraphItem.prototype.getName = function(){ + return this.name; +}; + +GraphItem.prototype.getId = function(){ + return this.id; +}; + +GraphItem.prototype.setName = function(name){ + var oldName = this.getName(); + this.name = name; + this.nameChanged.notify({"item": this, "previousName" : oldName}); +}; + + + + -GraphCanvas.prototype.setEdgeStroke = function(edgeId, value) { - this.getFormatter().getEdgeById(edgeId).getDefault().setStroke(value); -}; +function LayoutDataset(){ + this.dataset = null; + this.vertices = new Object(); + this.changed = new Event(this); + + + this.args = new Object(); + + //RANDOM, CIRCLE + this.args.type = "CIRCLE"; +}; + +LayoutDataset.prototype.loadFromJSON = function(dataset, json){ + var _this = this; + this.vertices = new Object(); + this.dataset = dataset; //new GraphDataset(); + for ( var vertex in json) { + this.vertices[vertex] = new NodeLayout(vertex, json[vertex].x, json[vertex].y); + this.vertices[vertex].changed.attach(function (sender, item){ + _this.changed.notify(item); + }); + } + this._attachDatasetEvents(); +}; + + +LayoutDataset.prototype.toJSON = function(){ + var serialize = new Object(); + for ( var vertex in this.vertices) { + serialize[vertex] = new Object(); + serialize[vertex].x = this.vertices[vertex].x; + serialize[vertex].y = this.vertices[vertex].y; + } + serialize.dataset = new Object(); + serialize.dataset =this.dataset.toJSON(); + return serialize; +}; + +LayoutDataset.prototype.dataBind = function(graphDataset){ + this.dataset = graphDataset; + this._attachDatasetEvents(); + this._calculateLayout(); +}; + +LayoutDataset.prototype._removeVertex = function(vertexId){ + delete this.vertices[vertexId]; +}; + +LayoutDataset.prototype._attachDatasetEvents = function(){ + var _this = this; + + this.dataset.vertexDeleted.attach(function (sender, item){ + _this._removeVertex(item.getId()); + }); + + this.dataset.newVertex.attach(function (sender, item){ + _this.vertices[item.getId()] = new NodeLayout(item.getId(), 0.5, 0.5); + _this.vertices[item.getId()].changed.attach(function (sender, item){ + _this.changed.notify(item); + }); + }); +}; + +LayoutDataset.prototype.getType = function(){ + return this.args.type; +}; + +LayoutDataset.prototype._calculateLayoutVertices = function(type, count){ + + if (type == "CIRCLE"){ + var radius = 0.4; + var centerX = 0.5; + var centerY = 0.5; + var verticesCoordinates = new Array(); + for(var i = 0; i < count; i++){ + x = centerX + radius * Math.sin(i * 2 * Math.PI/count); + y = centerY + radius * Math.cos(i * 2 * Math.PI/count); + verticesCoordinates.push({'x':x,'y':y}); + } + return verticesCoordinates; + } +}; + + +LayoutDataset.prototype._calculateLayout = function(){ + var _this = this; + if (this.getType() == "RANDOM"){ + for ( var vertex in this.dataset.getVertices()) { + if (this.vertices[vertex] == null){ + this.vertices[vertex] = new NodeLayout(vertex, 0, 0); + } + this.vertices[vertex].setCoordinates(Math.random(), Math.random()); + this.vertices[vertex].changed.attach(function (sender, item){ + _this.changed.notify(item); + }); + } + } + + if ( this.getType() == "CIRCLE"){ + + var count = this.dataset._getVerticesCount(); + var verticesCoordinates = this._calculateLayoutVertices(this.getType(), count); + + var aux = 0; + for ( var vertex in this.dataset.getVertices()) { + if (this.vertices[vertex] == null){ + this.vertices[vertex] = new NodeLayout(vertex, 0, 0); + } + this.vertices[vertex].setCoordinates(verticesCoordinates[aux].x, verticesCoordinates[aux].y);//{"x":, "y":}; + aux++; + this.vertices[vertex].changed.attach(function (sender, item){ + _this.changed.notify(item); + }); + } + } + + + if (this.getType() == "SQUARE"){ + + var count = this.dataset._getVerticesCount(); + var xMin = 0.1; + var xMax = 0.9; + var yMin = 0.1; + var yMax = 0.9; + + var rows = Math.sqrt(count); + var step = (xMax - xMin) / rows; + + var verticesCoordinates = new Array(); + for(var i = 0; i < rows; i ++){ + for ( var j = 0; j < rows; j++) { + x = i * step + xMin; + y = j * step + yMin; + verticesCoordinates.push({'x':x,'y':y}); + } + } + + var aux = 0; + for ( var vertex in this.dataset.getVertices()) { + if (this.vertices[vertex] == null){ + this.vertices[vertex] = new NodeLayout(vertex, 0, 0); + } + this.vertices[vertex].setCoordinates(verticesCoordinates[aux].x, verticesCoordinates[aux].y);//{"x":, "y":}; + aux++; + this.vertices[vertex].changed.attach(function (sender, item){ + _this.changed.notify(item); + }); + } + } + +}; + +LayoutDataset.prototype.getNodeById = function(id){ + return this.vertices[id]; +}; + +LayoutDataset.prototype.getVerticesByArea = function(x1, y1, x2, y2){ + var vertices = new Array(); + for ( var vertex in this.dataset.getVertices()) { + if ((this.vertices[vertex].x >= x1)&&(this.vertices[vertex].x <= x2)){ + if ((this.vertices[vertex].y >= y1)&&(this.vertices[vertex].y <= y2)){ + vertices.push(this.vertices[vertex]); + } + } + } + return vertices; +}; + + + + +LayoutDataset.prototype.getLayout = function(type){ + + if (type == "CIRCLE"){ + this.args.type = "CIRCLE"; + this._calculateLayout(); + return; + } + + if (type == "SQUARE"){ + this.args.type = "SQUARE"; + this._calculateLayout(); + return; + } + + if (type == "RANDOM"){ + this.args.type = "RANDOM"; + this._calculateLayout(); + return; + } + + + var dotText = this.dataset.toDOTID(); + var url = "http://bioinfo.cipf.es/utils/ws/rest/network/layout/"+type+".coords"; + var _this = this; + + $.ajax({ + async: true, + type: "POST", + url: url, + dataType: "text", + data: { + dot :dotText + }, + cache: false, + success: function(data){ + var response = JSON.parse(data); + for ( var vertexId in response) { + _this.vertices[vertexId].setCoordinates(response[vertexId].x, response[vertexId].y); + } + } + }); + +// $.ajax({ +// async: true, +// type: "POST", +// url: url, +// dataType: "script", +// data: { +// dot :dotText +// }, +// cache: false, +// success: function(data){ +// var response = JSON.parse(data); +// for ( var vertexId in response) { +// _this.vertices[vertexId].setCoordinates(response[vertexId].x, response[vertexId].y); +// } +// } +// }); + +}; + +function NodeLayout(id, x, y, args){ + this.id = id; + this.x = x; + this.y = y; + this.changed = new Event(this); +}; + +NodeLayout.prototype.getId = function(id){ + return this.id; +}; + +NodeLayout.prototype.setCoordinates = function(x, y){ + this.x = x; + this.y = y; + this.changed.notify(this); +}; + -GraphCanvas.prototype.getEdgeStroke = function(edgeId) { - return this.getFormatter().getEdgeById(edgeId).getDefault().getStroke(); -}; + +function NetworkDataSetFormatter(vertexFormatProperties, defaultEdgeProperties, args){ + DataSet.prototype.constructor.call(this); + + this.args = new Object(); + + this.vertices = new Object(); + this.edges = new Object(); + this.dataset = null; + this.maxClass = 0; + this.minClass = 0; + + /** Coordenates with default Setting */ + this.args.width = 1100; + this.args.height = 500; + + /** Optional parameters */ + this.args.backgroundColor = "#FFFFFF"; + this.args.backgroundImage = null; + this.args.backgroundImageHeight = null; + this.args.backgroundImageWidth = null; + this.args.backgroundImageX = null; + this.args.backgroundImageY = null; + + + this.args.balanceNodes = false; + this.args.nodesMaxSize = 3; + this.args.nodesMinSize = 0.5; + + + /** Zoom **/ + this.args.zoomScale = 1; + this.args.zoomScaleStepFactor = 0.4; + + if (args != null){ + if (args.backgroundImage != null){ + this.args.backgroundImage = args.backgroundImage; + } + + if (args.width != null){ + this.args.width = args.width; + } + + if (args.height != null){ + this.args.height = args.height; + this.args.svgHeight = args.height; + } + + if (args.svgHeight != null){ + this.args.svgHeight = args.svgHeight; + } + + if (args.backgroundColor != null){ + this.args.backgroundColor = args.backgroundColor; + } + + if(args.balanceNodes != null){ + this.args.balanceNodes = args.balanceNodes; + } + + if(args.nodesMaxSize != null){ + this.args.nodesMaxSize = args.nodesMaxSize; + } + if(args.nodesMinSize != null){ + this.args.nodesMinSize = args.nodesMinSize; + } + } + + this.args.defaultEdgeProperties = {"fill":"#000000", "radius":"1", "stroke":"#000000", "size":1, "title":{"fontSize":10, "fontColor":"#000000"}}; + this.args.vertexFormatProperties = { "fill":"#000000", "stroke":"#000000", "stroke-opacity":"#000000"}; + + if (vertexFormatProperties!= null){ + this.args.vertexFormatProperties = vertexFormatProperties; + } + if (defaultEdgeProperties!= null){ + this.args.defaultEdgeProperties = defaultEdgeProperties; + } + + /** Events **/ + this.changed = new Event(this); + this.edgeChanged = new Event(this); + this.resized = new Event(this); + this.backgroundImageChanged= new Event(this); + this.backgroundColorChanged= new Event(this); +}; + +NetworkDataSetFormatter.prototype.loadFromJSON = function(dataset, json){ + this.args = new Object(); + this.vertices = new Object(); + this.args = json; + this._setDataset(dataset); + var _this = this; + + for ( var vertex in json.vertices) { + this.addVertex(vertex, json.vertices[vertex]); + } + + for ( var edgeId in json.edges) { + this.addEdge(edgeId, json.edges[edgeId]); + } +}; + + +NetworkDataSetFormatter.prototype.toJSON = function(){ + + +// this.args.vertices = new Object(); +// this.args.edges = new Object(); +// +// for ( var vertex in this.vertices) { +// this.args.vertices[vertex] = this.getVertexById(vertex).toJSON(); +// } +// for ( var edge in this.edges) { +// this.args.edges[edge] = this.getEdgeById(edge).toJSON(); +// } +// +// return (this.args); + + + var serialize = new Object(); + serialize = JSON.parse(JSON.stringify(this.args)); + serialize.vertices = new Object(); + serialize.edges = new Object(); + + for ( var vertex in this.vertices) { + serialize.vertices[vertex] = this.getVertexById(vertex).toJSON(); + } + for ( var edge in this.edges) { + serialize.edges[edge] = this.getEdgeById(edge).toJSON(); + } + + return (serialize); +}; + + + +NetworkDataSetFormatter.prototype._getNodeSize = function(nodeId){ + if (this.isVerticesBalanced()){ + var total = this.maxClass - this.minClass; + if (total == 0){ total = 1;} + var nodeConnection = this.dataset.getVertexById(nodeId).getEdges().length; + return ((nodeConnection*this.args.nodesMaxSize)/total) + this.args.nodesMinSize; + } + return this.getVertexById(nodeId).getDefault().getSize(); +}; + +NetworkDataSetFormatter.prototype._recalculateSize = function(){ + if (this.isVerticesBalanced()){ + this.maxClass = this.dataset.getMaxClass(); + this.minClass = this.dataset.getMinClass(); + for ( var vertexIdAll in this.vertices) { + var size = this._getNodeSize(vertexIdAll); + this.vertices[vertexIdAll].getDefault().setSize(size); + this.vertices[vertexIdAll].getSelected().setSize(size); + this.vertices[vertexIdAll].getOver().setSize(size); + } + } +}; + + +NetworkDataSetFormatter.prototype.addVertex = function(vertexId, json){ + + + if (json == null){ + this.vertices[vertexId] = new CircleVertexGraphFormatter(vertexId, this.args.vertexFormatProperties.defaultFormat, this.args.vertexFormatProperties.selected, this.args.vertexFormatProperties.over, this.args.vertexFormatProperties.dragging); + } + else{ + + /** Cargo los attributos desde el json **/ + if (json.type == null){ + this.vertices[vertexId] = new CircleVertexGraphFormatter(vertexId, this.args.vertexFormatProperties.defaultFormat, this.args.vertexFormatProperties.selected, this.args.vertexFormatProperties.over, this.args.vertexFormatProperties.dragging); + } + + if ((json.type == "SquareVertexGraphFormatter")||(json.type == "SquareVertexNetworkFormatter")){ + this.vertices[vertexId] = new SquareVertexGraphFormatter(vertexId); + this.vertices[vertexId].loadFromJSON(json); + } + + if ((json.type == "CircleVertexGraphFormatter")||(json.type == "CircleVertexNetworkFormatter")){ + this.vertices[vertexId] = new CircleVertexGraphFormatter(vertexId); + this.vertices[vertexId].loadFromJSON(json); + } + + if ((json.type == "EllipseVertexGraphFormatter")||(json.type == "EllipseVertexNetworkFormatter")){ + this.vertices[vertexId] = new EllipseVertexGraphFormatter(vertexId); + this.vertices[vertexId].loadFromJSON(json); + } + + if ((json.type == "RectangleVertexGraphFormatter")||(json.type == "RectangleVertexNetworkFormatter")){ + this.vertices[vertexId] = new RectangleVertexGraphFormatter(vertexId); + this.vertices[vertexId].loadFromJSON(json); + } + + if ((json.type == "RoundedVertexGraphFormatter")||(json.type == "RoundedVertexNetworkFormatter")){ + this.vertices[vertexId] = new RoundedVertexGraphFormatter(vertexId); + this.vertices[vertexId].loadFromJSON(json); + } + + } + + + var _this = this; + this.vertices[vertexId].stateChanged.attach(function (sender, item){ + _this.changed.notify(item); + }); + + var size = this._getNodeSize(vertexId); + this.vertices[vertexId].defaultFormat.args.size = size; + this.vertices[vertexId].selected.args.size = size; + this.vertices[vertexId].over.args.size = size; + +}; + +NetworkDataSetFormatter.prototype.addEdge = function(edgeId, json){ + + /** Es un edge nuevo que le doy los atributos por defecto **/ + if (json == null){ + if (this.dataset.getEdgeById(edgeId).getNodeSource().getId() == this.dataset.getEdgeById(edgeId).getNodeTarget().getId()){ + this.edges[edgeId] = new BezierEdgeGraphFormatter(edgeId, this.args.defaultEdgeProperties.defaultFormat, this.args.defaultEdgeProperties.selected, this.args.defaultEdgeProperties.over, this.args.defaultEdgeProperties.dragging); + }else{ + this.edges[edgeId] = new DirectedLineEdgeGraphFormatter(edgeId, this.args.defaultEdgeProperties.defaultFormat, this.args.defaultEdgeProperties.selected, this.args.defaultEdgeProperties.over, this.args.defaultEdgeProperties.dragging); + } + } + else{ + /** Cargo los attributos desde el json **/ + if (json.type == null){ + this.edges[edgeId] = new LineEdgeGraphFormatter(edgeId, this.args.defaultEdgeProperties.defaultFormat, this.args.defaultEdgeProperties.selected, this.args.defaultEdgeProperties.over, this.args.defaultEdgeProperties.dragging); + } + + if ((json.type == "LineEdgeGraphFormatter")||(json.type == "LineEdgeNetworkFormatter")){ + this.edges[edgeId] = new LineEdgeGraphFormatter(edgeId); + this.edges[edgeId].loadFromJSON(json); + } + if ((json.type == "DirectedLineEdgeGraphFormatter")||(json.type == "DirectedLineEdgeNetworkFormatter")){ + this.edges[edgeId] = new DirectedLineEdgeGraphFormatter(edgeId); + this.edges[edgeId].loadFromJSON(json); + } + + if ((json.type == "BezierEdgeGraphFormatter")||(json.type == "BezierEdgeNetworkFormatter")){ + this.edges[edgeId] = new BezierEdgeGraphFormatter(edgeId); + this.edges[edgeId].loadFromJSON(json); + } + + + if ((json.type == "CutDirectedLineEdgeGraphFormatter")||(json.type == "CutDirectedLineEdgeNetworkFormatter")){ + this.edges[edgeId] = new CutDirectedLineEdgeGraphFormatter(edgeId); + this.edges[edgeId].loadFromJSON(json); + } + + if ((json.type == "DotDirectedLineEdgeGraphFormatter")||(json.type == "DotDirectedLineEdgeNetworkFormatter")){ + this.edges[edgeId] = new DotDirectedLineEdgeGraphFormatter(edgeId); + this.edges[edgeId].loadFromJSON(json); + } + if ((json.type == "OdotDirectedLineEdgeGraphFormatter")||(json.type == "OdotDirectedLineEdgeNetworkFormatter")){ + this.edges[edgeId] = new OdotDirectedLineEdgeGraphFormatter(edgeId); + this.edges[edgeId].loadFromJSON(json); + } + + if ((json.type == "OdirectedLineEdgeGraphFormatter")||(json.type == "OdirectedLineEdgeNetworkFormatter")){ + this.edges[edgeId] = new OdirectedLineEdgeGraphFormatter(edgeId); + this.edges[edgeId].loadFromJSON(json); + } + } + + var _this = this; + this.edges[edgeId].stateChanged.attach(function (sender, item){ + _this.edgeChanged.notify(item); + }); + + this._recalculateSize(); +}; + +NetworkDataSetFormatter.prototype._setDataset = function(dataset){ + this.dataset = dataset; + this.maxClass = dataset.getMaxClass(); + this.minClass = dataset.getMinClass(); + this._attachDatasetEvents(); +}; + +NetworkDataSetFormatter.prototype.changeEdgeType = function(edgeId, type){ + if ((type == "LineEdgeGraphFormatter")||(type == "LineEdgeNetworkFormatter")){ + this.edges[edgeId] = new LineEdgeGraphFormatter(edgeId, this.args.defaultEdgeProperties.defaultFormat, this.args.defaultEdgeProperties.selected, this.args.defaultEdgeProperties.over, this.args.defaultEdgeProperties.dragging); + + } + if ((type == "DirectedLineEdgeGraphFormatter")||(type == "DirectedLineEdgeNetworkFormatter")){ + this.edges[edgeId] = new DirectedLineEdgeGraphFormatter(edgeId, this.args.defaultEdgeProperties.defaultFormat, this.args.defaultEdgeProperties.selected, this.args.defaultEdgeProperties.over, this.args.defaultEdgeProperties.dragging); + } + + if ((type == "CutDirectedLineEdgeGraphFormatter")||(type == "CutDirectedLineEdgeNetworkFormatter")){ + this.edges[edgeId] = new CutDirectedLineEdgeGraphFormatter(edgeId, this.args.defaultEdgeProperties.defaultFormat, this.args.defaultEdgeProperties.selected, this.args.defaultEdgeProperties.over, this.args.defaultEdgeProperties.dragging); + } + + if ((type == "DotDirectedLineEdgeGraphFormatter")||(type == "DotDirectedLineEdgeNetworkFormatter")){ + this.edges[edgeId] = new DotDirectedLineEdgeGraphFormatter(edgeId, this.args.defaultEdgeProperties.defaultFormat, this.args.defaultEdgeProperties.selected, this.args.defaultEdgeProperties.over, this.args.defaultEdgeProperties.dragging); + } + + if ((type == "OdotDirectedLineEdgeGraphFormatter")||(type == "OdotDirectedLineEdgeNetworkFormatter")){ + this.edges[edgeId] = new OdotDirectedLineEdgeGraphFormatter(edgeId, this.args.defaultEdgeProperties.defaultFormat, this.args.defaultEdgeProperties.selected, this.args.defaultEdgeProperties.over, this.args.defaultEdgeProperties.dragging); + } + + if ((type == "OdirectedLineEdgeGraphFormatter")||(type == "OdirectedLineEdgeNetworkFormatter")){ + this.edges[edgeId] = new OdirectedLineEdgeGraphFormatter(edgeId, this.args.defaultEdgeProperties.defaultFormat, this.args.defaultEdgeProperties.selected, this.args.defaultEdgeProperties.over, this.args.defaultEdgeProperties.dragging); + } + + + + var _this = this; + this.edges[edgeId].stateChanged.attach(function (sender, item){ + _this.edgeChanged.notify(item); + }); + _this.edgeChanged.notify(this.edges[edgeId]); +}; +/* +classe = "SquareNetworkNodeFormatter"; +} +if (value == "circle"){ + classe = "CircleNetworkNodeFormatter"; + **/ +NetworkDataSetFormatter.prototype.changeNodeType = function(vertexId, type){ + var defaultFormat = JSON.parse(JSON.stringify(this.vertices[vertexId].getDefault())); + var selectedFormat = JSON.parse(JSON.stringify(this.vertices[vertexId].getSelected())); + var defaultFormat = JSON.parse(JSON.stringify(this.vertices[vertexId].getDefault())); + var defaultFormat = JSON.parse(JSON.stringify(this.vertices[vertexId].getDefault())); + + if ((type == "SquareVertexGraphFormatter")||(type == "SquareVertexNetworkFormatter")){ + this.vertices[vertexId] = new SquareVertexGraphFormatter(vertexId,defaultFormat, this.args.vertexFormatProperties.selected, this.args.vertexFormatProperties.over, this.args.vertexFormatProperties.dragging); + } + + if ((type == "CircleVertexGraphFormatter")||(type == "CircleVertexNetworkFormatter")){ + this.vertices[vertexId] = new CircleVertexGraphFormatter(vertexId, defaultFormat, this.args.vertexFormatProperties.selected, this.args.vertexFormatProperties.over, this.args.vertexFormatProperties.dragging); + } + + if ((type == "EllipseVertexGraphFormatter")||(type == "EllipseVertexNetworkFormatter")){ + this.vertices[vertexId] = new EllipseVertexGraphFormatter(vertexId, defaultFormat, this.args.vertexFormatProperties.selected, this.args.vertexFormatProperties.over, this.args.vertexFormatProperties.dragging); + } + + if ((type == "RectangleVertexGraphFormatter")||(type == "RectangleVertexNetworkFormatter")){ + this.vertices[vertexId] = new RectangleVertexGraphFormatter(vertexId, defaultFormat, this.args.vertexFormatProperties.selected, this.args.vertexFormatProperties.over, this.args.vertexFormatProperties.dragging); + } + + if ((type == "RoundedVertexGraphFormatter")||(type == "RoundedVertexNetworkFormatter")){ + this.vertices[vertexId] = new RoundedVertexGraphFormatter(vertexId, defaultFormat, this.args.vertexFormatProperties.selected, this.args.vertexFormatProperties.over, this.args.vertexFormatProperties.dragging); + } + + if ((type == "OctagonVertexGraphFormatter")||(type == "OctagonVertexNetworkhFormatter")){ + this.vertices[vertexId] = new OctagonVertexGraphFormatter(vertexId, defaultFormat, this.args.vertexFormatProperties.selected, this.args.vertexFormatProperties.over, this.args.vertexFormatProperties.dragging); + } + + + var _this = this; + this.vertices[vertexId].stateChanged.attach(function (sender, item){ + _this.changed.notify(item); + }); + _this.changed.notify(this.vertices[vertexId]); +}; + + + +NetworkDataSetFormatter.prototype.dataBind = function(networkDataSet){ + var _this = this; + this._setDataset(networkDataSet); + + for ( var vertex in this.dataset.getVertices()) { + this.addVertex(vertex); + } + + for ( var edge in this.dataset.getEdges()) { + this.addEdge(edge); + } +}; + +NetworkDataSetFormatter.prototype._removeEdge = function(edgeId){ + delete this.edges[edgeId]; +}; + +NetworkDataSetFormatter.prototype._removeVertex = function(vertexId){ + delete this.vertices[vertexId]; +}; + +NetworkDataSetFormatter.prototype._attachDatasetEvents = function(id){ + var _this = this; + this.dataset.vertexDeleted.attach(function (sender, item){ + _this._removeVertex(item.getId()); + }); + + this.dataset.edgeDeleted.attach(function (sender, item){ + _this._removeEdge(item.getId()); + }); + + this.dataset.newVertex.attach(function (sender, item){ + _this.addVertex(item.getId()); + }); + + this.dataset.newEdge.attach(function (sender, item){ + _this.addEdge(item.getId()); + }); +}; + + +NetworkDataSetFormatter.prototype.getVertexById = function(id){ + return this.vertices[id]; +}; + +NetworkDataSetFormatter.prototype.getEdgeById = function(id){ + return this.edges[id]; +}; + +NetworkDataSetFormatter.prototype.makeLabelsBigger = function(){ +for ( var vertexId in this.vertices) { + var fontSize = this.vertices[vertexId].getDefault().getFontSize() + 2; + this.vertices[vertexId].getDefault().setFontSize(fontSize); + } +}; + +NetworkDataSetFormatter.prototype.makeLabelsSmaller = function(){ + for ( var vertexId in this.vertices) { + var fontSize = this.vertices[vertexId].getDefault().getFontSize() - 2; + this.vertices[vertexId].getDefault().setFontSize(fontSize); + } +}; + + +NetworkDataSetFormatter.prototype.resize = function(width, height){ + this.args.width = width; + this.args.height = height; + this.resized.notify(); +}; + +/** ZOOM GETTERS **/ +NetworkDataSetFormatter.prototype.getZoomScaleStepFactor = function(){return this.args.zoomScaleStepFactor;}; +NetworkDataSetFormatter.prototype.setZoomScaleStepFactor = function(scaleFactor){this.args.zoomScaleStepFactor = scaleFactor;}; +NetworkDataSetFormatter.prototype.getZoomScale = function(){return this.args.zoomScale;}; +NetworkDataSetFormatter.prototype.setZoomScale = function(scale){this.args.zoomScale = scale;}; + +NetworkDataSetFormatter.prototype.getNodesMaxSize = function(){return this.args.nodesMaxSize;}; +NetworkDataSetFormatter.prototype.getNodesMinSize = function(){return this.args.nodesMinSize;}; + +/** SIZE SETTERS AND GETTERS **/ +NetworkDataSetFormatter.prototype.isVerticesBalanced = function(){return this.args.balanceNodes;}; +NetworkDataSetFormatter.prototype.getWidth = function(){return this.args.width;}; +NetworkDataSetFormatter.prototype.getHeight = function(){return this.args.height;}; + +/** OPTIONAL PARAMETERS GETTERS AND SETTERS **/ +NetworkDataSetFormatter.prototype.getBackgroundImage = function(){return this.args.backgroundImage;}; +NetworkDataSetFormatter.prototype.setBackgroundImage = function(value){this.args.backgroundImage = value; this.backgroundImageChanged.notify(this);}; + + +NetworkDataSetFormatter.prototype.getBackgroundImageWidth = function(){return this.args.backgroundImageWidth;}; +NetworkDataSetFormatter.prototype.setBackgroundImageWidth = function(value){this.args.backgroundImageWidth = value; this.backgroundImageChanged.notify(this);}; + +NetworkDataSetFormatter.prototype.getBackgroundImageHeight = function(){return this.args.backgroundImageHeight;}; +NetworkDataSetFormatter.prototype.setBackgroundImageHeight = function(value){this.args.backgroundImageHeight = value; this.backgroundImageChanged.notify(this);}; + +NetworkDataSetFormatter.prototype.getBackgroundImageX = function(){return this.args.backgroundImageX;}; +NetworkDataSetFormatter.prototype.setBackgroundImageX = function(value){this.args.backgroundImageX = value; this.backgroundImageChanged.notify(this);}; + +NetworkDataSetFormatter.prototype.getBackgroundImageY = function(){return this.args.backgroundImageX;}; +NetworkDataSetFormatter.prototype.setBackgroundImageY = function(value){this.args.backgroundImageY = value; this.backgroundImageChanged.notify(this);}; + + + +NetworkDataSetFormatter.prototype.getBackgroundColor = function(){return this.args.backgroundColor;}; +NetworkDataSetFormatter.prototype.setBackgroundColor = function(value){this.args.backgroundColor = value; this.backgroundColorChanged.notify(this);}; + +NetworkDataSetFormatter.prototype.getWidth = function(){return this.args.width;}; +NetworkDataSetFormatter.prototype.setWidth = function(value){this.args.width = value;}; + +NetworkDataSetFormatter.prototype.getHeight = function(){return this.args.height;}; +NetworkDataSetFormatter.prototype.setHeight = function(value){this.args.height = value;}; + + -GraphCanvas.prototype.setEdgeStrokeOpacity = function(edgeId, value) { - this.getFormatter().getEdgeById(edgeId).getDefault().setStrokeOpacity(value); -}; + +Vertex.prototype.getName = GraphItem.prototype.getName; +Vertex.prototype.setName = GraphItem.prototype.setName; +Vertex.prototype.getId = GraphItem.prototype.getId; + +function Vertex(id, name, args){ + GraphItem.prototype.constructor.call(this, id, name, args); + this.edgesIn= new Array(); + this.edgesOut= new Array(); +}; + +Vertex.prototype.getEdges = function(){ + return this.edgesIn.concat(this.edgesOut); +}; + +Vertex.prototype.getEdgesCount = function(){ + return this.getEdges().length; +}; + +Vertex.prototype.getEdgesIn = function(){ + return this.edgesIn; +}; + +Vertex.prototype.getEdgesOut = function(){ + return this.edgesOut; +}; + +Vertex.prototype.addEdge = function(edge){ + if (edge.getNodeSource().getId() == this.id){ + this.edgesIn.push(edge); + } + else{ + this.edgesOut.push(edge); + } +}; + +Vertex.prototype.removeEdge = function(edge){ + for ( var i = 0; i < this.getEdgesIn().length; i++) { + var edgeIn = this.edgesIn[i]; + if (edgeIn.getId() == edge.getId()){ + this.edgesIn.splice(i, 1); + } + } + for ( var i = 0; i < this.getEdgesOut().length; i++) { + var edgeIn = this.edgesOut[i]; + if (edgeIn.getId() == edge.getId()){ + this.edgesOut.splice(i, 1); + } + } +}; + +Vertex.prototype.remove = function(){ + var edges = this.getEdges(); + for ( var i = 0; i < edges.length; i++) { + var edge = edges[i]; + edge.remove(); + } + this.deleted.notify(this); +}; + + + + -GraphCanvas.prototype.getEdgeStrokeOpacity = function(edgeId) { - return this.getFormatter().getEdgeById(edgeId).getDefault().getStrokeOpacity(); -}; +VertexGraphFormatter.prototype.getDefault = ItemGraphFormatter.prototype.getDefault; +VertexGraphFormatter.prototype.getSelected = ItemGraphFormatter.prototype.getSelected; +VertexGraphFormatter.prototype.getOver = ItemGraphFormatter.prototype.getOver; +VertexGraphFormatter.prototype.getDragging = ItemGraphFormatter.prototype.getDragging; +VertexGraphFormatter.prototype.getId = ItemGraphFormatter.prototype.getId; +VertexGraphFormatter.prototype.toJSON = ItemGraphFormatter.prototype.toJSON; +VertexGraphFormatter.prototype.loadFromJSON = ItemGraphFormatter.prototype.loadFromJSON; +VertexGraphFormatter.prototype._setEvents = ItemGraphFormatter.prototype._setEvents; + + +function VertexGraphFormatter(defaultFormat, selectedFormat, overFormat, draggingFormat){ + ItemGraphFormatter.prototype.constructor.call(this, defaultFormat, selectedFormat, overFormat, draggingFormat); +}; + + +CircleVertexGraphFormatter.prototype.getDefault = VertexGraphFormatter.prototype.getDefault; +CircleVertexGraphFormatter.prototype.getSelected = VertexGraphFormatter.prototype.getSelected; +CircleVertexGraphFormatter.prototype.getOver = VertexGraphFormatter.prototype.getOver; +CircleVertexGraphFormatter.prototype.getDragging = VertexGraphFormatter.prototype.getDragging; +CircleVertexGraphFormatter.prototype.getId = VertexGraphFormatter.prototype.getId; +CircleVertexGraphFormatter.prototype.toJSON = VertexGraphFormatter.prototype.toJSON; +CircleVertexGraphFormatter.prototype.loadFromJSON = VertexGraphFormatter.prototype.loadFromJSON; +CircleVertexGraphFormatter.prototype._setEvents = VertexGraphFormatter.prototype._setEvents; + +function CircleVertexGraphFormatter(id, defaultFormat, selectedFormat, overFormat, draggingFormat){ + VertexGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, draggingFormat); + this.args.type = "CircleVertexGraphFormatter"; + if (defaultFormat != null){ + if (defaultFormat.radius != null){ + this.defaultFormat.args.radius = defaultFormat.radius; + } + } + + if (selectedFormat != null){ + if (selectedFormat.radius != null){ + this.selected.args.radius = selectedFormat.radius; + } + } + + if (overFormat != null){ + if (overFormat.radius != null){ + this.over.args.radius = overFormat.radius; + } + } + + if (draggingFormat != null){ + if (draggingFormat.radius != null){ + this.dragging.args.draggingFormat = draggingFormat.radius; + } + } +}; + + + +SquareVertexGraphFormatter.prototype.getDefault = VertexGraphFormatter.prototype.getDefault; +SquareVertexGraphFormatter.prototype.getSelected = VertexGraphFormatter.prototype.getSelected; +SquareVertexGraphFormatter.prototype.getOver = VertexGraphFormatter.prototype.getOver; +SquareVertexGraphFormatter.prototype.getDragging = VertexGraphFormatter.prototype.getDragging; +SquareVertexGraphFormatter.prototype.getId = VertexGraphFormatter.prototype.getId; +SquareVertexGraphFormatter.prototype.toJSON = VertexGraphFormatter.prototype.toJSON; +SquareVertexGraphFormatter.prototype.loadFromJSON = VertexGraphFormatter.prototype.loadFromJSON; +SquareVertexGraphFormatter.prototype._setEvents = VertexGraphFormatter.prototype._setEvents; + +function SquareVertexGraphFormatter(id, defaultFormat, selectedFormat, overFormat, draggingFormat){ + VertexGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, draggingFormat); + this.args.type = "SquareVertexGraphFormatter"; + if (defaultFormat != null){ + if (defaultFormat.radius != null){ + this.defaultFormat.args.radius = defaultFormat.radius; + } + } + + if (selectedFormat != null){ + if (selectedFormat.radius != null){ + this.selected.args.radius = selectedFormat.radius; + } + } + + if (overFormat != null){ + if (overFormat.radius != null){ + this.over.args.radius = overFormat.radius; + } + } + + if (draggingFormat != null){ + if (draggingFormat.radius != null){ + this.dragging.args.draggingFormat = draggingFormat.radius; + } + } +}; + + +EllipseVertexGraphFormatter.prototype.getDefault = VertexGraphFormatter.prototype.getDefault; +EllipseVertexGraphFormatter.prototype.getSelected = VertexGraphFormatter.prototype.getSelected; +EllipseVertexGraphFormatter.prototype.getOver = VertexGraphFormatter.prototype.getOver; +EllipseVertexGraphFormatter.prototype.getDragging = VertexGraphFormatter.prototype.getDragging; +EllipseVertexGraphFormatter.prototype.getId = VertexGraphFormatter.prototype.getId; +EllipseVertexGraphFormatter.prototype.toJSON = VertexGraphFormatter.prototype.toJSON; +EllipseVertexGraphFormatter.prototype.loadFromJSON = VertexGraphFormatter.prototype.loadFromJSON; +EllipseVertexGraphFormatter.prototype._setEvents = VertexGraphFormatter.prototype._setEvents; + +function EllipseVertexGraphFormatter(id, defaultFormat, selectedFormat, overFormat, draggingFormat){ + VertexGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, draggingFormat); + this.args.type = "EllipseVertexGraphFormatter"; + if (defaultFormat != null){ + if (defaultFormat.radius != null){ + this.defaultFormat.args.radius = defaultFormat.radius; + } + } + + if (selectedFormat != null){ + if (selectedFormat.radius != null){ + this.selected.args.radius = selectedFormat.radius; + } + } + + if (overFormat != null){ + if (overFormat.radius != null){ + this.over.args.radius = overFormat.radius; + } + } + + if (draggingFormat != null){ + if (draggingFormat.radius != null){ + this.dragging.args.draggingFormat = draggingFormat.radius; + } + } +}; + + + +RectangleVertexGraphFormatter.prototype.getDefault = ItemGraphFormatter.prototype.getDefault; +RectangleVertexGraphFormatter.prototype.getSelected = ItemGraphFormatter.prototype.getSelected; +RectangleVertexGraphFormatter.prototype.getOver = ItemGraphFormatter.prototype.getOver; +RectangleVertexGraphFormatter.prototype.getDragging = ItemGraphFormatter.prototype.getDragging; +RectangleVertexGraphFormatter.prototype.getId = ItemGraphFormatter.prototype.getId; +RectangleVertexGraphFormatter.prototype.toJSON = ItemGraphFormatter.prototype.toJSON; +RectangleVertexGraphFormatter.prototype.loadFromJSON = ItemGraphFormatter.prototype.loadFromJSON; +RectangleVertexGraphFormatter.prototype._setEvents = ItemGraphFormatter.prototype._setEvents; + +function RectangleVertexGraphFormatter(id, defaultFormat, selectedFormat, overFormat, draggingFormat){ + VertexGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, draggingFormat); + this.args.type = "RectangleVertexGraphFormatter"; + if (defaultFormat != null){ + if (defaultFormat.radius != null){ + this.defaultFormat.args.radius = defaultFormat.radius; + } + } + + if (selectedFormat != null){ + if (selectedFormat.radius != null){ + this.selected.args.radius = selectedFormat.radius; + } + } + + if (overFormat != null){ + if (overFormat.radius != null){ + this.over.args.radius = overFormat.radius; + } + } + + if (draggingFormat != null){ + if (draggingFormat.radius != null){ + this.dragging.args.draggingFormat = draggingFormat.radius; + } + } +}; + + + +RoundedVertexGraphFormatter.prototype.getDefault = ItemGraphFormatter.prototype.getDefault; +RoundedVertexGraphFormatter.prototype.getSelected = ItemGraphFormatter.prototype.getSelected; +RoundedVertexGraphFormatter.prototype.getOver = ItemGraphFormatter.prototype.getOver; +RoundedVertexGraphFormatter.prototype.getDragging = ItemGraphFormatter.prototype.getDragging; +RoundedVertexGraphFormatter.prototype.getId = ItemGraphFormatter.prototype.getId; +RoundedVertexGraphFormatter.prototype.toJSON = ItemGraphFormatter.prototype.toJSON; +RoundedVertexGraphFormatter.prototype.loadFromJSON = ItemGraphFormatter.prototype.loadFromJSON; +RoundedVertexGraphFormatter.prototype._setEvents = ItemGraphFormatter.prototype._setEvents; + +function RoundedVertexGraphFormatter(id, defaultFormat, selectedFormat, overFormat, draggingFormat){ + VertexGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, draggingFormat); + this.args.type = "RoundedVertexGraphFormatter"; + if (defaultFormat != null){ + if (defaultFormat.radius != null){ + this.defaultFormat.args.radius = defaultFormat.radius; + } + } + + if (selectedFormat != null){ + if (selectedFormat.radius != null){ + this.selected.args.radius = selectedFormat.radius; + } + } + + if (overFormat != null){ + if (overFormat.radius != null){ + this.over.args.radius = overFormat.radius; + } + } + + if (draggingFormat != null){ + if (draggingFormat.radius != null){ + this.dragging.args.draggingFormat = draggingFormat.radius; + } + } +}; + + + +OctagonVertexGraphFormatter.prototype.getDefault = ItemGraphFormatter.prototype.getDefault; +OctagonVertexGraphFormatter.prototype.getSelected = ItemGraphFormatter.prototype.getSelected; +OctagonVertexGraphFormatter.prototype.getOver = ItemGraphFormatter.prototype.getOver; +OctagonVertexGraphFormatter.prototype.getDragging = ItemGraphFormatter.prototype.getDragging; +OctagonVertexGraphFormatter.prototype.getId = ItemGraphFormatter.prototype.getId; +OctagonVertexGraphFormatter.prototype.toJSON = ItemGraphFormatter.prototype.toJSON; +OctagonVertexGraphFormatter.prototype.loadFromJSON = ItemGraphFormatter.prototype.loadFromJSON; +OctagonVertexGraphFormatter.prototype._setEvents = ItemGraphFormatter.prototype._setEvents; + +function OctagonVertexGraphFormatter(id, defaultFormat, selectedFormat, overFormat, draggingFormat){ + VertexGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, draggingFormat); + this.args.type = "OctagonVertexGraphFormatter"; + if (defaultFormat != null){ + if (defaultFormat.radius != null){ + this.defaultFormat.args.radius = defaultFormat.radius; + } + } + + if (selectedFormat != null){ + if (selectedFormat.radius != null){ + this.selected.args.radius = selectedFormat.radius; + } + } + + if (overFormat != null){ + if (overFormat.radius != null){ + this.over.args.radius = overFormat.radius; + } + } + + if (draggingFormat != null){ + if (draggingFormat.radius != null){ + this.dragging.args.draggingFormat = draggingFormat.radius; + } + } +}; + -GraphCanvas.prototype.setEdgeFill = function(edgeId, color) { - this.getFormatter().getEdgeById(edgeId).getDefault().setFill(color); + +var Colors = new function() +{ + this.hashColor = []; + this.getColorByScoreArrayValue = function (arrayScore) + { + var array = new Array(); + + for (var i = 0; i< arrayScore.length; i++) + { + + var color = this.getColorByScoreValue(arrayScore[i]) + array.push( color); + + } + return array; + }; + + this.getHexStringByScoreArrayValue = function (arrayScore) + { + var arrayColor = this.getColorByScoreArrayValue(arrayScore); + var arrayHex = new Array(); + for (var i = 0; i< arrayColor.length; i++) + { + arrayHex.push( arrayColor[i].HexString()); + } + return arrayHex; + }; + + this.getColorByScoreValue = function (score) + { + + var truncate = score.toString().substr(0,4); + if (this.hashColor[truncate]!=null) + { + return this.hashColor[truncate]; + } + + + if(isNaN(score)) { + return Colors.ColorFromRGB(0,0,0); + } + var value; + + var from, to; + if(score < 0.5) { + from = Colors.ColorFromRGB(0,0,255); + to = Colors.ColorFromRGB(255,255,255); + value = (score * 2); + } else { + from = Colors.ColorFromRGB(255,255,255); + to = Colors.ColorFromRGB(255,0,0); + value = (score * 2) - 1; + } + + var x = value; + var y = 1.0 - value; + var color = Colors.ColorFromRGB(y * from.Red() + x * to.Red(), y * from.Green() + x * to.Green(), y * from.Blue() + x * to.Blue()); + + this.hashColor[truncate] = color; + + return color; + }; + + this.ColorFromHSV = function(hue, sat, val) + { + var color = new Color(); + color.SetHSV(hue,sat,val); + return color; + }; + + this.ColorFromRGB = function(r, g, b) + { + var color = new Color(); + color.SetRGB(r,g,b); + return color; + }; + + this.ColorFromHex = function(hexStr) + { + var color = new Color(); + color.SetHexString(hexStr); + return color; + }; + + function Color() { + //Stored as values between 0 and 1 + var red = 0; + var green = 0; + var blue = 0; + + //Stored as values between 0 and 360 + var hue = 0; + + //Strored as values between 0 and 1 + var saturation = 0; + var value = 0; + + this.SetRGB = function(r, g, b) + { + red = r/255.0; + green = g/255.0; + blue = b/255.0; + calculateHSV(); + }; + + this.Red = function() { return Math.round(red*255); }; + + this.Green = function() { return Math.round(green*255); }; + + this.Blue = function() { return Math.round(blue*255); }; + + this.SetHSV = function(h, s, v) + { + hue = h; + saturation = s; + value = v; + calculateRGB(); + }; + + this.Hue = function() + { return hue; }; + + this.Saturation = function() + { return saturation; }; + + this.Value = function() + { return value; }; + + this.SetHexString = function(hexString) + { + if(hexString == null || typeof(hexString) != "string") + { + this.SetRGB(0,0,0); + return; + } + + if (hexString.substr(0, 1) == '#') + hexString = hexString.substr(1); + + if(hexString.length != 6) + { + this.SetRGB(0,0,0); + return; + } + + var r = parseInt(hexString.substr(0, 2), 16); + var g = parseInt(hexString.substr(2, 2), 16); + var b = parseInt(hexString.substr(4, 2), 16); + if (isNaN(r) || isNaN(g) || isNaN(b)) + { + this.SetRGB(0,0,0); + return; + } + + this.SetRGB(r,g,b); + }; + + this.HexString = function() + { + + var rStr = this.Red().toString(16); + if (rStr.length == 1) + rStr = '0' + rStr; + var gStr = this.Green().toString(16); + if (gStr.length == 1) + gStr = '0' + gStr; + var bStr = this.Blue().toString(16); + if (bStr.length == 1) + bStr = '0' + bStr; + return ('#' + rStr + gStr + bStr).toUpperCase(); + }; + + this.Complement = function() + { + var newHue = (hue >= 180) ? hue - 180 : hue + 180; + var newVal = (value * (saturation - 1) + 1); + var newSat = (value*saturation) / newVal; + var newColor = new Color(); + newColor.SetHSV(newHue, newSat, newVal); + return newColor; + } ; + + function calculateHSV() + { + var max = Math.max(Math.max(red, green), blue); + var min = Math.min(Math.min(red, green), blue); + + value = max; + + saturation = 0; + if(max != 0) + saturation = 1 - min/max; + + hue = 0; + if(min == max) + return; + + var delta = (max - min); + if (red == max) + hue = (green - blue) / delta; + else if (green == max) + hue = 2 + ((blue - red) / delta); + else + hue = 4 + ((red - green) / delta); + hue = hue * 60; + if(hue < 0) + hue += 360; + } + + function calculateRGB() + { + red = value; + green = value; + blue = value; + + if(value == 0 || saturation == 0) + return; + + var tHue = (hue / 60); + var i = Math.floor(tHue); + var f = tHue - i; + var p = value * (1 - saturation); + var q = value * (1 - saturation * f); + var t = value * (1 - saturation * (1 - f)); + switch(i) + { + case 0: + red = value; green = t; blue = p; + break; + case 1: + red = q; green = value; blue = p; + break; + case 2: + red = p; green = value; blue = t; + break; + case 3: + red = p; green = q; blue = value; + break; + case 4: + red = t; green = p; blue = value; + break; + default: + red = value; green = p; blue = q; + break; + } + } + } +} +(); +/* + * Clase gestiona la insercción, eliminación de los elementos en el DOM + * + * Vital hacerla SIEMPRE compatible hacia atrás + * + * Last update: 28-10-2010 + * + */ + + +var DOM = {}; + +DOM.createNewElement = function(type, nodeParent, attributes) +{ + + var node = document.createElement(type); + for (var i=0; i0) + { + parent.removeChild(parent.childNodes[0]); + + } +}; + +DOM.select = function(targetID) +{ + return document.getElementById(targetID); +// return $("#"+targetID); }; +var Geometry = +{ + + /** From tow points obtains the angles formed with the cartesian side **/ + getAngleBetweenTwoPoints : function(x1, y1, x2, y2){ + //var m = (y1 - y2)/ (x1 - x2); + return Math.atan2(y2-y1, x2-x1); + }, + + getAdjacentSideOfRectangleRight : function(angle, hypotenuse){ + return Math.abs(Math.cos(angle)*hypotenuse); + }, + + getOppositeSideOfRectangleRight : function(angle, hypotenuse){ + return Math.abs(Math.sin(angle)*hypotenuse); + }, + + toDegree: function(radian){ + return radian*180/Math.PI; + + } + + +}; -GraphCanvas.prototype.getEdgeFill = function(edgeId) { - return this.getFormatter().getEdgeById(edgeId).getDefault().getFill(); -}; +var SVG = +{ + svgns : 'http://www.w3.org/2000/svg', + xlinkns : "http://www.w3.org/1999/xlink", + + createSVGCanvas: function(parentNode, attributes) + { + + attributes.push( ['xmlns', SVG.svgns], ['xmlns:xlink', 'http://www.w3.org/1999/xlink']); + var svg = document.createElementNS("http://www.w3.org/2000/svg", "svg"); + this._setProperties(svg, attributes); + parentNode.appendChild( svg); + return svg; + + }, + + createRectangle : function (x, y, width, height, attributes){ + var rect = document.createElementNS(this.svgns, "rect"); + rect.setAttribute("x",x); + rect.setAttribute("y",y); + rect.setAttribute("width",width); + rect.setAttribute("height",height); + SVG._setProperties(rect, attributes); + return rect; + }, + + drawImage64 : function (x, y, width, height, base64, svgNode, attributes) { + var node = SVG.createImage64(x, y, width, height, base64, attributes); + svgNode.appendChild(node); + return node; + }, + + createImage64 : function (x, y, width, height, base64, attributes) { + var img = document.createElementNS(this.svgns, "image"); + img.setAttribute("x",x); + img.setAttribute("y",y); + img.setAttribute("width",width); + img.setAttribute("height",height); + img.setAttribute("xlink:href",base64); + SVG._setProperties(img, attributes); + return img; + }, + + createLine: function (x1, y1, x2, y2, attributes){ + var line = document.createElementNS(this.svgns,"line"); + line.setAttribute("x1",x1); + line.setAttribute("y1",y1); + line.setAttribute("x2", x2); + line.setAttribute("y2", y2); + SVG._setProperties(line, attributes); + return line; + }, + + createClip: function (id, nodeToClip, attributes){ + var clip = document.createElementNS(this.svgns,"clipPath"); + clip.setAttribute("id",id); + clip.appendChild(nodeToClip); + return clip; + }, + + drawClip : function (id, nodeToClip, svgNode) { + var node = SVG.createClip(id, nodeToClip); + svgNode.appendChild(node); + return node; + }, + + drawRectangle : function (cx, cy, width, height, svgNode, attributes) { + try{ + var node = SVG.createRectangle(cx, cy, width, height, attributes); + svgNode.appendChild(node); + } + catch(e){ + + console.log("-------------------- "); + console.log("Error on drawRectangle " + e); + console.log(attributes); + console.log("-------------------- "); + } + return node; + }, + + createEllipse : function (x, y, rx, ry, attributes){ + var rect = document.createElementNS(this.svgns, "ellipse"); + rect.setAttribute("cx",x); + rect.setAttribute("cy",y); + rect.setAttribute("rx",rx); + rect.setAttribute("ry",ry); + SVG._setProperties(rect, attributes); + return rect; + }, + + drawEllipse : function (cx, cy, rx, ry, svgNode, attributes) { + var node = SVG.createEllipse(cx, cy, rx, ry, attributes); + svgNode.appendChild(node); + return node; + }, + + drawImage : function (x, y, canvasSVG, attributes) { + var image = document.createElementNS(this.svgns, "image"); + image.setAttribute("x",x); + image.setAttribute("y",y); + canvasSVG.appendChild(image); + SVG._setProperties(image, attributes); + }, + + drawLine : function (x1, y1, x2, y2, nodeSVG, attributes) { + try{ + var line = SVG.createLine(x1, y1, x2, y2, attributes); + nodeSVG.appendChild(line); + }catch(e){ + } + return line; + }, + + + drawPath: function (d, nodeSVG, attributes) { + var path = SVG.createPath(d, attributes); + nodeSVG.appendChild(path); + return path; + }, + + createPoligon : function (points, attributes){ + var poligon = document.createElementNS(this.svgns, "polygon"); + poligon.setAttribute("points",points); + SVG._setProperties(poligon, attributes); + return poligon; + }, + + drawPoligon : function (points, canvasSVG, attributes){ + var poligon = SVG.createPoligon(points, attributes); + canvasSVG.appendChild(poligon); + return poligon; + }, + // + + createPath : function (d, attributes){ + var path = document.createElementNS(this.svgns, "path"); + path.setAttribute("d",d); + SVG._setProperties(path, attributes); + return path; + }, + + drawCircle : function (x, y, radio, canvasSVG, attributes) { + + var newText = document.createElementNS(this.svgns,"circle"); + newText.setAttribute("cx",x); + newText.setAttribute("cy",y); + newText.setAttribute("r",radio); + + canvasSVG.appendChild(newText); + attributes["cursor"] = "pointer"; + this._setProperties(newText, attributes); + return newText; + }, + + + _setProperties: function(node, attributes) + { + if (attributes instanceof Array){ + for (var i=0; i< attributes.length; i++) + { + node.setAttribute(attributes[i][0], attributes[i][1]); + } + } + else{ + for ( var key in attributes){ + node.setAttribute(key, attributes[key]); + } + } + }, + +/* drawPath: function(pointsArray, canvasSVG, attributes){ + var path = document.createElementNS(this.svgns,"polyline"); + path.setAttribute ('id', id); + + var d= pointsArray[0].x+ " "+ pointsArray[0].y; + for (var i=1; i< pointsArray.length; i++) + { + d=d+" "+pointsArray[i].x+" "+pointsArray[i].y; + } + path.setAttribute ('points', d); + canvasSVG.appendChild(path); + },*/ + + createText : function (x, y, text, attributes) { + var node = document.createElementNS(this.svgns,"text"); + node.setAttributeNS(null , "x",x); + node.setAttributeNS(null, "y",y); + + var textNode = document.createTextNode(text); + node.appendChild(textNode); + + this._setProperties(node, attributes); + return node; + }, + + drawText : function (x, y, text, canvasSVG, attributes) { + var text = SVG.createText(x, y, text, attributes); + canvasSVG.appendChild(text); + return text; + }, + + + + drawGroup: function(svgNode, attributes) + { + var group = SVG.createGroup(attributes); + svgNode.appendChild(group); + return group; + }, + + createGroup: function(attributes){ + var group = document.createElementNS(this.svgns,"g"); + this._setProperties(group, attributes); + return group; + } + +}; + + + +var CanvasToSVG = { + + convert: function(sourceCanvas, targetSVG, x, y, id, attributes) { + + var img = this._convert(sourceCanvas, targetSVG, x, y, id); + + for (var i=0; i< attributes.length; i++) + { + img.setAttribute(attributes[i][0], attributes[i][1]); + } + }, + + _convert: function(sourceCanvas, targetSVG, x, y, id) { + var svgNS = "http://www.w3.org/2000/svg"; + var xlinkNS = "http://www.w3.org/1999/xlink"; + // get base64 encoded png from Canvas + var image = sourceCanvas.toDataURL(); + + // must be careful with the namespaces + var svgimg = document.createElementNS(svgNS, "image"); + + svgimg.setAttribute('id', id); + + //svgimg.setAttribute('class', class); + //svgimg.setAttribute('xlink:href', image); + svgimg.setAttributeNS(xlinkNS, 'xlink:href', image); + + + + + svgimg.setAttribute('x', x ? x : 0); + svgimg.setAttribute('y', y ? y : 0); + svgimg.setAttribute('width', sourceCanvas.width); + svgimg.setAttribute('height', sourceCanvas.height); + //svgimg.setAttribute('cursor', 'pointer'); + svgimg.imageData = image; + + targetSVG.appendChild(svgimg); + return svgimg; + }, + + importSVG: function(sourceSVG, targetCanvas) { + svg_xml = sourceSVG;//(new XMLSerializer()).serializeToString(sourceSVG); + var ctx = targetCanvas.getContext('2d'); + + var img = new Image(); + img.src = "data:image/svg+xml;base64," + btoa(svg_xml); +// img.onload = function() { + ctx.drawImage(img, 0, 0); +// }; + } + +}; +/* +Graph.prototype.importSVG = function(sourceSVG, targetCanvas) { + sourceSVG = this._svg; + targetCanvas = document.createElementNS('canvas'); + // https://developer.mozilla.org/en/XMLSerializer + svg_xml = (new XMLSerializer()).serializeToString(sourceSVG); + var ctx = targetCanvas.getContext('2d'); + // this is just a JavaScript (HTML) image + var img = new Image(); + // http://en.wikipedia.org/wiki/SVG#Native_support + // https://developer.mozilla.org/en/DOM/window.btoa + img.src = "data:image/svg+xml;base64," + btoa(svg_xml); + img.onload = function() { + // after this, Canvas’ origin-clean is DIRTY + ctx.drawImage(img, 0, 0); + } +}; +*/ -/** API LAYOUT **/ -GraphCanvas.prototype.setCoordinates = function(vertexId, x, y) { - return this.getLayout().getEdgeById(vertexId).setCoordinates(x, y); -}; +/* + +Normalizacion de datos para dibujar colores +Issues: + No sé como debería llamarse esta libreria + No sé si ya existe una funciçon en javascript que lo haga + + +*/ + + +var Normalizer = new function() +{ + this.normalizeArray = function (arrayData) + { + + return this.standardizeArray(this.normal(arrayData)); + +// var result = this._getMaxAndMin(arrayData); +// var min =result[0]; +// var max = result[1]; +// +// +// //los hacemos todos positivos +// for (var i = 0; i< arrayData.length; i++) +// { +// arrayData[i]= Math.abs(min) + parseFloat(arrayData[i]); +// } +// +// var result = this._getMaxAndMin(arrayData); +// var min =result[0]; +// var max = result[1]; +// +// +// var resultArray = new Array(); +// for (var i = 0; i< arrayData.length; i++) +// { +// resultArray.push(arrayData[i]*1/max); +// } +// return resultArray; + }; + + this.normal = function(arrayData){ + var mean = this._getMean(arrayData); + var deviation = this._getStdDeviation(arrayData); + var result = this._getMaxAndMin(arrayData); + var min = result[0]; + var max = result[1]; + + var resultArray = new Array(); + for (var i = 0; i< arrayData.length; i++) { + if (deviation!=0){ + resultArray.push((arrayData[i]-mean)/deviation); + }else{ + resultArray.push(arrayData[i]); + } + } + return resultArray; + }; + + this.standardizeArray = function(arrayData) + { + var result = this._getMaxAndMin(arrayData); + var min = result[0]; + var max = result[1]; + + var offset = ( min <= 0 ) ? Math.abs(min) : (-1 * min); + var resultArray = new Array(); + for (var i = 0; i< arrayData.length; i++) { + if(max + offset!=0){ + resultArray.push((arrayData[i] + offset) / (max + offset)); + }else{ + resultArray.push(arrayData[i]+offset); + } + } + return resultArray; + }; + + + this._getMean = function(arrayData) { + var sum = 0; + for (var i = 0; i< arrayData.length; i++) { + sum = sum + parseFloat(arrayData[i]); + } + return sum/arrayData.length; + }; + + this._getStdDeviation = function(arrayData) { + var mean = this._getMean(arrayData); + var acum = 0.0; + for(var i=0; i max) max = parseFloat(arrayData[i]); + } + + return [min, max]; + }; +}; +function GraphCanvas(componentID, targetNode, args) { + this.args = {}; + /** target */ + this.targetID = targetNode.id; + + /** id manage */ + this.id = componentID; + this.args.idGraph = this.id + "main"; + this.args.idBackgroundNode = this.id + "background"; + + this.args.idEdgesGraph = this.id + "edges"; + this.args.idNodesGraph = this.id + "vertices"; + this.args.idLabelGraph = this.id + "label"; + this.args.idBackground = this.id + "background"; + + /** Objects Graph **/ + this.dataset = null; + this.formatter = null; + this.layout = null; + + /** Drawing **/ + this.circleDefaultRadius = 2; + this.squareDefaultSide = this.circleDefaultRadius * 1.5; + + /** Directed Arrow **/ + this.arrowDefaultSize = this.circleDefaultRadius; + + /** Groups **/ + this.GraphGroup = null; + this.GraphNodeGroup = null; + this.GraphLabelGroup = null; + this.GraphBackground = null; + + /** SETTINGS FLAGS **/ + this.args.draggingCanvasEnabled = false; //Flag to set if the canvas can be dragged + this.args.multipleSelectionEnabled = false; + this.args.interactive = false; + this.args.labeled = false; + this.args.linkEnabled = false; + + /** If numberEdge > maxNumberEdgesMoving then only it will move edges when mouse up **/ + this.args.maxNumberEdgesMoving = 3; + this.args.maxNumberEdgesFiringEvents = 50; + + /** Linking edges **/ + this.args.linking = false; + this.linkStartX = 0; + this.linkStartY = 0; + this.linkSVGNode = null; + this.linkNodeSource = null; + this.linkNodeTarget = null; + + /** Dragging Control **/ + this.draggingElement = null; + this.dragging = false; + this.nMouseOffsetX = 0; + this.nMouseOffsetY = 0; + this.dragStartX = 0; + this.dragStartY = 0; + this.desplazamientoX = 0; + this.desplazamientoY = 0; + + /** Selection Control **/ + this.selecting = false; + this.selectorX = null; + this.selectorY = null; + this.selectorSVGNode = null; + + /** Node status **/ + this.args.isVertexSelected = {}; + this.args.selectedVertices = []; + + /** Edges status **/ + this.args.isEdgeSelected = {}; + //this.args.selectedEdges = []; + + if (args != null) { + if (args.multipleSelectionEnabled != null) { + this.args.multipleSelectionEnabled = args.multipleSelectionEnabled; + this.args.draggingCanvasEnabled = !(this.args.multipleSelectionEnabled); + } + if (args.draggingCanvasEnabled != null) { + this.args.draggingCanvasEnabled = args.draggingCanvasEnabled; + this.args.multipleSelectionEnabled = !(this.args.draggingCanvasEnabled); + } + if (args.interactive != null) { + this.args.interactive = args.interactive; + } + if (args.labeled != null) { + this.args.labeled = args.labeled; + } + + } + + /** Hashmap with the svg node labels **/ + this.svgLabels = {}; + + /** EVENTS **/ + this.onVertexOut = new Event(this); + this.onVertexOver = new Event(this); + this.onVertexSelect = new Event(this); + this.onEdgeSelect = new Event(this); + this.onCanvasClicked = new Event(this); + this.onVertexUp = new Event(this); +} + +GraphCanvas.prototype.showLabels = function(value) { + this.args.labeled = value; + this.removeLabels(); + if (value) { + this.renderLabels(); + } +}; + +GraphCanvas.prototype.getSelectedVertices = function() { + return this.args.selectedVertices; +}; + +GraphCanvas.prototype.getSelectedEdges = function() { + var selected = []; + for ( var selectedEdge in this.args.isEdgeSelected) { + selected.push(selectedEdge); + } + return selected; +}; + +GraphCanvas.prototype.createSVGDom = function(targetID, id, width, height, backgroundColor) { + var container = document.getElementById(targetID); + this._svg = SVG.createSVGCanvas(container, [ + [ "style", "background-color:" + backgroundColor + ";" ], [ "id", id ], [ "dragx", 0 ], [ "dragy", 0 ], + [ "height", this.getFormatter().getHeight() ], [ "width", this.getFormatter().getWidth() ] ]); + return this._svg; +}; + +/** MULTIPLE SELECTION **/ +GraphCanvas.prototype.isMultipleSelectionEnabled = function() { + return this.args.multipleSelectionEnabled; +}; + +GraphCanvas.prototype.setMultipleSelection = function(value) { + this.args.multipleSelectionEnabled = value; + this.args.draggingCanvasEnabled = (!value); +}; + +GraphCanvas.prototype.setSelecting = function(value) { + this.selecting = value; +}; + +/** linking **/ +GraphCanvas.prototype.setLinking = function(value) { + this.args.linkEnabled = value; + this.selecting = !value; + this.dragging = !value; +}; + +/** CANVAS MOVING **/ +GraphCanvas.prototype.setDraggingCanvas = function(value) { + this.args.draggingCanvasEnabled = value; + this.args.multipleSelectionEnabled = !value; +}; + +GraphCanvas.prototype.isDraggingCanvasEnabled = function() { + return this.args.draggingCanvasEnabled; +}; +/** ZOOM **/ +GraphCanvas.prototype.getScale = function() { + return this.getFormatter().getZoomScale(); +}; + +GraphCanvas.prototype.setScale = function(scale) { + var graphNode = document.getElementById(this.args.idGraph); + graphNode.setAttribute("transform", graphNode.getAttribute("transform").replace("scale(" + this.getScale() + ")", "scale(" + scale + ")")); + this.getFormatter().setZoomScale(scale); +}; + +GraphCanvas.prototype.zoomIn = function() { + this.setScale(this.getScale() + this.getFormatter().getZoomScaleStepFactor()); +}; + +GraphCanvas.prototype.zoomOut = function() { + this.setScale(this.getScale() - this.getFormatter().getZoomScaleStepFactor()); + +}; + +/** SVG COORDENATES **/ +GraphCanvas.prototype.getSVGCoordenates = function(evt) { + var p = this._svg.createSVGPoint(); + p.x = evt.clientX; + p.y = evt.clientY; + + var m = this._svg.getScreenCTM(document.documentElement); + p = p.matrixTransform(m.inverse()); + return p; +}; + +/** SVG EVENTS **/ +GraphCanvas.prototype.mouseClick = function(event) { + if (event.button == 0) { + if (!this.args.interactive) { + return; + } + + if (this.isVertex(event.target)) { + this.clickNode(this.getVertexIdFromSVGId(event.target.id)); + } + /** Como el evento mouseClick viene despues del mouse up es aqui donde manejo el tema de deseccionar los elementos que estoy dragging **/ + if (this.dragging) { + this.dragging = false; + } + } +}; + +GraphCanvas.prototype.mouseMove = function(evt) { + if (this.selecting) { + this.clearLabels(); + + var width = (this.getSVGCoordenates(evt).x - this.selectorX); + var height = (this.getSVGCoordenates(evt).y - this.selectorY); + if ((width > 0) && (height > 0)) { + this.displaySelection(this.selectorX, this.selectorY, width, height); + } + if ((width > 0) && (height < 0)) { + this.displaySelection(this.selectorX, this.getSVGCoordenates(evt).y, width, Math.abs(height)); + } + if ((width < 0) && (height < 0)) { + this.displaySelection(this.getSVGCoordenates(evt).x, this.getSVGCoordenates(evt).y, Math.abs(width), Math.abs(height)); + } + if ((width < 0) && (height > 0)) { + this.displaySelection(this.selectorX + width, this.selectorY, Math.abs(width), Math.abs(height)); + } + + var x1 = (parseFloat(this.selectorSVGNode.getAttribute("x")) - DOM.select(this.id).getAttribute("dragx")) / this.getFormatter().getWidth(); + var y1 = (parseFloat(this.selectorSVGNode.getAttribute("y")) - DOM.select(this.id).getAttribute("dragy")) / this.getFormatter().getHeight(); + var x2 = (x1 + parseFloat(this.selectorSVGNode.getAttribute("width") / this.getFormatter().getWidth())); + var y2 = (y1 + parseFloat(this.selectorSVGNode.getAttribute("height") / this.getFormatter().getHeight())); + + this.deselectNodes(this.getLayout()); + var verticesSelected = this.getLayout().getVerticesByArea(x1 / this.getFormatter().getZoomScale(), y1 / this.getFormatter().getZoomScale(), + x2 / this.getFormatter().getZoomScale(), y2 / this.getFormatter().getZoomScale()); + for ( var i = 0; i < verticesSelected.length; i++) { + this.selectNode(verticesSelected[i].getId()); + this.renderLabel(verticesSelected[i].getId()); + } + + } + var p = null; + if (this.args.linking) { + p = this.getSVGCoordenates(evt); + if (this.linkSVGNode != null) { + this.linkSVGNode.setAttribute("x2", p.x - 2); + this.linkSVGNode.setAttribute("y2", p.y - 2); + } + } + + if (this.dragging) { + p = this.getSVGCoordenates(evt); + p.x -= this.nMouseOffsetX; + p.y -= this.nMouseOffsetY; + this.desplazamientoX = (this.getSVGCoordenates(evt).x - this.dragStartX);// + parseFloat(DOM.select(this.id).getAttribute("dragx")); + this.desplazamientoY = (this.getSVGCoordenates(evt).y - this.dragStartY);// + parseFloat(DOM.select(this.id).getAttribute("dragy")); + + if (this.draggingElement != null) { + /** Click sobre el recct del banground que provoca que mueva todo el canvas **/ + if (this.isNodeCanvas(this.draggingElement)) { + + p = this.getSVGCoordenates(evt); + p.x = this.desplazamientoX; + p.y = this.desplazamientoY; + + this.draggingElement.setAttribute("dragx", p.x); + this.draggingElement.setAttribute("dragy", p.y); + this.draggingElement = document.getElementById(this.args.idGraph); + this.draggingElement.setAttribute("transform", "translate(" + p.x + "," + p.y + "), scale(" + this.getScale() + ")"); + + DOM.select(this.id).setAttribute("dragx", p.x); + DOM.select(this.id).setAttribute("dragy", p.y); + + if (this.NodeSVGbackgroundImage != null) { + this.NodeSVGbackgroundImage.setAttribute("dragx", p.x); + this.NodeSVGbackgroundImage.setAttribute("dragy", p.y); + } + } else { + if (this.isVertex(this.draggingElement)) { + this.selectNode(this.getVertexIdFromSVGId(this.draggingElement.id)); + this.desplazamientoX = this.desplazamientoX / this.getFormatter().getZoomScale(); + this.desplazamientoY = this.desplazamientoY / this.getFormatter().getZoomScale(); + this.moveSelectedNodes(this.desplazamientoX, this.desplazamientoY); + + this.dragStartX = this.getSVGCoordenates(evt).x; + this.dragStartY = this.getSVGCoordenates(evt).y; + } else { + if (this.isNodeBackground(this.draggingElement)) { + + this.draggingElement.setAttribute("dragx", p.x); + this.draggingElement.setAttribute("dragy", p.y); + this.draggingElement = document.getElementById(this.args.idGraph); + this.draggingElement.setAttribute("transform", "translate(" + p.x + "," + p.y + "), scale(" + this.getScale() + ")"); + } else { + this.draggingElement.setAttribute("transform", "translate(" + p.x + "," + p.y + ")"); + } + } + } + } + } +}; + +GraphCanvas.prototype.moveSelectedNodes = function(offsetX, offsetY) { + for ( var i = 0; i < this.getSelectedVertices().length; i++) { + + var nodeId = this.getSelectedVertices()[i]; + var svgNodeId = this.getSVGNodeId(nodeId); + + var x = parseFloat(DOM.select(svgNodeId).getAttribute("dragx")) + parseFloat(offsetX);// - parseFloat(DOM.select(this.id).getAttribute("dragx")); + var y = parseFloat(DOM.select(svgNodeId).getAttribute("dragy")) + parseFloat(offsetY);// + parseFloat(DOM.select(this.id).getAttribute("dragy")); + + this._movingNode(DOM.select(svgNodeId), x, y); + } +}; + +GraphCanvas.prototype.mouseDown = function(evt) { + if (event.button == 0) { + + /** if !no interactive mouse events do anything **/ + if (!this.args.interactive) { + return; + } + + var p = this.getSVGCoordenates(evt); + + /** When click on canvas or background deselect all **/ + if (this.isNodeCanvas(evt.target) || this.isNodeBackground(evt.target)) { + this.deselectNodes(); + this.deselectEdges(); + this.onCanvasClicked.notify(); + } + + /** if I am linking vertices **/ + if (this.args.linkEnabled) { + + if (!this.args.linking) { + this.args.linking = true; + if (this.isVertex(evt.target)) { + this.linkStartX = p.x; + this.linkStartY = p.y; + this.linkSVGNode = SVG.drawLine(p.x, p.y, p.x, p.y, this._svg, { + "stroke" : "#FF0000" + }); + this.linkNodeSource = this.getVertexIdFromSVGId(evt.target.id); + } + } else { + this.linkNodeTarget = this.getVertexIdFromSVGId(evt.target.id); + this.args.linking = false; + this.args.linkEnabled = false; + if (this.isVertex(evt.target)) { + this.getDataset().addEdge(this.linkNodeSource + "_" + this.linkNodeTarget, this.linkNodeSource, this.linkNodeTarget, {}); + } + this.linkSVGNode.parentNode.removeChild(this.linkSVGNode); + } + return; + } + + /** Id is a vertex or the canvas **/ + if (this.isVertex(evt.target) || this.isNodeCanvas(evt.target) || this.isNodeBackground(evt.target)) { + this._startDragging(evt); + } + /** if i is edge **/ + if (this.isEdge(evt.target)) { + this.selectEdge(this.getEdgeIdFromSVGId(evt.target.getAttribute("id"))); + } + + if (this.args.multipleSelectionEnabled) { + if (!this.dragging) { + this.setSelecting(true); + this.selectorX = p.x; + this.selectorY = p.y; + this.displaySelection(p.x, p.y, 1, 1); + } + } + + } + if (event.button == 1) { + this.setLinking(false); + this.setMultipleSelection(false); + this.selecting = false; + + /** Id is a vertex or the canvas **/ + if (this.isVertex(evt.target) || this.isNodeCanvas(evt.target) || this.isNodeBackground(evt.target)) { + this._startDragging(evt); + } + } +}; + +GraphCanvas.prototype.mouseUp = function(event) { + if (!this.args.interactive) { + return; + } + + if (this.dragging) { + this._stopDragging(event); + if (this.isVertex(event.target)) { + var vertexId = this.getVertexIdFromSVGId(event.target.id); + if (this.getDataset().getVertexById(vertexId).getEdges().length >= this.args.maxNumberEdgesMoving) { + this.moveEdge(vertexId); + } + } + } + + if (this.selecting) { + this.setSelecting(false); + + var x1 = (parseFloat(this.selectorSVGNode.getAttribute("x")) - DOM.select(this.id).getAttribute("dragx")) / this.getFormatter().getWidth(); + var y1 = (parseFloat(this.selectorSVGNode.getAttribute("y")) - DOM.select(this.id).getAttribute("dragy")) / this.getFormatter().getHeight(); + var x2 = (x1 + parseFloat(this.selectorSVGNode.getAttribute("width") / this.formatter.getWidth())); + var y2 = (y1 + parseFloat(this.selectorSVGNode.getAttribute("height") / this.formatter.getHeight())); + + var verticesSelected = this.getLayout().getVerticesByArea(x1 / this.getFormatter().getZoomScale, y1 / this.getFormatter().getZoomScale, + x2 / this.getFormatter().getZoomScale, y2 / this.getFormatter().getZoomScale); + + for ( var i = 0; i < verticesSelected.length; i++) { + this.selectNode(verticesSelected[i].getId()); + } + + if (this.selectorSVGNode != null) { + this._svg.removeChild(this.selectorSVGNode); + } + + if (this.args.labeled) { + this.clearLabels(); + this.renderLabels(); + } + + this.selectorSVGNode = null; + // this.renderLabels(); + } +}; + +/** SELECTION **/ +GraphCanvas.prototype.displaySelection = function(x, y, width, height) { + if (this.selectorSVGNode != null) { + this.selectorSVGNode.setAttribute("x", x); + this.selectorSVGNode.setAttribute("y", y); + this.selectorSVGNode.setAttribute("width", width); + this.selectorSVGNode.setAttribute("height", height); + } else { + this.selectorSVGNode = SVG.drawRectangle(x, y, width, height, this._svg, { + "fill" : "red", + "stroke" : "black", + "opacity" : "0.2", + "stroke-opacity" : "1" + }); + } +}; + +/** DRAGGING **/ +GraphCanvas.prototype._startDragging = function(evt) { + if (!this.isDraggingCanvasEnabled()) { + if (this.isNodeCanvas(evt.target)) { + this.draggingElement = null; + } + } + + if (this.isVertex(evt.target) || (this.isNodeBackground(evt.target) && (this.isDraggingCanvasEnabled()))|| (this.isNodeCanvas(evt.target) && (this.isDraggingCanvasEnabled()))) { + this.clearLabels(); + this.draggingElement = evt.target; + this.dragging = true; + var p = this.getSVGCoordenates(evt); + + this.nMouseOffsetX = p.x - parseInt(evt.target.getAttribute("dragx")); + this.nMouseOffsetY = p.y - parseInt(evt.target.getAttribute("dragy")); + + if (this.isVertex(evt.target)) { + this.dragStartX = parseInt(this.draggingElement.getAttribute("dragx")) * this.getFormatter().getZoomScale() + + parseFloat(DOM.select(this.id).getAttribute("dragx")); + this.dragStartY = parseInt(this.draggingElement.getAttribute("dragy")) * this.getFormatter().getZoomScale() + + parseFloat(DOM.select(this.id).getAttribute("dragy")); + } else { + this.dragStartX = p.x - parseInt(this.draggingElement.getAttribute("dragx"));// + parseFloat(DOM.select(this.id).getAttribute("dragx")); + this.dragStartY = p.y - parseInt(this.draggingElement.getAttribute("dragy"));// + parseFloat(DOM.select(this.id).getAttribute("dragy")); + } + } +}; + +GraphCanvas.prototype._stopDragging = function(event) { + this.nMouseOffsetX = 0; + this.nMouseOffsetX = 0; + /** despues del evento up viene el evento click entonces acabo el dragging en el mouseclick **/ + this.dragging = false; + this.draggingElement = null; + this.renderLabels(); + + this.setLinking(false); + this.setMultipleSelection(true); + this.selecting = false; + +}; + +/** Move the edges of the vertex with the vertexId indicado **/ +GraphCanvas.prototype.moveEdge = function(vertexId) { + var x = this.getLayout().getNodeById(vertexId).x * this.getFormatter().getWidth(); + var y = this.getLayout().getNodeById(vertexId).y * this.getFormatter().getHeight(); + + /** Moving edges out **/ + for ( var i = 0; i < this.getDataset().getVertexById(vertexId).getEdgesOut().length; i++) { + var edgeId = this.getDataset().getVertexById(vertexId).getEdgesOut()[i].getId(); + var svgEdgeId = this.getSVGEdgeId(edgeId); + var edgeFormatter = this.getFormatter().getEdgeById(edgeId); + if (edgeFormatter instanceof LineEdgeGraphFormatter) { + DOM.select(svgEdgeId + "_shadow").setAttribute("x2", x); + DOM.select(svgEdgeId + "_shadow").setAttribute("y2", y); + DOM.select(svgEdgeId).setAttribute("x2", x); + DOM.select(svgEdgeId).setAttribute("y2", y); + } + + if ((edgeFormatter instanceof DirectedLineEdgeGraphFormatter) || (edgeFormatter instanceof OdirectedLineEdgeGraphFormatter) + || (edgeFormatter instanceof OdotDirectedLineEdgeGraphFormatter) || (edgeFormatter instanceof DotDirectedLineEdgeGraphFormatter) + || (edgeFormatter instanceof CutDirectedLineEdgeGraphFormatter)) { + this.removeEdge(edgeId); + this.renderEdge(edgeId); + } + } + + /** Moving edges in **/ + for ( var i = 0; i < this.getDataset().getVertexById(vertexId).getEdgesIn().length; i++) { + var edgeId = this.getDataset().getVertexById(vertexId).getEdgesIn()[i].getId(); + var svgEdgeId = this.getSVGEdgeId(edgeId); + var edgeFormatter = this.getFormatter().getEdgeById(edgeId); + if (edgeFormatter instanceof LineEdgeGraphFormatter) { + DOM.select(svgEdgeId).setAttribute("x1", x); + DOM.select(svgEdgeId).setAttribute("y1", y); + DOM.select(svgEdgeId + "_shadow").setAttribute("x1", x); + DOM.select(svgEdgeId + "_shadow").setAttribute("y1", y); + } + + if ((edgeFormatter instanceof DirectedLineEdgeGraphFormatter) || (edgeFormatter instanceof OdirectedLineEdgeGraphFormatter) + || (edgeFormatter instanceof OdotDirectedLineEdgeGraphFormatter) || (edgeFormatter instanceof DotDirectedLineEdgeGraphFormatter) + || (edgeFormatter instanceof CutDirectedLineEdgeGraphFormatter)) { + this.removeEdge(edgeId); + this.renderEdge(edgeId); + } + + if (edgeFormatter instanceof BezierEdgeGraphFormatter) { + var radius = this.getFormatter().getVertexById(vertexId).getDefault().getSize() * this.getFormatter().getNodesMaxSize(); + var d = this.calculateCoordenatesBezier(radius, x, y); + DOM.select(svgEdgeId).setAttribute("d", d); + } + } +}; + +GraphCanvas.prototype.moveNode = function(vertexId) { + var x = this.getLayout().getNodeById(vertexId).x * this.getFormatter().getWidth(); + var y = this.getLayout().getNodeById(vertexId).y * this.getFormatter().getHeight(); + var svgNodeElement = DOM.select(this.getSVGNodeId(vertexId)); + + svgNodeElement.setAttribute("dragx", x); + svgNodeElement.setAttribute("dragy", y); + svgNodeElement.setAttribute("transform", "translate(" + x + "," + y + ")"); + + if (this.getDataset().getVertexById(vertexId).getEdges().length < this.args.maxNumberEdgesMoving) { + this.moveEdge(vertexId); + } +}; + +GraphCanvas.prototype._movingNode = function(svgNodeElement, x, y) { + var vertexId = this.getVertexIdFromSVGId(svgNodeElement.getAttribute("id")); + this.getLayout().getNodeById(vertexId).setCoordinates(x / this.getFormatter().getWidth(), y / this.getFormatter().getHeight()); + this.desplazamientoX = 0; + this.desplazamientoY = 0; + this.removeLabel(vertexId); + this.renderLabel(vertexId); +}; + +/** INIT **/ +GraphCanvas.prototype.init = function() { + + this._svg = this.createSVGDom(this.targetID, this.id, this.getFormatter().getWidth(), this.getFormatter().getHeight(), this.getFormatter() + .getBackgroundColor()); + this.GraphGroup = SVG.drawGroup(this._svg, [ [ "id", this.args.idGraph ], [ "transform", "translate(0,0), scale(1)" ] ]); + this.GraphBackground = SVG.drawGroup(this.GraphGroup, [ [ "id", this.args.idBackground ] ]); + this.GraphEdgeGroup = SVG.drawGroup(this.GraphGroup, [ [ "id", this.args.idEdgesGraph ] ]); + this.GraphNodeGroup = SVG.drawGroup(this.GraphGroup, [ [ "id", this.args.idNodesGraph ] ]); + this.GraphLabelGroup = SVG.drawGroup(this.GraphGroup, [ [ "id", this.args.idLabelGraph ] ]); + + if ((this.getFormatter().getBackgroundImage() != null) && (this.getFormatter().getBackgroundImage() != "")) { + this.setBackgroundImage(this.getFormatter().getBackgroundImage()); + } + /** SVG Events listener */ + var _this = this; + this._svg.addEventListener("click", function(event) { + _this.mouseClick(event); + }, false); + this._svg.addEventListener("mousemove", function(event) { + _this.mouseMove(event, _this); + }, false); + this._svg.addEventListener("mousedown", function(event) { + _this.mouseDown(event, _this); + }, false); + this._svg.addEventListener("mouseup", function(event) { + _this.mouseUp(event, _this); + }, false); +}; + +/* + GraphCanvas.prototype.backgroungToSVG = function(){ + var _this = this; + var canvas = document.createElement('canvas'); + canvas.setAttribute("id", "canvas"); + canvas.width = this.formatter.getWidth(); + canvas.height = this.formatter.getHeight(); + + this._svg.parentNode.parentNode.appendChild(canvas); + var ctx = document.getElementById('canvas').getContext('2d'); + var img = new Image(); + + img.src = this.formatter.getBackgroundImage(); + ctx.drawImage(img,0,0 ,_this.formatter.getWidth(), _this.formatter.getHeight()); + + + img.onload = function() { + canvas.parentNode.removeChild(canvas); + } + + this.NodeSVGbackgroundImage.setAttribute("xlink:href", document.getElementById("canvas").toDataURL()); + this.NodeSVGbackgroundImage.removeAttribute("href"); + + // + + };*/ + +GraphCanvas.prototype.setBackgroundImage = function() { + if (this.NodeSVGbackgroundImage != null) { + this.NodeSVGbackgroundImage.parentNode.removeChild(this.NodeSVGbackgroundImage); + } + $('#' + this.targetID).svg(); + $('#' + this.targetID).svg("get"); + + $('#' + this.targetID).svg("get")._svg = document.getElementById(this.id); + + var svg = $('#' + this.targetID).svg("get"); + this.NodeSVGbackgroundImage = svg.image(0, 0, this.getFormatter().getWidth(), this.getFormatter().getHeight(), this.getFormatter() + .getBackgroundImage()); + this.NodeSVGbackgroundImage.setAttribute("id", this.args.idBackgroundNode); + + this.NodeSVGbackgroundImage.setAttribute("x", 0); + this.NodeSVGbackgroundImage.setAttribute("y", 0); + + this.NodeSVGbackgroundImage.setAttribute("dragx", 0); + this.NodeSVGbackgroundImage.setAttribute("dragy", 0); + + if (this.getFormatter().args.backgroundImageHeight != null) { + this.NodeSVGbackgroundImage.setAttribute("height", this.getFormatter().args.backgroundImageHeight); + } + if (this.getFormatter().args.backgroundImageWidth != null) { + this.NodeSVGbackgroundImage.setAttribute("width", this.getFormatter().args.backgroundImageWidth); + } + + if (this.getFormatter().args.backgroundImageX != null) { + this.NodeSVGbackgroundImage.setAttribute("x", this.getFormatter().args.backgroundImageX); + } + if (this.getFormatter().args.backgroundImageY != null) { + this.NodeSVGbackgroundImage.setAttribute("y", this.getFormatter().args.backgroundImageY); + } + + this.GraphBackground.appendChild(this.NodeSVGbackgroundImage); + this.NodeSVGbackgroundImage.removeAttribute("href"); + this.NodeSVGbackgroundImage.setAttribute("xlink:href", this.getFormatter().getBackgroundImage()); +}; + +GraphCanvas.prototype.removeBackgroundImage = function() { + if (this.NodeSVGbackgroundImage != null) { + this.NodeSVGbackgroundImage.parentNode.removeChild(this.NodeSVGbackgroundImage); + } +}; + +GraphCanvas.prototype._setBackgroundColor = function(color) { + var attributes = [ [ "fill", color ] ]; + SVG.drawRectangle(0, 0, this.getFormatter().getWidth(), this.getFormatter().getHeight(), this.GraphBackground, attributes); +}; + +/** Serialize **/ +GraphCanvas.prototype.toJSON = function() { + var json = {}; + json.dataset = {}; + json.formatter = {}; + json.layout = {}; + json.dataset = this.getDataset().toJSON(); + json.formatter = this.getFormatter().toJSON(); + json.layout = this.getLayout().toJSON(); + return json; +}; + +GraphCanvas.prototype.toHTML = function() { + //this.backgroungToSVG(); + var html = this._svg.parentElement.innerHTML; + + var start = html.indexOf("") + 6; + + return html.substr(start, end); +}; + +/** DRAW **/ +GraphCanvas.prototype.draw = function(graphdataset, graphformatter, graphlayout) { + this.setDataset(graphdataset); + this.setFormatter(graphformatter); + this.setLayout(graphlayout); + + var _this = this; + this.getFormatter().changed.attach(function(sender, item) { + _this.removeNode(item.getId()); + _this.renderNode(item.getId()); + if (_this.args.labeled) { + _this.removeLabel(item.getId()); + _this.renderLabel(item.getId()); + } + + }); + //TODO + this.getFormatter().edgeChanged.attach(function(sender, item) { + _this.removeEdge(item.getId()); + _this.renderEdge(item.getId()); + }); + + this.getFormatter().resized.attach(function(sender, item) { + _this.resize(_this.getFormatter().getWidth(), _this.getFormatter().getHeight()); + }); + + this.getFormatter().backgroundImageChanged.attach(function(sender, item) { + _this.setBackgroundImage(_this.getFormatter().getBackgroundImage()); + }); + + this.getFormatter().backgroundColorChanged.attach(function(sender, item) { + _this._setBackgroundColor(_this.getFormatter().getBackgroundColor()); + }); + + this.getLayout().changed.attach(function(sender, item) { + _this.moveNode(item.getId()); + _this.moveEdge(item.getId()); + if (_this.args.labeled) { + _this.removeLabel(item.getId()); + _this.renderLabel(item.getId()); + } + }); + + this.getDataset().newVertex.attach(function(sender, item) { + + _this.renderNode(item.getId()); + if (_this.args.labeled) { + _this.renderLabel(item.getId()); + } + }); + + this.getDataset().newEdge.attach(function(sender, item) { + _this.renderEdge(item.getId()); + }); + + this.getDataset().vertexDeleted.attach(function(sender, item) { + _this.removeNode(item.getId()); + if (_this.args.labeled) { + _this.removeLabel(item.getId()); + } + }); + + this.getDataset().edgeDeleted.attach(function(sender, item) { + _this.removeEdge(item.getId()); + }); + + this.getDataset().vertexNameChanged.attach(function(sender, args) { + if (_this.args.labeled) { + _this.removeLabel(args.item.getId()); + _this.removeLabel(args.item.getId()); + _this.renderLabel(args.item.getId()); + } + }); + this.init(); + this.render(); +}; + +GraphCanvas.prototype.render = function() { + for ( var id in this.getDataset().getVertices()) { + this.renderNode(id); + } + this.renderLabels(); + this.renderEdges(); +}; + +GraphCanvas.prototype.renderLabels = function() { + if (this.args.labeled) { + for ( var id in this.getDataset().getVertices()) { + this.renderLabel(id); + } + } +}; + +GraphCanvas.prototype.removeLabels = function() { + for ( var id in this.getDataset().getVertices()) { + this.removeLabel(id); + } +}; + +/** Utilities method for nodes **/ +GraphCanvas.prototype.isNodeCanvas = function(node) { + return ((node.id == this.args.idGraph) || (node.id == this.id)); +}; + +GraphCanvas.prototype.isNodeBackground = function(node) { + return ((node.id == this.args.idBackgroundNode)); +}; + +GraphCanvas.prototype.isVertex = function(node) { + if (node.getAttribute("id") != null) { + if (node.getAttribute("id").indexOf("_v_") != -1) { + return true; + } + } + return false; +}; + +GraphCanvas.prototype.isLabel = function(node) { + if (node.getAttribute("id") != null) { + if (node.getAttribute("id").indexOf("_l_") != -1) { + return true; + } + } + return false; +}; + +GraphCanvas.prototype.isEdge = function(node) { + if (node.getAttribute("id") != null) { + if (node.getAttribute("id").indexOf("_e_") != -1) { + return true; + } + } + return false; +}; + +/** Resize **/ +GraphCanvas.prototype.resize = function(width, height) { + // this._svg.setAttribute("width", width); + // this._svg.setAttribute("height", height); + if (this.NodeSVGbackgroundImage != null) { + this.NodeSVGbackgroundImage.setAttribute("width", width); + this.NodeSVGbackgroundImage.setAttribute("height", height); + } + + this._svg.setAttribute("width", width); + this._svg.setAttribute("height", height); + + this.clearCanvas(); + this.render(); +}; + +GraphCanvas.prototype.clearCanvas = function() { + DOM.removeChilds(this.GraphEdgeGroup.getAttribute("id")); + DOM.removeChilds(this.GraphNodeGroup.getAttribute("id")); + this.clearLabels(); +}; + +GraphCanvas.prototype.clearLabels = function() { + DOM.removeChilds(this.GraphLabelGroup.getAttribute("id")); +}; + +/** ID'S converter **/ +GraphCanvas.prototype.getSVGNodeId = function(nodeId) { + return this.id + "_v_" + nodeId; +}; + +GraphCanvas.prototype.getSVGEdgeId = function(edgeId) { + return this.id + "_e_" + edgeId; +}; + +GraphCanvas.prototype.getSVGArrowEdgeId = function(edgeId) { + return this.id + "_arrow_" + edgeId; +}; + +GraphCanvas.prototype.getSVGLabelId = function(edgeId) { + return this.id + "_l_" + edgeId; +}; + +GraphCanvas.prototype.blinkVertexById = function(vertexId) { + $("#" + this.getSVGNodeId(vertexId)).fadeIn().fadeOut().fadeIn().fadeOut().fadeIn().fadeOut(); +}; + +GraphCanvas.prototype.getVertexIdFromSVGId = function(svgVertexId) { + return svgVertexId.replace(this.id, "").replace("_v_", ""); +}; + +GraphCanvas.prototype.getEdgeIdFromSVGId = function(svgEdgeId) { + return svgEdgeId.replace(this.id, "").replace("_e_", ""); +}; + +/** VERTEX **/ +GraphCanvas.prototype.getVertexById = function(id) { + return document.getElementById(this.getSVGNodeId(id)); +}; + +GraphCanvas.prototype.renderNodes = function() { + for ( var id in this.getDataset().getVertices()) { + this.renderNode(id); + } +}; + +GraphCanvas.prototype.overNode = function(nodeId) { + if (!this.args.interactive) { + return; + } + /** If selected we don't change the format **/ + if (this.args.isVertexSelected[nodeId] == null) { + var args = this.getFormatter().getVertexById(nodeId).getOver(); + args.args["cursor"] = 'pointer'; + this.changeVertexFormat(nodeId, args); + } +}; + +GraphCanvas.prototype.outNode = function(nodeId) { + if (!this.args.interactive) { + return; + } + + /** If selected we don't change the format **/ + if (this.args.isVertexSelected[nodeId] == null) { + this.changeVertexFormat(nodeId, this.getFormatter().getVertexById(nodeId).getDefault()); + } +}; + +GraphCanvas.prototype.overLabel = function(nodeId) { + this.overNode(nodeId); + // this.svgLabels[nodeId].setAttribute("cursor", "pointer"); +}; + +GraphCanvas.prototype.outLabel = function(nodeId) { + this.outNode(nodeId); + // this.svgLabels[nodeId].setAttribute("cursor", ""); +}; + +GraphCanvas.prototype.clickNode = function(nodeId) { + if (!this.args.interactive) { + return; + } + + /** si el evento se dispara oprque estaba dragging entonces no activo nada **/ + if (this.args.isVertexSelected[nodeId] == null) { + this.selectNode(nodeId); + } else { + this.deselectNode(nodeId); + } +}; + +GraphCanvas.prototype.selectNode = function(nodeId) { + for ( var i = 0; i < this.args.selectedVertices.length; i++) { + var format = this.getFormatter().getVertexById(nodeId).getSelected(); + format.opacity = 0.2; + this.changeVertexFormat(nodeId, this.getFormatter().getVertexById(nodeId).getSelected()); + } + + if (this.args.isVertexSelected[nodeId] == null) { + var format = this.getFormatter().getVertexById(nodeId).getSelected(); + format.opacity = 1; + this.changeVertexFormat(nodeId, this.getFormatter().getVertexById(nodeId).getSelected()); + this.args.selectedVertices.push(nodeId); + this.args.isVertexSelected[nodeId] = this.args.selectedVertices.length - 1; + this.onVertexSelect.notify(nodeId); + } +}; + +GraphCanvas.prototype.selectAllEdges = function() { + this.deselectNodes(); + this.deselectEdges(); + + for ( var edgesId in this.getDataset().edges) { + this.selectEdge(edgesId); + } +}; + +GraphCanvas.prototype.selectAllNodes = function() { + this.deselectNodes(); + this.deselectEdges(); + + for ( var vertexId in this.getDataset().vertices) { + this.selectNode(vertexId); + } +}; + +GraphCanvas.prototype.selectAll = function() { + this.deselectNodes(); + this.deselectEdges(); + + for ( var vertexId in this.getDataset().vertices) { + this.selectNode(vertexId); + } + + for ( var edgesId in this.getDataset().edges) { + this.selectEdge(edgesId); + } +}; + +GraphCanvas.prototype.selectEdge = function(edgeId) { + if (this.args.isEdgeSelected[edgeId] == null) { + this.changeEdgeFormat(edgeId, this.getFormatter().getEdgeById(edgeId).getSelected()); + //this.args.selectedEdges.push(edgeId); + this.args.isEdgeSelected[edgeId] = true; //this.args.selectedEdges.length - 1; + this.onEdgeSelect.notify(edgeId); + } +}; + +GraphCanvas.prototype.selectEdges = function(edges) { + + for ( var i = 0; i < edges.length; i++) { + this.selectEdge(edges[i]); + } +}; + +GraphCanvas.prototype.deselectNode = function(nodeId) { + if (this.args.isVertexSelected[nodeId] != null) { + this.changeVertexFormat(nodeId, this.getFormatter().getVertexById(nodeId).getDefault()); + this.args.selectedVertices.splice(this.args.isVertexSelected[nodeId], 1); + var index = this.args.isVertexSelected[nodeId]; + delete this.args.isVertexSelected[nodeId]; + + for ( var vertex in this.args.isVertexSelected) { + if (this.args.isVertexSelected[vertex] > index) { + this.args.isVertexSelected[vertex] = this.args.isVertexSelected[vertex] - 1; + } + } + } +}; + +GraphCanvas.prototype.deselectNodes = function() { + var selected = JSON.parse(JSON.stringify(this.getSelectedVertices())); + for ( var i = 0; i < selected.length; i++) { + this.deselectNode(selected[i]); + } +}; +GraphCanvas.prototype.selectNodes = function(idNodes) { + + for ( var i = 0; i < idNodes.length; i++) { + this.selectNode(idNodes[i]); + } + + // for ( var vertex in this.args.isVertexSelected) { + // if (this.args.isVertexSelected[vertex] > index){ + // this.args.isVertexSelected[vertex] = this.args.isVertexSelected[vertex] - 1; + // } + // } + +}; + +GraphCanvas.prototype.changeVertexFormat = function(nodeId, format) { + var svgNode = DOM.select(this.getSVGNodeId(nodeId)); + if (svgNode != null) { + var properties = format.toJSON(); + for ( var item in properties) { + svgNode.setAttribute(item, properties[item]); + } + + if (this.getFormatter().getVertexById(nodeId) instanceof CircleVertexGraphFormatter) { + var transform = "translate(" + svgNode.getAttribute("dragx") + "," + svgNode.getAttribute("dragy") + "), scale(" + format.getSize() + ")"; + svgNode.setAttribute("transform", transform); + } + } +}; + +GraphCanvas.prototype.renderLabel = function(nodeId) { + var x = Math.ceil(this.getLayout().getNodeById(nodeId).x * this.getFormatter().getWidth()); + var y = Math.ceil(this.getLayout().getNodeById(nodeId).y * this.getFormatter().getHeight()); + + var svgAttributesNode = JSON.parse(JSON.stringify(this.getFormatter().getVertexById(nodeId).getDefault().toJSON().title)); + svgAttributesNode.id = this.getSVGLabelId(this.getDataset().getVertexById(nodeId).getId()); + svgAttributesNode.dx = (-1) * (this.getDataset().getVertexById(nodeId).getName().length * svgAttributesNode["font-size"]) / 4 - 4; + + svgAttributesNode.dy = parseFloat((this.getFormatter().getVertexById(nodeId).getDefault().getSize())) + + parseFloat(svgAttributesNode["font-size"]) + parseFloat(this.getFormatter().getVertexById(nodeId).getDefault().getStrokeWidth()) - 4; + svgAttributesNode.dragx = Math.ceil(this.getLayout().getNodeById(nodeId).x * this.getFormatter().getWidth()); + + var gragy = parseFloat(this.getFormatter().getVertexById(nodeId).getDefault().getSize()) + + Math.ceil(this.getLayout().getNodeById(nodeId).y * this.getFormatter().getHeight()); + svgAttributesNode.dragy = gragy; + svgAttributesNode.transform = "translate(" + svgAttributesNode.dragx + "," + svgAttributesNode.dragy + ")";//, scale("+this.formatter.getVertexById(nodeId).getDefault().getSize()+")"; + + var nodeSVG = SVG.drawText(0, 0, this.getDataset().getVertexById(nodeId).getName(), this.GraphLabelGroup, svgAttributesNode); + + this.svgLabels[nodeId] = nodeSVG; + + /** Events for the SVG node **/ + var _this = this; + if (nodeSVG != null) { + nodeSVG.addEventListener("mouseover", function() { + _this.overLabel(nodeId); + }, false); + nodeSVG.addEventListener("mouseout", function() { + _this.outLabel(nodeId); + }, false); + } + +}; + +GraphCanvas.prototype.removeLabel = function(labelId) { + if (DOM.select(this.getSVGLabelId(labelId)) != null) { + DOM.select(this.getSVGLabelId(labelId)).parentNode.removeChild(DOM.select(this.getSVGLabelId(labelId))); + } +}; + +GraphCanvas.prototype.renderNode = function(nodeId) { + var svgAttributesNode = JSON.parse(JSON.stringify(this.getFormatter().getVertexById(nodeId).getDefault().toJSON())); + svgAttributesNode.dragx = Math.ceil(this.getLayout().getNodeById(nodeId).x * this.getFormatter().getWidth()); + svgAttributesNode.dragy = Math.ceil(this.getLayout().getNodeById(nodeId).y * this.getFormatter().getHeight()); + svgAttributesNode.transform = "translate(" + svgAttributesNode.dragx + "," + svgAttributesNode.dragy + ")"; + svgAttributesNode.id = this.getSVGNodeId(nodeId); + /*svgAttributesNode["stroke-width"] = 3 ; + svgAttributesNode["stroke-opacity"] = 1 ; + svgAttributesNode["fill-opacity"] = svgAttributesNode["opacity"] ; + svgAttributesNode["opacity"] = 1 ;*/ + this.circleDefaultRadius = this.getFormatter().getVertexById(nodeId).getDefault().getSize(); + var nodeSVG; + + if (this.getFormatter().getVertexById(nodeId) instanceof CircleVertexGraphFormatter) { + nodeSVG = SVG.drawCircle(0, 0, this.circleDefaultRadius, this.GraphNodeGroup, svgAttributesNode); + } + + if (this.getFormatter().getVertexById(nodeId) instanceof SquareVertexGraphFormatter) { + //nodeSVG = SVG.drawRectangle(0 - (this.circleDefaultRadius) ,0 - (this.formatter.getVertexById(nodeId).getDefault().getSize()) , (this.getFormatter().getVertexById(nodeId).getDefault().getSize()*2), (this.getFormatter().getVertexById(nodeId).getDefault().getSize()*2), this.GraphNodeGroup, svgAttributesNode); + nodeSVG = SVG.drawRectangle(0 - (this.circleDefaultRadius), 0 - (this.circleDefaultRadius), (this.circleDefaultRadius * 2), + (this.circleDefaultRadius * 2), this.GraphNodeGroup, svgAttributesNode); + } + + if (this.getFormatter().getVertexById(nodeId) instanceof EllipseVertexGraphFormatter) { + nodeSVG = SVG.drawEllipse(0, 0, this.circleDefaultRadius * 1.5, this.circleDefaultRadius, this.GraphNodeGroup, svgAttributesNode); + } + + if (this.getFormatter().getVertexById(nodeId) instanceof RectangleVertexGraphFormatter) { + //nodeSVG = SVG.drawRectangle(0 - (this.circleDefaultRadius) ,0 - ((this.circleDefaultRadius*2)/2) , (this.circleDefaultRadius*2), (this.circleDefaultRadius), this.GraphNodeGroup, svgAttributesNode); + nodeSVG = SVG.drawRectangle(0 - (this.circleDefaultRadius * 1.5), 0 - (this.circleDefaultRadius), (this.circleDefaultRadius * 2 * 1.5), + (this.circleDefaultRadius * 2), this.GraphNodeGroup, svgAttributesNode); + + } + + if (this.getFormatter().getVertexById(nodeId) instanceof RoundedVertexGraphFormatter) { + svgAttributesNode.ry = 2;// this.formatter.getVertexById(nodeId).getDefault().getSize()/4; + svgAttributesNode.rx = 2;// this.formatter.getVertexById(nodeId).getDefault().getSize()/4; + nodeSVG = SVG.drawRectangle(0 - (this.circleDefaultRadius * 1.5), 0 - (this.circleDefaultRadius), (this.circleDefaultRadius * 2 * 1.5), + (this.circleDefaultRadius * 2), this.GraphNodeGroup, svgAttributesNode); + } + + // + + if (this.getFormatter().getVertexById(nodeId) instanceof OctagonVertexGraphFormatter) { + svgAttributesNode.ry = 2; + svgAttributesNode.rx = 2; + nodeSVG = SVG.drawRectangle(0 - (this.circleDefaultRadius * 1.5), 0 - (this.circleDefaultRadius), (this.circleDefaultRadius * 2 * 1.5), + (this.circleDefaultRadius * 2), this.GraphNodeGroup, svgAttributesNode); + } + + nodeSVG.internalId = nodeId; + // + var _this = this; + + /** Events for the SVG node **/ + if (nodeSVG != null) { + nodeSVG.addEventListener("mouseover", function() { + _this.onVertexOver.notify(nodeId); + _this.overNode(nodeId); + }, false); + nodeSVG.addEventListener("mouseout", function() { + _this.onVertexOut.notify(nodeId); + _this.outNode(nodeId); + }, false); + //nodeSVG.addEventListener("click", function(){_this.clickNode(nodeId);}, false); + // + nodeSVG.addEventListener("mouseup", function() { + _this.onVertexUp.notify(nodeId); + }, false); + } +}; + +GraphCanvas.prototype.removeNode = function(nodeId) { + DOM.select(this.getSVGNodeId(nodeId)).parentNode.removeChild(DOM.select(this.getSVGNodeId(nodeId))); + if (this.args.labeled) { + this.removeLabel(nodeId); + } +}; + +/** REMOVING **/ +GraphCanvas.prototype.removeSelected = function() { + /** El orden importa **/ + this.removeSelectedEdges(); + this.removeSelectedNode(); + +}; + +GraphCanvas.prototype.removeSelectedNode = function() { + var selected = JSON.parse(JSON.stringify(this.getSelectedVertices())); + this.deselectNodes(); + var sorted = selected.sort(function(a, b) { + return a - b + }); + for ( var i = 0; i < sorted.length; i++) { + if (this.getDataset().getVertexById(sorted[i]) != null) { + this.getDataset().getVertexById(sorted[i]).remove(); + } + } +}; + +/** EDGES **/ +GraphCanvas.prototype.removeEdge = function(edgeId) { + if (DOM.select(this.getSVGEdgeId(edgeId)) != null) { + DOM.select(this.getSVGEdgeId(edgeId)).parentNode.removeChild(DOM.select(this.getSVGEdgeId(edgeId))); + } + + if (DOM.select(this.getSVGEdgeId(edgeId) + "_shadow") != null) { + DOM.select(this.getSVGEdgeId(edgeId) + "_shadow").parentNode.removeChild(DOM.select(this.getSVGEdgeId(edgeId) + "_shadow")); + } + + if (DOM.select(this.getSVGArrowEdgeId(edgeId)) != null) { + DOM.select(this.getSVGArrowEdgeId(edgeId)).parentNode.removeChild(DOM.select(this.getSVGArrowEdgeId(edgeId))); + } +}; + +GraphCanvas.prototype.overEdge = function(edgeId) { + if ((!this.args.interactive) || this.dragging || this.selecting) { + return; + } + + /** If selected we don't change the format **/ + if (this.args.isEdgeSelected[edgeId] == null) { + var format = this.getFormatter().getEdgeById(edgeId).getOver(); + format.args["cursor"] = "pointer"; + this.changeEdgeFormat(edgeId, format); + } +}; + +GraphCanvas.prototype.outEdge = function(edgeId) { + if (!this.args.interactive) { + return; + } + + /** If selected we don't change the format **/ + if (this.args.isEdgeSelected[edgeId] == null) { + this.changeEdgeFormat(edgeId, this.getFormatter().getEdgeById(edgeId).getDefault()); + } +}; + +GraphCanvas.prototype.changeEdgeFormat = function(edgeId, format) { + var svgEdge = DOM.select(this.getSVGEdgeId(edgeId) + "_shadow"); + if (svgEdge != null) { + var properties = format.toJSON(); + for ( var item in properties) { + svgEdge.setAttribute(item, properties[item]); + } + } +}; + +GraphCanvas.prototype.deselectEdge = function(edgeID) { + if (this.args.isEdgeSelected[edgeID] != null) { + this.changeEdgeFormat(edgeID, this.getFormatter().getEdgeById(edgeID).getDefault()); + var index = this.args.isEdgeSelected[edgeID]; + delete this.args.isEdgeSelected[edgeID]; + } +}; + +GraphCanvas.prototype.deselectEdges = function() { + var selected = JSON.parse(JSON.stringify(this.getSelectedEdges())); + for ( var i = 0; i < selected.length; i++) { + this.deselectEdge(selected[i]); + } +}; + +GraphCanvas.prototype.removeSelectedEdges = function() { + var selected = JSON.parse(JSON.stringify(this.getSelectedEdges())); + this.deselectEdges(); + for ( var i = 0; i < selected.length; i++) { + if (this.getDataset().getEdgeById(selected[i]) != null) { + this.getDataset().getEdgeById(selected[i]).remove(); + } + } +}; + +GraphCanvas.prototype.renderEdge = function(edgeId) { + var svgAttributesEdge = this.getFormatter().getEdgeById(edgeId).getDefault().toJSON(); + var edge = this.getDataset().getEdgeById(edgeId); + + var svgNodeTarget = this.getVertexById(edge.getNodeTarget().getId()); + var svgNodeSource = this.getVertexById(edge.getNodeSource().getId()); + svgAttributesEdge.id = this.getSVGEdgeId(edge.getId()) + "_shadow"; + + var svgEdge = null; + + if (this.getFormatter().getEdgeById(edgeId) instanceof LineEdgeGraphFormatter) { + var coordenateSourceX = svgNodeSource.getAttribute("dragx"); + var coordenateSourceY = svgNodeSource.getAttribute("dragy"); + var coordenateTargetX = svgNodeTarget.getAttribute("dragx"); + var coordenateTargetY = svgNodeTarget.getAttribute("dragy"); + + SVG.drawLine(coordenateSourceX, coordenateSourceY, coordenateTargetX, coordenateTargetY, this.GraphEdgeGroup, svgAttributesEdge); + var attributesShadow = {}; + attributesShadow.id = this.getSVGEdgeId(edge.getId()); + attributesShadow["stroke-opacity"] = 0; + attributesShadow["stroke-width"] = 4; + attributesShadow["stroke"] = "black"; + svgEdge = SVG.drawLine(svgNodeSource.getAttribute("dragx"), svgNodeSource.getAttribute("dragy"), svgNodeTarget.getAttribute("dragx"), + svgNodeTarget.getAttribute("dragy"), this.GraphEdgeGroup, attributesShadow); + } + + if (this.getFormatter().getEdgeById(edgeId) instanceof BezierEdgeGraphFormatter) { + var nodeId = edge.getNodeTarget().getId(); + var nodeSize = this.formatter.getVertexById(nodeId).getDefault().getSize() * this.getFormatter().getNodesMaxSize(); + svgAttributesEdge.fill = "none"; + svgAttributesEdge.id = this.getSVGEdgeId(edgeId); + var d = this.calculateCoordenatesBezier(nodeSize, svgNodeSource.getAttribute("dragx"), svgNodeSource.getAttribute("dragy")); + svgEdge = SVG.drawPath(d, this.GraphEdgeGroup, svgAttributesEdge); + } + ; + + if ((this.getFormatter().getEdgeById(edgeId) instanceof DirectedLineEdgeGraphFormatter) + || (this.getFormatter().getEdgeById(edgeId) instanceof CutDirectedLineEdgeGraphFormatter) + || (this.getFormatter().getEdgeById(edgeId) instanceof DotDirectedLineEdgeGraphFormatter) + || (this.getFormatter().getEdgeById(edgeId) instanceof OdotDirectedLineEdgeGraphFormatter) + || (this.getFormatter().getEdgeById(edgeId) instanceof OdirectedLineEdgeGraphFormatter)) { + var coordenateSourceX = svgNodeSource.getAttribute("dragx"); + var coordenateSourceY = svgNodeSource.getAttribute("dragy"); + var coordenateTargetX = svgNodeTarget.getAttribute("dragx"); + var coordenateTargetY = svgNodeTarget.getAttribute("dragy"); + + var offset = parseFloat(this.getFormatter().getVertexById(this.getDataset().getEdgeById(edgeId).getNodeTarget().getId()).getDefault() + .getSize() + * this.circleDefaultRadius); + var point = this._calculateEdgePointerPosition(coordenateSourceX, coordenateSourceY, coordenateTargetX, coordenateTargetY, offset); + coordenateTargetX = point.x; + coordenateTargetY = point.y; + SVG.drawLine(coordenateSourceX, coordenateSourceY, coordenateTargetX, coordenateTargetY, this.GraphEdgeGroup, svgAttributesEdge); + + var attributesShadow = {}; + attributesShadow.id = this.getSVGEdgeId(edge.getId()); + attributesShadow["stroke-opacity"] = 0; + attributesShadow["stroke-width"] = 4; + attributesShadow["stroke"] = "black"; + svgEdge = SVG.drawLine(coordenateSourceX, coordenateSourceY, coordenateTargetX, coordenateTargetY, this.GraphEdgeGroup, attributesShadow); + } + + if (this.getFormatter().getEdgeById(edgeId) instanceof DirectedLineEdgeGraphFormatter + || (this.getFormatter().getEdgeById(edgeId) instanceof OdirectedLineEdgeGraphFormatter)) { + var coordenateSourceX = svgNodeSource.getAttribute("dragx"); + var coordenateSourceY = svgNodeSource.getAttribute("dragy"); + var coordenateTargetX = svgNodeTarget.getAttribute("dragx"); + var coordenateTargetY = svgNodeTarget.getAttribute("dragy"); + + var point = this._calculateEdgePointerPosition(coordenateSourceX, coordenateSourceY, coordenateTargetX, coordenateTargetY, offset); + coordenateTargetX = point.x; + coordenateTargetY = point.y; + + var angle = Geometry.toDegree(point.angle) + 90; + this.arrowDefaultSize = this.getFormatter().getEdgeById(edgeId).getArrowSize(); //getDefault().getArrowSize(); + var d = "-" + this.arrowDefaultSize + ",0 0,-" + parseFloat(this.arrowDefaultSize) * 2 + " " + this.arrowDefaultSize + ",0"; + + var attributes; + + if (this.getFormatter().getEdgeById(edgeId) instanceof DirectedLineEdgeGraphFormatter) { + attributes = [ + [ "fill", this.getFormatter().getEdgeById(edgeId).getDefault().getStroke() ], + [ "stroke", this.getFormatter().getEdgeById(edgeId).getDefault().getStroke() ], [ "id", this.getSVGArrowEdgeId(edgeId) ] ]; + } else { + attributes = [ + [ "fill", "#FFFFFF" ], [ "stroke", this.getFormatter().getEdgeById(edgeId).getDefault().getStroke() ], + [ "id", this.getSVGArrowEdgeId(edgeId) ] ]; + } + + var flechaSVGNode = SVG.drawPoligon(d, this.GraphEdgeGroup, attributes);//, ["transform", "rotate("+angle+"), translate(0,0)"]]); + flechaSVGNode.setAttribute("transform", " translate(" + coordenateTargetX + ", " + coordenateTargetY + "), rotate(" + angle + ")"); + } + ; + + if (this.getFormatter().getEdgeById(edgeId) instanceof CutDirectedLineEdgeGraphFormatter) { + var coordenateSourceX = svgNodeSource.getAttribute("dragx"); + var coordenateSourceY = svgNodeSource.getAttribute("dragy"); + var coordenateTargetX = svgNodeTarget.getAttribute("dragx"); + var coordenateTargetY = svgNodeTarget.getAttribute("dragy"); + + var point = this._calculateEdgePointerPosition(coordenateSourceX, coordenateSourceY, coordenateTargetX, coordenateTargetY, offset); + coordenateTargetX = point.x; + coordenateTargetY = point.y; + + var angle = Geometry.toDegree(point.angle) + 90; + + //this.arrowDefaultSize = 2; //getDefault().getArrowSize(); + var d = "-4,0 4,0 4,-2 -4,-2"; + + var flechaSVGNode = SVG.drawPoligon(d, this.GraphEdgeGroup, [ + [ "fill", this.getFormatter().getEdgeById(edgeId).getDefault().getStroke() ], + [ "stroke", this.getFormatter().getEdgeById(edgeId).getDefault().getStroke() ], [ "id", this.getSVGArrowEdgeId(edgeId) ] ]);//, ["transform", "rotate("+angle+"), translate(0,0)"]]); + flechaSVGNode.setAttribute("transform", " translate(" + coordenateTargetX + ", " + coordenateTargetY + "), rotate(" + angle + ")"); + } + ; + + if ((this.getFormatter().getEdgeById(edgeId) instanceof DotDirectedLineEdgeGraphFormatter) + || (this.getFormatter().getEdgeById(edgeId) instanceof OdotDirectedLineEdgeGraphFormatter)) { + var coordenateSourceX = svgNodeSource.getAttribute("dragx"); + var coordenateSourceY = svgNodeSource.getAttribute("dragy"); + var coordenateTargetX = svgNodeTarget.getAttribute("dragx"); + var coordenateTargetY = svgNodeTarget.getAttribute("dragy"); + var point = this._calculateEdgePointerPosition(coordenateSourceX, coordenateSourceY, coordenateTargetX, coordenateTargetY, offset); + coordenateTargetX = point.x; + coordenateTargetY = point.y; + var angle = Geometry.toDegree(point.angle) + 90; + // this.arrowDefaultSize = this.formatter.getEdgeById(edgeId).getArrowSize(); //getDefault().getArrowSize(); + var attributes = []; + if (this.getFormatter().getEdgeById(edgeId) instanceof OdotDirectedLineEdgeGraphFormatter) { + attributes = [ + [ "fill", "#FFFFFF" ], [ "stroke", this.getFormatter().getEdgeById(edgeId).getDefault().getStroke() ], + [ "id", this.getSVGArrowEdgeId(edgeId) ] ]; + } else { + attributes = [ + [ "fill", this.getFormatter().getEdgeById(edgeId).getDefault().getStroke() ], + [ "stroke", this.getFormatter().getEdgeById(edgeId).getDefault().getStroke() ], [ "id", this.getSVGArrowEdgeId(edgeId) ] ]; + } + var flechaSVGNode = SVG.drawCircle(0, 0, 4, this.GraphEdgeGroup, attributes); + flechaSVGNode.setAttribute("transform", " translate(" + coordenateTargetX + ", " + coordenateTargetY + "), rotate(" + angle + ")"); + } + ; + + var _this = this; + /** Events for the SVG edge **/ + if (svgEdge != null) { + if (this.getDataset().getEdgesCount() < this.args.maxNumberEdgesFiringEvents) { + svgEdge.addEventListener("mouseover", function() { + _this.overEdge(edgeId); + }, false); + svgEdge.addEventListener("mouseout", function() { + _this.outEdge(edgeId); + }, false); + } + } +}; + +GraphCanvas.prototype._calculateEdgePointerPosition = function(sourceX, sourceY, targetX, targetY, radius) { + var angle = Geometry.getAngleBetweenTwoPoints(sourceX, sourceY, targetX, targetY); + + /** Suponiendo el node source que este a la derecha **/ + if ((targetX - sourceX) < 0) { + var b = Geometry.getAdjacentSideOfRectangleRight(angle, radius); + targetX = parseFloat(targetX) + parseFloat(b); + arrowX = parseFloat(targetX) + parseFloat(b) + this.arrowDefaultSize / 2; + } else { + var b = Geometry.getAdjacentSideOfRectangleRight(angle, radius); + targetX = parseFloat(targetX) - parseFloat(b); + arrowX = parseFloat(targetX) - parseFloat(b) - this.arrowDefaultSize / 2; + } + + /** Suponiendo el node source que este a la arriba **/ + if ((targetY - sourceY) > 0) { + var a = Geometry.getOppositeSideOfRectangleRight(angle, radius); + targetY = parseFloat(targetY) - parseFloat(a); + arrowY = parseFloat(targetY) - parseFloat(a) - this.arrowDefaultSize / 2; + } else { + var a = Geometry.getOppositeSideOfRectangleRight(angle, radius); + targetY = parseFloat(targetY) + parseFloat(a); + arrowY = parseFloat(targetY) + parseFloat(a) - this.arrowDefaultSize / 2; + + } + + return { + "x" : arrowX, + "y" : arrowY, + "angle" : angle + }; +}; + +GraphCanvas.prototype.calculateCoordenatesBezier = function(nodeSize, x1, y1) { + var x11 = x1 - (nodeSize / 2); + var y11 = y1 - (nodeSize / 2); + + var x12 = parseFloat(x1) + parseFloat(nodeSize / 2); + var y12 = y1 - (nodeSize / 2); + + var curvePointX = (x12 - x11) / 2 + x11; + var curvePointY = y1 - (nodeSize * 2); + var d = "M" + x11 + "," + y11 + " T" + curvePointX + "," + curvePointY + " " + x12 + "," + y12; + return d; + +}; + +GraphCanvas.prototype.renderEdges = function() { + for ( var edge in this.getDataset().getEdges()) { + this.renderEdge(this.getDataset().getEdgeById(edge).getId()); + + } +}; + +GraphCanvas.prototype.getLastSelectedNode = function() { + var node = null; + if (this.getSelectedVertices().length > 0) { + var nodeId = this.getSelectedVertices()[this.getSelectedVertices().length - 1]; + node = this.getDataset().getVertexById(nodeId); + } + return node; +}; +/* + GraphCanvas.prototype.getNodeByNameAndIndex = function(node, index){ + var nodeId = this.getDataset().verticesIndex[node][index]; + var nodeItem = this.getDataset().getVertexById(nodeId); + return nodeItem; + }; + */ + +GraphCanvas.prototype.setDataset = function(dataset) { + this.dataset = dataset; +}; + +GraphCanvas.prototype.setFormatter = function(formatter) { + this.formatter = formatter; +}; + +GraphCanvas.prototype.setLayout = function(layout) { + this.layout = layout; +}; + +/** API **/ +GraphCanvas.prototype.getDataset = function() { + return this.dataset; +}; + +GraphCanvas.prototype.getFormatter = function() { + return this.formatter; +}; + +GraphCanvas.prototype.getLayout = function() { + return this.layout; +}; + +/** API DATASET **/ +GraphCanvas.prototype.addVertex = function(name, args) { + this.getDataset().addNode(name, args); +}; + +GraphCanvas.prototype.removeVertex = function(vertexId) { + this.getDataset().getVertexById(vertexId).remove(); +}; + +GraphCanvas.prototype.addEdge = function(edgeName, nodeSourceId, nodeTargetId, args) { + this.getDataset().addEdge(edgeName, nodeSourceId, nodeTargetId, args); +}; +/* + GraphCanvas.prototype.removeEdge = function(edgeId){ + this.getDataset().getEdgeById(edgeId).remove(); + }; + */ + +/** API FORMATTER **/ +GraphCanvas.prototype.getWidth = function() { + return this.getFormatter().getWidth(); +}; + +GraphCanvas.prototype.getHeight = function() { + return this.getFormatter().getHeight(); +}; + +GraphCanvas.prototype.getBackgroundImage = function() { + return this.getFormatter().getBackgroundImage(); +}; + +//GraphCanvas.prototype.setBackgroundImage = function(value){ +// this.getFormatter().setBackgroundImage(value); +//}; + +GraphCanvas.prototype.getBackgroundColor = function() { + return this.getFormatter().getBackgroundColor(); +}; + +GraphCanvas.prototype.setBackgroundColor = function() { + this.getFormatter().setBackgroundColor(value); +}; + +//GraphCanvas.prototype.setEdgeFill = function(edgeId, value){ +// this.getFormatter().getEdgeById(edgeId).getDefault().setFill(value); +//}; +// +//GraphCanvas.prototype.getEdgeFill = function(edgeId){ +// return this.getFormatter().getEdgeById(edgeId).getDefault().getFill(); +//}; + +/** VERTICES FORMATTER **/ +GraphCanvas.prototype.setVertexSize = function(vertexId, value) { + this.getFormatter().getVertexById(vertexId).getDefault().setSize(value); +}; + +GraphCanvas.prototype.getVertexSize = function(vertexId) { + return this.getFormatter().getVertexById(vertexId).getDefault().getSize(); +}; + +GraphCanvas.prototype.setVertexStroke = function(vertexId, value) { + this.getFormatter().getVertexById(vertexId).getDefault().setStroke(value); +}; + +GraphCanvas.prototype.getVertexStroke = function(vertexId) { + return this.getFormatter().getVertexById(vertexId).getDefault().getStroke(); +}; + +GraphCanvas.prototype.setVertexStrokeOpacity = function(vertexId, value) { + this.getFormatter().getVertexById(vertexId).getDefault().setStrokeOpacity(value); +}; + +GraphCanvas.prototype.getVertexStrokeOpacity = function(vertexId) { + return this.getFormatter().getVertexById(vertexId).getDefault().getStrokeOpacity(); +}; + +GraphCanvas.prototype.setVertexOpacity = function(vertexId, value) { + this.getFormatter().getVertexById(vertexId).getDefault().setOpacity(value); +}; + +GraphCanvas.prototype.getVertexOpacity = function(vertexId) { + return this.getFormatter().getVertexById(vertexId).getDefault().getOpacity(); +}; + +GraphCanvas.prototype.setVertexFill = function(vertexId, color) { + this.getFormatter().getVertexById(vertexId).getDefault().setFill(color); +}; + +GraphCanvas.prototype.getVertexFill = function(vertexId) { + return this.getFormatter().getVertexById(vertexId).getDefault().getFill(); +}; + +/** EDGES FORMATTER **/ +GraphCanvas.prototype.setEdgeSize = function(edgeId, value) { + this.getFormatter().getEdgeById(edgeId).getDefault().setSize(value); +}; + +GraphCanvas.prototype.getEdgeSize = function(edgeId) { + return this.getFormatter().getEdgeById(edgeId).getDefault().getSize(); +}; + +GraphCanvas.prototype.setEdgeStroke = function(edgeId, value) { + this.getFormatter().getEdgeById(edgeId).getDefault().setStroke(value); +}; + +GraphCanvas.prototype.getEdgeStroke = function(edgeId) { + return this.getFormatter().getEdgeById(edgeId).getDefault().getStroke(); +}; + +GraphCanvas.prototype.setEdgeStrokeOpacity = function(edgeId, value) { + this.getFormatter().getEdgeById(edgeId).getDefault().setStrokeOpacity(value); +}; + +GraphCanvas.prototype.getEdgeStrokeOpacity = function(edgeId) { + return this.getFormatter().getEdgeById(edgeId).getDefault().getStrokeOpacity(); +}; + +GraphCanvas.prototype.setEdgeFill = function(edgeId, color) { + this.getFormatter().getEdgeById(edgeId).getDefault().setFill(color); +}; + +GraphCanvas.prototype.getEdgeFill = function(edgeId) { + return this.getFormatter().getEdgeById(edgeId).getDefault().getFill(); +}; + +/** API LAYOUT **/ +GraphCanvas.prototype.setCoordinates = function(vertexId, x, y) { + return this.getLayout().getEdgeById(vertexId).setCoordinates(x, y); +}; @@ -14339,283 +14339,283 @@ MergesHPLCGraph.prototype.getMenu = function () { } }); - return actions; + return actions; +}; + + +MergesHPLCGraph.prototype.input = function () { + return DATADOC.getScatteringHPLCFrameData(); +}; + +MergesHPLCGraph.prototype.test = function (targetId) { + var mainPlotPanel = new MergesHPLCGraph({ + title : 'Scattering', + width : this.plotWidth, + height : 500, + showRangeSelector : false, + xParam : 0, + xlabel : "scattering_I", + plots : { + "scattering_I" : true, + "subtracted_I" : true, + "buffer_I" : true + } + }); + mainPlotPanel.getPanel().render(targetId); + mainPlotPanel.loadData(mainPlotPanel.input()); +}; + + +function NetworkWidget(args) { + this.id = "NetworkViewer_" + Math.random().toString().replace(".", "_"); + + this.label = true; + if (args != null) { + if (args.targetId != null) { + this.targetId = args.targetId; + } + if (args.label != null) { + this.label = args.label; + } + } + + this.onVertexOver = new Event(this); + this.onVertexOut = new Event(this); +} + +NetworkWidget.prototype.draw = function(dataset, formatter, layout) { + + this.graphCanvas = new GraphCanvas(this.id, document.getElementById(this.targetId), { + "labeled" : this.label, + "multipleSelectionEnabled" : false, + "draggingCanvasEnabled" : false + }); + this.graphCanvas.draw(dataset, formatter, layout); + + var _this = this; + this.graphCanvas.onVertexOver.attach(function(sender, nodeId) { + _this.onVertexOver.notify(nodeId); + }); + + this.graphCanvas.onVertexOut.attach(function(sender, nodeId) { + _this.onVertexOut.notify(nodeId); + }); +}; + +/** SELECT VERTICES BY NAME * */ +NetworkWidget.prototype.selectVertexByName = function(vertexName) { + var vertices = this.getDataset().getVertexByName(vertexName); + if (vertices != null) { + for ( var nodeId in vertices) { + if (vertices.hasOwnProperty(nodeId)) { + var vertexId = vertices[nodeId].getId(); + this.selectVertexById(vertexId); + } + } + } +}; + +NetworkWidget.prototype.selectVerticesByName = function(verticesName) { + for ( var i = 0; i < verticesName.length; i++) { + this.selectVertexByName(verticesName[i]); + } +}; + +/** SELECT VERTICES BY ID * */ +NetworkWidget.prototype.selectVertexById = function(vertexId) { + this.graphCanvas.selectNode(vertexId); + this.blinkVertexById(vertexId); +}; + +NetworkWidget.prototype.selectVerticesById = function(verticesId) { + for ( var i = 0; i < verticesId.length; i++) { + this.selectVertexById(verticesId[i]); + } +}; + +/** VECINDARIO * */ +NetworkWidget.prototype.selectNeighbourhood = function() { + this.selectEdgesFromVertices(); + this.selectAdjacent(); +}; + +/** DESELECT * */ +NetworkWidget.prototype.deselectNodes = function() { + this.graphCanvas.deselectNodes(); +}; + +/** SELECT ALL NODES * */ +NetworkWidget.prototype.selectAllNodes = function() { + this.getGraphCanvas().selectAllNodes(); +}; + +/** SELECT EVERYTHING * */ +NetworkWidget.prototype.selectAll = function() { + this.getGraphCanvas().selectAll(); +}; + +/** SELECT ALL EDGES * */ +NetworkWidget.prototype.selectAllEdges = function() { + this.getGraphCanvas().selectAllEdges(); +}; + +/** ZOOM * */ +NetworkWidget.prototype.setScale = function(value) { + this.graphCanvas.setScale(value); +}; + +NetworkWidget.prototype.getScale = function() { + return this.graphCanvas.getScale(value); +}; + +/** SELECT ADJACENT VERTICES FROM SELECTED VERTICES * */ +NetworkWidget.prototype.selectAdjacent = function() { + var selectedVertices = this.getGraphCanvas().getSelectedVertices(); + var edges = []; + for ( var i = 0; i < selectedVertices.length; i++) { + edges = edges.concat(this.getGraphCanvas().getDataset().getVertexById(selectedVertices[i]).getEdges()); + } + var vertices = []; + for ( i = 0; i < edges.length; i++) { + vertices.push(edges[i].getNodeSource().getId()); + vertices.push(edges[i].getNodeTarget().getId()); + } + + this.selectVerticesById(vertices); +}; + +/** SELECT EDGES FROM SELECTED VERTICES * */ +NetworkWidget.prototype.selectEdgesFromVertices = function() { + var selectedVertices = this.getGraphCanvas().getSelectedVertices(); + var edges = []; + for ( var i = 0; i < selectedVertices.length; i++) { + edges = edges.concat(this.getGraphCanvas().getDataset().getVertexById(selectedVertices[i]).getEdges()); + } + var edgesId = []; + for ( i = 0; i < edges.length; i++) { + edgesId.push(edges[i].getId()); + } + this.getGraphCanvas().selectEdges(edgesId); +}; + +/** BLINKING * */ +NetworkWidget.prototype.blinkVertexById = function(vertexId) { + this.graphCanvas.blinkVertexById(vertexId); +}; + +/** COMPONENTE CONEXA DE LOS NODOS SELECCIONADOS * */ +NetworkWidget.prototype.selectConnectedComponent = function() { + var elements = this.getConnectedComponent(); + this.selectVerticesById(elements.nodes); + this.graphCanvas.selectEdges(elements.edges); +}; + +NetworkWidget.prototype.getConnectedComponent = function() { + var nodosVisitados = {}; + var aristasVisitadas = {}; + var arrNodos = []; + var arrAristas = []; + var selectedGraphNodesId = this.getGraphCanvas().getSelectedVertices(); + for ( var i = 0; i < selectedGraphNodesId.length; i++) { + this.visitNode(selectedGraphNodesId[i], nodosVisitados, aristasVisitadas, arrNodos, arrAristas); + } + return { + nodes : arrNodos, + edges : arrAristas + }; +}; + +NetworkWidget.prototype.visitNode = function(nodeId, nodosVisitados, aristasVisitadas, arrNodos, arrAristas) { + nodosVisitados[nodeId] = true; + arrNodos.push(nodeId); + var nodeEdges = this.getDataset().getVertexById(nodeId).getEdges(); + for ( var j = 0; j < nodeEdges.length; j++) { + var edge = nodeEdges[j]; + var edgeId = edge.getId(); + if (aristasVisitadas[edgeId] == null) { + aristasVisitadas[edgeId] = true; + arrAristas.push(edgeId); + var nodeTargetId = edge.getNodeTarget().getId(); + if (nodosVisitados[nodeTargetId] == null) { + this.visitNode(nodeTargetId, nodosVisitados, aristasVisitadas, arrNodos, arrAristas); + } + var nodeSourceId = edge.getNodeSource().getId(); + if (nodosVisitados[nodeSourceId] == null) { + this.visitNode(nodeSourceId, nodosVisitados, aristasVisitadas, arrNodos, arrAristas); + } + } + } +}; + +/** COLLAPSE SELECTED VERTICES * */ +NetworkWidget.prototype.collapse = function() { + var selectedVertices = this.getGraphCanvas().getSelectedVertices(); + var xMin = -Infinity; + var xMax = Infinity; + var yMin = -Infinity; + var yMax = Infinity; + + for ( var i = 0; i < selectedVertices.length; i++) { + var vertex = this.getGraphCanvas().getLayout().getNodeById(selectedVertices[i]); + if (xMin < vertex.x) { + xMin = vertex.x; + } + if (xMax > vertex.x) { + xMax = vertex.x; + } + if (yMin < vertex.y) { + yMin = vertex.y; + } + if (yMax > vertex.y) { + yMax = vertex.y; + } + } + + var centerX = xMin - xMax; + var centerY = yMin - yMax; + var radius = (xMax - xMin) / 4; + + var count = selectedVertices.length; + var verticesCoordinates = []; + + for ( i = 0; i < selectedVertices.length; i++) { + x = centerX + radius * Math.sin(i * 2 * Math.PI / count); + y = centerY + radius * Math.cos(i * 2 * Math.PI / count); + verticesCoordinates.push({ + 'x' : x, + 'y' : y + }); + } + + for ( i = 0; i < selectedVertices.length; i++) { + this.getGraphCanvas().getLayout().getNodeById(selectedVertices[i]).setCoordinates(verticesCoordinates[i].x, verticesCoordinates[i].y); + } }; +/** SETTER FONT SIZE * */ +NetworkWidget.prototype.setVerticesFontSize = function(value) { + for ( var nodeId in this.getDataset().getVertices()) { + if (this.getDataset().getVertices().hasOwnProperty(nodeId)) { + this.getFormatter().getVertexById(nodeId).getDefault().setFontSize(value); + } + } +}; -MergesHPLCGraph.prototype.input = function () { - return DATADOC.getScatteringHPLCFrameData(); +/** GETTERS * */ +NetworkWidget.prototype.getFormatter = function() { + return this.getGraphCanvas().getFormatter(); +}; +NetworkWidget.prototype.getLayout = function() { + return this.getGraphCanvas().getLayout(); }; -MergesHPLCGraph.prototype.test = function (targetId) { - var mainPlotPanel = new MergesHPLCGraph({ - title : 'Scattering', - width : this.plotWidth, - height : 500, - showRangeSelector : false, - xParam : 0, - xlabel : "scattering_I", - plots : { - "scattering_I" : true, - "subtracted_I" : true, - "buffer_I" : true - } - }); - mainPlotPanel.getPanel().render(targetId); - mainPlotPanel.loadData(mainPlotPanel.input()); +NetworkWidget.prototype.getDataset = function() { + return this.getGraphCanvas().getDataset(); }; - -function NetworkWidget(args) { - this.id = "NetworkViewer_" + Math.random().toString().replace(".", "_"); - - this.label = true; - if (args != null) { - if (args.targetId != null) { - this.targetId = args.targetId; - } - if (args.label != null) { - this.label = args.label; - } - } - - this.onVertexOver = new Event(this); - this.onVertexOut = new Event(this); -} - -NetworkWidget.prototype.draw = function(dataset, formatter, layout) { - - this.graphCanvas = new GraphCanvas(this.id, document.getElementById(this.targetId), { - "labeled" : this.label, - "multipleSelectionEnabled" : false, - "draggingCanvasEnabled" : false - }); - this.graphCanvas.draw(dataset, formatter, layout); - - var _this = this; - this.graphCanvas.onVertexOver.attach(function(sender, nodeId) { - _this.onVertexOver.notify(nodeId); - }); - - this.graphCanvas.onVertexOut.attach(function(sender, nodeId) { - _this.onVertexOut.notify(nodeId); - }); -}; - -/** SELECT VERTICES BY NAME * */ -NetworkWidget.prototype.selectVertexByName = function(vertexName) { - var vertices = this.getDataset().getVertexByName(vertexName); - if (vertices != null) { - for ( var nodeId in vertices) { - if (vertices.hasOwnProperty(nodeId)) { - var vertexId = vertices[nodeId].getId(); - this.selectVertexById(vertexId); - } - } - } -}; - -NetworkWidget.prototype.selectVerticesByName = function(verticesName) { - for ( var i = 0; i < verticesName.length; i++) { - this.selectVertexByName(verticesName[i]); - } -}; - -/** SELECT VERTICES BY ID * */ -NetworkWidget.prototype.selectVertexById = function(vertexId) { - this.graphCanvas.selectNode(vertexId); - this.blinkVertexById(vertexId); -}; - -NetworkWidget.prototype.selectVerticesById = function(verticesId) { - for ( var i = 0; i < verticesId.length; i++) { - this.selectVertexById(verticesId[i]); - } -}; - -/** VECINDARIO * */ -NetworkWidget.prototype.selectNeighbourhood = function() { - this.selectEdgesFromVertices(); - this.selectAdjacent(); -}; - -/** DESELECT * */ -NetworkWidget.prototype.deselectNodes = function() { - this.graphCanvas.deselectNodes(); -}; - -/** SELECT ALL NODES * */ -NetworkWidget.prototype.selectAllNodes = function() { - this.getGraphCanvas().selectAllNodes(); -}; - -/** SELECT EVERYTHING * */ -NetworkWidget.prototype.selectAll = function() { - this.getGraphCanvas().selectAll(); -}; - -/** SELECT ALL EDGES * */ -NetworkWidget.prototype.selectAllEdges = function() { - this.getGraphCanvas().selectAllEdges(); -}; - -/** ZOOM * */ -NetworkWidget.prototype.setScale = function(value) { - this.graphCanvas.setScale(value); -}; - -NetworkWidget.prototype.getScale = function() { - return this.graphCanvas.getScale(value); -}; - -/** SELECT ADJACENT VERTICES FROM SELECTED VERTICES * */ -NetworkWidget.prototype.selectAdjacent = function() { - var selectedVertices = this.getGraphCanvas().getSelectedVertices(); - var edges = []; - for ( var i = 0; i < selectedVertices.length; i++) { - edges = edges.concat(this.getGraphCanvas().getDataset().getVertexById(selectedVertices[i]).getEdges()); - } - var vertices = []; - for ( i = 0; i < edges.length; i++) { - vertices.push(edges[i].getNodeSource().getId()); - vertices.push(edges[i].getNodeTarget().getId()); - } - - this.selectVerticesById(vertices); -}; - -/** SELECT EDGES FROM SELECTED VERTICES * */ -NetworkWidget.prototype.selectEdgesFromVertices = function() { - var selectedVertices = this.getGraphCanvas().getSelectedVertices(); - var edges = []; - for ( var i = 0; i < selectedVertices.length; i++) { - edges = edges.concat(this.getGraphCanvas().getDataset().getVertexById(selectedVertices[i]).getEdges()); - } - var edgesId = []; - for ( i = 0; i < edges.length; i++) { - edgesId.push(edges[i].getId()); - } - this.getGraphCanvas().selectEdges(edgesId); -}; - -/** BLINKING * */ -NetworkWidget.prototype.blinkVertexById = function(vertexId) { - this.graphCanvas.blinkVertexById(vertexId); -}; - -/** COMPONENTE CONEXA DE LOS NODOS SELECCIONADOS * */ -NetworkWidget.prototype.selectConnectedComponent = function() { - var elements = this.getConnectedComponent(); - this.selectVerticesById(elements.nodes); - this.graphCanvas.selectEdges(elements.edges); -}; - -NetworkWidget.prototype.getConnectedComponent = function() { - var nodosVisitados = {}; - var aristasVisitadas = {}; - var arrNodos = []; - var arrAristas = []; - var selectedGraphNodesId = this.getGraphCanvas().getSelectedVertices(); - for ( var i = 0; i < selectedGraphNodesId.length; i++) { - this.visitNode(selectedGraphNodesId[i], nodosVisitados, aristasVisitadas, arrNodos, arrAristas); - } - return { - nodes : arrNodos, - edges : arrAristas - }; -}; - -NetworkWidget.prototype.visitNode = function(nodeId, nodosVisitados, aristasVisitadas, arrNodos, arrAristas) { - nodosVisitados[nodeId] = true; - arrNodos.push(nodeId); - var nodeEdges = this.getDataset().getVertexById(nodeId).getEdges(); - for ( var j = 0; j < nodeEdges.length; j++) { - var edge = nodeEdges[j]; - var edgeId = edge.getId(); - if (aristasVisitadas[edgeId] == null) { - aristasVisitadas[edgeId] = true; - arrAristas.push(edgeId); - var nodeTargetId = edge.getNodeTarget().getId(); - if (nodosVisitados[nodeTargetId] == null) { - this.visitNode(nodeTargetId, nodosVisitados, aristasVisitadas, arrNodos, arrAristas); - } - var nodeSourceId = edge.getNodeSource().getId(); - if (nodosVisitados[nodeSourceId] == null) { - this.visitNode(nodeSourceId, nodosVisitados, aristasVisitadas, arrNodos, arrAristas); - } - } - } -}; - -/** COLLAPSE SELECTED VERTICES * */ -NetworkWidget.prototype.collapse = function() { - var selectedVertices = this.getGraphCanvas().getSelectedVertices(); - var xMin = -Infinity; - var xMax = Infinity; - var yMin = -Infinity; - var yMax = Infinity; - - for ( var i = 0; i < selectedVertices.length; i++) { - var vertex = this.getGraphCanvas().getLayout().getNodeById(selectedVertices[i]); - if (xMin < vertex.x) { - xMin = vertex.x; - } - if (xMax > vertex.x) { - xMax = vertex.x; - } - if (yMin < vertex.y) { - yMin = vertex.y; - } - if (yMax > vertex.y) { - yMax = vertex.y; - } - } - - var centerX = xMin - xMax; - var centerY = yMin - yMax; - var radius = (xMax - xMin) / 4; - - var count = selectedVertices.length; - var verticesCoordinates = []; - - for ( i = 0; i < selectedVertices.length; i++) { - x = centerX + radius * Math.sin(i * 2 * Math.PI / count); - y = centerY + radius * Math.cos(i * 2 * Math.PI / count); - verticesCoordinates.push({ - 'x' : x, - 'y' : y - }); - } - - for ( i = 0; i < selectedVertices.length; i++) { - this.getGraphCanvas().getLayout().getNodeById(selectedVertices[i]).setCoordinates(verticesCoordinates[i].x, verticesCoordinates[i].y); - } -}; - -/** SETTER FONT SIZE * */ -NetworkWidget.prototype.setVerticesFontSize = function(value) { - for ( var nodeId in this.getDataset().getVertices()) { - if (this.getDataset().getVertices().hasOwnProperty(nodeId)) { - this.getFormatter().getVertexById(nodeId).getDefault().setFontSize(value); - } - } -}; - -/** GETTERS * */ -NetworkWidget.prototype.getFormatter = function() { - return this.getGraphCanvas().getFormatter(); -}; -NetworkWidget.prototype.getLayout = function() { - return this.getGraphCanvas().getLayout(); -}; - -NetworkWidget.prototype.getDataset = function() { - return this.getGraphCanvas().getDataset(); -}; - -NetworkWidget.prototype.getGraphCanvas = function() { - return this.graphCanvas; -}; +NetworkWidget.prototype.getGraphCanvas = function() { + return this.graphCanvas; +}; RangeWhiskerGraph.prototype.cleanArray = GenericGraph.prototype.cleanArray; RangeWhiskerGraph.prototype.plotAxes = GenericGraph.prototype.plotAxes; @@ -14883,280 +14883,280 @@ RangeWhiskerGraph.prototype.plotWhisters = function(data, properties) { } } } - } - -}; - -RangeWhiskerGraph.prototype.draw = function(targetId, data) { - //this.calculate(data); - this.targetId = targetId; - var properties = (this.getDimensions(data)); - this.svg = SVG.createSVGCanvas(document.getElementById(this.targetId), [ [ "width", this.width ], [ "height", this.height ] ]); - this.plotAxes(properties); - this.plotWhisters(data, properties); -}; - -RangeWhiskerGraph.prototype.input = function() { - return DATADOC.getBoxWhikerData(); -}; - -RangeWhiskerGraph.prototype.test = function(targetId) { - var plot = new RangeWhiskerGraph({ - targetId : targetId, - height : 350, - width : 450, - maxBoxWidth : 25 - }); - plot.refresh(this.input()); -}; - -StdDevDyGraph.prototype.dblclick = DygraphWidget.prototype.dblclick; -StdDevDyGraph.prototype._createHTLMWrapper = DygraphWidget.prototype._createHTLMWrapper; -StdDevDyGraph.prototype.draw = DygraphWidget.prototype.draw; - -function StdDevDyGraph(targetId, args) { - this.scaled = false; - if (args == null) { - args = {}; - } - args.customBars = true; - DygraphWidget.prototype.constructor.call(this, targetId, args); -} - -StdDevDyGraph.prototype.input = function () { - return { - data : [ [ 1, [ 2, 3, 3.5 ], [ 4, 4.2, 5 ] ], [ 2, [ 5, 5.5, 5.7 ], [ 4, 4.2, 5 ] ] ], - colors : [ "blue", "red" ], - labels : [ "", 'data1', 'data2' ] - }; -}; - -StdDevDyGraph.prototype.test = function (targetId) { - var dygraphObject = new StdDevDyGraph(targetId, { - width : 500, - height : 400, - xlabel : "xLabel", - showRangeSelector : false + } + +}; + +RangeWhiskerGraph.prototype.draw = function(targetId, data) { + //this.calculate(data); + this.targetId = targetId; + var properties = (this.getDimensions(data)); + this.svg = SVG.createSVGCanvas(document.getElementById(this.targetId), [ [ "width", this.width ], [ "height", this.height ] ]); + this.plotAxes(properties); + this.plotWhisters(data, properties); +}; + +RangeWhiskerGraph.prototype.input = function() { + return DATADOC.getBoxWhikerData(); +}; + +RangeWhiskerGraph.prototype.test = function(targetId) { + var plot = new RangeWhiskerGraph({ + targetId : targetId, + height : 350, + width : 450, + maxBoxWidth : 25 + }); + plot.refresh(this.input()); +}; + +StdDevDyGraph.prototype.dblclick = DygraphWidget.prototype.dblclick; +StdDevDyGraph.prototype._createHTLMWrapper = DygraphWidget.prototype._createHTLMWrapper; +StdDevDyGraph.prototype.draw = DygraphWidget.prototype.draw; + +function StdDevDyGraph(targetId, args) { + this.scaled = false; + if (args == null) { + args = {}; + } + args.customBars = true; + DygraphWidget.prototype.constructor.call(this, targetId, args); +} + +StdDevDyGraph.prototype.input = function () { + return { + data : [ [ 1, [ 2, 3, 3.5 ], [ 4, 4.2, 5 ] ], [ 2, [ 5, 5.5, 5.7 ], [ 4, 4.2, 5 ] ] ], + colors : [ "blue", "red" ], + labels : [ "", 'data1', 'data2' ] + }; +}; + +StdDevDyGraph.prototype.test = function (targetId) { + var dygraphObject = new StdDevDyGraph(targetId, { + width : 500, + height : 400, + xlabel : "xLabel", + showRangeSelector : false + }); + + dygraphObject.draw(dygraphObject.input().data, dygraphObject.input().colors, dygraphObject.input().labels); +}; + + + +function AbinitioGrid(args) { + this.height = null; + this.width = null; + this.id = BUI.id(); + + if (args != null) { + if (args.height != null) { + this.height = args.height; + + if (args.width != null) { + this.width = args.width; + } + } + } + + this.onSelected = new Event(this); +}; + + +AbinitioGrid.prototype.refresh = function(subtractions){ + this.store.loadData(this._prepareData(subtractions)); +}; + +AbinitioGrid.prototype._prepareData = function(subtractions){ + /** Parsing data * */ + var models = []; + for (var l = 0; l < subtractions.length; l++) { + var subtraction = subtractions[l]; + for (var k = 0; k < subtraction.substractionToAbInitioModel3VOs.length; k++) { + var data = subtraction.substractionToAbInitioModel3VOs[k].abinitiomodel3VO; + if (data.averagedModel != null) { + models.push(data.averagedModel); + models[models.length - 1].type = "Reference"; + } + + if (data.shapeDeterminationModel != null) { + models.push(data.shapeDeterminationModel); + models[models.length - 1].type = "Refined"; + } + + if (data.modelList3VO != null) { + if (data.modelList3VO.modeltolist3VOs != null) { + for (var i = 0; i < data.modelList3VO.modeltolist3VOs.length; i++) { + models.push(data.modelList3VO.modeltolist3VOs[i].model3VO); + models[models.length - 1].type = "Model"; + } + } + } + } + } + return models; +}; + +AbinitioGrid.prototype.getPanel = function(){ + var _this = this; + + + var modelFields = [ "modelId", "type", "chiSqrt", "dmax", "firFile", "logFile", "fitFile", "pdbFile", "rfactor", "rg", "volume" ]; + Ext.define('AbinitioModel', { + extend : 'Ext.data.Model', + fields : modelFields + + }); + + /** + * Store in Memory + */ + this.store = Ext.create('Ext.data.Store', { + model : 'AbinitioModel', + autoload : true, + groupField : 'type' + }); + + + var groupingFeature = Ext.create('Ext.grid.feature.Grouping',{ + groupHeaderTpl: '{name} ({rows.length} model{[values.rows.length > 1 ? "s" : ""]})', + startCollapsed: true, + collapsible : true + }); + + this.grid = Ext.create('Ext.grid.Panel', { + collapsible : false, + resizable : true, + features: [groupingFeature], + autoscroll : true, + multiSelect : true, + store : this.store, + height : this.height, + width : this.width, + margin : 10, + columns : [ { + text : "Type", + dataindex : "type", + hidden : true, + renderer : function(a, b, record) { + return record.data.type; + }, + flex : 1 + }, + { + text : "ModelId", + dataindex : "modelId", + hidden : true, + renderer : function(a, b, record) { + return record.data.modelId; + + }, + flex : 1 + }, + + { + text : "chiSqrt", + dataindex : "chiSqrt", + renderer : function(a, b, record) { + if (record.data.dmax != null) { + return BUI.formatValuesUnits(record.data.chiSqrt, "", 12, this.decimals); + } + + }, + flex : 1 + }, + { + text : "Dmax", + dataindex : "dmax", + renderer : function(a, b, record) { + if (record.data.dmax != null) { + return BUI.formatValuesUnits(record.data.dmax, "nm", 12, this.decimals); + } + + }, + flex : 1 + }, { + text : "rFactor", + dataindex : "rfactor", + hidden : true, + renderer : function(a, b, record) { + if (record.data.rfactor != null) { + return record.data.rfactor; + } + }, + flex : 1 + }, { + text : "Rg", + dataindex : "rg", + renderer : function(a, b, record) { + if (record.data.rg != null) { + return BUI.formatValuesUnits(record.data.rg, "nm", 12, this.decimals); + } + + }, + flex : 1 + }, + { + text : "Volume", + dataindex : "volume", + renderer : function(a, b, record) { + if (record.raw.volume != null){ + return BUI.formatValuesUnits(record.raw.volume, '') + " nm3"; + } + }, + flex : 1 + }, + { + text : "PDB", + dataindex : "pdbFile", + renderer : function(a, b, record) { + if (record.data.pdbFile != null){ + return record.data.pdbFile.split("/")[record.data.pdbFile.split("/").length - 1]; + } + }, + flex : 1 + }, { + text : "Fir", + dataindex : "firFile", + renderer : function(a, b, record) { + if (record.data.firFile != null){ + return record.data.firFile.split("/")[record.data.firFile.split("/").length - 1]; + } + }, + flex : 1 + }, { + text : "LOG", + dataindex : "logFile", + hidden : true, + renderer : function(a, b, record) { + if (record.data.logFile != null){ + return record.data.logFile.split("/")[record.data.logFile.split("/").length - 1]; + } + }, + flex : 1 + } + ], + viewConfig : { + enableTextSelection : true, + preserveScrollOnRefresh : true, + stripeRows : true, + listeners : { + 'celldblclick' : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + }, + 'cellclick' : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + var models = []; + for (var i = 0; i < grid.getSelectionModel().selected.items.length; i++) { + models.push(grid.getSelectionModel().selected.items[i].raw); + } + _this.onSelected.notify(models); + } + } + } }); - - dygraphObject.draw(dygraphObject.input().data, dygraphObject.input().colors, dygraphObject.input().labels); + return this.grid; + }; - - -function AbinitioGrid(args) { - this.height = null; - this.width = null; - this.id = BUI.id(); - - if (args != null) { - if (args.height != null) { - this.height = args.height; - - if (args.width != null) { - this.width = args.width; - } - } - } - - this.onSelected = new Event(this); -}; - - -AbinitioGrid.prototype.refresh = function(subtractions){ - this.store.loadData(this._prepareData(subtractions)); -}; - -AbinitioGrid.prototype._prepareData = function(subtractions){ - /** Parsing data * */ - var models = []; - for (var l = 0; l < subtractions.length; l++) { - var subtraction = subtractions[l]; - for (var k = 0; k < subtraction.substractionToAbInitioModel3VOs.length; k++) { - var data = subtraction.substractionToAbInitioModel3VOs[k].abinitiomodel3VO; - if (data.averagedModel != null) { - models.push(data.averagedModel); - models[models.length - 1].type = "Reference"; - } - - if (data.shapeDeterminationModel != null) { - models.push(data.shapeDeterminationModel); - models[models.length - 1].type = "Refined"; - } - - if (data.modelList3VO != null) { - if (data.modelList3VO.modeltolist3VOs != null) { - for (var i = 0; i < data.modelList3VO.modeltolist3VOs.length; i++) { - models.push(data.modelList3VO.modeltolist3VOs[i].model3VO); - models[models.length - 1].type = "Model"; - } - } - } - } - } - return models; -}; - -AbinitioGrid.prototype.getPanel = function(){ - var _this = this; - - - var modelFields = [ "modelId", "type", "chiSqrt", "dmax", "firFile", "logFile", "fitFile", "pdbFile", "rfactor", "rg", "volume" ]; - Ext.define('AbinitioModel', { - extend : 'Ext.data.Model', - fields : modelFields - - }); - - /** - * Store in Memory - */ - this.store = Ext.create('Ext.data.Store', { - model : 'AbinitioModel', - autoload : true, - groupField : 'type' - }); - - - var groupingFeature = Ext.create('Ext.grid.feature.Grouping',{ - groupHeaderTpl: '{name} ({rows.length} model{[values.rows.length > 1 ? "s" : ""]})', - startCollapsed: true, - collapsible : true - }); - - this.grid = Ext.create('Ext.grid.Panel', { - collapsible : false, - resizable : true, - features: [groupingFeature], - autoscroll : true, - multiSelect : true, - store : this.store, - height : this.height, - width : this.width, - margin : 10, - columns : [ { - text : "Type", - dataindex : "type", - hidden : true, - renderer : function(a, b, record) { - return record.data.type; - }, - flex : 1 - }, - { - text : "ModelId", - dataindex : "modelId", - hidden : true, - renderer : function(a, b, record) { - return record.data.modelId; - - }, - flex : 1 - }, - - { - text : "chiSqrt", - dataindex : "chiSqrt", - renderer : function(a, b, record) { - if (record.data.dmax != null) { - return BUI.formatValuesUnits(record.data.chiSqrt, "", 12, this.decimals); - } - - }, - flex : 1 - }, - { - text : "Dmax", - dataindex : "dmax", - renderer : function(a, b, record) { - if (record.data.dmax != null) { - return BUI.formatValuesUnits(record.data.dmax, "nm", 12, this.decimals); - } - - }, - flex : 1 - }, { - text : "rFactor", - dataindex : "rfactor", - hidden : true, - renderer : function(a, b, record) { - if (record.data.rfactor != null) { - return record.data.rfactor; - } - }, - flex : 1 - }, { - text : "Rg", - dataindex : "rg", - renderer : function(a, b, record) { - if (record.data.rg != null) { - return BUI.formatValuesUnits(record.data.rg, "nm", 12, this.decimals); - } - - }, - flex : 1 - }, - { - text : "Volume", - dataindex : "volume", - renderer : function(a, b, record) { - if (record.raw.volume != null){ - return BUI.formatValuesUnits(record.raw.volume, '') + " nm3"; - } - }, - flex : 1 - }, - { - text : "PDB", - dataindex : "pdbFile", - renderer : function(a, b, record) { - if (record.data.pdbFile != null){ - return record.data.pdbFile.split("/")[record.data.pdbFile.split("/").length - 1]; - } - }, - flex : 1 - }, { - text : "Fir", - dataindex : "firFile", - renderer : function(a, b, record) { - if (record.data.firFile != null){ - return record.data.firFile.split("/")[record.data.firFile.split("/").length - 1]; - } - }, - flex : 1 - }, { - text : "LOG", - dataindex : "logFile", - hidden : true, - renderer : function(a, b, record) { - if (record.data.logFile != null){ - return record.data.logFile.split("/")[record.data.logFile.split("/").length - 1]; - } - }, - flex : 1 - } - ], - viewConfig : { - enableTextSelection : true, - preserveScrollOnRefresh : true, - stripeRows : true, - listeners : { - 'celldblclick' : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { - }, - 'cellclick' : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { - var models = []; - for (var i = 0; i < grid.getSelectionModel().selected.items.length; i++) { - models.push(grid.getSelectionModel().selected.items[i].raw); - } - _this.onSelected.notify(models); - } - } - } - }); - return this.grid; - -}; - function AdditiveGrid(args) { this.onRemoveButtonClicked = new Event(this); @@ -16236,255 +16236,255 @@ HPLCAnalysisGrid.prototype._getFramesColumn = function() { }; - - -/** - * Rigid body grid to show PDB, symmetry and multiplicity - * - * - * #onUploadFile click on upload file - */ -function AprioriRigidBodyGrid(args) { - - this.height = 250; - this.btnEditVisible = true; - this.btnRemoveVisible = true; - - if (args != null) { - if (args.height != null) { - this.height = args.height; - } - if (args.btnEditVisible != null) { - this.btnEditVisible = args.btnEditVisible; - } - if (args.btnRemoveVisible != null) { - this.btnRemoveVisible = args.btnRemoveVisible; - } - } - - /** Events **/ - this.onUploadFile = new Event(this); - this.onRemove = new Event(this); -} - -AprioriRigidBodyGrid.prototype._getColumns = function() { -}; - -AprioriRigidBodyGrid.prototype._getTopButtons = function() { - var _this = this; - /** Actions buttons **/ - var actions = []; - - /** ADD BUTTON **/ - actions.push(Ext.create('Ext.Action', { - icon : '../images/add.png', - text : 'Add', - disabled : false, - handler : function(widget, event) { - _this.onAddButtonClicked.notify(); - } - })); - - return actions; -}; - -AprioriRigidBodyGrid.prototype.refresh = function(macromolecule) { - this.macromolecule = macromolecule; - if (macromolecule != null){ - this.pdbStore.loadData(macromolecule.structure3VOs); - } -}; - -AprioriRigidBodyGrid.prototype._prepareData = function() { - var data = []; - for ( var i = 0; i < this.features.length; i++) { - data.push(this.features[i]); - } - return data; -}; - -AprioriRigidBodyGrid.prototype._getPlugins = function() { - var _this = this; - var plugins = []; - plugins.push(Ext.create('Ext.grid.plugin.RowEditing', { - clicksToEdit : 1, - listeners : { - validateedit : function(grid, e) { - /** Comments are always updatable* */ - e.record.raw.symmetry = e.newValues.symmetry; - e.record.raw.multiplicity = e.newValues.multiplicity; - - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, proposal) { - BIOSAXS.proposal.setItems(proposal); - _this.refresh(BIOSAXS.proposal.getMacromoleculeById(_this.macromolecule.macromoleculeId)); - _this.panel.setLoading(false); - }); - adapter.onError.attach(function() { - alert("Error"); - }); - - _this.panel.setLoading(); - adapter.saveStructure(e.record.raw); - } - } - })); - return plugins; -}; - -AprioriRigidBodyGrid.prototype.getPanel = function() { - var _this = this; - - this.pdbStore = Ext.create('Ext.data.Store', { - fields : [ 'filePath', 'structureId', 'structureType', 'symmetry', 'structureId', 'name', 'multiplicity' ], - groupField : 'structureType', - sorters : { - property : 'structureId', - direction : 'DESC' - } - }); - - var groupingFeature = Ext.create('Ext.grid.feature.Grouping', { - groupHeaderTpl : Ext.create('Ext.XTemplate', - "
{name:this.formatName}
", { - formatName : function(name) { - return name; - } - }), - hideGroupedHeader : true, - startCollapsed : false - }); - - this.panel = Ext.create('Ext.grid.Panel', { - margin : "15 10 0 10", - height : this.height, - store : this.pdbStore, - plugins : _this._getPlugins(), - tbar : [ { - text : 'Add Modeling Option (PDB)', - icon : '../images/add.png', - handler : function() { - _this.onUploadFile.notify('PDB', 'Upload PDB File'); - } - } - - ], - columns : [ - { - text : "structureId", - flex : 0.2, - hidden : true, - dataIndex : 'structureId', - sortable : true - }, - { - text : "File", - flex : 0.5, - dataIndex : 'filePath', - sortable : true, - hidden : true - }, - { - text : "PDB", - flex : 0.4, - dataIndex : 'name', - sortable : true - }, - { - text : "Symmetry", - flex : 0.2, - dataIndex : 'symmetry', - sortable : true, - editor : { - xtype : 'combobox', - typeAhead : true, - triggerAction : 'all', - selectOnTab : true, - store : [ [ "P1", "P1" ], [ "P2", "P2" ], [ "P3", "P3" ], [ "P4", "P4" ], [ "P5", "P5" ], [ "P6", "P6" ], [ "P32", "P32" ], [ "P42", "P42" ], - [ "P52", "P52" ], [ "P62", "P62" ], [ "P222", "P222" ] ], - } - }, { - text : "Multiplicity", - flex : 0.2, - dataIndex : 'multiplicity', - sortable : true, - editor : { - xtype : 'textfield' - } - - }, { - text : "Subunit", - flex : 0.2, - dataIndex : 'isSubunit', - sortable : true, - hidden : true - }, { - text : "Type", - flex : 0.2, - dataIndex : 'structureType', - sortable : true, - hidden : true - }, - - { - id : this.id + 'REMOVE', - flex : 0.2, - sortable : false, - renderer : function(value, metaData, record, rowIndex, colIndex, store) { - return BUI.getRedButton('REMOVE'); - } - }, ], - - listeners : { - itemdblclick : function(dataview, record, item, e) { - _this._editExperiment(record.raw.experimentId); - }, - cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { - - if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'REMOVE') { - var dataAdapter = new BiosaxsDataAdapter(); - dataAdapter.onSuccess.attach(function() { - _this.panel.setLoading(false); - _this.onRemove.notify(); - }); - _this.panel.setLoading("Removing PDB file"); - dataAdapter.removeStructure(record.data.structureId); - } - - } - }, - viewConfig : { - getRowClass : function(record, rowIdx, params, store) { - if (record.raw.isSubunit != null) { - return "blue-row"; - } - } - } - }); - - return this.panel; -}; - - - - -AprioriRigidBodyGrid.prototype.input = function() { - return { - proposal : DATADOC.getProposal_10(), - dewars : DATADOC.getDewars_10() - - }; -}; - -AprioriRigidBodyGrid.prototype.test = function(targetId) { - var AprioriRigidBodyGrid = new AprioriRigidBodyGrid({ - height : 150 - }); - BIOSAXS.proposal = new Proposal(AprioriRigidBodyGrid.input().proposal); - var panel = AprioriRigidBodyGrid.getPanel(AprioriRigidBodyGrid.input().dewars); - panel.render(targetId); - -}; + + +/** + * Rigid body grid to show PDB, symmetry and multiplicity + * + * + * #onUploadFile click on upload file + */ +function AprioriRigidBodyGrid(args) { + + this.height = 250; + this.btnEditVisible = true; + this.btnRemoveVisible = true; + + if (args != null) { + if (args.height != null) { + this.height = args.height; + } + if (args.btnEditVisible != null) { + this.btnEditVisible = args.btnEditVisible; + } + if (args.btnRemoveVisible != null) { + this.btnRemoveVisible = args.btnRemoveVisible; + } + } + + /** Events **/ + this.onUploadFile = new Event(this); + this.onRemove = new Event(this); +} + +AprioriRigidBodyGrid.prototype._getColumns = function() { +}; + +AprioriRigidBodyGrid.prototype._getTopButtons = function() { + var _this = this; + /** Actions buttons **/ + var actions = []; + + /** ADD BUTTON **/ + actions.push(Ext.create('Ext.Action', { + icon : '../images/add.png', + text : 'Add', + disabled : false, + handler : function(widget, event) { + _this.onAddButtonClicked.notify(); + } + })); + + return actions; +}; + +AprioriRigidBodyGrid.prototype.refresh = function(macromolecule) { + this.macromolecule = macromolecule; + if (macromolecule != null){ + this.pdbStore.loadData(macromolecule.structure3VOs); + } +}; + +AprioriRigidBodyGrid.prototype._prepareData = function() { + var data = []; + for ( var i = 0; i < this.features.length; i++) { + data.push(this.features[i]); + } + return data; +}; + +AprioriRigidBodyGrid.prototype._getPlugins = function() { + var _this = this; + var plugins = []; + plugins.push(Ext.create('Ext.grid.plugin.RowEditing', { + clicksToEdit : 1, + listeners : { + validateedit : function(grid, e) { + /** Comments are always updatable* */ + e.record.raw.symmetry = e.newValues.symmetry; + e.record.raw.multiplicity = e.newValues.multiplicity; + + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, proposal) { + BIOSAXS.proposal.setItems(proposal); + _this.refresh(BIOSAXS.proposal.getMacromoleculeById(_this.macromolecule.macromoleculeId)); + _this.panel.setLoading(false); + }); + adapter.onError.attach(function() { + alert("Error"); + }); + + _this.panel.setLoading(); + adapter.saveStructure(e.record.raw); + } + } + })); + return plugins; +}; + +AprioriRigidBodyGrid.prototype.getPanel = function() { + var _this = this; + + this.pdbStore = Ext.create('Ext.data.Store', { + fields : [ 'filePath', 'structureId', 'structureType', 'symmetry', 'structureId', 'name', 'multiplicity' ], + groupField : 'structureType', + sorters : { + property : 'structureId', + direction : 'DESC' + } + }); + + var groupingFeature = Ext.create('Ext.grid.feature.Grouping', { + groupHeaderTpl : Ext.create('Ext.XTemplate', + "
{name:this.formatName}
", { + formatName : function(name) { + return name; + } + }), + hideGroupedHeader : true, + startCollapsed : false + }); + + this.panel = Ext.create('Ext.grid.Panel', { + margin : "15 10 0 10", + height : this.height, + store : this.pdbStore, + plugins : _this._getPlugins(), + tbar : [ { + text : 'Add Modeling Option (PDB)', + icon : '../images/add.png', + handler : function() { + _this.onUploadFile.notify('PDB', 'Upload PDB File'); + } + } + + ], + columns : [ + { + text : "structureId", + flex : 0.2, + hidden : true, + dataIndex : 'structureId', + sortable : true + }, + { + text : "File", + flex : 0.5, + dataIndex : 'filePath', + sortable : true, + hidden : true + }, + { + text : "PDB", + flex : 0.4, + dataIndex : 'name', + sortable : true + }, + { + text : "Symmetry", + flex : 0.2, + dataIndex : 'symmetry', + sortable : true, + editor : { + xtype : 'combobox', + typeAhead : true, + triggerAction : 'all', + selectOnTab : true, + store : [ [ "P1", "P1" ], [ "P2", "P2" ], [ "P3", "P3" ], [ "P4", "P4" ], [ "P5", "P5" ], [ "P6", "P6" ], [ "P32", "P32" ], [ "P42", "P42" ], + [ "P52", "P52" ], [ "P62", "P62" ], [ "P222", "P222" ] ], + } + }, { + text : "Multiplicity", + flex : 0.2, + dataIndex : 'multiplicity', + sortable : true, + editor : { + xtype : 'textfield' + } + + }, { + text : "Subunit", + flex : 0.2, + dataIndex : 'isSubunit', + sortable : true, + hidden : true + }, { + text : "Type", + flex : 0.2, + dataIndex : 'structureType', + sortable : true, + hidden : true + }, + + { + id : this.id + 'REMOVE', + flex : 0.2, + sortable : false, + renderer : function(value, metaData, record, rowIndex, colIndex, store) { + return BUI.getRedButton('REMOVE'); + } + }, ], + + listeners : { + itemdblclick : function(dataview, record, item, e) { + _this._editExperiment(record.raw.experimentId); + }, + cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + + if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'REMOVE') { + var dataAdapter = new BiosaxsDataAdapter(); + dataAdapter.onSuccess.attach(function() { + _this.panel.setLoading(false); + _this.onRemove.notify(); + }); + _this.panel.setLoading("Removing PDB file"); + dataAdapter.removeStructure(record.data.structureId); + } + + } + }, + viewConfig : { + getRowClass : function(record, rowIdx, params, store) { + if (record.raw.isSubunit != null) { + return "blue-row"; + } + } + } + }); + + return this.panel; +}; + + + + +AprioriRigidBodyGrid.prototype.input = function() { + return { + proposal : DATADOC.getProposal_10(), + dewars : DATADOC.getDewars_10() + + }; +}; + +AprioriRigidBodyGrid.prototype.test = function(targetId) { + var AprioriRigidBodyGrid = new AprioriRigidBodyGrid({ + height : 150 + }); + BIOSAXS.proposal = new Proposal(AprioriRigidBodyGrid.input().proposal); + var panel = AprioriRigidBodyGrid.getPanel(AprioriRigidBodyGrid.input().dewars); + panel.render(targetId); + +}; /** * It shows buffer grid with a top bar with "Add" button @@ -16513,159 +16513,552 @@ function BufferGrid(args) { this.tbar = args.tbar; } - if (args.collapsed != null) { - this.collapsed = args.collapsed; - } + if (args.collapsed != null) { + this.collapsed = args.collapsed; + } + + if (args.width != null) { + this.width = args.width; + } + } +} + +BufferGrid.prototype._edit = function(bufferId) { + var _this = this; + var window = new BufferWindow(); + window.onSuccess.attach(function(sender, proposal) { + _this.store.loadData(BIOSAXS.proposal.getBuffers()); + }); + window.draw(BIOSAXS.proposal.getBufferById(bufferId)); +}; + +BufferGrid.prototype.refresh = function(buffers) { + this.store.loadData(this._prepareData(buffers), false); +}; + +BufferGrid.prototype._prepareData = function(buffers) { + return buffers; +}; + +BufferGrid.prototype._getTbar = function() { + var _this = this; + var actions = []; + + actions.push(Ext.create('Ext.Action', { + icon : '../images/add.png', + text : 'Add buffer', + disabled : false, + handler : function(widget, event) { + var window = new BufferWindow(); + window.onSuccess.attach(function(sender) { + _this.store.loadData(BIOSAXS.proposal.getBuffers()); + }); + window.draw({}); + } + })); + return actions; +}; + +BufferGrid.prototype.getPanel = function(buffers) { + var _this = this; + + this.store = Ext.create('Ext.data.Store', { + fields : [ 'bufferId', 'acronym', 'name', 'composition' ], + data : buffers + }); + + this.store.sort('acronym'); + + var type = 'Ext.grid.Panel'; + if (this.searchBar == true) { + type = 'Ext.ux.LiveSearchGridPanel'; + } + + this.grid = Ext.create(type, { + title : 'Buffers', + collapsible : true, + collapsed : this.collapsed, + store : this.store, + height : this.height, + width : this.width, + columns : [ + /*{ + text : '', + dataIndex : 'bufferId', + width : 20, + renderer : function(val, y, sample) { + return BUI.getRectangleColorDIV(BIOSAXS.proposal.bufferColors[val], 10, 10); + } + },*/ + { + text : 'Acronym', + dataIndex : 'acronym', + flex : 1 + }, { + text : 'Name', + dataIndex : 'name', + flex : 1, + hidden : true + }, { + text : 'Composition', + dataIndex : 'composition', + flex : 1, + hidden : true + }, { + id : _this.id + 'buttonEditBuffer', + width : 80, + sortable : false, + renderer : function(value, metaData, record, rowIndex, colIndex, store) { + return BUI.getGreenButton('EDIT'); + } + }, { + id : _this.id + 'buttonRemoveBuffer', + width : 85, + hidden : true, + sortable : false, + renderer : function(value, metaData, record, rowIndex, colIndex, store) { + return BUI.getRedButton('REMOVE'); + } + } ], + flex : 1, + viewConfig : { + stripeRows : true, + listeners : { + 'celldblclick' : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + _this._edit(record.data.bufferId); + }, + 'cellclick' : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonEditBuffer') { + _this._edit(record.data.bufferId); + } + + if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonRemoveBuffer') { + BUI.showBetaWarning(); + } + } + + } + } + }); + + /** Adding the tbar **/ + if (this.tbar) { + this.grid.addDocked({ + xtype : 'toolbar', + items : this._getTbar() + }); + } + return this.grid; +}; + +BufferGrid.prototype.input = function() { + return new MacromoleculeGrid().input(); +}; + +BufferGrid.prototype.test = function(targetId) { + var bufferGrid = new BufferGrid({ + width : 800, + height : 350, + collapsed : false, + tbar : true + }); + + BIOSAXS.proposal = new Proposal(bufferGrid.input().proposal); + var panel = bufferGrid.getPanel(BIOSAXS.proposal.macromolecules); + panel.render(targetId); +}; + +/** + * A shipment may contains one or more cases where stock solutions and sample plates are stored + * + * @height + * @btnEditVisible + * @btnRemoveVisible + * + * #onEditButtonClicked + * #onAddButtonClicked + * #onRemoveButtonClicked + * #onDuplicateButtonClicked + */ +function CaseGrid(args) { + + this.height = 100; + this.btnEditVisible = true; + this.btnRemoveVisible = true; + + if (args != null) { + if (args.height != null) { + this.height = args.height; + } + if (args.btnEditVisible != null) { + this.btnEditVisible = args.btnEditVisible; + } + if (args.btnRemoveVisible != null) { + this.btnRemoveVisible = args.btnRemoveVisible; + } + } + + /** Events **/ + this.onEditButtonClicked = new Event(this); + this.onAddButtonClicked = new Event(this); + this.onRemoveButtonClicked = new Event(this); + this.onDuplicateButtonClicked = new Event(this); +} + +CaseGrid.prototype._getColumns = function() { + var _this = this; + + function actionItemRenderer(value, meta, record, rowIx, ColIx, store) { + /*if (record.raw.specimen3VOs.length > 0){ + return 'x-hide-display'; + }*/ + } + + var columns = [ + { + header : 'Code', + dataIndex : 'code', + name : 'code', + type : 'string', + flex : 1 + }, + { + header : 'Bar Code', + dataIndex : 'barCode', + name : 'barCode', + type : 'string', + flex : 1, + hidden : true + }, + { + header : 'BeamLine', + dataIndex : 'barCode', + type : 'string', + flex : 1, + renderer : function(comp, val, record) { + if (record.raw.sessionVO != null) { + return "" + record.raw.sessionVO.beamlineName + ""; + } + } + }, + { + header : 'Session', + dataIndex : 'barCode', + type : 'string', + flex : 1, + renderer : function(comp, val, record) { + if (record.raw.sessionVO != null) { + return "" + moment(record.raw.sessionVO.startDate).format("MMM Do YY") + ""; + } + } + }, + { + header : 'Status', + dataIndex : 'dewarStatus', + name : 'dewarStatus', + type : 'string', + flex : 1, + renderer : function(comp, val, record) { + if (new String(record.raw.dewarStatus).toUpperCase() == 'OPENED') { + return "" + new String(record.raw.dewarStatus).toUpperCase() + ""; + } + return "" + new String(record.raw.dewarStatus).toUpperCase() + ""; + } + }, + { + header : 'Type', + dataIndex : 'type', + name : 'type', + type : 'string', + flex : 1, + hidden : true + }, + { + header : 'Sample Plates', + dataIndex : 'plates', + name : 'plates', + type : 'string', + flex : 1, + hidden : true + }, + { + header : 'Stock Solutions', + dataIndex : 'plates', + name : 'plates', + type : 'string', + width : 100, + renderer : function(comp, val, record) { + var html = "
"; + var stockSolutions = BIOSAXS.proposal.getStockSolutionsByDewarId(record.raw.dewarId); + html = html + "" + stockSolutions.length + " x"; + return html + "
"; + } + }, { + header : 'isStorageDewar', + dataIndex : 'isStorageDewar', + name : 'isStorageDewar', + type : 'string', + flex : 1, + hidden : true + }, { + header : 'Tracking Number From Synchrotron', + dataIndex : 'trackingNumberFromSynchrotron', + name : 'trackingNumberFromSynchrotron', + type : 'string', + flex : 1, + hidden : true + }, { + header : 'Tracking Number To Synchrotron', + dataIndex : 'trackingNumberToSynchrotron', + name : 'trackingNumberToSynchrotron', + type : 'string', + flex : 1, + hidden : true + }, { + header : 'Storage Location', + dataIndex : 'storageLocation', + name : 'storageLocation', + type : 'string', + flex : 1, + hidden : false + }, { + header : 'Comments', + dataIndex : 'comments', + name : 'comments', + type : 'string', + flex : 1, + hidden : false + } + ]; + + if (this.btnEditVisible) { + columns.push({ + id : _this.id + 'buttonEdit', + text : '', + hidden : !_this.btnEditVisible, + width : 85, + sortable : false, + renderer : function(value, metaData, record, rowIndex, colIndex, store) { + return BUI.getGreenButton('EDIT'); + } + }); + } - if (args.width != null) { - this.width = args.width; - } + if (this.btnRemoveVisible) { + columns.push({ + id : _this.id + 'buttonRemove', + width : 85, + sortable : false, + renderer : function(value, metaData, record, rowIndex, colIndex, store) { + if (_this.btnRemoveVisible) { + return BUI.getRedButton('REMOVE'); + } + } + }); } -} -BufferGrid.prototype._edit = function(bufferId) { - var _this = this; - var window = new BufferWindow(); - window.onSuccess.attach(function(sender, proposal) { - _this.store.loadData(BIOSAXS.proposal.getBuffers()); + columns.push({ + dataIndex : 'comments', + type : 'string', + width : 85, + hidden : false, + renderer : function(comp, val, record) { + return BUI.getBlueButton("LABELS", { + href : BUI.getPrintcomponentURL(record.raw.dewarId) + }); + } }); - window.draw(BIOSAXS.proposal.getBufferById(bufferId)); -}; - -BufferGrid.prototype.refresh = function(buffers) { - this.store.loadData(this._prepareData(buffers), false); -}; -BufferGrid.prototype._prepareData = function(buffers) { - return buffers; + return columns; }; -BufferGrid.prototype._getTbar = function() { +CaseGrid.prototype._getTopButtons = function() { var _this = this; + /** Actions buttons **/ var actions = []; + /** ADD BUTTON **/ actions.push(Ext.create('Ext.Action', { icon : '../images/add.png', - text : 'Add buffer', + text : 'Add', disabled : false, handler : function(widget, event) { - var window = new BufferWindow(); - window.onSuccess.attach(function(sender) { - _this.store.loadData(BIOSAXS.proposal.getBuffers()); - }); - window.draw({}); + _this.onAddButtonClicked.notify(); } })); + return actions; }; -BufferGrid.prototype.getPanel = function(buffers) { - var _this = this; +CaseGrid.prototype.refresh = function(dewars) { + this.features = dewars; + this.store.loadData(this._prepareData(dewars), false); +}; - this.store = Ext.create('Ext.data.Store', { - fields : [ 'bufferId', 'acronym', 'name', 'composition' ], - data : buffers - }); +CaseGrid.prototype._sort = function(store) { + store.sort('dewarId', 'DESC'); +}; - this.store.sort('acronym'); +CaseGrid.prototype._prepareData = function() { + var data = []; + for ( var i = 0; i < this.features.length; i++) { + data.push(this.features[i]); + } + return data; +}; - var type = 'Ext.grid.Panel'; - if (this.searchBar == true) { - type = 'Ext.ux.LiveSearchGridPanel'; +CaseGrid.prototype.getPanel = function(dewars, plates) { + this.features = dewars; + this.plates = plates; + return this._renderGrid(); +}; + +CaseGrid.prototype._edit = function(dewar) { + var _this = this; + var caseWindow = new CaseWindow(); + /**SAVED **/ + caseWindow.onSaved.attach(function(sender, dewar) { + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, shipment) { + _this.refresh(shipment.dewarVOs); + }); + adapter.saveCase(BIOSAXS.proposal.getShipmentByDewarId(dewar.dewarId).shippingId, dewar); + }); + caseWindow.draw(dewar); +}; + +CaseGrid.prototype._getStoreFields = function(data) { + return [ { + name : 'dewarId', + type : 'string' + }, { + name : 'barCode', + type : 'string' + }, { + name : 'code', + type : 'string' + }, { + name : 'comments', + type : 'string' + }, { + name : 'dewarStatus', + type : 'string' + }, { + name : 'isStorageDewar', + type : 'string' + }, { + name : 'plates', + type : 'string' + }, { + name : 'transportValue', + type : 'string' + }, { + name : 'trackingNumberFromSynchrotron', + type : 'string' + }, { + name : 'trackingNumberToSynchrotron', + type : 'string' + }, { + name : 'timeStamp', + type : 'string' + }, { + name : 'storageLocation', + type : 'string' + }, { + name : 'type', + type : 'string' } - this.grid = Ext.create(type, { - title : 'Buffers', - collapsible : true, - collapsed : this.collapsed, - store : this.store, + ]; +}; + +CaseGrid.prototype._renderGrid = function() { + var _this = this; + + /** Store **/ + var data = this._prepareData(); + this.store = Ext.create('Ext.data.Store', { + fields : this._getStoreFields(data), + autoload : true, + data : data + }); + this._sort(this.store); + + this.store.loadData(data, false); + + this.grid = Ext.create('Ext.grid.Panel', { + style : { + padding : 5 + }, height : this.height, - width : this.width, - columns : [ - /*{ - text : '', - dataIndex : 'bufferId', - width : 20, - renderer : function(val, y, sample) { - return BUI.getRectangleColorDIV(BIOSAXS.proposal.bufferColors[val], 10, 10); - } - },*/ - { - text : 'Acronym', - dataIndex : 'acronym', - flex : 1 - }, { - text : 'Name', - dataIndex : 'name', - flex : 1, - hidden : true - }, { - text : 'Composition', - dataIndex : 'composition', - flex : 1, - hidden : true - }, { - id : _this.id + 'buttonEditBuffer', - width : 80, - sortable : false, - renderer : function(value, metaData, record, rowIndex, colIndex, store) { - return BUI.getGreenButton('EDIT'); - } - }, { - id : _this.id + 'buttonRemoveBuffer', - width : 85, - hidden : true, - sortable : false, - renderer : function(value, metaData, record, rowIndex, colIndex, store) { - return BUI.getRedButton('REMOVE'); - } - } ], - flex : 1, + store : this.store, + columns : this._getColumns(), viewConfig : { stripeRows : true, listeners : { - 'celldblclick' : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { - _this._edit(record.data.bufferId); + itemdblclick : function(dataview, record, item, e) { + _this._edit(record.raw); }, - 'cellclick' : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { - if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonEditBuffer') { - _this._edit(record.data.bufferId); + cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonEdit') { + _this._edit(record.raw); } - if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonRemoveBuffer') { - BUI.showBetaWarning(); + if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonRemove') { + _this.onRemoveButtonClicked.notify(_this.store.getAt(rowIndex).raw.dewarId); } } - } + }, + selModel : { + mode : 'SINGLE' } }); - /** Adding the tbar **/ - if (this.tbar) { - this.grid.addDocked({ - xtype : 'toolbar', - items : this._getTbar() - }); - } + var actions = _this._getTopButtons(); + this.grid.addDocked({ + xtype : 'toolbar', + items : actions + }); + + this.grid.getSelectionModel().on({ + selectionchange : function(sm, selections) { + if (selections.length) { + for ( var i = 0; i < actions.length; i++) { + if (actions[i].enable) { + actions[i].enable(); + } + } + } else { + for ( var i = 0; i < actions.length; i++) { + if (actions[i].alwaysEnabled == false) { + if (actions[i].disable) { + actions[i].disable(); + } + } + } + } + } + }); return this.grid; }; -BufferGrid.prototype.input = function() { - return new MacromoleculeGrid().input(); +CaseGrid.prototype.input = function() { + return { + proposal : DATADOC.getProposal_10(), + dewars : DATADOC.getDewars_10() + + }; }; -BufferGrid.prototype.test = function(targetId) { - var bufferGrid = new BufferGrid({ - width : 800, - height : 350, - collapsed : false, - tbar : true +CaseGrid.prototype.test = function(targetId) { + var CaseGrid = new CaseGrid({ + height : 150 }); - - BIOSAXS.proposal = new Proposal(bufferGrid.input().proposal); - var panel = bufferGrid.getPanel(BIOSAXS.proposal.macromolecules); + BIOSAXS.proposal = new Proposal(CaseGrid.input().proposal); + var panel = CaseGrid.getPanel(CaseGrid.input().dewars); panel.render(targetId); + }; /** @@ -16680,7 +17073,7 @@ BufferGrid.prototype.test = function(targetId) { * #onRemoveButtonClicked * #onDuplicateButtonClicked */ -function CaseGrid(args) { +function ExampleGrid(args) { this.height = 100; this.btnEditVisible = true; @@ -16705,7 +17098,7 @@ function CaseGrid(args) { this.onDuplicateButtonClicked = new Event(this); } -CaseGrid.prototype._getColumns = function() { +ExampleGrid.prototype._getColumns = function() { var _this = this; function actionItemRenderer(value, meta, record, rowIx, ColIx, store) { @@ -16872,7 +17265,7 @@ CaseGrid.prototype._getColumns = function() { return columns; }; -CaseGrid.prototype._getTopButtons = function() { +ExampleGrid.prototype._getTopButtons = function() { var _this = this; /** Actions buttons **/ var actions = []; @@ -16890,16 +17283,16 @@ CaseGrid.prototype._getTopButtons = function() { return actions; }; -CaseGrid.prototype.refresh = function(dewars) { +ExampleGrid.prototype.refresh = function(dewars) { this.features = dewars; this.store.loadData(this._prepareData(dewars), false); }; -CaseGrid.prototype._sort = function(store) { +ExampleGrid.prototype._sort = function(store) { store.sort('dewarId', 'DESC'); }; -CaseGrid.prototype._prepareData = function() { +ExampleGrid.prototype._prepareData = function() { var data = []; for ( var i = 0; i < this.features.length; i++) { data.push(this.features[i]); @@ -16907,27 +17300,66 @@ CaseGrid.prototype._prepareData = function() { return data; }; -CaseGrid.prototype.getPanel = function(dewars, plates) { - this.features = dewars; - this.plates = plates; - return this._renderGrid(); -}; +ExampleGrid.prototype.getPanel = function() { + this.grid = Ext.create('Ext.grid.Panel', { + style : { + padding : 5 + }, + height : this.height, + store : this.store, + columns : this._getColumns(), + viewConfig : { + stripeRows : true, + listeners : { + itemdblclick : function(dataview, record, item, e) { + _this._edit(record.raw); + }, + cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonEdit') { + _this._edit(record.raw); + } -CaseGrid.prototype._edit = function(dewar) { - var _this = this; - var caseWindow = new CaseWindow(); - /**SAVED **/ - caseWindow.onSaved.attach(function(sender, dewar) { - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, shipment) { - _this.refresh(shipment.dewarVOs); - }); - adapter.saveCase(BIOSAXS.proposal.getShipmentByDewarId(dewar.dewarId).shippingId, dewar); + if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonRemove') { + _this.onRemoveButtonClicked.notify(_this.store.getAt(rowIndex).raw.dewarId); + } + } + } + }, + selModel : { + mode : 'SINGLE' + } }); - caseWindow.draw(dewar); + + var actions = _this._getTopButtons(); + this.grid.addDocked({ + xtype : 'toolbar', + items : actions + }); + + this.grid.getSelectionModel().on({ + selectionchange : function(sm, selections) { + if (selections.length) { + for ( var i = 0; i < actions.length; i++) { + if (actions[i].enable) { + actions[i].enable(); + } + } + } else { + for ( var i = 0; i < actions.length; i++) { + if (actions[i].alwaysEnabled == false) { + if (actions[i].disable) { + actions[i].disable(); + } + } + } + } + } + }); + return this.grid; }; -CaseGrid.prototype._getStoreFields = function(data) { + +ExampleGrid.prototype._getStoreFields = function(data) { return [ { name : 'dewarId', type : 'string' @@ -16972,7 +17404,7 @@ CaseGrid.prototype._getStoreFields = function(data) { ]; }; -CaseGrid.prototype._renderGrid = function() { +ExampleGrid.prototype._renderGrid = function() { var _this = this; /** Store **/ @@ -17043,7 +17475,7 @@ CaseGrid.prototype._renderGrid = function() { return this.grid; }; -CaseGrid.prototype.input = function() { +ExampleGrid.prototype.input = function() { return { proposal : DATADOC.getProposal_10(), dewars : DATADOC.getDewars_10() @@ -17051,447 +17483,15 @@ CaseGrid.prototype.input = function() { }; }; -CaseGrid.prototype.test = function(targetId) { - var CaseGrid = new CaseGrid({ +ExampleGrid.prototype.test = function(targetId) { + var ExampleGrid = new ExampleGrid({ height : 150 }); - BIOSAXS.proposal = new Proposal(CaseGrid.input().proposal); - var panel = CaseGrid.getPanel(CaseGrid.input().dewars); - panel.render(targetId); - -}; - -/** - * A shipment may contains one or more cases where stock solutions and sample plates are stored - * - * @height - * @btnEditVisible - * @btnRemoveVisible - * - * #onEditButtonClicked - * #onAddButtonClicked - * #onRemoveButtonClicked - * #onDuplicateButtonClicked - */ -function ExampleGrid(args) { - - this.height = 100; - this.btnEditVisible = true; - this.btnRemoveVisible = true; - - if (args != null) { - if (args.height != null) { - this.height = args.height; - } - if (args.btnEditVisible != null) { - this.btnEditVisible = args.btnEditVisible; - } - if (args.btnRemoveVisible != null) { - this.btnRemoveVisible = args.btnRemoveVisible; - } - } - - /** Events **/ - this.onEditButtonClicked = new Event(this); - this.onAddButtonClicked = new Event(this); - this.onRemoveButtonClicked = new Event(this); - this.onDuplicateButtonClicked = new Event(this); -} - -ExampleGrid.prototype._getColumns = function() { - var _this = this; - - function actionItemRenderer(value, meta, record, rowIx, ColIx, store) { - /*if (record.raw.specimen3VOs.length > 0){ - return 'x-hide-display'; - }*/ - } - - var columns = [ - { - header : 'Code', - dataIndex : 'code', - name : 'code', - type : 'string', - flex : 1 - }, - { - header : 'Bar Code', - dataIndex : 'barCode', - name : 'barCode', - type : 'string', - flex : 1, - hidden : true - }, - { - header : 'BeamLine', - dataIndex : 'barCode', - type : 'string', - flex : 1, - renderer : function(comp, val, record) { - if (record.raw.sessionVO != null) { - return "" + record.raw.sessionVO.beamlineName + ""; - } - } - }, - { - header : 'Session', - dataIndex : 'barCode', - type : 'string', - flex : 1, - renderer : function(comp, val, record) { - if (record.raw.sessionVO != null) { - return "" + moment(record.raw.sessionVO.startDate).format("MMM Do YY") + ""; - } - } - }, - { - header : 'Status', - dataIndex : 'dewarStatus', - name : 'dewarStatus', - type : 'string', - flex : 1, - renderer : function(comp, val, record) { - if (new String(record.raw.dewarStatus).toUpperCase() == 'OPENED') { - return "" + new String(record.raw.dewarStatus).toUpperCase() + ""; - } - return "" + new String(record.raw.dewarStatus).toUpperCase() + ""; - } - }, - { - header : 'Type', - dataIndex : 'type', - name : 'type', - type : 'string', - flex : 1, - hidden : true - }, - { - header : 'Sample Plates', - dataIndex : 'plates', - name : 'plates', - type : 'string', - flex : 1, - hidden : true - }, - { - header : 'Stock Solutions', - dataIndex : 'plates', - name : 'plates', - type : 'string', - width : 100, - renderer : function(comp, val, record) { - var html = "
"; - var stockSolutions = BIOSAXS.proposal.getStockSolutionsByDewarId(record.raw.dewarId); - html = html + "" + stockSolutions.length + " x"; - return html + "
"; - } - }, { - header : 'isStorageDewar', - dataIndex : 'isStorageDewar', - name : 'isStorageDewar', - type : 'string', - flex : 1, - hidden : true - }, { - header : 'Tracking Number From Synchrotron', - dataIndex : 'trackingNumberFromSynchrotron', - name : 'trackingNumberFromSynchrotron', - type : 'string', - flex : 1, - hidden : true - }, { - header : 'Tracking Number To Synchrotron', - dataIndex : 'trackingNumberToSynchrotron', - name : 'trackingNumberToSynchrotron', - type : 'string', - flex : 1, - hidden : true - }, { - header : 'Storage Location', - dataIndex : 'storageLocation', - name : 'storageLocation', - type : 'string', - flex : 1, - hidden : false - }, { - header : 'Comments', - dataIndex : 'comments', - name : 'comments', - type : 'string', - flex : 1, - hidden : false - } - ]; - - if (this.btnEditVisible) { - columns.push({ - id : _this.id + 'buttonEdit', - text : '', - hidden : !_this.btnEditVisible, - width : 85, - sortable : false, - renderer : function(value, metaData, record, rowIndex, colIndex, store) { - return BUI.getGreenButton('EDIT'); - } - }); - } - - if (this.btnRemoveVisible) { - columns.push({ - id : _this.id + 'buttonRemove', - width : 85, - sortable : false, - renderer : function(value, metaData, record, rowIndex, colIndex, store) { - if (_this.btnRemoveVisible) { - return BUI.getRedButton('REMOVE'); - } - } - }); - } - - columns.push({ - dataIndex : 'comments', - type : 'string', - width : 85, - hidden : false, - renderer : function(comp, val, record) { - return BUI.getBlueButton("LABELS", { - href : BUI.getPrintcomponentURL(record.raw.dewarId) - }); - } - }); - - return columns; -}; - -ExampleGrid.prototype._getTopButtons = function() { - var _this = this; - /** Actions buttons **/ - var actions = []; - - /** ADD BUTTON **/ - actions.push(Ext.create('Ext.Action', { - icon : '../images/add.png', - text : 'Add', - disabled : false, - handler : function(widget, event) { - _this.onAddButtonClicked.notify(); - } - })); - - return actions; -}; - -ExampleGrid.prototype.refresh = function(dewars) { - this.features = dewars; - this.store.loadData(this._prepareData(dewars), false); -}; - -ExampleGrid.prototype._sort = function(store) { - store.sort('dewarId', 'DESC'); -}; - -ExampleGrid.prototype._prepareData = function() { - var data = []; - for ( var i = 0; i < this.features.length; i++) { - data.push(this.features[i]); - } - return data; -}; - -ExampleGrid.prototype.getPanel = function() { - this.grid = Ext.create('Ext.grid.Panel', { - style : { - padding : 5 - }, - height : this.height, - store : this.store, - columns : this._getColumns(), - viewConfig : { - stripeRows : true, - listeners : { - itemdblclick : function(dataview, record, item, e) { - _this._edit(record.raw); - }, - cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { - if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonEdit') { - _this._edit(record.raw); - } - - if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonRemove') { - _this.onRemoveButtonClicked.notify(_this.store.getAt(rowIndex).raw.dewarId); - } - } - } - }, - selModel : { - mode : 'SINGLE' - } - }); - - var actions = _this._getTopButtons(); - this.grid.addDocked({ - xtype : 'toolbar', - items : actions - }); - - this.grid.getSelectionModel().on({ - selectionchange : function(sm, selections) { - if (selections.length) { - for ( var i = 0; i < actions.length; i++) { - if (actions[i].enable) { - actions[i].enable(); - } - } - } else { - for ( var i = 0; i < actions.length; i++) { - if (actions[i].alwaysEnabled == false) { - if (actions[i].disable) { - actions[i].disable(); - } - } - } - } - } - }); - return this.grid; -}; - - -ExampleGrid.prototype._getStoreFields = function(data) { - return [ { - name : 'dewarId', - type : 'string' - }, { - name : 'barCode', - type : 'string' - }, { - name : 'code', - type : 'string' - }, { - name : 'comments', - type : 'string' - }, { - name : 'dewarStatus', - type : 'string' - }, { - name : 'isStorageDewar', - type : 'string' - }, { - name : 'plates', - type : 'string' - }, { - name : 'transportValue', - type : 'string' - }, { - name : 'trackingNumberFromSynchrotron', - type : 'string' - }, { - name : 'trackingNumberToSynchrotron', - type : 'string' - }, { - name : 'timeStamp', - type : 'string' - }, { - name : 'storageLocation', - type : 'string' - }, { - name : 'type', - type : 'string' - } - - ]; -}; - -ExampleGrid.prototype._renderGrid = function() { - var _this = this; - - /** Store **/ - var data = this._prepareData(); - this.store = Ext.create('Ext.data.Store', { - fields : this._getStoreFields(data), - autoload : true, - data : data - }); - this._sort(this.store); - - this.store.loadData(data, false); - - this.grid = Ext.create('Ext.grid.Panel', { - style : { - padding : 5 - }, - height : this.height, - store : this.store, - columns : this._getColumns(), - viewConfig : { - stripeRows : true, - listeners : { - itemdblclick : function(dataview, record, item, e) { - _this._edit(record.raw); - }, - cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { - if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonEdit') { - _this._edit(record.raw); - } - - if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonRemove') { - _this.onRemoveButtonClicked.notify(_this.store.getAt(rowIndex).raw.dewarId); - } - } - } - }, - selModel : { - mode : 'SINGLE' - } - }); - - var actions = _this._getTopButtons(); - this.grid.addDocked({ - xtype : 'toolbar', - items : actions - }); - - this.grid.getSelectionModel().on({ - selectionchange : function(sm, selections) { - if (selections.length) { - for ( var i = 0; i < actions.length; i++) { - if (actions[i].enable) { - actions[i].enable(); - } - } - } else { - for ( var i = 0; i < actions.length; i++) { - if (actions[i].alwaysEnabled == false) { - if (actions[i].disable) { - actions[i].disable(); - } - } - } - } - } - }); - return this.grid; -}; - -ExampleGrid.prototype.input = function() { - return { - proposal : DATADOC.getProposal_10(), - dewars : DATADOC.getDewars_10() - - }; -}; - -ExampleGrid.prototype.test = function(targetId) { - var ExampleGrid = new ExampleGrid({ - height : 150 - }); - BIOSAXS.proposal = new Proposal(ExampleGrid.input().proposal); - var panel = ExampleGrid.getPanel(ExampleGrid.input().dewars); - panel.render(targetId); - -}; + BIOSAXS.proposal = new Proposal(ExampleGrid.input().proposal); + var panel = ExampleGrid.getPanel(ExampleGrid.input().dewars); + panel.render(targetId); + +}; /** @@ -18106,188 +18106,188 @@ ExperimentGrid.prototype.test = function(targetId) { panel.render(targetId); }; - -function FitStructureToExperimentDataGrid(args) { - this.height = null; - this.width = null; - this.id = BUI.id(); - - if (args != null) { - if (args.height != null) { - this.height = args.height; - - if (args.width != null) { - this.width = args.width; - } - } - } - - this.onSelected = new Event(this); -} - -FitStructureToExperimentDataGrid.prototype.getStructuresByMacromolecule = function(macromolecule) { - var structures = []; - if (macromolecule.structure3VOs != null) { - if (macromolecule.structure3VOs.length > 0) { - for (var i = 0; i < macromolecule.structure3VOs.length; i++) { - structures.push(macromolecule.structure3VOs[i]); - } - } - } - return structures; -}; - -FitStructureToExperimentDataGrid.prototype._prepareData = function(subtractions) { - var data = []; - for (var i = 0; i < subtractions.length; i++) { - - for (var j = 0; j < subtractions[i].fitStructureToExperimentalData3VOs.length; j++) { - var fit = subtractions[i].fitStructureToExperimentalData3VOs[j]; - data.push({ - fit : fit.fitFilePath, - fitStructureToExperimentalDataId : fit.fitStructureToExperimentalDataId, - mixtureToStructure3VOs : fit.mixtureToStructure3VOs, - subtractedFile : subtractions[i].substractedFilePath - }); - } - } - return data; - -}; - -FitStructureToExperimentDataGrid.prototype.refresh = function(subtractions) { - this.store.loadData(this._prepareData(subtractions), false); -}; - -FitStructureToExperimentDataGrid.prototype.getPanel = function() { - var _this = this; - this.store = Ext.create('Ext.data.Store', { - fields : [ 'fit', 'subtractedFile', 'structureId', 'fitFilePath', 'mixtureToStructure3VOs' ], - data : [] - }); - - this.store.sort([ { - property : 'name', - direction : 'ASC' - } ]); - - this.selModel = Ext.create('Ext.selection.RowModel', { - allowDeselect : true, - mode : 'MULTI', - listeners : { - selectionchange : function(sm, selections) { - var selected = []; - for (var i = 0; i < selections.length; i++) { - selected.push(selections[i].raw); - } - _this.onSelected.notify(selected); - } - } - }); - - this.panel = Ext.create('Ext.grid.Panel', { - store : this.store, - deferredRender : false, - width : this.width, - height : this.height, - margin : 10, - selModel : this.selModel, - columns : [ { - text : 'Name', - dataIndex : 'fit', - flex : 1, - renderer : function(val, b, record) { - return BUI.getFileName(val); - } - }, { - text : 'PDB', - dataIndex : 'mixtureToStructure3VOs', - flex : 1, - renderer : function(values, b, record) { - var html = ""; - for (var i = 0; i < values.length; i++) { - var structure = BIOSAXS.proposal.getStructuresById(values[i].structureId); - html = html + ""; - if (structure != null){ - html = html + ""; - } - html = html + ""; - } - return html + "
" + structure.name + "
"; - } - }, { - text : 'Volume Fraction', - dataIndex : 'mixtureToStructure3VOs', - flex : 1, - renderer : function(values, b, record) { - var html = ""; - for (var i = 0; i < values.length; i++) { - html = html + ""; - html = html + ""; - html = html + ""; - } - - return html + "
" + values[i].volumeFraction + "
"; - } - },{ - text : 'Subtraction', - dataIndex : 'subtractedFile', - flex : 1, - renderer : function(values, b, record) { - return values.split("/")[values.split("/").length - 1]; - } - },], - - viewConfig : { - enableTextSelection : true, - preserveScrollOnRefresh : true - }, - listeners : { - cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { - }, - afterrender : function() { - } - } - }); - return this.panel; -}; - -/** Static method **/ -FitStructureToExperimentDataGrid.prototype.RUNFitScattering = function(subtractionId, structureId) { - var _this = this; - /** Add to Workflow **/ - var adapter = new BiosaxsDataAdapter(); - var workflow = { - 'workflowTitle' : 'FitExperimentalDatatoStructure', - 'comments' : 'FitExperimentalDatatoStructure run from ISPyB for subtractionId: ' + subtractionId + ' and structureId ' + structureId - } - - var inputs = [ { - name : 'subtractionId', - value : subtractionId - }, { - name : 'structureId', - value : structureId - } ]; - - adapter.onSuccess.attach(function(sender, data) { - /** Add to Fit **/ - var adapter2 = new BiosaxsDataAdapter(); - var fit = { - 'workflowId' : data.workflowId, - 'subtractionId' : subtractionId, - 'structureId' : structureId, - 'comments' : 'Sent to workflow engine' - } - adapter2.onSuccess.attach(function(sender, fit) { - _this.refresh(_this.subtractionId, _this.macromolecule); - }); - adapter2.addFitStructureData(fit); - - }); - adapter.addWorkflow(workflow, inputs); - -}; + +function FitStructureToExperimentDataGrid(args) { + this.height = null; + this.width = null; + this.id = BUI.id(); + + if (args != null) { + if (args.height != null) { + this.height = args.height; + + if (args.width != null) { + this.width = args.width; + } + } + } + + this.onSelected = new Event(this); +} + +FitStructureToExperimentDataGrid.prototype.getStructuresByMacromolecule = function(macromolecule) { + var structures = []; + if (macromolecule.structure3VOs != null) { + if (macromolecule.structure3VOs.length > 0) { + for (var i = 0; i < macromolecule.structure3VOs.length; i++) { + structures.push(macromolecule.structure3VOs[i]); + } + } + } + return structures; +}; + +FitStructureToExperimentDataGrid.prototype._prepareData = function(subtractions) { + var data = []; + for (var i = 0; i < subtractions.length; i++) { + + for (var j = 0; j < subtractions[i].fitStructureToExperimentalData3VOs.length; j++) { + var fit = subtractions[i].fitStructureToExperimentalData3VOs[j]; + data.push({ + fit : fit.fitFilePath, + fitStructureToExperimentalDataId : fit.fitStructureToExperimentalDataId, + mixtureToStructure3VOs : fit.mixtureToStructure3VOs, + subtractedFile : subtractions[i].substractedFilePath + }); + } + } + return data; + +}; + +FitStructureToExperimentDataGrid.prototype.refresh = function(subtractions) { + this.store.loadData(this._prepareData(subtractions), false); +}; + +FitStructureToExperimentDataGrid.prototype.getPanel = function() { + var _this = this; + this.store = Ext.create('Ext.data.Store', { + fields : [ 'fit', 'subtractedFile', 'structureId', 'fitFilePath', 'mixtureToStructure3VOs' ], + data : [] + }); + + this.store.sort([ { + property : 'name', + direction : 'ASC' + } ]); + + this.selModel = Ext.create('Ext.selection.RowModel', { + allowDeselect : true, + mode : 'MULTI', + listeners : { + selectionchange : function(sm, selections) { + var selected = []; + for (var i = 0; i < selections.length; i++) { + selected.push(selections[i].raw); + } + _this.onSelected.notify(selected); + } + } + }); + + this.panel = Ext.create('Ext.grid.Panel', { + store : this.store, + deferredRender : false, + width : this.width, + height : this.height, + margin : 10, + selModel : this.selModel, + columns : [ { + text : 'Name', + dataIndex : 'fit', + flex : 1, + renderer : function(val, b, record) { + return BUI.getFileName(val); + } + }, { + text : 'PDB', + dataIndex : 'mixtureToStructure3VOs', + flex : 1, + renderer : function(values, b, record) { + var html = ""; + for (var i = 0; i < values.length; i++) { + var structure = BIOSAXS.proposal.getStructuresById(values[i].structureId); + html = html + ""; + if (structure != null){ + html = html + ""; + } + html = html + ""; + } + return html + "
" + structure.name + "
"; + } + }, { + text : 'Volume Fraction', + dataIndex : 'mixtureToStructure3VOs', + flex : 1, + renderer : function(values, b, record) { + var html = ""; + for (var i = 0; i < values.length; i++) { + html = html + ""; + html = html + ""; + html = html + ""; + } + + return html + "
" + values[i].volumeFraction + "
"; + } + },{ + text : 'Subtraction', + dataIndex : 'subtractedFile', + flex : 1, + renderer : function(values, b, record) { + return values.split("/")[values.split("/").length - 1]; + } + },], + + viewConfig : { + enableTextSelection : true, + preserveScrollOnRefresh : true + }, + listeners : { + cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + }, + afterrender : function() { + } + } + }); + return this.panel; +}; + +/** Static method **/ +FitStructureToExperimentDataGrid.prototype.RUNFitScattering = function(subtractionId, structureId) { + var _this = this; + /** Add to Workflow **/ + var adapter = new BiosaxsDataAdapter(); + var workflow = { + 'workflowTitle' : 'FitExperimentalDatatoStructure', + 'comments' : 'FitExperimentalDatatoStructure run from ISPyB for subtractionId: ' + subtractionId + ' and structureId ' + structureId + } + + var inputs = [ { + name : 'subtractionId', + value : subtractionId + }, { + name : 'structureId', + value : structureId + } ]; + + adapter.onSuccess.attach(function(sender, data) { + /** Add to Fit **/ + var adapter2 = new BiosaxsDataAdapter(); + var fit = { + 'workflowId' : data.workflowId, + 'subtractionId' : subtractionId, + 'structureId' : structureId, + 'comments' : 'Sent to workflow engine' + } + adapter2.onSuccess.attach(function(sender, fit) { + _this.refresh(_this.subtractionId, _this.macromolecule); + }); + adapter2.addFitStructureData(fit); + + }); + adapter.addWorkflow(workflow, inputs); + +}; /** * It shows buffer grid with a top bar with "Add" button @@ -19893,244 +19893,244 @@ MeasurementGrid.prototype.getPanel = function(measurements, experiments) { }, 1000); } - } catch (e) { - } + } catch (e) { + } + } + } + } + }); + + return this.grid; +}; + +/** Method for testing * */ +MeasurementGrid.prototype.input = function() { + var experiment = DATADOC.getExperiment_10(); + var measurements = DATADOC.getMeasurements_10(); + var proposal = DATADOC.getProposal_10(); + return { + experiment : experiment, + measurements : measurements, + proposal : proposal + }; +}; + +MeasurementGrid.prototype.test = function(targetId) { + var measurementGrid = new MeasurementGrid({ + tbar : true + + }); + BIOSAXS.proposal = new Proposal(measurementGrid.input().proposal); + var panel = measurementGrid.getPanel(measurementGrid.input().measurements, new ExperimentList([ new Experiment(measurementGrid.input().experiment) ])); + panel.render(targetId); +}; + +/** + * A shipment may contains one or more cases where stock solutions and sample + * plates are stored + * + * @height + * @btnEditVisible + * @btnRemoveVisible + * + * #onEditButtonClicked + */ +function MolarityGrid(args) { + this.height = 100; + this.width = 100; + + if (args != null) { + if (args.height != null) { + this.height = args.height; + } + if (args.width != null) { + this.width = args.width; + } + } + + var _this = this; + + this.molarityForm = new MolarityForm({height : 180, width : 455}); + + this.molarityForm.onSave.attach(function(sender){ + _this.molarityWindow.destroy(); + _this.updateProposal(); + + }); + + this.molarityForm.onClose.attach(function(sender){ + _this.molarityWindow.destroy(); + + }); + + /** Events * */ + this.onEditButtonClicked = new Event(this); +} + +MolarityGrid.prototype._getColumns = function() { + return [ { + text : 'Subunit', + columns : [ { + text : "Acronym", + width : 100, + hidden : false, + dataIndex : 'acronym', + sortable : true + }, { + text : "Name", + width : 125, + hidden : false, + dataIndex : 'name', + sortable : true + }, { + text : "MM Est.", + width : 100, + dataIndex : 'molecularMass', + sortable : true, + renderer : function(grid, cls, record){ + return BUI.formatValuesUnits(record.data.molecularMass , "Da", 10, 2); + + } + } ] + }, { +// text : "Number
in assymmetric units", + text : "Ratio", + width : 100, + dataIndex : 'ratio', + tooltip : 'Number of times the subunit is present in the macromolecule', + sortable : true + }, { + id : this.id + 'MOLARITY_REMOVE', + flex : 0.1, + sortable : false, + renderer : function(value, metaData, record, rowIndex, colIndex, store) { + return BUI.getRedButton('REMOVE'); + } + } ]; +}; + +MolarityGrid.prototype._openMolarityWindow = function() { + this.molarityWindow = Ext.create('Ext.window.Window', { + title : 'Molarity', + height : 220, + width : 500, + modal : true, + items : [this.molarityForm.getPanel() ] + }).show(); +}; + +MolarityGrid.prototype._getButtons = function() { + var _this = this; + return [ { + text : 'Add subunit', + icon : '../images/add.png', + handler : function() { + _this._openMolarityWindow(); + } + }]; +}; + + +MolarityGrid.prototype.updateProposal = function() { + var _this = this; + this.panel.setLoading(); + BIOSAXS.proposal.onInitialized.attach(function() { + if (BIOSAXS.proposal != null) { + var macromolecules = BIOSAXS.proposal.macromolecules; + for (var i = 0; i < macromolecules.length; i++) { + + if (macromolecules[i].macromoleculeId == _this.macromolecule.macromoleculeId) { + _this.refresh(macromolecules[i]); + _this.panel.setLoading(false); + } + } + } + }); + BIOSAXS.proposal.init(); +}; + + +MolarityGrid.prototype.getPanel = function() { + var _this = this; + + this.molarityStore = Ext.create('Ext.data.Store', { + fields : [ 'acronym', 'ratio', 'comments', 'stoichiometryId', 'name', 'molecularMass' ], + sorters : { + property : 'ratio', + direction : 'DESC' + } + }); + + this.panel = Ext.create('Ext.grid.Panel', { + store : this.molarityStore, + height : this.height, + padding : 5, + columns : this._getColumns(), + listeners : { + cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + + /** Remove entry * */ + if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'MOLARITY_REMOVE') { + var dataAdapter = new BiosaxsDataAdapter(); + dataAdapter.onSuccess.attach(function(sender) { + _this.updateProposal(); + + }); + dataAdapter.removeStoichiometry(record.data.stoichiometryId); + _this.panel.setLoading("Removing Structure"); } } - } + }, + tbar : this._getButtons() }); + return this.panel; +}; - return this.grid; +MolarityGrid.prototype._prepareData = function(macromolecule) { + /** Return an array of [{ratio,acronym, stoichiometryId, name}] **/ + var data = []; + if (macromolecule.stoichiometry != null) { + for (var i = 0; i < macromolecule.stoichiometry.length; i++) { + var hostMacromolecule = BIOSAXS.proposal.getMacromoleculeById(macromolecule.stoichiometry[i].macromoleculeId); + data.push({ + ratio : macromolecule.stoichiometry[i].ratio, + acronym : hostMacromolecule.acronym, + comments : hostMacromolecule.comments, + molecularMass : hostMacromolecule.molecularMass, + stoichiometryId : macromolecule.stoichiometry[i].stoichiometryId, + name : hostMacromolecule.name + }); + } + } + return data; }; -/** Method for testing * */ -MeasurementGrid.prototype.input = function() { - var experiment = DATADOC.getExperiment_10(); - var measurements = DATADOC.getMeasurements_10(); - var proposal = DATADOC.getProposal_10(); +MolarityGrid.prototype.refresh = function(macromolecule) { + if (macromolecule != null){ + this.molarityStore.loadData(this._prepareData(macromolecule)); + this.molarityForm.refresh(macromolecule); + this.macromolecule = macromolecule; + } +}; + +MolarityGrid.prototype.input = function() { return { - experiment : experiment, - measurements : measurements, - proposal : proposal + proposal : DATADOC.getProposal_10(), + dewars : DATADOC.getDewars_10() + }; }; -MeasurementGrid.prototype.test = function(targetId) { - var measurementGrid = new MeasurementGrid({ - tbar : true - +MolarityGrid.prototype.test = function(targetId) { + var MolarityGrid = new MolarityGrid({ + height : 150 }); - BIOSAXS.proposal = new Proposal(measurementGrid.input().proposal); - var panel = measurementGrid.getPanel(measurementGrid.input().measurements, new ExperimentList([ new Experiment(measurementGrid.input().experiment) ])); + BIOSAXS.proposal = new Proposal(MolarityGrid.input().proposal); + var panel = MolarityGrid.getPanel(MolarityGrid.input().dewars); panel.render(targetId); + }; -/** - * A shipment may contains one or more cases where stock solutions and sample - * plates are stored - * - * @height - * @btnEditVisible - * @btnRemoveVisible - * - * #onEditButtonClicked - */ -function MolarityGrid(args) { - this.height = 100; - this.width = 100; - - if (args != null) { - if (args.height != null) { - this.height = args.height; - } - if (args.width != null) { - this.width = args.width; - } - } - - var _this = this; - - this.molarityForm = new MolarityForm({height : 180, width : 455}); - - this.molarityForm.onSave.attach(function(sender){ - _this.molarityWindow.destroy(); - _this.updateProposal(); - - }); - - this.molarityForm.onClose.attach(function(sender){ - _this.molarityWindow.destroy(); - - }); - - /** Events * */ - this.onEditButtonClicked = new Event(this); -} - -MolarityGrid.prototype._getColumns = function() { - return [ { - text : 'Subunit', - columns : [ { - text : "Acronym", - width : 100, - hidden : false, - dataIndex : 'acronym', - sortable : true - }, { - text : "Name", - width : 125, - hidden : false, - dataIndex : 'name', - sortable : true - }, { - text : "MM Est.", - width : 100, - dataIndex : 'molecularMass', - sortable : true, - renderer : function(grid, cls, record){ - return BUI.formatValuesUnits(record.data.molecularMass , "Da", 10, 2); - - } - } ] - }, { -// text : "Number
in assymmetric units", - text : "Ratio", - width : 100, - dataIndex : 'ratio', - tooltip : 'Number of times the subunit is present in the macromolecule', - sortable : true - }, { - id : this.id + 'MOLARITY_REMOVE', - flex : 0.1, - sortable : false, - renderer : function(value, metaData, record, rowIndex, colIndex, store) { - return BUI.getRedButton('REMOVE'); - } - } ]; -}; - -MolarityGrid.prototype._openMolarityWindow = function() { - this.molarityWindow = Ext.create('Ext.window.Window', { - title : 'Molarity', - height : 220, - width : 500, - modal : true, - items : [this.molarityForm.getPanel() ] - }).show(); -}; - -MolarityGrid.prototype._getButtons = function() { - var _this = this; - return [ { - text : 'Add subunit', - icon : '../images/add.png', - handler : function() { - _this._openMolarityWindow(); - } - }]; -}; - - -MolarityGrid.prototype.updateProposal = function() { - var _this = this; - this.panel.setLoading(); - BIOSAXS.proposal.onInitialized.attach(function() { - if (BIOSAXS.proposal != null) { - var macromolecules = BIOSAXS.proposal.macromolecules; - for (var i = 0; i < macromolecules.length; i++) { - - if (macromolecules[i].macromoleculeId == _this.macromolecule.macromoleculeId) { - _this.refresh(macromolecules[i]); - _this.panel.setLoading(false); - } - } - } - }); - BIOSAXS.proposal.init(); -}; - - -MolarityGrid.prototype.getPanel = function() { - var _this = this; - - this.molarityStore = Ext.create('Ext.data.Store', { - fields : [ 'acronym', 'ratio', 'comments', 'stoichiometryId', 'name', 'molecularMass' ], - sorters : { - property : 'ratio', - direction : 'DESC' - } - }); - - this.panel = Ext.create('Ext.grid.Panel', { - store : this.molarityStore, - height : this.height, - padding : 5, - columns : this._getColumns(), - listeners : { - cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { - - /** Remove entry * */ - if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'MOLARITY_REMOVE') { - var dataAdapter = new BiosaxsDataAdapter(); - dataAdapter.onSuccess.attach(function(sender) { - _this.updateProposal(); - - }); - dataAdapter.removeStoichiometry(record.data.stoichiometryId); - _this.panel.setLoading("Removing Structure"); - } - } - }, - tbar : this._getButtons() - }); - return this.panel; -}; - -MolarityGrid.prototype._prepareData = function(macromolecule) { - /** Return an array of [{ratio,acronym, stoichiometryId, name}] **/ - var data = []; - if (macromolecule.stoichiometry != null) { - for (var i = 0; i < macromolecule.stoichiometry.length; i++) { - var hostMacromolecule = BIOSAXS.proposal.getMacromoleculeById(macromolecule.stoichiometry[i].macromoleculeId); - data.push({ - ratio : macromolecule.stoichiometry[i].ratio, - acronym : hostMacromolecule.acronym, - comments : hostMacromolecule.comments, - molecularMass : hostMacromolecule.molecularMass, - stoichiometryId : macromolecule.stoichiometry[i].stoichiometryId, - name : hostMacromolecule.name - }); - } - } - return data; -}; - -MolarityGrid.prototype.refresh = function(macromolecule) { - if (macromolecule != null){ - this.molarityStore.loadData(this._prepareData(macromolecule)); - this.molarityForm.refresh(macromolecule); - this.macromolecule = macromolecule; - } -}; - -MolarityGrid.prototype.input = function() { - return { - proposal : DATADOC.getProposal_10(), - dewars : DATADOC.getDewars_10() - - }; -}; - -MolarityGrid.prototype.test = function(targetId) { - var MolarityGrid = new MolarityGrid({ - height : 150 - }); - BIOSAXS.proposal = new Proposal(MolarityGrid.input().proposal); - var panel = MolarityGrid.getPanel(MolarityGrid.input().dewars); - panel.render(targetId); - -}; - /** * Given data analysis parsed with ResultsAssemblyWidget makes a selector grid with buffer/protein @@ -20815,135 +20815,135 @@ ResultsAssemblyGrid.prototype.test = function(targetId) { }; - -function RigidModelGrid(args) { - this.height = null; - this.width = null; - this.id = BUI.id(); - - if (args != null) { - if (args.height != null) { - this.height = args.height; - - if (args.width != null) { - this.width = args.width; - } - } - } - - this.onSelected = new Event(this); -} - - -RigidModelGrid.prototype.refresh = function(subtractions) { - var data = []; - if (subtractions != null){ - if (subtractions.length > 0){ - for (j in subtractions){ - var subtraction = subtractions[j]; - if (subtraction.rigidBodyModeling3VOs != null){ - for (i in subtraction.rigidBodyModeling3VOs){ - data.push(subtraction.rigidBodyModeling3VOs[i]); - } - } - } - } - } - this.store.loadData(data); -}; - -RigidModelGrid.prototype.getPanel = function() { - var _this = this; - this.store = Ext.create('Ext.data.Store', { - fields : [ 'symmetry', 'subtractionId', 'subUnitConfigFilePath', 'rigidBodyModelingId', 'rigidBodyModelFilePath', 'logFilePath', 'fitFilePath', 'curveConfigFilePath', - 'crossCorrConfigFilePath', 'contactDescriptionFilePath' ], - data : [] - }); - - this.store.sort([ { - property : 'name', - direction : 'ASC' - } ]); - - this.panel = Ext.create('Ext.grid.Panel', { - store : this.store, - width : this.width, - height : this.height, - margin : 10, - deferredRender : false, - columns : [ { - text : 'RBM', - dataIndex : 'rigidBodyModelFilePath', - hidden : false, - flex : 1, - renderer : function(val){ - return BUI.getFileName(val); - } - }, { - text : 'Sub Unit Conf.', - dataIndex : 'subUnitConfigFilePath', - hidden : true, - flex : 1, - renderer : function(val){ - return BUI.getFileName(val); - } - }, { - text : 'Log', - dataIndex : 'logFilePath', - hidden : true, - flex : 1, - renderer : function(val){ - return BUI.getFileName(val); - } - }, { - text : 'Fit', - dataIndex : 'fitFilePath', - hidden : false, - flex : 1, - renderer : function(val){ - return BUI.getFileName(val); - } - }, { - text : 'Curve Conf.', - dataIndex : 'curveConfigFilePath', - hidden : false, - flex : 1, - renderer : function(val){ - return BUI.getFileName(val); - } - }, { - text : 'Cross Corr.', - dataIndex : 'crossCorrConfigFilePath', - hidden : false, - flex : 1, - renderer : function(val){ - return BUI.getFileName(val); - } - }, { - text : 'Contact Desc.', - dataIndex : 'contactDescriptionFilePath', - hidden : true, - flex : 1, - renderer : function(val){ - return BUI.getFileName(val); - } - } ], - - viewConfig : { - enableTextSelection : true, - preserveScrollOnRefresh : true - }, - listeners : { - cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { - - }, - afterrender : function() { - } - } - }); - return this.panel; -}; - + +function RigidModelGrid(args) { + this.height = null; + this.width = null; + this.id = BUI.id(); + + if (args != null) { + if (args.height != null) { + this.height = args.height; + + if (args.width != null) { + this.width = args.width; + } + } + } + + this.onSelected = new Event(this); +} + + +RigidModelGrid.prototype.refresh = function(subtractions) { + var data = []; + if (subtractions != null){ + if (subtractions.length > 0){ + for (j in subtractions){ + var subtraction = subtractions[j]; + if (subtraction.rigidBodyModeling3VOs != null){ + for (i in subtraction.rigidBodyModeling3VOs){ + data.push(subtraction.rigidBodyModeling3VOs[i]); + } + } + } + } + } + this.store.loadData(data); +}; + +RigidModelGrid.prototype.getPanel = function() { + var _this = this; + this.store = Ext.create('Ext.data.Store', { + fields : [ 'symmetry', 'subtractionId', 'subUnitConfigFilePath', 'rigidBodyModelingId', 'rigidBodyModelFilePath', 'logFilePath', 'fitFilePath', 'curveConfigFilePath', + 'crossCorrConfigFilePath', 'contactDescriptionFilePath' ], + data : [] + }); + + this.store.sort([ { + property : 'name', + direction : 'ASC' + } ]); + + this.panel = Ext.create('Ext.grid.Panel', { + store : this.store, + width : this.width, + height : this.height, + margin : 10, + deferredRender : false, + columns : [ { + text : 'RBM', + dataIndex : 'rigidBodyModelFilePath', + hidden : false, + flex : 1, + renderer : function(val){ + return BUI.getFileName(val); + } + }, { + text : 'Sub Unit Conf.', + dataIndex : 'subUnitConfigFilePath', + hidden : true, + flex : 1, + renderer : function(val){ + return BUI.getFileName(val); + } + }, { + text : 'Log', + dataIndex : 'logFilePath', + hidden : true, + flex : 1, + renderer : function(val){ + return BUI.getFileName(val); + } + }, { + text : 'Fit', + dataIndex : 'fitFilePath', + hidden : false, + flex : 1, + renderer : function(val){ + return BUI.getFileName(val); + } + }, { + text : 'Curve Conf.', + dataIndex : 'curveConfigFilePath', + hidden : false, + flex : 1, + renderer : function(val){ + return BUI.getFileName(val); + } + }, { + text : 'Cross Corr.', + dataIndex : 'crossCorrConfigFilePath', + hidden : false, + flex : 1, + renderer : function(val){ + return BUI.getFileName(val); + } + }, { + text : 'Contact Desc.', + dataIndex : 'contactDescriptionFilePath', + hidden : true, + flex : 1, + renderer : function(val){ + return BUI.getFileName(val); + } + } ], + + viewConfig : { + enableTextSelection : true, + preserveScrollOnRefresh : true + }, + listeners : { + cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + + }, + afterrender : function() { + } + } + }); + return this.panel; +}; + /** * shows shipments @@ -22309,119 +22309,119 @@ StockSolutionGrid.prototype.test = function(targetId) { panel.render(targetId); }; - -function SuperpositionGrid(args) { - this.height = null; - this.width = null; - this.id = BUI.id(); - - if (args != null) { - if (args.height != null) { - this.height = args.height; - - if (args.width != null) { - this.width = args.width; - } - } - } - - this.onSelected = new Event(this); -} - -SuperpositionGrid.prototype._prepareData = function(data) { - return data; -}; - -SuperpositionGrid.prototype.refresh = function(subtractions) { - var data = []; - if (subtractions != null){ - if (subtractions.length > 0){ - for (j in subtractions){ - var subtraction = subtractions[j]; - if (subtraction.superposition3VOs != null){ - for (i in subtraction.superposition3VOs){ - data.push(subtraction.superposition3VOs[i]); - } - } - } - } - } - this.store.loadData(data); -}; - -SuperpositionGrid.prototype.getPanel = function() { - var _this = this; - this.store = Ext.create('Ext.data.Store', { - fields : [ 'abinitioModelPdbFilePath', 'aprioriPdbFilePath', 'alignedPdbFilePath' ], - data : [] - }); - - this.selModel = Ext.create('Ext.selection.RowModel', { - allowDeselect : true, - mode : 'MULTI', - listeners : { - selectionchange : function(sm, selections) { - var selected = []; - for (var i = 0; i < selections.length; i++) { - selected.push(selections[i].raw); - } - _this.onSelected.notify(selected); - } - } - }); - - this.store.sort([ { - property : 'name', - direction : 'ASC' - } ]); - - this.panel = Ext.create('Ext.grid.Panel', { - store : this.store, - selModel : this.selModel, - width : this.width, - height : this.height, - margin : 10, - deferredRender : false, - columns : [ { - text : 'Abinitio', - dataIndex : 'abinitioModelPdbFilePath', - hidden : false, - flex : 1, - renderer : function(val){ - return BUI.getFileName(val); - } - }, { - text : 'Apriori', - dataIndex : 'aprioriPdbFilePath', - hidden : false, - flex : 1, - renderer : function(val){ - return BUI.getFileName(val); - } - }, { - text : 'Aligned', - dataIndex : 'alignedPdbFilePath', - hidden : false, - flex : 1, - renderer : function(val){ - return BUI.getFileName(val); - } - } ], - - viewConfig : { - enableTextSelection : true, - preserveScrollOnRefresh : true - }, - listeners : { - cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { - - }, - afterrender : function() { - } - } - }); - return this.panel; -}; + +function SuperpositionGrid(args) { + this.height = null; + this.width = null; + this.id = BUI.id(); + + if (args != null) { + if (args.height != null) { + this.height = args.height; + + if (args.width != null) { + this.width = args.width; + } + } + } + + this.onSelected = new Event(this); +} + +SuperpositionGrid.prototype._prepareData = function(data) { + return data; +}; + +SuperpositionGrid.prototype.refresh = function(subtractions) { + var data = []; + if (subtractions != null){ + if (subtractions.length > 0){ + for (j in subtractions){ + var subtraction = subtractions[j]; + if (subtraction.superposition3VOs != null){ + for (i in subtraction.superposition3VOs){ + data.push(subtraction.superposition3VOs[i]); + } + } + } + } + } + this.store.loadData(data); +}; + +SuperpositionGrid.prototype.getPanel = function() { + var _this = this; + this.store = Ext.create('Ext.data.Store', { + fields : [ 'abinitioModelPdbFilePath', 'aprioriPdbFilePath', 'alignedPdbFilePath' ], + data : [] + }); + + this.selModel = Ext.create('Ext.selection.RowModel', { + allowDeselect : true, + mode : 'MULTI', + listeners : { + selectionchange : function(sm, selections) { + var selected = []; + for (var i = 0; i < selections.length; i++) { + selected.push(selections[i].raw); + } + _this.onSelected.notify(selected); + } + } + }); + + this.store.sort([ { + property : 'name', + direction : 'ASC' + } ]); + + this.panel = Ext.create('Ext.grid.Panel', { + store : this.store, + selModel : this.selModel, + width : this.width, + height : this.height, + margin : 10, + deferredRender : false, + columns : [ { + text : 'Abinitio', + dataIndex : 'abinitioModelPdbFilePath', + hidden : false, + flex : 1, + renderer : function(val){ + return BUI.getFileName(val); + } + }, { + text : 'Apriori', + dataIndex : 'aprioriPdbFilePath', + hidden : false, + flex : 1, + renderer : function(val){ + return BUI.getFileName(val); + } + }, { + text : 'Aligned', + dataIndex : 'alignedPdbFilePath', + hidden : false, + flex : 1, + renderer : function(val){ + return BUI.getFileName(val); + } + } ], + + viewConfig : { + enableTextSelection : true, + preserveScrollOnRefresh : true + }, + listeners : { + cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + + }, + afterrender : function() { + } + } + }); + return this.panel; +}; /** * See ExperimentGrid @@ -26158,502 +26158,502 @@ ConcentrationHTMLTableWidget.prototype.input = function() { "values" : [] } } - } ] - }, - "concentrationLabel" : "7.17 mg/ml ,3.53 mg/ml ,1.75 mg/ml ,0.91 mg/ml " + } ] + }, + "concentrationLabel" : "7.17 mg/ml ,3.53 mg/ml ,1.75 mg/ml ,0.91 mg/ml " + } + }; +}; + +ConcentrationHTMLTableWidget.prototype.test = function(targetId) { + var concentrationHTMLTableWidget = new ConcentrationHTMLTableWidget(); + document.getElementById(targetId).innerHTML = concentrationHTMLTableWidget.getPanel(concentrationHTMLTableWidget.input().data); +}; + +function DataCollectionWidget() { + +}; + +DataCollectionWidget.prototype.refresh = function(subtractionId) { + +}; + +DataCollectionWidget.prototype.getMacromolecule = function(data) { + for (var i = 0; i < data.length; i++) { + if (data[i].macromoleculeId != null) { + return BIOSAXS.proposal.getMacromoleculeById(data[i].macromoleculeId); + } + } + return null; +}; + +DataCollectionWidget.prototype.getMacromoleculeContainer = function(data) { + var macromolecule = this.getMacromolecule(data); + + var disabled = false; + if (macromolecule == null) { + disabled = true; + } + + var acronym = (macromolecule == null ? "" : macromolecule.acronym); + var mm = (macromolecule == null ? "" : macromolecule.molecularMass); + var comments = (macromolecule == null ? "" : macromolecule.comments); + + return Ext.create('Ext.Panel', { + width : 500, + height : 200, + disabled : disabled, + title : "Macromolecule", + layout : 'form', + bodyPadding : 5, + defaultType : 'textfield', + tbar : { + defaultButtonUI : 'default', + items : [ { + xtype : 'button', + text : 'Edit', + cls : 'btn-with-border', + handler : function() { + var macromoleculeWindow = new MacromoleculeWindow(); + macromoleculeWindow.draw(macromolecule); + /** TODO: update when save **/ + } + } ] + }, + items : [ { + fieldLabel : 'Acronym', + name : 'first', + readOnly : true, + value : acronym + }, { + fieldLabel : 'Molecular Mass', + name : 'last', + readOnly : true, + value : mm + }, { + xtype : 'textareafield', + fieldLabel : 'Comments', + value : '', + name : 'last', + readOnly : true, + value : comments + } ] + }); +}; + +DataCollectionWidget.prototype.getBufferContainer = function(data) { + var _this = this; + var buffer = BIOSAXS.proposal.getBufferById(data[0].bufferId); + + return Ext.create('Ext.Panel', { + width : 500, + height : 200, + title : "Buffer", + layout : 'form', + // collapsed : true, + // collapsible : true, + bodyPadding : 5, + defaultType : 'textfield', + style : { + padding : '0px 0px 0px 2px' + }, + tbar : { + defaultButtonUI : 'default', + items : [ { + xtype : 'button', + text : 'Edit', + cls : 'btn-with-border', + handler : function() { + var bufferWindow = new BufferWindow(); + bufferWindow.draw(BIOSAXS.proposal.getBufferById(data[0].bufferId)); + /** TODO: update when save **/ + } + + } ] + }, + items : [ { + fieldLabel : 'Buffer', + name : 'acronym', + readOnly : true, + value : buffer.acronym + }, { + fieldLabel : 'Composition', + name : 'last', + readOnly : true, + value : buffer.composition + }, { + xtype : 'textareafield', + fieldLabel : 'Comments', + value : buffer.comments, + name : 'last', + readOnly : true + + } ] + }); +}; + +DataCollectionWidget.prototype.getSpecimenContainer = function(data) { + return Ext.create('Ext.container.Container', { + layout : { + type : 'hbox' + }, + style : { + margin : '10px 10px 10px 10px' + }, + border : 0, + style : { + borderColor : '#000000', + borderStyle : 'solid', + borderWidth : '1px' + }, + defaults : { + labelWidth : 80, + // implicitly create Container by specifying xtype + xtype : 'datefield', + flex : 1, + + }, + items : [ this.getMacromoleculeContainer(data), this.getBufferContainer(data) ] + }); +}; + +DataCollectionWidget.prototype.getSeparator = function() { + return { + html : "
", + border : 0 + } +}; + +DataCollectionWidget.prototype.getFitStructurePanel = function(data) { + var _this = this; + + return Ext.create('Ext.container.Container', { + layout : { + type : 'hbox' + }, + style : { + margin : '10px 10px 10px 10px' + }, + border : 0, + style : { + borderColor : '#000000', + borderStyle : 'solid', + borderWidth : '1px' + }, + defaults : { + labelWidth : 80, + xtype : 'datefield', + flex : 1 + }, + items : [ + + ] + }); +}; + +DataCollectionWidget.prototype.getFitStructurePanelWorkflow = function(data) { + var _this = this; + var macromolecule = this.getMacromolecule(data); + + var items = []; + + /** Adding all the pdb files linked to the macromolecule **/ + // if (macromolecule.structure3VOs != null) { + // if (macromolecule.structure3VOs.length > 0) { + // + // items.push(); + // } + // } else { + // items.push({ + // html : "No apriori information added to this macromolecule. Apriori information needed for further analysis", + // margin : "5 5 5 5" + // }); + // } + var fitStructureToExperimentDataGrid = new FitStructureToExperimentDataGrid(); + + fitStructureToExperimentDataGrid.refresh(_this.data[0].subtractionId, _this.getMacromolecule(data)); + return Ext.create('Ext.container.Container', { + style : { + margin : '10px 10px 10px 10px' + }, + border : 0, + + style : { + borderColor : '#000000', + borderStyle : 'solid', + borderWidth : '1px' + }, + defaults : { + labelWidth : 80, + xtype : 'datefield', + flex : 1 + }, + items : [ fitStructureToExperimentDataGrid.getPanel() ], + listeners : { + afterrender : function() { + } + } + }); +}; +/** Static method **/ +DataCollectionWidget.prototype.RUNFitScattering = function(subtractionId, structureId) { + var _this = this; + /** Add to Workflow **/ + var adapter = new BiosaxsDataAdapter(); + var workflow = { + 'workflowTitle' : 'FitExperimentalDatatoStructure', + 'comments' : 'FitExperimentalDatatoStructure run from ISPyB for subtractionId: ' + subtractionId + ' and structureId ' + structureId + } + + var inputs = [ { + name : 'subtractionId', + value : subtractionId + }, { + name : 'structureId', + value : structureId + } ]; + + adapter.onSuccess.attach(function(sender, data) { + /** Add to Fit **/ + var adapter2 = new BiosaxsDataAdapter(); + var fit = { + 'workflowId' : data.workflowId, + 'subtractionId' : subtractionId, + 'structureId' : structureId, + 'comments' : 'Sent to workflow engine' + } + adapter2.onSuccess.attach(function(sender, fit) { + + }); + adapter2.addFitStructureData(fit); + + }); + adapter.addWorkflow(workflow, inputs); + +}; + +DataCollectionWidget.prototype.getSuperpositionTab = function(data) { + var _this = this; + + this.superpositionGrid = new SuperpositionGrid(); + + return Ext.create('Ext.container.Container', { + style : { + margin : '10px 10px 10px 10px' + }, + border : 0, + style : { + borderColor : '#000000', + borderStyle : 'solid', + borderWidth : '1px' + }, + items : [ _this.superpositionGrid.getPanel() ], + listeners : { + cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + + }, + afterrender : function() { + /** Getting Rigid bodies **/ + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, result) { + _this.superpositionGrid.refresh(result); + }); + adapter.getSuperpositionBySubtractionId(data[1].subtractionId); + } + } + }); +}; + +DataCollectionWidget.prototype.getAdvancedTab = function(data) { + var _this = this; + return Ext.create('Ext.container.Container', { + style : { + margin : '10px 10px 10px 10px' + }, + border : 0, + style : { + borderColor : '#000000', + borderStyle : 'solid', + borderWidth : '1px' + }, + items : [ _this.getFitStructurePanel(data), _this.getFitStructurePanelWorkflow(data) ] + }); +}; + +DataCollectionWidget.prototype.getRigiBodyForm = function(data) { + var _this = this; + _this.rigiBodyGrid = new RigidModelGrid(); + return _this.rigiBodyGrid.getPanel(); + +}; + +DataCollectionWidget.prototype.getRigidBodyModelingTab = function(data) { + var _this = this; + return Ext.create('Ext.container.Container', { + style : { + margin : '10px 10px 10px 10px' + }, + border : 0, + style : { + borderColor : '#000000', + borderStyle : 'solid', + borderWidth : '1px' + }, + items : [ _this.getRigiBodyForm(data) ], + listeners : { + cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + + }, + afterrender : function() { + /** Getting Rigid bodies **/ + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, result) { + _this.rigiBodyGrid.refresh(result); + }); + adapter.getRigidBodyModelingBySubtractionId(data[1].subtractionId); + } + } + }); +}; + +DataCollectionWidget.prototype.getTabs = function(data) { + var _this = this; + + this.subtractionCurveVisualizer = new SubtractionCurveVisualizer(); + + this.tabs = Ext.createWidget('tabpanel', { + activeTab : 1, + plain : true, + defaults : { + autoScroll : true, + bodyPadding : 10 + }, + items : [ { + title : 'Solution', + items : [ _this.getSpecimenContainer(data) ] + }, { + title : 'Primary Data Reduction', + active : true, + tabConfig : { + xtype : 'tab', + margins : '0 0 0 20', + }, + items : [ _this.subtractionCurveVisualizer.getPanel() ] + }, { + title : 'Abinitio', + items : [ _this.getAbinitioModellingContainer(data), _this.getSeparator(), _this.getModelViz(data) ] + }, { + title : 'Mixtures', + items : [ _this.getAdvancedTab(data) ] + }, { + title : 'Superpositions', + items : [ _this.getSuperpositionTab(data) ] + }, { + title : 'Rigid Body Modeling', + items : [ _this.getRigidBodyModelingTab(data) ] + } ] + }); + return this.tabs; +}; + +DataCollectionWidget.prototype.getPanel = function(data) { + var _this = this; + _this.data = data; + this.panel = Ext.create('Ext.container.Container', { + width : 1000, + layout : { + type : 'vbox', + align : 'stretch', + padding : 5 + }, + items : [ _this.getTabs(data) + + ] + }); + + this.panel.on("afterrender", function() { + _this.subtractionCurveVisualizer.refresh([ _this.data[1].mergeId ], [ _this.data[1].subtractionId ]); + }); + return this.panel; +}; + +DataCollectionWidget.prototype.getModelViz = function(data) { + this.modelViz = new ModelVisualizerForm({ + height : 800, + width : 1000 + }); + return this.modelViz.getPanel(); +}; + +/** + * PRIMARY DATA PROCESSING + */ +DataCollectionWidget.prototype.getPrimaryDataProcessingContainer = function(data) { + var _this = this; + this.primaryDataProcessingGrid = new PrimaryDataProcessingGrid({ + height : 250, + width : 1000, + title : 'Primary Data Processing' + }); + + this.primaryDataProcessingGrid.onSelected.attach(function(sender, selected) { + /** Refresh tabs **/ + var averagesId = []; + var subtractionIds = []; + + var subtractionKeys = {}; + for (var i = 0; i < selected.length; i++) { + if (selected[i].mergeId != null) { + averagesId.push(selected[i].mergeId); + } + + if (selected[i].subtractionId != null) { + if (selected[i].macromoleculeId != null) { + if (subtractionKeys[selected[i].subtractionId] == null) { + subtractionIds.push(selected[i].subtractionId); + } + subtractionKeys[selected[i].subtractionId] = true; + } + } } - }; + + /** Refreshing Primary Data Reduction **/ + _this.subtractionCurveVisualizer.refresh(averagesId, subtractionIds); + + }); + return this.primaryDataProcessingGrid.getPanel(data); }; +/** + * getAbinitioModellingContainer + */ +DataCollectionWidget.prototype.getAbinitioModellingContainer = function(data) { + var _this = this; -ConcentrationHTMLTableWidget.prototype.test = function(targetId) { - var concentrationHTMLTableWidget = new ConcentrationHTMLTableWidget(); - document.getElementById(targetId).innerHTML = concentrationHTMLTableWidget.getPanel(concentrationHTMLTableWidget.input().data); + this.abinitioGrid = new AbinitioGrid(); + this.abinitioGrid.onSelected.attach(function(sender, models) { + _this.modelViz.refresh(models); + + }); + /** It may be abinitio models linked to the buffers **/ + var abinitioIdList = []; + for (var i = 0; i < data.length; i++) { + abinitioIdList.push(data[i].abInitioId); + } + + var uniqueIds = []; + $.each(abinitioIdList, function(i, el) { + if ($.inArray(el, uniqueIds) === -1) + uniqueIds.push(el); + }); + + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, data) { + _this.abinitioGrid.refresh(data); + }); + adapter.getAbinitioByIdsList(uniqueIds); + return this.abinitioGrid.getPanel([]); }; -function DataCollectionWidget() { - -}; - -DataCollectionWidget.prototype.refresh = function(subtractionId) { - -}; - -DataCollectionWidget.prototype.getMacromolecule = function(data) { - for (var i = 0; i < data.length; i++) { - if (data[i].macromoleculeId != null) { - return BIOSAXS.proposal.getMacromoleculeById(data[i].macromoleculeId); - } - } - return null; -}; - -DataCollectionWidget.prototype.getMacromoleculeContainer = function(data) { - var macromolecule = this.getMacromolecule(data); - - var disabled = false; - if (macromolecule == null) { - disabled = true; - } - - var acronym = (macromolecule == null ? "" : macromolecule.acronym); - var mm = (macromolecule == null ? "" : macromolecule.molecularMass); - var comments = (macromolecule == null ? "" : macromolecule.comments); - - return Ext.create('Ext.Panel', { - width : 500, - height : 200, - disabled : disabled, - title : "Macromolecule", - layout : 'form', - bodyPadding : 5, - defaultType : 'textfield', - tbar : { - defaultButtonUI : 'default', - items : [ { - xtype : 'button', - text : 'Edit', - cls : 'btn-with-border', - handler : function() { - var macromoleculeWindow = new MacromoleculeWindow(); - macromoleculeWindow.draw(macromolecule); - /** TODO: update when save **/ - } - } ] - }, - items : [ { - fieldLabel : 'Acronym', - name : 'first', - readOnly : true, - value : acronym - }, { - fieldLabel : 'Molecular Mass', - name : 'last', - readOnly : true, - value : mm - }, { - xtype : 'textareafield', - fieldLabel : 'Comments', - value : '', - name : 'last', - readOnly : true, - value : comments - } ] - }); -}; - -DataCollectionWidget.prototype.getBufferContainer = function(data) { - var _this = this; - var buffer = BIOSAXS.proposal.getBufferById(data[0].bufferId); - - return Ext.create('Ext.Panel', { - width : 500, - height : 200, - title : "Buffer", - layout : 'form', - // collapsed : true, - // collapsible : true, - bodyPadding : 5, - defaultType : 'textfield', - style : { - padding : '0px 0px 0px 2px' - }, - tbar : { - defaultButtonUI : 'default', - items : [ { - xtype : 'button', - text : 'Edit', - cls : 'btn-with-border', - handler : function() { - var bufferWindow = new BufferWindow(); - bufferWindow.draw(BIOSAXS.proposal.getBufferById(data[0].bufferId)); - /** TODO: update when save **/ - } - - } ] - }, - items : [ { - fieldLabel : 'Buffer', - name : 'acronym', - readOnly : true, - value : buffer.acronym - }, { - fieldLabel : 'Composition', - name : 'last', - readOnly : true, - value : buffer.composition - }, { - xtype : 'textareafield', - fieldLabel : 'Comments', - value : buffer.comments, - name : 'last', - readOnly : true - - } ] - }); -}; - -DataCollectionWidget.prototype.getSpecimenContainer = function(data) { - return Ext.create('Ext.container.Container', { - layout : { - type : 'hbox' - }, - style : { - margin : '10px 10px 10px 10px' - }, - border : 0, - style : { - borderColor : '#000000', - borderStyle : 'solid', - borderWidth : '1px' - }, - defaults : { - labelWidth : 80, - // implicitly create Container by specifying xtype - xtype : 'datefield', - flex : 1, - - }, - items : [ this.getMacromoleculeContainer(data), this.getBufferContainer(data) ] - }); -}; - -DataCollectionWidget.prototype.getSeparator = function() { - return { - html : "
", - border : 0 - } -}; - -DataCollectionWidget.prototype.getFitStructurePanel = function(data) { - var _this = this; - - return Ext.create('Ext.container.Container', { - layout : { - type : 'hbox' - }, - style : { - margin : '10px 10px 10px 10px' - }, - border : 0, - style : { - borderColor : '#000000', - borderStyle : 'solid', - borderWidth : '1px' - }, - defaults : { - labelWidth : 80, - xtype : 'datefield', - flex : 1 - }, - items : [ - - ] - }); -}; - -DataCollectionWidget.prototype.getFitStructurePanelWorkflow = function(data) { - var _this = this; - var macromolecule = this.getMacromolecule(data); - - var items = []; - - /** Adding all the pdb files linked to the macromolecule **/ - // if (macromolecule.structure3VOs != null) { - // if (macromolecule.structure3VOs.length > 0) { - // - // items.push(); - // } - // } else { - // items.push({ - // html : "No apriori information added to this macromolecule. Apriori information needed for further analysis", - // margin : "5 5 5 5" - // }); - // } - var fitStructureToExperimentDataGrid = new FitStructureToExperimentDataGrid(); - - fitStructureToExperimentDataGrid.refresh(_this.data[0].subtractionId, _this.getMacromolecule(data)); - return Ext.create('Ext.container.Container', { - style : { - margin : '10px 10px 10px 10px' - }, - border : 0, - - style : { - borderColor : '#000000', - borderStyle : 'solid', - borderWidth : '1px' - }, - defaults : { - labelWidth : 80, - xtype : 'datefield', - flex : 1 - }, - items : [ fitStructureToExperimentDataGrid.getPanel() ], - listeners : { - afterrender : function() { - } - } - }); -}; -/** Static method **/ -DataCollectionWidget.prototype.RUNFitScattering = function(subtractionId, structureId) { - var _this = this; - /** Add to Workflow **/ - var adapter = new BiosaxsDataAdapter(); - var workflow = { - 'workflowTitle' : 'FitExperimentalDatatoStructure', - 'comments' : 'FitExperimentalDatatoStructure run from ISPyB for subtractionId: ' + subtractionId + ' and structureId ' + structureId - } - - var inputs = [ { - name : 'subtractionId', - value : subtractionId - }, { - name : 'structureId', - value : structureId - } ]; - - adapter.onSuccess.attach(function(sender, data) { - /** Add to Fit **/ - var adapter2 = new BiosaxsDataAdapter(); - var fit = { - 'workflowId' : data.workflowId, - 'subtractionId' : subtractionId, - 'structureId' : structureId, - 'comments' : 'Sent to workflow engine' - } - adapter2.onSuccess.attach(function(sender, fit) { - - }); - adapter2.addFitStructureData(fit); - - }); - adapter.addWorkflow(workflow, inputs); - -}; - -DataCollectionWidget.prototype.getSuperpositionTab = function(data) { - var _this = this; - - this.superpositionGrid = new SuperpositionGrid(); - - return Ext.create('Ext.container.Container', { - style : { - margin : '10px 10px 10px 10px' - }, - border : 0, - style : { - borderColor : '#000000', - borderStyle : 'solid', - borderWidth : '1px' - }, - items : [ _this.superpositionGrid.getPanel() ], - listeners : { - cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { - - }, - afterrender : function() { - /** Getting Rigid bodies **/ - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, result) { - _this.superpositionGrid.refresh(result); - }); - adapter.getSuperpositionBySubtractionId(data[1].subtractionId); - } - } - }); -}; - -DataCollectionWidget.prototype.getAdvancedTab = function(data) { - var _this = this; - return Ext.create('Ext.container.Container', { - style : { - margin : '10px 10px 10px 10px' - }, - border : 0, - style : { - borderColor : '#000000', - borderStyle : 'solid', - borderWidth : '1px' - }, - items : [ _this.getFitStructurePanel(data), _this.getFitStructurePanelWorkflow(data) ] - }); -}; - -DataCollectionWidget.prototype.getRigiBodyForm = function(data) { - var _this = this; - _this.rigiBodyGrid = new RigidModelGrid(); - return _this.rigiBodyGrid.getPanel(); - -}; - -DataCollectionWidget.prototype.getRigidBodyModelingTab = function(data) { - var _this = this; - return Ext.create('Ext.container.Container', { - style : { - margin : '10px 10px 10px 10px' - }, - border : 0, - style : { - borderColor : '#000000', - borderStyle : 'solid', - borderWidth : '1px' - }, - items : [ _this.getRigiBodyForm(data) ], - listeners : { - cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { - - }, - afterrender : function() { - /** Getting Rigid bodies **/ - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, result) { - _this.rigiBodyGrid.refresh(result); - }); - adapter.getRigidBodyModelingBySubtractionId(data[1].subtractionId); - } - } - }); -}; - -DataCollectionWidget.prototype.getTabs = function(data) { - var _this = this; - - this.subtractionCurveVisualizer = new SubtractionCurveVisualizer(); - - this.tabs = Ext.createWidget('tabpanel', { - activeTab : 1, - plain : true, - defaults : { - autoScroll : true, - bodyPadding : 10 - }, - items : [ { - title : 'Solution', - items : [ _this.getSpecimenContainer(data) ] - }, { - title : 'Primary Data Reduction', - active : true, - tabConfig : { - xtype : 'tab', - margins : '0 0 0 20', - }, - items : [ _this.subtractionCurveVisualizer.getPanel() ] - }, { - title : 'Abinitio', - items : [ _this.getAbinitioModellingContainer(data), _this.getSeparator(), _this.getModelViz(data) ] - }, { - title : 'Mixtures', - items : [ _this.getAdvancedTab(data) ] - }, { - title : 'Superpositions', - items : [ _this.getSuperpositionTab(data) ] - }, { - title : 'Rigid Body Modeling', - items : [ _this.getRigidBodyModelingTab(data) ] - } ] - }); - return this.tabs; -}; - -DataCollectionWidget.prototype.getPanel = function(data) { - var _this = this; - _this.data = data; - this.panel = Ext.create('Ext.container.Container', { - width : 1000, - layout : { - type : 'vbox', - align : 'stretch', - padding : 5 - }, - items : [ _this.getTabs(data) - - ] - }); - - this.panel.on("afterrender", function() { - _this.subtractionCurveVisualizer.refresh([ _this.data[1].mergeId ], [ _this.data[1].subtractionId ]); - }); - return this.panel; -}; - -DataCollectionWidget.prototype.getModelViz = function(data) { - this.modelViz = new ModelVisualizerForm({ - height : 800, - width : 1000 - }); - return this.modelViz.getPanel(); -}; - -/** - * PRIMARY DATA PROCESSING - */ -DataCollectionWidget.prototype.getPrimaryDataProcessingContainer = function(data) { - var _this = this; - this.primaryDataProcessingGrid = new PrimaryDataProcessingGrid({ - height : 250, - width : 1000, - title : 'Primary Data Processing' - }); - - this.primaryDataProcessingGrid.onSelected.attach(function(sender, selected) { - /** Refresh tabs **/ - var averagesId = []; - var subtractionIds = []; - - var subtractionKeys = {}; - for (var i = 0; i < selected.length; i++) { - if (selected[i].mergeId != null) { - averagesId.push(selected[i].mergeId); - } - - if (selected[i].subtractionId != null) { - if (selected[i].macromoleculeId != null) { - if (subtractionKeys[selected[i].subtractionId] == null) { - subtractionIds.push(selected[i].subtractionId); - } - subtractionKeys[selected[i].subtractionId] = true; - } - } - } - - /** Refreshing Primary Data Reduction **/ - _this.subtractionCurveVisualizer.refresh(averagesId, subtractionIds); - - }); - return this.primaryDataProcessingGrid.getPanel(data); -}; -/** - * getAbinitioModellingContainer - */ -DataCollectionWidget.prototype.getAbinitioModellingContainer = function(data) { - var _this = this; - - this.abinitioGrid = new AbinitioGrid(); - this.abinitioGrid.onSelected.attach(function(sender, models) { - _this.modelViz.refresh(models); - - }); - /** It may be abinitio models linked to the buffers **/ - var abinitioIdList = []; - for (var i = 0; i < data.length; i++) { - abinitioIdList.push(data[i].abInitioId); - } - - var uniqueIds = []; - $.each(abinitioIdList, function(i, el) { - if ($.inArray(el, uniqueIds) === -1) - uniqueIds.push(el); - }); - - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, data) { - _this.abinitioGrid.refresh(data); - }); - adapter.getAbinitioByIdsList(uniqueIds); - return this.abinitioGrid.getPanel([]); -}; - /** * Edit the information of a buffer * @@ -29163,258 +29163,258 @@ ShippingWidget.prototype.refresh = function() { }; -/** - * Widget container of Specimen grid and samplePlate widget - * Depending of the sample changer layout it may be displayed vertically or horizontally - * - * @param args - * - * #onExperimentChanged It happens when specimen are modified - */ -function SpecimenWidget(args){ - - this.width = 1000; - this.height = 600; - - if (args != null){ - if (args.width != null){ - this.width = args.width; - } - if (args.height != null){ - this.height = args.height; - } - } - - var _this = this; - - /** Specimen Grid **/ - this.specimenGrid = new SpecimenGrid({ - minHeight : 425, - selectionMode : "SINGLE", - editEnabled : false, - updateRowEnabled : true, - width : 900, - showTitle : false - }); - - - this.specimenGrid.onSpecimenChanged.attach(function(sender, specimen) { - _this.experiment.setSpecimenById(specimen); - _this.refresh(_this.experiment); - }); - - this.specimenGrid.onSelected.attach(function(sender, specimens) { - if (specimens.length > 0) { - _this.specimenSelected = specimens[0]; - } else { - _this.specimenSelected = null; - } - _this.samplePlateGroupWidget.selectSpecimens(specimens); - }); - - - /** Sample plate Widget **/ - this.samplePlateGroupWidget = new SamplePlateGroupWidget({ - showTitle : false, - height : 250, - margin : 5, - bbar : true - }); - - - this.samplePlateGroupWidget.onExperimentChanged.attach(function(sender, json) { - _this.refresh(new Experiment(json)); - }); - - this.samplePlateGroupWidget.onClick.attach(function(sender, args) { - /** Clicking on a plate * */ - var row = args.row; - var column = args.column; - var samplePlateId = args.samplePlate.samplePlateId; - - /** is specimen selected on the grid? * */ - if (_this.specimenSelected != null) { - /** Is position target empty * */ - if (_this.experiment.getSampleByPosition(args.samplePlate.samplePlateId, args.row, args.column).length == 0) { - var specimen = _this.experiment.getSampleById(_this.specimenSelected.specimenId); - if (specimen.sampleplateposition3VO == null) { - specimen.sampleplateposition3VO = {}; - } - - specimen.sampleplateposition3VO = { - columnNumber : column, - rowNumber : row, - samplePlateId : samplePlateId - }; - - _this.samplePlateGroupWidget.panel.setLoading("ISPyB: Saving specimen"); - var adapter = new BiosaxsDataAdapter(); - /** If success * */ - adapter.onSuccess.attach(function(sender, experiment) { - _this.samplePlateGroupWidget.panel.setLoading(false); - }); - - adapter.onError.attach(function(sender, error) { - _this.samplePlateGroupWidget.panel.setLoading(false); - showError(error); - }); - - adapter.saveSpecimen(specimen, _this.experiment); - - _this.samplePlateGroupWidget.refresh(_this.experiment); - _this.specimenGrid.refresh(_this.experiment); - //_this.refresh(_this.experiment); - _this.specimenGrid.deselectAll(); - - } else { - /** - * Can we merge? We can merge when specimen are the - * same. So, same buffer, macromolecule, concentration * - */ - var target = _this.experiment.getSampleByPosition(args.samplePlate.samplePlateId, args.row, args.column)[0]; - var specimen = _this.experiment.getSampleById(_this.specimenSelected.specimenId); - - if ((specimen.bufferId == target.bufferId) && (specimen.concentration == target.concentration)) { - if (((specimen.macromolecule3VO != null) && (target.macromolecule3VO != null) && (specimen.macromolecule3VO.macromoleculeId == target.macromolecule3VO.macromoleculeId)) || - ((specimen.macromolecule3VO == null) && (target.macromolecule3VO == null))) { - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, data) { - _this.refresh(new Experiment(data)); - _this.samplePlateGroupWidget.panel.setLoading(false); - - _this.onExperimentChanged.notify(experiment); - }); - adapter.onError.attach(function(sender, error) { - _this.samplePlateGroupWidget.panel.setLoading(false); - showError(error); - }); - _this.samplePlateGroupWidget.panel.setLoading("ISPyB: Merging specimens"); - adapter.mergeSpecimens(specimen.specimenId, target.specimenId); - } - } else { - alert("Well is not empty. Select another well!"); - } - } - } else { - var specimen = _this.experiment.getSampleByPosition(args.samplePlate.samplePlateId, args.row, args.column)[0]; - if (specimen != null) { - _this.specimenGrid.selectById(specimen.specimenId); - } - } - }); - - /** Events **/ - this.onExperimentChanged = new Event(this); -}; - -/** - * Return vbox or hbox depending on the slot positions of the plates - */ -SpecimenWidget.prototype.getContainerLayoutConfiguration = function(experiment){ - var dimensions = this.samplePlateGroupWidget.getDimensions(experiment.getSamplePlates()); - if (dimensions.maxSlotPositionRow < dimensions.maxSlotPositionColumn){ - return { - layout : "vbox", - specimenGridWidth : this.width - 10, - specimenGridHeight : this.height - 260, - samplePlateGroupWidth : this.width - 10, - samplePlateGroupHeight : 250 - }; - } - return { - layout : "hbox", - samplePlateGroupWidth : this.width*1/3 -10, - samplePlateGroupHeight : this.height - 10, - specimenGridWidth : this.width*2/3, - specimenGridHeight : this.height - 10 - }; - -}; - - -SpecimenWidget.prototype.refresh = function(experiment){ - this.experiment = experiment; - - /** Removing all components **/ - this.panel.removeAll(); - - var layoutConfiguration = this.getContainerLayoutConfiguration(experiment); - - /** Setting new width and height for layout vbox and hbox **/ - this.specimenGrid.width = layoutConfiguration.specimenGridWidth; - this.specimenGrid.height = layoutConfiguration.specimenGridHeight; - - this.samplePlateGroupWidget.width = layoutConfiguration.samplePlateGroupWidth; - this.samplePlateGroupWidget.height = layoutConfiguration.samplePlateGroupHeight; - - if (layoutConfiguration.layout == "hbox"){ - this.specimenGrid.margin = "0 0 0 5"; - this.specimenGrid.width =this.specimenGrid.width - 5; - } - /** Insert container depending on layout [vertical|horizontal] */ - var container = Ext.create('Ext.container.Container', { - layout : layoutConfiguration.layout, - height : this.height, - width : this.width, - padding : '2px', - items : [ ] - }); - if (layoutConfiguration.layout == "vbox"){ - container.insert(this.specimenGrid.getPanel()); - container.insert(this.samplePlateGroupWidget.getPanel()); - } - else{ - container.insert(this.samplePlateGroupWidget.getPanel()); - container.insert(this.specimenGrid.getPanel()); - } - - /** Insert Widget **/ - this.panel.insert(container); - - /** Load data **/ - this.specimenGrid.refresh(experiment); - this.samplePlateGroupWidget.refresh(experiment); - - -}; - -/** It creates a dummy container to be inserted the plates once the method refresh has been called - * This is necessay because we can not know the sample changer layout before hand - * **/ -SpecimenWidget.prototype.getPanel = function(){ - this.panel = Ext.create('Ext.container.Container', { - layout : 'vbox', - height : this.height, - border : 0, - margin : 5, - width : this.width, - items : [] - }); - return this.panel; -}; - - -SpecimenWidget.prototype.input = function() { - return { - experiment : DATADOC.getExperiment_10(), - proposal : DATADOC.getProposal_10() - }; -}; - -SpecimenWidget.prototype.test = function(targetId) { - var specimenWidget = new SpecimenWidget({ - height : 500, - width : 1000 - }); - BIOSAXS.proposal = new Proposal(specimenWidget.input().proposal); - var experiment = new Experiment(specimenWidget.input().experiment); - var panel = specimenWidget.getPanel(); - panel.render(targetId); - specimenWidget.refresh(experiment); - -}; - - +/** + * Widget container of Specimen grid and samplePlate widget + * Depending of the sample changer layout it may be displayed vertically or horizontally + * + * @param args + * + * #onExperimentChanged It happens when specimen are modified + */ +function SpecimenWidget(args){ + + this.width = 1000; + this.height = 600; + + if (args != null){ + if (args.width != null){ + this.width = args.width; + } + if (args.height != null){ + this.height = args.height; + } + } + + var _this = this; + + /** Specimen Grid **/ + this.specimenGrid = new SpecimenGrid({ + minHeight : 425, + selectionMode : "SINGLE", + editEnabled : false, + updateRowEnabled : true, + width : 900, + showTitle : false + }); + + + this.specimenGrid.onSpecimenChanged.attach(function(sender, specimen) { + _this.experiment.setSpecimenById(specimen); + _this.refresh(_this.experiment); + }); + + this.specimenGrid.onSelected.attach(function(sender, specimens) { + if (specimens.length > 0) { + _this.specimenSelected = specimens[0]; + } else { + _this.specimenSelected = null; + } + _this.samplePlateGroupWidget.selectSpecimens(specimens); + }); + + + /** Sample plate Widget **/ + this.samplePlateGroupWidget = new SamplePlateGroupWidget({ + showTitle : false, + height : 250, + margin : 5, + bbar : true + }); + + + this.samplePlateGroupWidget.onExperimentChanged.attach(function(sender, json) { + _this.refresh(new Experiment(json)); + }); + + this.samplePlateGroupWidget.onClick.attach(function(sender, args) { + /** Clicking on a plate * */ + var row = args.row; + var column = args.column; + var samplePlateId = args.samplePlate.samplePlateId; + + /** is specimen selected on the grid? * */ + if (_this.specimenSelected != null) { + /** Is position target empty * */ + if (_this.experiment.getSampleByPosition(args.samplePlate.samplePlateId, args.row, args.column).length == 0) { + var specimen = _this.experiment.getSampleById(_this.specimenSelected.specimenId); + if (specimen.sampleplateposition3VO == null) { + specimen.sampleplateposition3VO = {}; + } + + specimen.sampleplateposition3VO = { + columnNumber : column, + rowNumber : row, + samplePlateId : samplePlateId + }; + + _this.samplePlateGroupWidget.panel.setLoading("ISPyB: Saving specimen"); + var adapter = new BiosaxsDataAdapter(); + /** If success * */ + adapter.onSuccess.attach(function(sender, experiment) { + _this.samplePlateGroupWidget.panel.setLoading(false); + }); + + adapter.onError.attach(function(sender, error) { + _this.samplePlateGroupWidget.panel.setLoading(false); + showError(error); + }); + + adapter.saveSpecimen(specimen, _this.experiment); + + _this.samplePlateGroupWidget.refresh(_this.experiment); + _this.specimenGrid.refresh(_this.experiment); + //_this.refresh(_this.experiment); + _this.specimenGrid.deselectAll(); + + } else { + /** + * Can we merge? We can merge when specimen are the + * same. So, same buffer, macromolecule, concentration * + */ + var target = _this.experiment.getSampleByPosition(args.samplePlate.samplePlateId, args.row, args.column)[0]; + var specimen = _this.experiment.getSampleById(_this.specimenSelected.specimenId); + + if ((specimen.bufferId == target.bufferId) && (specimen.concentration == target.concentration)) { + if (((specimen.macromolecule3VO != null) && (target.macromolecule3VO != null) && (specimen.macromolecule3VO.macromoleculeId == target.macromolecule3VO.macromoleculeId)) || + ((specimen.macromolecule3VO == null) && (target.macromolecule3VO == null))) { + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, data) { + _this.refresh(new Experiment(data)); + _this.samplePlateGroupWidget.panel.setLoading(false); + + _this.onExperimentChanged.notify(experiment); + }); + adapter.onError.attach(function(sender, error) { + _this.samplePlateGroupWidget.panel.setLoading(false); + showError(error); + }); + _this.samplePlateGroupWidget.panel.setLoading("ISPyB: Merging specimens"); + adapter.mergeSpecimens(specimen.specimenId, target.specimenId); + } + } else { + alert("Well is not empty. Select another well!"); + } + } + } else { + var specimen = _this.experiment.getSampleByPosition(args.samplePlate.samplePlateId, args.row, args.column)[0]; + if (specimen != null) { + _this.specimenGrid.selectById(specimen.specimenId); + } + } + }); + + /** Events **/ + this.onExperimentChanged = new Event(this); +}; + +/** + * Return vbox or hbox depending on the slot positions of the plates + */ +SpecimenWidget.prototype.getContainerLayoutConfiguration = function(experiment){ + var dimensions = this.samplePlateGroupWidget.getDimensions(experiment.getSamplePlates()); + if (dimensions.maxSlotPositionRow < dimensions.maxSlotPositionColumn){ + return { + layout : "vbox", + specimenGridWidth : this.width - 10, + specimenGridHeight : this.height - 260, + samplePlateGroupWidth : this.width - 10, + samplePlateGroupHeight : 250 + }; + } + return { + layout : "hbox", + samplePlateGroupWidth : this.width*1/3 -10, + samplePlateGroupHeight : this.height - 10, + specimenGridWidth : this.width*2/3, + specimenGridHeight : this.height - 10 + }; + +}; + + +SpecimenWidget.prototype.refresh = function(experiment){ + this.experiment = experiment; + + /** Removing all components **/ + this.panel.removeAll(); + + var layoutConfiguration = this.getContainerLayoutConfiguration(experiment); + + /** Setting new width and height for layout vbox and hbox **/ + this.specimenGrid.width = layoutConfiguration.specimenGridWidth; + this.specimenGrid.height = layoutConfiguration.specimenGridHeight; + + this.samplePlateGroupWidget.width = layoutConfiguration.samplePlateGroupWidth; + this.samplePlateGroupWidget.height = layoutConfiguration.samplePlateGroupHeight; + + if (layoutConfiguration.layout == "hbox"){ + this.specimenGrid.margin = "0 0 0 5"; + this.specimenGrid.width =this.specimenGrid.width - 5; + } + /** Insert container depending on layout [vertical|horizontal] */ + var container = Ext.create('Ext.container.Container', { + layout : layoutConfiguration.layout, + height : this.height, + width : this.width, + padding : '2px', + items : [ ] + }); + if (layoutConfiguration.layout == "vbox"){ + container.insert(this.specimenGrid.getPanel()); + container.insert(this.samplePlateGroupWidget.getPanel()); + } + else{ + container.insert(this.samplePlateGroupWidget.getPanel()); + container.insert(this.specimenGrid.getPanel()); + } + + /** Insert Widget **/ + this.panel.insert(container); + + /** Load data **/ + this.specimenGrid.refresh(experiment); + this.samplePlateGroupWidget.refresh(experiment); + + +}; + +/** It creates a dummy container to be inserted the plates once the method refresh has been called + * This is necessay because we can not know the sample changer layout before hand + * **/ +SpecimenWidget.prototype.getPanel = function(){ + this.panel = Ext.create('Ext.container.Container', { + layout : 'vbox', + height : this.height, + border : 0, + margin : 5, + width : this.width, + items : [] + }); + return this.panel; +}; + + +SpecimenWidget.prototype.input = function() { + return { + experiment : DATADOC.getExperiment_10(), + proposal : DATADOC.getProposal_10() + }; +}; + +SpecimenWidget.prototype.test = function(targetId) { + var specimenWidget = new SpecimenWidget({ + height : 500, + width : 1000 + }); + BIOSAXS.proposal = new Proposal(specimenWidget.input().proposal); + var experiment = new Experiment(specimenWidget.input().experiment); + var panel = specimenWidget.getPanel(); + panel.render(targetId); + specimenWidget.refresh(experiment); + +}; + + function WarningWidget(args) { this.actions = []; diff --git a/ispyb-ws/src/main/java/ispyb/ws/rest/mx/AutoprocintegrationRestWebService.java b/ispyb-ws/src/main/java/ispyb/ws/rest/mx/AutoprocintegrationRestWebService.java index cdfe38b5e..4de80758b 100644 --- a/ispyb-ws/src/main/java/ispyb/ws/rest/mx/AutoprocintegrationRestWebService.java +++ b/ispyb-ws/src/main/java/ispyb/ws/rest/mx/AutoprocintegrationRestWebService.java @@ -8,6 +8,8 @@ import ispyb.server.mx.vos.autoproc.AutoProcIntegration3VO; import ispyb.server.mx.vos.autoproc.AutoProcProgram3VO; import ispyb.server.mx.vos.autoproc.AutoProcProgramAttachment3VO; +import ispyb.server.mx.vos.collections.DataCollection3VO; +import ispyb.server.mx.vos.collections.Session3VO; import java.io.File; import java.util.ArrayList; @@ -32,6 +34,7 @@ public class AutoprocintegrationRestWebService extends MXRestWebService { private final static Logger logger = Logger.getLogger(AutoprocintegrationRestWebService.class); + private static final String NOT_ALLOWED = "You don't have access to this resource"; @RolesAllowed({ "User", "Manager", "Industrial", "Localcontact" }) @GET @@ -126,6 +129,13 @@ public Response getAttachments(@PathParam("token") String token, @PathParam("pro } + + + private boolean checkProposalByAutoProcProgramId(int proposalId, int autoProcProgramId) throws NamingException, Exception{ + return this.getSession3Service().findByAutoProcProgramId(autoProcProgramId).getProposalVOId().equals(proposalId); + } + + /** * AutoProcProgramAttachment has not AutoProcProgramId mapped in the EJB object * so it is necessary to keep separately the possible list of ids in order @@ -147,13 +157,19 @@ public Response downloadAttachments( String methodName = "downloadAttachments"; long start = this.logInit(methodName, logger, token, proposal); try { - List ids = this.parseToInteger(autoprocattachmentids); + List autoProcProgramIds = this.parseToInteger(autoprocattachmentids); List> list = new ArrayList>(); HashMap filePaths = new HashMap(); String filename = "download.zip"; - for (Integer id : ids) { - AutoProcProgram3VO autoProcProgram3VO = this.getAutoProcProgram3Service().findByPk(id, true); + for (Integer autoProcProgramId : autoProcProgramIds) { + /** Check that id correspond to the proposal **/ + if (!this.checkProposalByAutoProcProgramId(this.getProposalId(proposal), autoProcProgramId)){ + throw new Exception(NOT_ALLOWED); + } + + + AutoProcProgram3VO autoProcProgram3VO = this.getAutoProcProgram3Service().findByPk(autoProcProgramId, true); /** Prefix for the name of the file and the internal structure if many results are retrieved **/ String prefix = String.format("%s_%s", autoProcProgram3VO.getProcessingPrograms(), autoProcProgram3VO.getAutoProcProgramId()); @@ -164,7 +180,7 @@ public Response downloadAttachments( String filePath = auto.getFilePath() + "/" + auto.getFileName(); if (new File(filePath).exists()){ if (new File(filePath).isFile()){ - if (ids.size() > 1){ + if (autoProcProgramIds.size() > 1){ String zipNameFile = prefix + "/" + auto.getFileName(); filePaths.put(zipNameFile, filePath); } @@ -176,7 +192,7 @@ public Response downloadAttachments( } /** If it is a single result then filename is the name of the program and the ID **/ - if (ids.size() == 1){ + if (autoProcProgramIds.size() == 1){ filename = prefix + ".zip"; } @@ -333,6 +349,10 @@ public Response getXScaleWilson(@PathParam("token") String token, @PathParam("pr } } + private boolean checkProposalByAutoProcProgramAttachmentId(int proposalId, int autoProcProgramAttachmentId) throws NamingException, Exception{ + return this.getSession3Service().findByAutoProcProgramAttachmentId(autoProcProgramAttachmentId).getProposalVOId().equals(proposalId); + } + @RolesAllowed({ "User", "Manager", "Industrial", "Localcontact" }) @GET @Path("{token}/proposal/{proposal}/mx/autoprocintegration/autoprocattachmentid/{autoProcAttachmentId}/download") @@ -343,11 +363,17 @@ public Response downloadAutoProcAttachment(@PathParam("token") String token, @Pa String methodName = "downloadAutoProcAttachment"; long start = this.logInit(methodName, logger, token, proposal); try { - AutoProcProgramAttachment3VO attachment = this.getAutoProcProgramAttachment3Service().findByPk(autoProcAttachmentId); - this.logFinish(methodName, start, logger); - File file = new File(attachment.getFilePath() + "/" + attachment.getFileName()); - this.logFinish(methodName, start, logger); - return this.downloadFileAsAttachment(file.getAbsolutePath()); + /** Checking that attachment is linked to the proposal **/ + if (this.checkProposalByAutoProcProgramAttachmentId(this.getProposalId(proposal), autoProcAttachmentId)){ + AutoProcProgramAttachment3VO attachment = this.getAutoProcProgramAttachment3Service().findByPk(autoProcAttachmentId); + this.logFinish(methodName, start, logger); + File file = new File(attachment.getFilePath() + "/" + attachment.getFileName()); + this.logFinish(methodName, start, logger); + return this.downloadFileAsAttachment(file.getAbsolutePath()); + } + else{ + throw new Exception(NOT_ALLOWED); + } } catch (Exception e) { return this.logError(methodName, e, start, logger); } @@ -360,13 +386,19 @@ public Response downloadAutoProcAttachment(@PathParam("token") String token, @Pa public Response getAutoProcAttachment(@PathParam("token") String token, @PathParam("proposal") String proposal, @PathParam("autoProcAttachmentId") int autoProcAttachmentId) { - String methodName = "downloadAutoProcAttachment"; + String methodName = "getAutoProcAttachment"; long start = this.logInit(methodName, logger, token, proposal); try { - AutoProcProgramAttachment3VO attachment = this.getAutoProcProgramAttachment3Service().findByPk(autoProcAttachmentId); - File file = new File(attachment.getFilePath() + "/" + attachment.getFileName()); - this.logFinish(methodName, start, logger); - return this.downloadFile(file.getAbsolutePath()); + /** Checking that attachment is linked to the proposal **/ + if (this.checkProposalByAutoProcProgramAttachmentId(this.getProposalId(proposal), autoProcAttachmentId)){ + AutoProcProgramAttachment3VO attachment = this.getAutoProcProgramAttachment3Service().findByPk(autoProcAttachmentId); + File file = new File(attachment.getFilePath() + "/" + attachment.getFileName()); + this.logFinish(methodName, start, logger); + return this.downloadFile(file.getAbsolutePath()); + } + else{ + throw new Exception(NOT_ALLOWED); + } } catch (Exception e) { return this.logError(methodName, e, start, logger); } From d1bac5a11316574f68c705aad350230ab52784c4 Mon Sep 17 00:00:00 2001 From: Alejandro De Maria Antolinos Date: Tue, 31 Oct 2017 10:24:22 +0100 Subject: [PATCH 30/38] Added security cheks for downloads. It references #185 --- .../services/sessions/Session3Service.java | 4 + .../sessions/Session3ServiceBean.java | 41 + .../src/main/webapp/js/ispyb/min/ispyb-bx.js | 24046 ++++++++-------- .../mx/AutoprocintegrationRestWebService.java | 62 +- 4 files changed, 12115 insertions(+), 12038 deletions(-) diff --git a/ispyb-ejb/src/main/java/ispyb/server/common/services/sessions/Session3Service.java b/ispyb-ejb/src/main/java/ispyb/server/common/services/sessions/Session3Service.java index a3fe103c2..472d285e6 100644 --- a/ispyb-ejb/src/main/java/ispyb/server/common/services/sessions/Session3Service.java +++ b/ispyb-ejb/src/main/java/ispyb/server/common/services/sessions/Session3Service.java @@ -175,6 +175,8 @@ public List findByStartDateAndBeamLineNameAndNbShifts(final Integer */ public Session3VO findByAutoProcScalingId(final Integer autoProcScalingId) throws Exception; + public Session3VO findByAutoProcProgramAttachmentId(final Integer autoProcProgramAttachmentId) throws Exception; + public void protectSession(Integer sessionId) throws Exception; /** @@ -195,6 +197,8 @@ public List findByStartDateAndBeamLineNameAndNbShifts(final Integer */ public Integer getNbOfTests(final Integer sesId) throws Exception; + public Session3VO findByAutoProcProgramId(int autoProcProgramId); + } \ No newline at end of file diff --git a/ispyb-ejb/src/main/java/ispyb/server/common/services/sessions/Session3ServiceBean.java b/ispyb-ejb/src/main/java/ispyb/server/common/services/sessions/Session3ServiceBean.java index 401f24495..27b4debdc 100644 --- a/ispyb-ejb/src/main/java/ispyb/server/common/services/sessions/Session3ServiceBean.java +++ b/ispyb-ejb/src/main/java/ispyb/server/common/services/sessions/Session3ServiceBean.java @@ -111,6 +111,19 @@ private static final String FIND_ALL(boolean fetchDataCollectionGroup, boolean f + " where s.sessionId = g.sessionId and " + " g.dataCollectionGroupId = c.dataCollectionGroupId and " + " c.dataCollectionId = api.dataCollectionId and " + " api.autoProcIntegrationId = apshi.autoProcIntegrationId and " + " apshi.autoProcScalingId = aps.autoProcScalingId and " + " aps.autoProcScalingId = :autoProcScalingId "; + + private static final String FIND_BY_AUTOPROCPROGRAMATTACHMENT_ID = "select s.* from BLSession s, " + + " DataCollectionGroup g, DataCollection c, AutoProcIntegration api, AutoProcProgram autoprocProgram, AutoProcProgramAttachment autoProcProgramAttachment" + + " where s.sessionId = g.sessionId and g.dataCollectionGroupId = c.dataCollectionGroupId and autoprocProgram.autoProcProgramId = api.autoProcProgramId" + + " and c.dataCollectionId = api.dataCollectionId and autoprocProgram.autoProcProgramId = autoProcProgramAttachment.autoProcProgramId " + + " and autoProcProgramAttachment.autoProcProgramAttachmentId = :autoProcProgramAttachmentId "; + + + private static final String FIND_BY_AUTOPROCPROGRAM_ID = "select s.* from BLSession s, " + + " DataCollectionGroup g, DataCollection c, AutoProcIntegration api, AutoProcProgram autoprocProgram " + + " where s.sessionId = g.sessionId and g.dataCollectionGroupId = c.dataCollectionGroupId and autoprocProgram.autoProcProgramId = api.autoProcProgramId" + + " and c.dataCollectionId = api.dataCollectionId and autoprocProgram.autoProcProgramId = :autoProcProgramId "; + private static String getProposalCodeNumberQuery() { String query = "select * " + " FROM BLSession ses, Proposal pro " @@ -525,6 +538,32 @@ public Session3VO findByAutoProcScalingId(final Integer autoProcScalingId) throw } return null; } + + + @SuppressWarnings("unchecked") + public Session3VO findByAutoProcProgramAttachmentId(final Integer autoProcProgramAttachmentId) throws Exception { + String query = FIND_BY_AUTOPROCPROGRAMATTACHMENT_ID; + List col = this.entityManager.createNativeQuery(query, "sessionNativeQuery") + .setParameter("autoProcProgramAttachmentId", autoProcProgramAttachmentId).getResultList(); + if (col != null && col.size() > 0) { + return col.get(0); + } + return null; + } + + + @Override + public Session3VO findByAutoProcProgramId(int autoProcProgramId) { + String query = FIND_BY_AUTOPROCPROGRAM_ID; + @SuppressWarnings("unchecked") + List col = this.entityManager.createNativeQuery(query, "sessionNativeQuery") + .setParameter("autoProcProgramId", autoProcProgramId).getResultList(); + if (col != null && col.size() > 0) { + return col.get(0); + } + return null; + } + /** @@ -818,4 +857,6 @@ private void checkChangeRemoveAccess(Session3VO vo) throws AccessDeniedException + + } \ No newline at end of file diff --git a/ispyb-ui/src/main/webapp/js/ispyb/min/ispyb-bx.js b/ispyb-ui/src/main/webapp/js/ispyb/min/ispyb-bx.js index 23e07209a..a98a06520 100644 --- a/ispyb-ui/src/main/webapp/js/ispyb/min/ispyb-bx.js +++ b/ispyb-ui/src/main/webapp/js/ispyb/min/ispyb-bx.js @@ -38,321 +38,321 @@ Event.prototype = { } }; -function GenericGraph(args) { - this.width = 600; - this.height = 400; - - this.targetId = null; - /** Free spaces in the borders * */ - this.top = 10; - this.left = 10; - this.bottom = 50; - this.right = 40; - - /** Ruler * */ - this.rulerHeight = 50; - this.rulerWidth = 50; - this.rulerStroke = 2; - this.rulerVerticalMarksNumber = 5; - - this.rulerMaxValue = null; - this.rulerMinValue = null; - - /** plot options * */ - this.plotPoints = true; - this.pointRadius = 2; - this.fillOpacityPoint = 0.2; - this.strokeOpacityPoint = 0.2; - - /** Cluster titles * */ - this.clusterTitleHeight = 20; - this.interClassesSpace = 2; - this.interClustersSpace = 4; - this.fontSize = 7; - - /** - * If true classes and title will be rendender in the rule otherwise will be - * integer values - */ - this.plotHorizontalByCluster = true; - - if (args != null) { - if (args.targetId != null) { - this.targetId = args.targetId; - } - if (args.plotHorizontalByCluster != null) { - this.plotHorizontalByCluster = args.plotHorizontalByCluster; - } - if (args.height != null) { - this.height = args.height; - } - if (args.width != null) { - this.width = args.width; - } - if (args.rulerMinValue != null) { - this.rulerMinValue = args.rulerMinValue; - } - if (args.rulerMaxValue != null) { - this.rulerMaxValue = args.rulerMaxValue; - } - if (args.rulerHeight != null) { - this.rulerHeight = args.rulerHeight; - } - - } -} - -GenericGraph.prototype.calculate = function(data) { - var result = {}; - - var checked = this.cleanArray(data); - - /** sorting array * */ - checked.sort(function(a, b) { - return a - b; - }); - - var median = this.getMedian(checked); - - result.median = median; - result.Q1 = Number(this.getQ1(checked)); - result.Q2 = Number(this.getQ2(checked)); - result.Q3 = Number(this.getQ3(checked)); - result.population = checked; - - result.IQR = Number(result.Q3 - result.Q1); - result.outlier_below_limit = result.Q1 - (1.5 * result.IQR); - result.outlier_above_limit = result.Q3 + (1.5 * result.IQR); - result.below_outliers = this.getBelowOutliers(result.outlier_below_limit, checked); - result.above_outliers = this.getAboveOutliers(result.outlier_above_limit, checked); - - result.min_whisker = this.minValueWhisker(result.outlier_below_limit, result.Q1, checked); - result.max_whisker = this.maxValueWhisker(result.outlier_above_limit, result.Q3, checked); - - return result; -}; - -GenericGraph.prototype.drawSVGVerticalText = function(x, y, text, properties) { - var transform = [ "transform", "translate(" + x + ", " + y + "), rotate(-90) " ]; - properties.push(transform); - SVG.drawText(0, 0, text, this.svg, properties); -}; - -/** Plot the numbers on the axis * */ -GenericGraph.prototype.plotRuler = function(rulerProperties) { - - SVG.drawRectangle(rulerProperties.vertical.x, rulerProperties.vertical.y, rulerProperties.vertical.width, rulerProperties.vertical.height, - this.svg, [ [ "fill", "black" ] ]); - var distance = rulerProperties.vertical.height / (this.rulerVerticalMarksNumber + 1); - for ( var i = 0; i <= this.rulerVerticalMarksNumber + 1; i++) { - var deltaHeight = distance * i; - var aux = rulerProperties.vertical.height - deltaHeight; - - var text = Number((aux / rulerProperties.vertical.height) * (rulerProperties.maxPoint - rulerProperties.minPoint) + rulerProperties.minPoint) - .toFixed(3); - - SVG.drawLine(rulerProperties.horizontal.x, this.top + deltaHeight, rulerProperties.horizontal.x - rulerProperties.markWidth, - this.top + deltaHeight, this.svg, [ [ "stroke", "black" ] ]); - SVG.drawText(this.left, this.top + distance * i + 5, text, this.svg, [ [ "style", "font-size:xx-small" ] ]); - /** Drawing the mark up to the end * */ - SVG.drawLine(rulerProperties.horizontal.x, this.top + deltaHeight, this.width - this.right, this.top + deltaHeight, this.svg, [ - [ "stroke", rulerProperties.markColor ], [ "stroke-width", "0.3" ] ]); - - if (i == this.rulerVerticalMarksNumber + 1) { - SVG.drawLine(rulerProperties.horizontal.x, this.top + deltaHeight, this.width - this.right, this.top + deltaHeight, this.svg, [ [ - "stroke", rulerProperties.markColor ] ]); - } - } - - /** Drawing horizontal rulers * */ - if (!this.plotHorizontalByCluster) { - var width = rulerProperties.horizontal.width; - var ratio = width / (rulerProperties.horizontal.xValues.range); - for ( i = 0; i < rulerProperties.horizontal.xValues.values.length; i++) { - var coorX = rulerProperties.horizontal.xValues.values[i] - rulerProperties.horizontal.xValues.min; - SVG.drawText(rulerProperties.horizontal.x + coorX * ratio, this.height - (this.bottom + (this.clusterTitleHeight)), - rulerProperties.horizontal.xValues.values[i], this.svg, [ [ "style", "font-size:small" ] ]); - } - } -}; - -GenericGraph.prototype.plotAxes = function(properties) { - /** - * Drawing canvas plot-free spaces SVG.drawRectangle(0, this.top, this.left, - * this.height, this.svg, [["fill", "pink"]]); SVG.drawRectangle(this.width - - * this.right, this.top, plot.width, plot.height, this.svg, [["fill", - * "pink"]]); SVG.drawRectangle(0, 0, this.width, this.top, this.svg, - * [["fill", "red"]]); SVG.drawRectangle(0, this.height - this.bottom, - * this.width, this.bottom, this.svg, [["fill", "red"]]); - */ - - /** Drawing ruler Space * */ - this.plotRuler({ - minPoint : Number(properties.minPoint), - maxPoint : Number(properties.maxPoint), - markColor : "black", - markWidth : 20, - vertical : { - x : this.left + this.rulerWidth - this.rulerStroke, - y : this.top, - width : this.rulerStroke, - height : this.height - (this.top + this.bottom + this.clusterTitleHeight) - this.rulerHeight - }, - horizontal : { - x : this.left + this.rulerWidth - this.rulerStroke, - y : this.height - (this.bottom + this.clusterTitleHeight + this.rulerHeight), - width : properties.width, - height : this.rulerStroke, - xValues : properties.xValues - } - }); - -}; - -/** Remove nulls and NaN elements in the array * */ -GenericGraph.prototype.cleanArray = function(data) { - var checked = []; - - /** checking data are numbers * */ - for ( var i = 0; i < data.length; i++) { - if (data[i] != null) { - if (!isNaN(data[i])) { - checked.push(data[i]); - } - } - } - return checked; -}; - -GenericGraph.prototype.refresh = function(data) { - document.getElementById(this.targetId).innerHTML = ""; - this.draw(this.targetId, data); -}; - -GenericGraph.prototype.getClassColor = function(className) { - for ( var i = 0; i < this.data.clusters.length; i++) { - var cluster = this.data.clusters[i]; - for ( var j = 0; j < cluster.classes.length; j++) { - var classes = cluster.classes[j]; - if (classes.name == className) { - if (classes.color != null) { - return classes.color; - } - } - } - } - return "black"; -}; - -GenericGraph.prototype.getDimensions = function(data) { - var results = {}; - var points = []; - - this.data = data; - var classesNumber = 0; - var xValues = []; - - for ( var i = 0; i < data.clusters.length; i++) { - var cluster = data.clusters[i]; - if (!this.plotHorizontalByCluster) { - xValues.push(data.clusters[i].x); - } - for ( var j = 0; j < cluster.classes.length; j++) { - var classes = cluster.classes[j]; - points = points.concat(classes.values); - classesNumber = classesNumber + 1; - } - } - - var checked = this.cleanArray(points); - - checked.sort(function(a, b) { - return a - b; - }); - - results.minPoint = checked[0]; - if (this.rulerMinValue != null) { - results.minPoint = this.rulerMinValue; - } - results.maxPoint = checked[checked.length - 1]; - if (this.rulerMaxValue != null) { - results.maxPoint = this.rulerMaxValue; - } - - results.classesNumber = classesNumber; - results.clusterNumber = data.clusters.length; - - var totalInterClassesFreeSpace = (classesNumber - data.clusters.length) * this.interClassesSpace; - var totalInterClusterFreeSpace = (data.clusters.length) * this.interClustersSpace; - results.classWidth = (this.width - (this.rulerWidth + this.left + this.right + totalInterClassesFreeSpace + totalInterClusterFreeSpace))/ classesNumber; - results.width = this.width - (this.right + this.left) - this.rulerWidth + this.rulerStroke; - - results.xValues = {}; - - xValues.sort(function(a, b) { - return a - b; - }); - results.xValues.values = xValues; - if (xValues.length > 0) { - results.xValues.min = xValues[0]; - results.xValues.max = xValues[xValues.length - 1]; - results.xValues.range = results.xValues.max - results.xValues.min; - } - return results; - -}; - -GenericGraph.prototype.draw = function(targetId, data) { - this.targetId = targetId; - var properties = (this.getDimensions(data)); - this.svg = SVG.createSVGCanvas(document.getElementById(this.targetId), [ [ "width", this.width ], [ "height", this.height ] ]); - this.plotAxes(properties); -}; - -GenericGraph.prototype.getMedian = function(checked) { - /** Calculating median * */ - if (checked.length % 2 == 1) { - return checked[Math.floor(checked.length / 2)]; - } else { - return (Number(checked[(checked.length / 2) - 1]) + Number(checked[checked.length / 2])) / 2; - } -}; - -GenericGraph.prototype.pointToPixel = function(value, boxProperties) { - var ratio = (value - boxProperties.minPoint) / (boxProperties.maxPoint - boxProperties.minPoint); - var pixelLength = boxProperties.height - boxProperties.y + this.top; - return (-1 * ratio) * (pixelLength) + (boxProperties.y + boxProperties.height); -}; - -GenericGraph.prototype.maxValueWhisker = function(aboveLimit, q3, array) { - var points = []; - for ( var i = 0; i < array.length; i++) { - if ((array[i] > q3) && (array[i]) <= aboveLimit) { - points.push(array[i]); - } - } - if (points.length > 0) { - points.sort(function(a, b) { - return a - b; - }); - return points[points.length - 1]; - } - return null; -}; - -GenericGraph.prototype.input = function() { - return DATADOC.getBoxWhikerData(); -}; - -GenericGraph.prototype.test = function(targetId) { - var plot = new GenericGraph({ - targetId : targetId, - height : 350, - width : 450, - maxBoxWidth : 25 - }); - plot.refresh(this.input()); -}; +function GenericGraph(args) { + this.width = 600; + this.height = 400; + + this.targetId = null; + /** Free spaces in the borders * */ + this.top = 10; + this.left = 10; + this.bottom = 50; + this.right = 40; + + /** Ruler * */ + this.rulerHeight = 50; + this.rulerWidth = 50; + this.rulerStroke = 2; + this.rulerVerticalMarksNumber = 5; + + this.rulerMaxValue = null; + this.rulerMinValue = null; + + /** plot options * */ + this.plotPoints = true; + this.pointRadius = 2; + this.fillOpacityPoint = 0.2; + this.strokeOpacityPoint = 0.2; + + /** Cluster titles * */ + this.clusterTitleHeight = 20; + this.interClassesSpace = 2; + this.interClustersSpace = 4; + this.fontSize = 7; + + /** + * If true classes and title will be rendender in the rule otherwise will be + * integer values + */ + this.plotHorizontalByCluster = true; + + if (args != null) { + if (args.targetId != null) { + this.targetId = args.targetId; + } + if (args.plotHorizontalByCluster != null) { + this.plotHorizontalByCluster = args.plotHorizontalByCluster; + } + if (args.height != null) { + this.height = args.height; + } + if (args.width != null) { + this.width = args.width; + } + if (args.rulerMinValue != null) { + this.rulerMinValue = args.rulerMinValue; + } + if (args.rulerMaxValue != null) { + this.rulerMaxValue = args.rulerMaxValue; + } + if (args.rulerHeight != null) { + this.rulerHeight = args.rulerHeight; + } + + } +} + +GenericGraph.prototype.calculate = function(data) { + var result = {}; + + var checked = this.cleanArray(data); + + /** sorting array * */ + checked.sort(function(a, b) { + return a - b; + }); + + var median = this.getMedian(checked); + + result.median = median; + result.Q1 = Number(this.getQ1(checked)); + result.Q2 = Number(this.getQ2(checked)); + result.Q3 = Number(this.getQ3(checked)); + result.population = checked; + + result.IQR = Number(result.Q3 - result.Q1); + result.outlier_below_limit = result.Q1 - (1.5 * result.IQR); + result.outlier_above_limit = result.Q3 + (1.5 * result.IQR); + result.below_outliers = this.getBelowOutliers(result.outlier_below_limit, checked); + result.above_outliers = this.getAboveOutliers(result.outlier_above_limit, checked); + + result.min_whisker = this.minValueWhisker(result.outlier_below_limit, result.Q1, checked); + result.max_whisker = this.maxValueWhisker(result.outlier_above_limit, result.Q3, checked); + + return result; +}; + +GenericGraph.prototype.drawSVGVerticalText = function(x, y, text, properties) { + var transform = [ "transform", "translate(" + x + ", " + y + "), rotate(-90) " ]; + properties.push(transform); + SVG.drawText(0, 0, text, this.svg, properties); +}; + +/** Plot the numbers on the axis * */ +GenericGraph.prototype.plotRuler = function(rulerProperties) { + + SVG.drawRectangle(rulerProperties.vertical.x, rulerProperties.vertical.y, rulerProperties.vertical.width, rulerProperties.vertical.height, + this.svg, [ [ "fill", "black" ] ]); + var distance = rulerProperties.vertical.height / (this.rulerVerticalMarksNumber + 1); + for ( var i = 0; i <= this.rulerVerticalMarksNumber + 1; i++) { + var deltaHeight = distance * i; + var aux = rulerProperties.vertical.height - deltaHeight; + + var text = Number((aux / rulerProperties.vertical.height) * (rulerProperties.maxPoint - rulerProperties.minPoint) + rulerProperties.minPoint) + .toFixed(3); + + SVG.drawLine(rulerProperties.horizontal.x, this.top + deltaHeight, rulerProperties.horizontal.x - rulerProperties.markWidth, + this.top + deltaHeight, this.svg, [ [ "stroke", "black" ] ]); + SVG.drawText(this.left, this.top + distance * i + 5, text, this.svg, [ [ "style", "font-size:xx-small" ] ]); + /** Drawing the mark up to the end * */ + SVG.drawLine(rulerProperties.horizontal.x, this.top + deltaHeight, this.width - this.right, this.top + deltaHeight, this.svg, [ + [ "stroke", rulerProperties.markColor ], [ "stroke-width", "0.3" ] ]); + + if (i == this.rulerVerticalMarksNumber + 1) { + SVG.drawLine(rulerProperties.horizontal.x, this.top + deltaHeight, this.width - this.right, this.top + deltaHeight, this.svg, [ [ + "stroke", rulerProperties.markColor ] ]); + } + } + + /** Drawing horizontal rulers * */ + if (!this.plotHorizontalByCluster) { + var width = rulerProperties.horizontal.width; + var ratio = width / (rulerProperties.horizontal.xValues.range); + for ( i = 0; i < rulerProperties.horizontal.xValues.values.length; i++) { + var coorX = rulerProperties.horizontal.xValues.values[i] - rulerProperties.horizontal.xValues.min; + SVG.drawText(rulerProperties.horizontal.x + coorX * ratio, this.height - (this.bottom + (this.clusterTitleHeight)), + rulerProperties.horizontal.xValues.values[i], this.svg, [ [ "style", "font-size:small" ] ]); + } + } +}; + +GenericGraph.prototype.plotAxes = function(properties) { + /** + * Drawing canvas plot-free spaces SVG.drawRectangle(0, this.top, this.left, + * this.height, this.svg, [["fill", "pink"]]); SVG.drawRectangle(this.width - + * this.right, this.top, plot.width, plot.height, this.svg, [["fill", + * "pink"]]); SVG.drawRectangle(0, 0, this.width, this.top, this.svg, + * [["fill", "red"]]); SVG.drawRectangle(0, this.height - this.bottom, + * this.width, this.bottom, this.svg, [["fill", "red"]]); + */ + + /** Drawing ruler Space * */ + this.plotRuler({ + minPoint : Number(properties.minPoint), + maxPoint : Number(properties.maxPoint), + markColor : "black", + markWidth : 20, + vertical : { + x : this.left + this.rulerWidth - this.rulerStroke, + y : this.top, + width : this.rulerStroke, + height : this.height - (this.top + this.bottom + this.clusterTitleHeight) - this.rulerHeight + }, + horizontal : { + x : this.left + this.rulerWidth - this.rulerStroke, + y : this.height - (this.bottom + this.clusterTitleHeight + this.rulerHeight), + width : properties.width, + height : this.rulerStroke, + xValues : properties.xValues + } + }); + +}; + +/** Remove nulls and NaN elements in the array * */ +GenericGraph.prototype.cleanArray = function(data) { + var checked = []; + + /** checking data are numbers * */ + for ( var i = 0; i < data.length; i++) { + if (data[i] != null) { + if (!isNaN(data[i])) { + checked.push(data[i]); + } + } + } + return checked; +}; + +GenericGraph.prototype.refresh = function(data) { + document.getElementById(this.targetId).innerHTML = ""; + this.draw(this.targetId, data); +}; + +GenericGraph.prototype.getClassColor = function(className) { + for ( var i = 0; i < this.data.clusters.length; i++) { + var cluster = this.data.clusters[i]; + for ( var j = 0; j < cluster.classes.length; j++) { + var classes = cluster.classes[j]; + if (classes.name == className) { + if (classes.color != null) { + return classes.color; + } + } + } + } + return "black"; +}; + +GenericGraph.prototype.getDimensions = function(data) { + var results = {}; + var points = []; + + this.data = data; + var classesNumber = 0; + var xValues = []; + + for ( var i = 0; i < data.clusters.length; i++) { + var cluster = data.clusters[i]; + if (!this.plotHorizontalByCluster) { + xValues.push(data.clusters[i].x); + } + for ( var j = 0; j < cluster.classes.length; j++) { + var classes = cluster.classes[j]; + points = points.concat(classes.values); + classesNumber = classesNumber + 1; + } + } + + var checked = this.cleanArray(points); + + checked.sort(function(a, b) { + return a - b; + }); + + results.minPoint = checked[0]; + if (this.rulerMinValue != null) { + results.minPoint = this.rulerMinValue; + } + results.maxPoint = checked[checked.length - 1]; + if (this.rulerMaxValue != null) { + results.maxPoint = this.rulerMaxValue; + } + + results.classesNumber = classesNumber; + results.clusterNumber = data.clusters.length; + + var totalInterClassesFreeSpace = (classesNumber - data.clusters.length) * this.interClassesSpace; + var totalInterClusterFreeSpace = (data.clusters.length) * this.interClustersSpace; + results.classWidth = (this.width - (this.rulerWidth + this.left + this.right + totalInterClassesFreeSpace + totalInterClusterFreeSpace))/ classesNumber; + results.width = this.width - (this.right + this.left) - this.rulerWidth + this.rulerStroke; + + results.xValues = {}; + + xValues.sort(function(a, b) { + return a - b; + }); + results.xValues.values = xValues; + if (xValues.length > 0) { + results.xValues.min = xValues[0]; + results.xValues.max = xValues[xValues.length - 1]; + results.xValues.range = results.xValues.max - results.xValues.min; + } + return results; + +}; + +GenericGraph.prototype.draw = function(targetId, data) { + this.targetId = targetId; + var properties = (this.getDimensions(data)); + this.svg = SVG.createSVGCanvas(document.getElementById(this.targetId), [ [ "width", this.width ], [ "height", this.height ] ]); + this.plotAxes(properties); +}; + +GenericGraph.prototype.getMedian = function(checked) { + /** Calculating median * */ + if (checked.length % 2 == 1) { + return checked[Math.floor(checked.length / 2)]; + } else { + return (Number(checked[(checked.length / 2) - 1]) + Number(checked[checked.length / 2])) / 2; + } +}; + +GenericGraph.prototype.pointToPixel = function(value, boxProperties) { + var ratio = (value - boxProperties.minPoint) / (boxProperties.maxPoint - boxProperties.minPoint); + var pixelLength = boxProperties.height - boxProperties.y + this.top; + return (-1 * ratio) * (pixelLength) + (boxProperties.y + boxProperties.height); +}; + +GenericGraph.prototype.maxValueWhisker = function(aboveLimit, q3, array) { + var points = []; + for ( var i = 0; i < array.length; i++) { + if ((array[i] > q3) && (array[i]) <= aboveLimit) { + points.push(array[i]); + } + } + if (points.length > 0) { + points.sort(function(a, b) { + return a - b; + }); + return points[points.length - 1]; + } + return null; +}; + +GenericGraph.prototype.input = function() { + return DATADOC.getBoxWhikerData(); +}; + +GenericGraph.prototype.test = function(targetId) { + var plot = new GenericGraph({ + targetId : targetId, + height : 350, + width : 450, + maxBoxWidth : 25 + }); + plot.refresh(this.input()); +}; /** * Using dygraph it plots a chart. Params: targetId, labelsContainerId, args @@ -2087,338 +2087,338 @@ MacromoleculeConditionGrid.prototype.test = function(targetId) { panel.render(targetId); }; - -function ItemGraphFormatter(id, defaultFormat, selectedFormat, overFormat, draggingFormat){ - this.id = id; - this.args = new Object(); - - this.defaultFormat = new ItemFormat(defaultFormat); - - if(selectedFormat != null){ - this.selected = new ItemFormat(selectedFormat); - } - else{ - this.selected = new ItemFormat(defaultFormat); - } - - if(overFormat != null){ - this.over = new ItemFormat(overFormat); - } - else{ - this.over = new ItemFormat(defaultFormat); - } - - if(draggingFormat != null){ - this.dragging = new ItemFormat(draggingFormat); - } - else{ - this.dragging = new ItemFormat(defaultFormat); - } - - //Events - this.stateChanged = new Event(this); - - - //Attaching events - var _this = this; - this._setEvents(); -}; - -ItemGraphFormatter.prototype.getType = function(){ - return this.args.type; -}; - - -ItemGraphFormatter.prototype.toJSON = function(){ - var json = this.args; - json.defaultFormat = this.getDefault().toJSON(); - json.over = this.getOver().toJSON(); - json.selected = this.getSelected().toJSON(); - json.dragging = this.getDragging().toJSON(); - json.id = this.id; - return json; -}; - -ItemGraphFormatter.prototype.loadFromJSON = function(json){ - this.args = json; - this.defaultFormat = new ItemFormat(json.defaultFormat); - this.over = new ItemFormat(json.over); - this.selected = new ItemFormat(json.selected); - this.dragging = new ItemFormat(json.dragging); - this._setEvents(); -}; - -ItemGraphFormatter.prototype._setEvents = function(){ - //Attaching events - var _this = this; - - this.defaultFormat.changed.attach(function (sender, item){ - _this.over.setSize(_this.defaultFormat.getSize()); - _this.selected.setSize(_this.defaultFormat.getSize()); - _this.dragging.setSize(_this.defaultFormat.getSize()); - _this.stateChanged.notify(_this); - }); - - this.selected.changed.attach(function (sender, item){ - _this.stateChanged.notify(_this); - }); - - this.over.changed.attach(function (sender, item){ - _this.stateChanged.notify(_this); - }); - - this.dragging.changed.attach(function (sender, item){ - _this.stateChanged.notify(_this); - }); -}; - -/** Getters **/ -ItemGraphFormatter.prototype.getId = function(){return this.id;}; -ItemGraphFormatter.prototype.getDefault = function(){return this.defaultFormat;}; -ItemGraphFormatter.prototype.getSelected = function(){return this.selected;}; -ItemGraphFormatter.prototype.getOver = function(){return this.over;}; -ItemGraphFormatter.prototype.getDragging = function(){return this.dragging;}; - -function ItemFormat(args){ - this.defaultFormat = new Object(); - this.args = new Object(); - this.args.title = new Object(); - //Defult properties - this.args.visible = true; - this.args.hidden = false; - this.args.stroke = "#000000"; - this.args.strokeOpacity = 0.8; - this.args["stroke-width"] = 1; - this.args.fill = "#000000"; - this.args["fill-opacity"] = 1; - this.args.size = 1; - this.args.opacity = 1; - this.args.fontSize = "8"; - this.args.fontColor = "#000000"; - - /** For directed edge with arrow **/ - //this.args.arrowSize = 1; - - - if (args != null){ - if (args.visible != null){ - this.args.visible = args.visible; - } - if (args.opacity != null){ - this.args.opacity = args.opacity; - } - if (args.size != null){ - this.args.size = args.size; - } - if (args.hidden != null){ - this.args.hidden = args.hidden; - } - if (args.stroke != null){ - this.args.stroke = this._fixColor(args.stroke); - } - if (args.strokeOpacity != null){ - this.args.strokeOpacity = args.strokeOpacity; - } - if (args["stroke-width"]!=null){ - this.args["stroke-width"] = args["stroke-width"]; - } - if (args["fill-opacity"]!=null){ - this.args["fill-opacity"] = args["fill-opacity"]; - } - if (args.shape!=null){ - this.args.shape = args.shape; - } - if (args.fill!=null){ - this.args.fill = this._fixColor(args.fill); - } - - - if (args.title!=null){ - if (args.title.fontSize!=null){ - this.args.title.fontSize = args.title.fontSize; - } - if (args.title.fill!=null){ - this.args.title.fill = this._fixColor(args.title.fill); - } - } - - /** For directed edge with arrow **/ - /*if (args.arrowSize!=null){ - this.args.arrowSize = args.arrowSize; - }*/ - - } - - this.changed = new Event(); -}; - -ItemFormat.prototype._fixColor = function(color){ - var fixed = color; - if (color.indexOf("green") != -1){ - fixed = '#04B431'; - } - - if (color.indexOf("blue") != -1){ - fixed = '#045FB4'; - } - - if (color.indexOf("red") != -1){ - fixed = '#DF0101'; - } - - if (color.indexOf("black") != -1){ - fixed = '#000000'; - } - - if (color.indexOf("white") != -1){ - fixed = '#FFFFFF'; - } - - if (color.indexOf("#") == -1){ - fixed = "#" + color; - } - return fixed; -}; - -ItemFormat.prototype.toJSON = function(){ - if(this.args.strokeOpacity != null){ - this.args["stroke-opacity"] = this.args.strokeOpacity; - delete this.args.strokeOpacity; - } - -// if(this.args.strokeWidth != null){ -// this.args["stroke-width"] = this.args.strokeWidth; -// delete this.args["stroke-width"]; -// } - - if(this.args.title.fontColor != null){ - this.args.title["font-color"] = this.args.title.fontColor; - } - else{ - this.args.title["font-color"] = this.args.fontColor;//;this.args.title.fontColor; - } - - if(this.args.title.fontSize != null){ - this.args.title["font-size"] = this.args.title.fontSize;//;this.args.title.fontColor; - } - else{ - this.args.title["font-size"] = this.args.fontSize;//;this.args.title.fontColor; - } - //return this.args; - return this.args; -}; - -ItemFormat.prototype.getAttribute = function(name){return this.args[name];}; - -//Getters and Setters -ItemFormat.prototype.setVisible = function(visible){ - if (this.args.visible != visible){ - this.args.visible = visible; - this.changed.notify(this); - } -}; - -ItemFormat.prototype.getVisible = function(){return this.args.visible;}; - -ItemFormat.prototype.setHidden = function(hidden){ - if (this.args.hidden != hidden){ - this.args.hidden = hidden; - this.changed.notify(this); - } -}; - -ItemFormat.prototype.getHidden = function(){return this.args.hidden;}; - - -ItemFormat.prototype.setStroke = function(stroke){ - if (this.args.stroke != stroke){ - this.args.stroke = stroke; - this.changed.notify(this); - } -}; - -ItemFormat.prototype.getStroke = function(){return this.args.stroke;}; - -ItemFormat.prototype.setStrokeOpacity = function(strokeOpacity){ - if (this.args.strokeOpacity != strokeOpacity){ - this.args.strokeOpacity = strokeOpacity; - this.changed.notify(this); - } -}; - -ItemFormat.prototype.getStrokeOpacity = function(){return this.args["stroke-opacity"];}; - -ItemFormat.prototype.setStrokeWidth = function(strokeWidth){ - if (this.args["stroke-width"] != strokeWidth){ - this.args["stroke-width"] = strokeWidth; - this.changed.notify(this); - } -}; - - -ItemFormat.prototype.getFillOpacity = function(){return this.args["fill-opacity"];}; - -ItemFormat.prototype.setfillOpacity = function(strokeWidth){ - if (this.args["fill-opacity"] != strokeWidth){ - this.args["fill-opacity"] = strokeWidth; - this.changed.notify(this); - } -}; - - -ItemFormat.prototype.getStrokeWidth = function(){ - return this.args["stroke-width"]; -}; - -ItemFormat.prototype.setFill = function(fill){ - if (this.args.fill != fill){ - this.args.fill = fill; - this.changed.notify(this); - } -}; - -ItemFormat.prototype.getFill = function(){return this.args.fill;}; - -ItemFormat.prototype.setSize = function(size){ - if (this.args.size != size){ - this.args.size = size; - this.changed.notify(this); - } -}; - -ItemFormat.prototype.getSize = function(){return this.args.size;}; - -ItemFormat.prototype.setOpacity = function(opacity){ - if (this.args.opacity != opacity){ - this.args.opacity = opacity; - this.changed.notify(this); - } -}; - -ItemFormat.prototype.getOpacity = function(){return this.args.opacity;}; - -ItemFormat.prototype.getArrowSize = function(){return this.args.arrowSize;}; - -ItemFormat.prototype.setArrowSize = function(arrowSize){ - if (this.args.arrowSize != arrowSize){ - this.args.arrowSize = arrowSize; - this.changed.notify(this); - } -}; - -ItemFormat.prototype.getFontSize = function(){return this.args.title.fontSize;}; - -ItemFormat.prototype.setFontSize = function(fontSize){ - - if (this.args.title.fontSize != fontSize){ - this.args.title.fontSize = fontSize; - this.changed.notify(this); - } -}; - - - - + +function ItemGraphFormatter(id, defaultFormat, selectedFormat, overFormat, draggingFormat){ + this.id = id; + this.args = new Object(); + + this.defaultFormat = new ItemFormat(defaultFormat); + + if(selectedFormat != null){ + this.selected = new ItemFormat(selectedFormat); + } + else{ + this.selected = new ItemFormat(defaultFormat); + } + + if(overFormat != null){ + this.over = new ItemFormat(overFormat); + } + else{ + this.over = new ItemFormat(defaultFormat); + } + + if(draggingFormat != null){ + this.dragging = new ItemFormat(draggingFormat); + } + else{ + this.dragging = new ItemFormat(defaultFormat); + } + + //Events + this.stateChanged = new Event(this); + + + //Attaching events + var _this = this; + this._setEvents(); +}; + +ItemGraphFormatter.prototype.getType = function(){ + return this.args.type; +}; + + +ItemGraphFormatter.prototype.toJSON = function(){ + var json = this.args; + json.defaultFormat = this.getDefault().toJSON(); + json.over = this.getOver().toJSON(); + json.selected = this.getSelected().toJSON(); + json.dragging = this.getDragging().toJSON(); + json.id = this.id; + return json; +}; + +ItemGraphFormatter.prototype.loadFromJSON = function(json){ + this.args = json; + this.defaultFormat = new ItemFormat(json.defaultFormat); + this.over = new ItemFormat(json.over); + this.selected = new ItemFormat(json.selected); + this.dragging = new ItemFormat(json.dragging); + this._setEvents(); +}; + +ItemGraphFormatter.prototype._setEvents = function(){ + //Attaching events + var _this = this; + + this.defaultFormat.changed.attach(function (sender, item){ + _this.over.setSize(_this.defaultFormat.getSize()); + _this.selected.setSize(_this.defaultFormat.getSize()); + _this.dragging.setSize(_this.defaultFormat.getSize()); + _this.stateChanged.notify(_this); + }); + + this.selected.changed.attach(function (sender, item){ + _this.stateChanged.notify(_this); + }); + + this.over.changed.attach(function (sender, item){ + _this.stateChanged.notify(_this); + }); + + this.dragging.changed.attach(function (sender, item){ + _this.stateChanged.notify(_this); + }); +}; + +/** Getters **/ +ItemGraphFormatter.prototype.getId = function(){return this.id;}; +ItemGraphFormatter.prototype.getDefault = function(){return this.defaultFormat;}; +ItemGraphFormatter.prototype.getSelected = function(){return this.selected;}; +ItemGraphFormatter.prototype.getOver = function(){return this.over;}; +ItemGraphFormatter.prototype.getDragging = function(){return this.dragging;}; + +function ItemFormat(args){ + this.defaultFormat = new Object(); + this.args = new Object(); + this.args.title = new Object(); + //Defult properties + this.args.visible = true; + this.args.hidden = false; + this.args.stroke = "#000000"; + this.args.strokeOpacity = 0.8; + this.args["stroke-width"] = 1; + this.args.fill = "#000000"; + this.args["fill-opacity"] = 1; + this.args.size = 1; + this.args.opacity = 1; + this.args.fontSize = "8"; + this.args.fontColor = "#000000"; + + /** For directed edge with arrow **/ + //this.args.arrowSize = 1; + + + if (args != null){ + if (args.visible != null){ + this.args.visible = args.visible; + } + if (args.opacity != null){ + this.args.opacity = args.opacity; + } + if (args.size != null){ + this.args.size = args.size; + } + if (args.hidden != null){ + this.args.hidden = args.hidden; + } + if (args.stroke != null){ + this.args.stroke = this._fixColor(args.stroke); + } + if (args.strokeOpacity != null){ + this.args.strokeOpacity = args.strokeOpacity; + } + if (args["stroke-width"]!=null){ + this.args["stroke-width"] = args["stroke-width"]; + } + if (args["fill-opacity"]!=null){ + this.args["fill-opacity"] = args["fill-opacity"]; + } + if (args.shape!=null){ + this.args.shape = args.shape; + } + if (args.fill!=null){ + this.args.fill = this._fixColor(args.fill); + } + + + if (args.title!=null){ + if (args.title.fontSize!=null){ + this.args.title.fontSize = args.title.fontSize; + } + if (args.title.fill!=null){ + this.args.title.fill = this._fixColor(args.title.fill); + } + } + + /** For directed edge with arrow **/ + /*if (args.arrowSize!=null){ + this.args.arrowSize = args.arrowSize; + }*/ + + } + + this.changed = new Event(); +}; + +ItemFormat.prototype._fixColor = function(color){ + var fixed = color; + if (color.indexOf("green") != -1){ + fixed = '#04B431'; + } + + if (color.indexOf("blue") != -1){ + fixed = '#045FB4'; + } + + if (color.indexOf("red") != -1){ + fixed = '#DF0101'; + } + + if (color.indexOf("black") != -1){ + fixed = '#000000'; + } + + if (color.indexOf("white") != -1){ + fixed = '#FFFFFF'; + } + + if (color.indexOf("#") == -1){ + fixed = "#" + color; + } + return fixed; +}; + +ItemFormat.prototype.toJSON = function(){ + if(this.args.strokeOpacity != null){ + this.args["stroke-opacity"] = this.args.strokeOpacity; + delete this.args.strokeOpacity; + } + +// if(this.args.strokeWidth != null){ +// this.args["stroke-width"] = this.args.strokeWidth; +// delete this.args["stroke-width"]; +// } + + if(this.args.title.fontColor != null){ + this.args.title["font-color"] = this.args.title.fontColor; + } + else{ + this.args.title["font-color"] = this.args.fontColor;//;this.args.title.fontColor; + } + + if(this.args.title.fontSize != null){ + this.args.title["font-size"] = this.args.title.fontSize;//;this.args.title.fontColor; + } + else{ + this.args.title["font-size"] = this.args.fontSize;//;this.args.title.fontColor; + } + //return this.args; + return this.args; +}; + +ItemFormat.prototype.getAttribute = function(name){return this.args[name];}; + +//Getters and Setters +ItemFormat.prototype.setVisible = function(visible){ + if (this.args.visible != visible){ + this.args.visible = visible; + this.changed.notify(this); + } +}; + +ItemFormat.prototype.getVisible = function(){return this.args.visible;}; + +ItemFormat.prototype.setHidden = function(hidden){ + if (this.args.hidden != hidden){ + this.args.hidden = hidden; + this.changed.notify(this); + } +}; + +ItemFormat.prototype.getHidden = function(){return this.args.hidden;}; + + +ItemFormat.prototype.setStroke = function(stroke){ + if (this.args.stroke != stroke){ + this.args.stroke = stroke; + this.changed.notify(this); + } +}; + +ItemFormat.prototype.getStroke = function(){return this.args.stroke;}; + +ItemFormat.prototype.setStrokeOpacity = function(strokeOpacity){ + if (this.args.strokeOpacity != strokeOpacity){ + this.args.strokeOpacity = strokeOpacity; + this.changed.notify(this); + } +}; + +ItemFormat.prototype.getStrokeOpacity = function(){return this.args["stroke-opacity"];}; + +ItemFormat.prototype.setStrokeWidth = function(strokeWidth){ + if (this.args["stroke-width"] != strokeWidth){ + this.args["stroke-width"] = strokeWidth; + this.changed.notify(this); + } +}; + + +ItemFormat.prototype.getFillOpacity = function(){return this.args["fill-opacity"];}; + +ItemFormat.prototype.setfillOpacity = function(strokeWidth){ + if (this.args["fill-opacity"] != strokeWidth){ + this.args["fill-opacity"] = strokeWidth; + this.changed.notify(this); + } +}; + + +ItemFormat.prototype.getStrokeWidth = function(){ + return this.args["stroke-width"]; +}; + +ItemFormat.prototype.setFill = function(fill){ + if (this.args.fill != fill){ + this.args.fill = fill; + this.changed.notify(this); + } +}; + +ItemFormat.prototype.getFill = function(){return this.args.fill;}; + +ItemFormat.prototype.setSize = function(size){ + if (this.args.size != size){ + this.args.size = size; + this.changed.notify(this); + } +}; + +ItemFormat.prototype.getSize = function(){return this.args.size;}; + +ItemFormat.prototype.setOpacity = function(opacity){ + if (this.args.opacity != opacity){ + this.args.opacity = opacity; + this.changed.notify(this); + } +}; + +ItemFormat.prototype.getOpacity = function(){return this.args.opacity;}; + +ItemFormat.prototype.getArrowSize = function(){return this.args.arrowSize;}; + +ItemFormat.prototype.setArrowSize = function(arrowSize){ + if (this.args.arrowSize != arrowSize){ + this.args.arrowSize = arrowSize; + this.changed.notify(this); + } +}; + +ItemFormat.prototype.getFontSize = function(){return this.args.title.fontSize;}; + +ItemFormat.prototype.setFontSize = function(fontSize){ + + if (this.args.title.fontSize != fontSize){ + this.args.title.fontSize = fontSize; + this.changed.notify(this); + } +}; + + + + /** * This class executes the actions @@ -4952,2095 +4952,1101 @@ function openExperiment(experimentId) { BIOSAXS.openExperiment(experimentId); } -var SITE_CONF = { - SAMPLE_CHANGER_CONFIGURATION : { - "3":{ - "platetype3VO": {"plateTypeId":4,"name":"96 Well plate","rowCount":8,"columnCount":12,"shape":"REC"}, - "name":"96 Well plate", - "slotPositionRow":"1", - "slotPositionColumn":"3", - "storageTemperature":"0", - "sampleplateposition3VOs":[], - "experimentId":0 - }, - "2":{ - "platetype3VO":{"plateTypeId":2,"name":" 4 x ( 8 + 3 ) Block","rowCount":4,"columnCount":11,"shape":"REC"}, - "name":" 4 x ( 8 + 3 ) Block", - "slotPositionRow":"1", - "slotPositionColumn":"2", - "storageTemperature":"0", - "sampleplateposition3VOs":[],"experimentId":0 - }, - "1":{ - "platetype3VO":{"plateTypeId":1,"name":"Deep Well","rowCount":8,"columnCount":12,"shape":"REC"}, - "name":"Deep Well", - "slotPositionRow":"1", - "slotPositionColumn":"1", - "storageTemperature":"0", - "sampleplateposition3VOs":[], - "experimentId":0 - } - } +var SITE_CONF = { + SAMPLE_CHANGER_CONFIGURATION : { + "3":{ + "platetype3VO": {"plateTypeId":4,"name":"96 Well plate","rowCount":8,"columnCount":12,"shape":"REC"}, + "name":"96 Well plate", + "slotPositionRow":"1", + "slotPositionColumn":"3", + "storageTemperature":"0", + "sampleplateposition3VOs":[], + "experimentId":0 + }, + "2":{ + "platetype3VO":{"plateTypeId":2,"name":" 4 x ( 8 + 3 ) Block","rowCount":4,"columnCount":11,"shape":"REC"}, + "name":" 4 x ( 8 + 3 ) Block", + "slotPositionRow":"1", + "slotPositionColumn":"2", + "storageTemperature":"0", + "sampleplateposition3VOs":[],"experimentId":0 + }, + "1":{ + "platetype3VO":{"plateTypeId":1,"name":"Deep Well","rowCount":8,"columnCount":12,"shape":"REC"}, + "name":"Deep Well", + "slotPositionRow":"1", + "slotPositionColumn":"1", + "storageTemperature":"0", + "sampleplateposition3VOs":[], + "experimentId":0 + } + } }; - -var ISPYB_CONF = { - load : function(config) { - for (var key in config) { - ISPYB_CONF[key] = config[key]; - } - }, - SAMPLE_CHANGER_CONFIGURATION : { - "1": { - "platetype3VO": {"plateTypeId": 1, "name": "Deep Well", "rowCount": 8, "columnCount": 12, "shape": "REC"}, - "name": "Deep Well", - "slotPositionRow": "1", - "slotPositionColumn": "1", - "storageTemperature": "0", - "sampleplateposition3VOs": [], - "experimentId": 0 - } - } + +var ISPYB_CONF = { + load : function(config) { + for (var key in config) { + ISPYB_CONF[key] = config[key]; + } + }, + SAMPLE_CHANGER_CONFIGURATION : { + "1": { + "platetype3VO": {"plateTypeId": 1, "name": "Deep Well", "rowCount": 8, "columnCount": 12, "shape": "REC"}, + "name": "Deep Well", + "slotPositionRow": "1", + "slotPositionColumn": "1", + "storageTemperature": "0", + "sampleplateposition3VOs": [], + "experimentId": 0 + } + } }; -var SITE_CONF = { - -}; - -/** - * Example form - * - * @witdh - * @height - */ -function AbinitioForm(args) { - this.id = BUI.id(); - this.width = null; - this.height = null; - - if (args != null) { - if (args.width != null) { - this.width = args.width; - } - if (args.height != null) { - this.height = args.height; - } - } - - var _this = this; - /** Widgets **/ - this.abinitioGrid = new AbinitioGrid({ - width : null, - height : 200 - }); - - this.abinitioGrid.onSelected.attach(function(sender, models) { - var modelsIdList = []; - for ( var i in models) { - modelsIdList.push(models[i].modelId); - } - _this._renderPlot(_this.subtractions[0].subtractionId, modelsIdList); - _this._renderPDB(modelsIdList); - }); - - /** Dygraph Widget that plots fir files**/ - this.plotWidget = new PlotWidget({ - width : 745 / 2, - height : 300, - margin : "10 0 5 10" - }); - - /** PDB viewer **/ - this.viewer = new PDBViewer({ - width : 745 / 2, - height : 300 - }); - -} - -AbinitioForm.prototype._renderPlot = function(subtractionId, modelsIdList) { - /** Trying to plot tje subtraction and the models **/ - try { - var colors = [ "#669900", "#0000FF" ]; - this.plotWidget.refresh([], [ ], modelsIdList, [], [], colors); - } catch (e) { - console.log(e); - } -}; - -AbinitioForm.prototype._renderPDB = function(modelsIdList) { - /** Trying to plot the PDB file **/ - try { - var viz = []; - for (var i = 0; i < modelsIdList.length; i++) { - viz.push({ - modelId : modelsIdList[i], - color : "0xFF6600", - opacity : 0.8 - }); - } - this.viewer.refresh(viz); - } catch (e) { - console.log(e); - } -}; - -AbinitioForm.prototype._getItems = function() { - var _this = this; - return [ { - xtype : 'label', - forId : 'myFieldId', - text : 'INLINE HELP: To be updated', - margin : '15 0 20 10', - cls : "inline-help" - }, this.abinitioGrid.getPanel(), { - xtype : 'container', - layout : 'hbox', - items : [ this.plotWidget.getPanel(), this.viewer.getPanel() ] - } ] -}; - -AbinitioForm.prototype._getButtons = function() { - return []; -}; - -AbinitioForm.prototype.getPanel = function() { - var _this = this; - this.panel = Ext.create('Ext.form.Panel', { - width : this.width, - height : this.height, - margin : 10, - border : 1, - defaultType : 'textfield', - items : this._getItems(), - buttons : this._getButtons(), - listeners : { - afterrender : function() { - _this._populate(); +var SITE_CONF = { + +}; - } - } - }); - return this.panel; -}; +/** + * Example form + * + * @witdh + * @height + */ +function AbinitioForm(args) { + this.id = BUI.id(); + this.width = null; + this.height = null; + + if (args != null) { + if (args.width != null) { + this.width = args.width; + } + if (args.height != null) { + this.height = args.height; + } + } + + var _this = this; + /** Widgets **/ + this.abinitioGrid = new AbinitioGrid({ + width : null, + height : 200 + }); + + this.abinitioGrid.onSelected.attach(function(sender, models) { + var modelsIdList = []; + for ( var i in models) { + modelsIdList.push(models[i].modelId); + } + _this._renderPlot(_this.subtractions[0].subtractionId, modelsIdList); + _this._renderPDB(modelsIdList); + }); + + /** Dygraph Widget that plots fir files**/ + this.plotWidget = new PlotWidget({ + width : 745 / 2, + height : 300, + margin : "10 0 5 10" + }); + + /** PDB viewer **/ + this.viewer = new PDBViewer({ + width : 745 / 2, + height : 300 + }); + +} + +AbinitioForm.prototype._renderPlot = function(subtractionId, modelsIdList) { + /** Trying to plot tje subtraction and the models **/ + try { + var colors = [ "#669900", "#0000FF" ]; + this.plotWidget.refresh([], [ ], modelsIdList, [], [], colors); + } catch (e) { + console.log(e); + } +}; + +AbinitioForm.prototype._renderPDB = function(modelsIdList) { + /** Trying to plot the PDB file **/ + try { + var viz = []; + for (var i = 0; i < modelsIdList.length; i++) { + viz.push({ + modelId : modelsIdList[i], + color : "0xFF6600", + opacity : 0.8 + }); + } + this.viewer.refresh(viz); + } catch (e) { + console.log(e); + } +}; + +AbinitioForm.prototype._getItems = function() { + var _this = this; + return [ { + xtype : 'label', + forId : 'myFieldId', + text : 'INLINE HELP: To be updated', + margin : '15 0 20 10', + cls : "inline-help" + }, this.abinitioGrid.getPanel(), { + xtype : 'container', + layout : 'hbox', + items : [ this.plotWidget.getPanel(), this.viewer.getPanel() ] + } ] +}; + +AbinitioForm.prototype._getButtons = function() { + return []; +}; + +AbinitioForm.prototype.getPanel = function() { + var _this = this; + this.panel = Ext.create('Ext.form.Panel', { + width : this.width, + height : this.height, + margin : 10, + border : 1, + defaultType : 'textfield', + items : this._getItems(), + buttons : this._getButtons(), + listeners : { + afterrender : function() { + _this._populate(); + + } + } + }); + return this.panel; +}; + +/** Populates could be call when the DOM is not filled yet **/ +AbinitioForm.prototype._populate = function() { +}; + +/** It populates the form * */ +AbinitioForm.prototype.refresh = function(subtractions) { + this.subtractions = subtractions; + this.abinitioGrid.refresh(subtractions); +}; + +AbinitioForm.prototype.input = function() { + return {}; +}; + +/** It populates the form **/ +AbinitioForm.prototype.test = function(targetId) { + var macromoleculeForm = new AbinitioForm(); + var panel = macromoleculeForm.getPanel(); + panel.render(targetId); +}; -/** Populates could be call when the DOM is not filled yet **/ -AbinitioForm.prototype._populate = function() { -}; - -/** It populates the form * */ -AbinitioForm.prototype.refresh = function(subtractions) { - this.subtractions = subtractions; - this.abinitioGrid.refresh(subtractions); -}; - -AbinitioForm.prototype.input = function() { - return {}; -}; - -/** It populates the form **/ -AbinitioForm.prototype.test = function(targetId) { - var macromoleculeForm = new AbinitioForm(); - var panel = macromoleculeForm.getPanel(); - panel.render(targetId); -}; - -/** - * Example form - * - * @witdh - * @height - */ -function DataReductionForm(args) { - this.id = BUI.id(); - this.width = null; - this.height = null; - - if (args != null) { - if (args.width != null) { - this.width = args.width; - } - if (args.height != null) { - this.height = args.height; - } - } - - var _this = this; - - /** Widgets **/ - this.plotWidget = new PlotWidget({ - width : 650, - height : 490 - }); - - /** Selected frames to be displayed **/ - this.selectedItems = { - frames : [], - averages : [], - subtractions : [] - }; - -} - -DataReductionForm.prototype._parseSelectedItemsToIds = function(selectedArray) { - var ids = []; - if (selectedArray != null) { - for (var i = 0; i < selectedArray.length; i++) { - ids.push(selectedArray[i].id); - } - } - return ids; -}; - -DataReductionForm.prototype.onSelectionChanged = function(columnName, selected) { - if (selected != null) { - if (columnName == "Frames") { - this.selectedItems.frames = selected; - this.selectedItems.subtractions = []; - } - if (columnName == "Averages") { - this.selectedItems.averages = selected; - } - if (columnName == "Subtractions") { - this.selectedItems.frames = []; - this.selectedItems.subtractions = selected; - } - } - this.plotWidget.refresh(this._parseSelectedItemsToIds(this.selectedItems.frames), this._parseSelectedItemsToIds(this.selectedItems.subtractions)); -}; - -DataReductionForm.prototype._getFramesExtPanel = function(store, columnName, height) { - var _this = this; - - var selModel = Ext.create('Ext.selection.RowModel', { - allowDeselect : true, - mode : 'MULTI', - listeners : { - selectionchange : function(sm, selections) { - var selected = []; - for (var i = 0; i < selections.length; i++) { - selected.push(selections[i].raw); - } - _this.onSelectionChanged(columnName, selected); - } - } - }); - - return Ext.create('Ext.grid.Panel', { - store : store, - margin : 10, - height : height, - width : 200, - selModel : selModel, - columns : [ { - text : columnName, - dataIndex : 'fileName', - flex : 1 - } ], - viewConfig : { - } - }); -}; - -DataReductionForm.prototype._getFramesPanel = function() { - var fields = [ 'fileName', 'type', 'id' ]; - - this.framesStore = Ext.create('Ext.data.Store', { - fields : fields, - sorters : 'fileName' - }); - - this.averagesStore = Ext.create('Ext.data.Store', { - fields : fields - }); - - this.subtractionStore = Ext.create('Ext.data.Store', { - fields : fields - }); - - var gridFrames = this._getFramesExtPanel(this.framesStore, "Frames", 375); - var gridAvgs = this._getFramesExtPanel(this.averagesStore, "Averages", 125); - var subtractionAvgs = this._getFramesExtPanel(this.subtractionStore, "Subtractions", 75); - - return { - xtype : 'container', - layout : 'vbox', - items : [ gridFrames, subtractionAvgs ] - }; -}; - -DataReductionForm.prototype._getImageContainer = function(name, help) { - var html = "
" + name + "
" - return { - xtype : 'container', - layout : 'vbox', - items : [ { - html : html, - margin : "5 0 0 0", - height : 95, - width : 100 - }, { - xtype : 'label', - forId : 'myFieldId', - text : help, - margin : '5 0 0 0', - cls : "inline-help" - } ] - } -}; - -DataReductionForm.prototype._getItems = function() { - var _this = this; - return [ { - xtype : 'container', - layout : 'hbox', - items : [ - this._getFramesPanel(), - this.plotWidget.getPanel(), - { - xtype : 'panel', - width : 110, - frame : true, - margin : "10 5 5 5", - border : 0, - layout : 'vbox', - items : [ this._getImageContainer("scattering", "Scattering"), this._getImageContainer("guinier", "Guinier Region"), - this._getImageContainer("kratky", "Kratky Plot"), this._getImageContainer("gnom", "P(r) distribution") ] - } ] - } ] -}; - -DataReductionForm.prototype._getButtons = function() { - return []; -}; - -DataReductionForm.prototype.getPanel = function() { - var _this = this; - this.panel = Ext.create('Ext.form.Panel', { - width : this.width, - height : this.height, - border : 0, - items : this._getItems(), - buttons : this._getButtons(), - listeners : { - afterrender : function() { - _this._populate(); - } - } - }); - return this.panel; -}; - -/** Populates could be call when the DOM is not filled yet **/ -DataReductionForm.prototype._populate = function() { -}; - -/** It populates the form * */ -DataReductionForm.prototype.refresh = function(subtractions) { - if (subtractions != null) { - for (var i = 0; i < subtractions.length; i++) { - /** Loading frame grids **/ - var subtraction = subtractions[i]; - var averages = [ { - fileName : BUI.getFileName(subtraction.bufferAverageFilePath), - type : 'bufferAvg', - id : subtraction.subtractionId - }, { - fileName : BUI.getFileName(subtraction.sampleAverageFilePath), - type : 'sampleAvg', - id : subtraction.subtractionId - } - - ]; - this.averagesStore.loadData(averages, true); - this.subtractionStore.loadData([ { - fileName : BUI.getFileName(subtraction.substractedFilePath), - type : 'SUBTRACTION', - id : subtraction.subtractionId - } ], true); - - var frames = []; - /** Buffers **/ - if (subtraction.bufferOneDimensionalFiles != null) { - if (subtraction.bufferOneDimensionalFiles.frametolist3VOs != null) { - for (var j = 0; j < subtraction.bufferOneDimensionalFiles.frametolist3VOs.length; j++) { - var frametolist3VO = subtraction.bufferOneDimensionalFiles.frametolist3VOs[j]; - if (frametolist3VO.frame3VO != null) { - frames.push({ - fileName : BUI.getFileName(frametolist3VO.frame3VO.filePath), - type : 'BUFFER', - id : frametolist3VO.frame3VO.frameId - }); - } - } - } - } - /** Samples **/ - if (subtraction.sampleOneDimensionalFiles != null) { - if (subtraction.sampleOneDimensionalFiles.frametolist3VOs != null) { - for (var j = 0; j < subtraction.sampleOneDimensionalFiles.frametolist3VOs.length; j++) { - var frametolist3VO = subtraction.sampleOneDimensionalFiles.frametolist3VOs[j]; - if (frametolist3VO.frame3VO != null) { - frames.push({ - fileName : BUI.getFileName(frametolist3VO.frame3VO.filePath), - type : 'SAMPLE', - id : frametolist3VO.frame3VO.frameId - }); - } - } - } - } - - this.framesStore.loadData(frames, true); - - /** Loading images **/ - this._displayImage("scattering", subtraction.subtractionId); - this._displayImage("kratky", subtraction.subtractionId); - this._displayImage("guinier", subtraction.subtractionId); - this._displayImage("gnom", subtraction.subtractionId); - } - } -}; - -DataReductionForm.prototype._displayImage = function(name, subtractionId) { - var url = BUI.getURL() + '&type=' + name + '&subtractionId=' + subtractionId; - var event = "OnClick= window.open('" + url + "')"; - document.getElementById(this.id + "_" + name).innerHTML = ''; -}; - -DataReductionForm.prototype.input = function() { - return {}; -}; - -/** It populates the form **/ -DataReductionForm.prototype.test = function(targetId) { - var macromoleculeForm = new DataReductionForm(); - var panel = macromoleculeForm.getPanel(); - panel.render(targetId); -}; - -function PlotWidget(args) { - this.width = 600; - this.height = 600; - this.id = BUI.id(); - - this.linear = false; - - this.margin = "10 0 0 0"; - /** for each stat the max and minimum value when it is scaled in order to show correctly in the legend **/ - this.ranges = {}; - if (args != null) { - if (args.width != null) { - this.width = args.width; - } - if (args.height != null) { - this.height = args.height; - } - if (args.margin != null) { - this.margin = args.margin; - } - } - -} - -PlotWidget.prototype.getMenu = function() { - var _this = this; - /** Actions buttons **/ - var actions = []; - actions.push("->"); - actions.push({ - text : "Export as Image", - scope : this, - icon : '../images/save.gif', - handler : function(item, pressed) { - var largeImage = document.createElement("img"); - largeImage.style.display = 'block'; - largeImage.style.width = 200 + "px"; - largeImage.style.height = 200 + "px"; - largeImage.setAttribute('src', Dygraph.Export.asCanvas(this.dygraphObject.dygraph).toDataURL()); - window.open(Dygraph.Export.asCanvas(this.dygraphObject.dygraph).toDataURL(), 'Image', ''); - } - }); - - return actions; -}; - -/** Looks for the maximum value and then divide everything but that value **/ -PlotWidget.prototype.scaledData = function(data) { - for (var i = 0; i < data.length; i++) { - var values = this.getMaxAndMinValue(data[i]); - data[i] = this.divideValuesByMax(data[i], values.max); - this.ranges[data[i].label] = values; - } - return data; -}; - -/** Given a stat float[] and a max number it will divide each value by max **/ -PlotWidget.prototype.divideValuesByMax = function(stat, max) { - for (var j = 0; j < stat.data.length; j++) { - if (max != 0) { - stat.data[j] = Number(stat.data[j]) / max; - stat.std[j] = Number(stat.std[j]) / max; - } - } - return stat; -}; - -/** returns max value of a stat **/ -PlotWidget.prototype.getMaxAndMinValue = function(stat) { - var max = 0; - var min = stat.data[0]; - for (var j = 0; j < stat.data.length; j++) { - if (Number(stat.data[j]) > max) { - max = Number(stat.data[j]); - } - if (Number(stat.std[j]) > max) { - max = Number(stat.std[j]); - } - if (Number(stat.data[j]) < min) { - min = Number(stat.data[j]); - } - } - return { - max : Number(max), - min : Number(min) - }; -}; - -PlotWidget.prototype._renderDygraph = function(parsed, colors, labels) { - this.dygraphObject = new StdDevDyGraph(this.id, { - width : this.width - 20, - height : this.height - 40, - xlabel : "", - }); - - this.dygraphObject.draw(parsed, colors, labels); - -}; - -PlotWidget.prototype.getPanel = function() { - this.panel = Ext.create('Ext.panel.Panel', { - width : this.width, - height : this.height, - margin : this.margin, - tbar : this.getMenu(), - items : [ { - html : "", - id : this.id, - width : this.width, - height : this.height, - padding : 10, - margin : "0 0 0 -30", - border : 0 - } ] - }); - - return this.panel; -}; - -PlotWidget.prototype.getPoint = function(y, error) { - var minus = y - error; - var max = y + error; - - if (this.linear) { - return [ Math.abs(minus), y, Math.abs(max) ]; - } - if ((minus != 0) && (max != 0)) { - return [ Math.log(Math.abs(minus)), Math.log(y), Math.log(Math.abs(max)) ]; - } else { - return [ Math.log(y), Math.log(y), Math.log(y) ]; - } - -}; - -PlotWidget.prototype.getDataPlot = function(frames, subtractions, models, fits, rbms) { - var files = []; - var labels = [ "Intensity" ]; - if (frames != null) { - for (var i = 0; i < frames.length; i++) { - files.push(frames[i].data); - labels.push(frames[i].fileName); - } - } - function splitData(data, column, errorColumn, name){ - var result = [] - for (var j = 0; j < data.length; j++) { - console.log(data[j][column]); - result.push([j,data[j][column],data[j][errorColumn]]);//[0, data[i][column],0]]); - } - files.push(result); - labels.push(name); - } - - if (subtractions != null) { - for (var i = 0; i < subtractions.length; i++) { - /** For subtraction **/ - files.push(subtractions[i].subtraction.data); - labels.push(subtractions[i].subtraction.fileName); - /** For sample average **/ -// files.push(subtractions[i].sampleAvg.data); -// labels.push(subtractions[i].sampleAvg.fileName); - /** For buffer average **/ -// files.push(subtractions[i].bufferAvg.data); -// labels.push(subtractions[i].bufferAvg.fileName); - } - } - - if (models != null) { - for (var i = 0; i < models.length; i++) { - for ( var key in models[i]) { - splitData(models[i][key].fir.data, 1, 2, "Intensity"); - splitData(models[i][key].fir.data, 3, 3, "Fit"); - } - } - } - - if (fits != null) { - for (var i = 0; i < fits.length; i++) { - for ( var key in fits[i]) { - - /** adding fit file to be plotted **/ - if (fits[i][key].fit.data[0].length == 3){ - splitData(fits[i][key].fit.data, 1, 1, "Intensity"); - splitData(fits[i][key].fit.data, 2, 2, "Fit"); - } - - /** s, Iexp(s), err, Ifit(s). **/ - if (fits[i][key].fit.data[0].length == 4){ - splitData(fits[i][key].fit.data, 1, 2, "Intensity"); - splitData(fits[i][key].fit.data, 3, 3, "Fit"); - } - - if (fits[i][key].fit.data[0].length == 5){ - /** X Intensity Fit Error Residues **/ - - splitData(fits[i][key].fit.data, 1, 3, "Intensity"); - splitData(fits[i][key].fit.data, 2, 2, "Fit"); - } - } - } - } - - var dataPoints = []; - if (files.length > 0) { - for (var i = 0; i < files[0].length; i++) { - dataPoints.push([ files[0][i][0], this.getPoint(files[0][i][1], files[0][i][2]) ]); - } - if (files.length > 1) { - for (var i = 1; i < files.length; i++) { - for (var j = 0; j < dataPoints.length; j++) { - if (files[i][j] != null){ - dataPoints[j].push(this.getPoint(files[i][j][1], files[i][j][2])); - } - else{ - dataPoints[j].push([0,0,0]); - } - } - } - } - } - - return { - dataPoints : dataPoints, - labels : labels - } -}; - -PlotWidget.prototype.refresh = function(frames, subtractions, models, fits, rbms, colors) { - - var _this = this; - this.panel.setLoading("Reading Files"); - - var dataAdapter = new BiosaxsDataAdapter(); - dataAdapter.onSuccess.attach(function(sender, data) { - _this.panel.setLoading("Rendering"); - var parsed = _this.getDataPlot(data.frames, data.subtractions, data.models, data.fits, rbms); - _this._renderDygraph(parsed.dataPoints, colors, parsed.labels); - _this.panel.setLoading(false); - }); - dataAdapter.onError.attach(function(sender, data) { - _this.panel.setLoading(false); - }); - dataAdapter.getDataPlot(frames, subtractions, models, fits, rbms); -}; - -PlotWidget.prototype.input = function() { - return DATADOC.getHPLCData(); -}; - - -/** - * Fit form - * - * @witdh - * @height - */ -function FitForm(args) { - this.id = BUI.id(); - this.width = null; - this.height = null; - - if (args != null) { - if (args.width != null) { - this.width = args.width; - } - if (args.height != null) { - this.height = args.height; - } - } - - var _this = this; - - /** Widgets **/ - this.fitGrid = new FitStructureToExperimentDataGrid({ - width : null, - height : 200 - }); - - this.fitGrid.onSelected.attach(function(sender, fits) { - var modelsIdList = []; - for ( var i in fits) { - modelsIdList.push(fits[i].fitStructureToExperimentalDataId); - } - _this._renderPlot(_this.subtractions[0].subtractionId, modelsIdList); -// var adapter = new BiosaxsDataAdapter(); -// adapter.onSuccess.attach(function(sender, data) { -// -// }); -// adapter.getScatteringCurveByFrameIdsList([], [], [], ids); - }); - - - /** Dygraph Widget that plots fir files**/ - this.plotWidget = new PlotWidget({ - width : 745, - height : 300, - margin : "10 0 10 10" - }); -} - -FitForm.prototype._renderPlot = function(subtractionId, modelsIdList) { - /** Trying to plot tje subtraction and the models **/ - try { - var colors = [ "#669900", "#0000FF" ]; - - this.plotWidget.refresh([], [ ], [], modelsIdList, [], colors); - } catch (e) { - console.log(e); - } -}; - -FitForm.prototype._getItems = function() { - var _this = this; - return [ { - xtype : 'label', - forId : 'myFieldId', - text : 'INLINE HELP: To be updated', - margin : '15 0 20 10', - cls : "inline-help" - }, this.fitGrid.getPanel(), this.plotWidget.getPanel() ] - -}; - -FitForm.prototype._getButtons = function() { - return []; -}; - -FitForm.prototype.getPanel = function() { - var _this = this; - this.panel = Ext.create('Ext.form.Panel', { - width : this.width, - height : this.height, - margin : 10, - border : 1, - defaultType : 'textfield', - items : this._getItems(), - buttons : this._getButtons(), - listeners : { - afterrender : function() { - _this._populate(); - - } - } - }); - return this.panel; -}; - -/** Populates could be call when the DOM is not filled yet **/ -FitForm.prototype._populate = function() { -}; - -/** It populates the form * */ -FitForm.prototype.refresh = function(subtractions) { - this.subtractions = subtractions; - this.fitGrid.refresh(subtractions); -}; - -FitForm.prototype.input = function() { - return {}; -}; - -/** It populates the form **/ -FitForm.prototype.test = function(targetId) { - var macromoleculeForm = new FitForm(); - var panel = macromoleculeForm.getPanel(); - panel.render(targetId); -}; - -/** - * Example form - * - * @witdh - * @height - */ -function RigidBodyModelingResultForm(args) { - this.id = BUI.id(); - this.width = null; - this.height = null; - - if (args != null) { - if (args.width != null) { - this.width = args.width; - } - if (args.height != null) { - this.height = args.height; - } - } - - var _this = this; - /** Widgets **/ - this.rigidModelGrid = new RigidModelGrid({ - width : null, - height : 200 - }); - - this.rigidModelGrid.onSelected.attach(function(sender, fits){ - var ids = []; - for ( var i in fits) { - ids.push(fits[i].fitStructureToExperimentalDataId); - } - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, data){ -// debugger - - }); - - adapter.getScatteringCurveByFrameIdsList([], [], [], ids); - }); - -} - -RigidBodyModelingResultForm.prototype._getItems = function() { - var _this = this; - return [ { - xtype : 'label', - forId : 'myFieldId', - text : 'INLINE HELP: To be updated', - margin : '15 0 20 10', - cls : "inline-help" - }, this.rigidModelGrid.getPanel() ] - -}; - -RigidBodyModelingResultForm.prototype._getButtons = function() { - return []; -}; - -RigidBodyModelingResultForm.prototype.getPanel = function() { - var _this = this; - this.panel = Ext.create('Ext.form.Panel', { - width : this.width, - height : this.height, - margin : 10, - border : 1, - defaultType : 'textfield', - items : this._getItems(), - buttons : this._getButtons(), - listeners : { - afterrender : function() { - _this._populate(); - - } - } - }); - return this.panel; -}; - -/** Populates could be call when the DOM is not filled yet **/ -RigidBodyModelingResultForm.prototype._populate = function() { -}; - -/** It populates the form * */ -RigidBodyModelingResultForm.prototype.refresh = function(subtractions) { - this.rigidModelGrid.refresh(subtractions); -}; - -RigidBodyModelingResultForm.prototype.input = function() { - return {}; -}; - -/** It populates the form **/ -RigidBodyModelingResultForm.prototype.test = function(targetId) { - var macromoleculeForm = new RigidBodyModelingResultForm(); - var panel = macromoleculeForm.getPanel(); - panel.render(targetId); -}; - -/** - * Example form - * - * @witdh - * @height - */ -function SuperpositionForm(args) { - this.id = BUI.id(); - this.width = null; - this.height = null; - - if (args != null) { - if (args.width != null) { - this.width = args.width; - } - if (args.height != null) { - this.height = args.height; - } - } - - var _this = this; - - /** Widgets **/ - this.superpositionGrid = new SuperpositionGrid({ - width : null, - height : 200 - }); - - this.superpositionGrid.onSelected.attach(function(sender, superpositions) { - var ids = []; - for ( var i in superpositions) { - ids.push(superpositions[i].superpositionId); - } -// _this._renderAbinitio(ids); -// _this.aprioriPDBViewer.refresh(); - _this._renderAligned(ids); - // getAlignedPDBContentBySuperpositionList - }); - - /** PDB viewer **/ -// this.abinitioPDBViewer = new PDBViewer({ -// width : 860 / 2, -// height : 300 / 2, -// margin : 0 -// }); -// -// /** PDB viewer **/ -// this.aprioriPDBViewer = new StructurePDBViewer({ -// width : 860 / 2, -// height : 300 / 2, -// margin : 0 -// }); - - this.alignedPDBViewer = new AlignedSuperpositionPDBViewer({ - width : 860 , - height : 300 - }); - -} - -SuperpositionForm.prototype._renderAligned = function(modelsIdList) { - /** Trying to plot the PDB file **/ - try { - this.alignedPDBViewer.refresh(modelsIdList); - } catch (e) { - console.log(e); - } -}; - -SuperpositionForm.prototype._renderAbinitio = function(modelsIdList) { - /** Trying to plot the PDB file **/ - try { - var viz = []; - for (var i = 0; i < modelsIdList.length; i++) { - viz.push({ - modelId : modelsIdList[i], - color : "0xFF6600", - opacity : 0.8 - }); - } - this.abinitioPDBViewer.refresh(viz); - } catch (e) { - console.log(e); - } -}; - -SuperpositionForm.prototype._getItems = function() { - var _this = this; - return [ { - xtype : 'label', - forId : 'myFieldId', - text : 'INLINE HELP: To be updated', - margin : '15 0 20 10', - cls : "inline-help" - }, this.superpositionGrid.getPanel(), { - xtype : 'container', - layout : 'hbox', - items : [ -// { -// xtype : 'container', -// layout : 'vbox', -// items : [ this.abinitioPDBViewer.getPanel(), this.aprioriPDBViewer.getPanel() -// -// ] -// }, - - this.alignedPDBViewer.getPanel() ] - } ] - -}; - -SuperpositionForm.prototype._getButtons = function() { - return []; -}; - -SuperpositionForm.prototype.getPanel = function() { - var _this = this; - this.panel = Ext.create('Ext.form.Panel', { - width : this.width, - height : this.height, - margin : 10, - border : 1, - defaultType : 'textfield', - items : this._getItems(), - buttons : this._getButtons(), - listeners : { - afterrender : function() { - _this._populate(); - - } - } - }); - return this.panel; -}; - -/** Populates could be call when the DOM is not filled yet **/ -SuperpositionForm.prototype._populate = function() { -}; - -/** It populates the form * */ -SuperpositionForm.prototype.refresh = function(subtractions) { - this.subtractions = subtractions; - this.superpositionGrid.refresh(subtractions); -}; - -SuperpositionForm.prototype.input = function() { - return {}; -}; - -/** It populates the form **/ -SuperpositionForm.prototype.test = function(targetId) { - var macromoleculeForm = new SuperpositionForm(); - var panel = macromoleculeForm.getPanel(); - panel.render(targetId); -}; - -/** - * Example form - * - * @witdh - * @height - */ -function AssemblyForm(args) { - this.id = BUI.id(); - this.width = 700; - this.height = 500; - - if (args != null) { - if (args.width != null) { - this.width = args.width; - } - if (args.height != null) { - this.height = args.height; - } - } - - this.molarityGrid = new MolarityGrid({height : this.height - 50}); -} - -AssemblyForm.prototype._getItems = function() { - return [ { - xtype : 'label', - forId : 'myFieldId', - text : 'List of previously defined macromolecules present in the assembly. This information will be used for additional cross-checks where possible', - margin : '15 0 20 10', - cls : "inline-help" - }, this.molarityGrid.getPanel() ]; -}; - -AssemblyForm.prototype._getButtons = function() { - return []; -}; - -AssemblyForm.prototype.getPanel = function() { - this.panel = Ext.create('Ext.form.Panel', { - width : this.width, - height : this.height, - margin : 10, - border : 0, - defaultType : 'textfield', - items : this._getItems(), - buttons : this._getButtons() - }); - return this.panel; -}; - - -/** It populates the form **/ -AssemblyForm.prototype.refresh = function(macromolecule) { - this.macromolecule = macromolecule; - this.molarityGrid.refresh(macromolecule); -}; - -AssemblyForm.prototype.input = function() { - return {}; -}; - - -AssemblyForm.prototype.test = function(targetId) { - var assemblyForm = new AssemblyForm(); - - var panel = assemblyForm.getPanel(); - panel.render(targetId); -}; /** - * Edit the information of a buffer + * Example form * - * #onSaved - * #onRemoveAdditive + * @witdh + * @height */ -function BufferForm() { +function DataReductionForm(args) { + this.id = BUI.id(); + this.width = null; + this.height = null; + + if (args != null) { + if (args.width != null) { + this.width = args.width; + } + if (args.height != null) { + this.height = args.height; + } + } + var _this = this; - this.additiveGrid = new AdditiveGrid(); - this.additiveGrid.onRemoveButtonClicked.attach(function(sender, args) { - _this.onRemoveAdditive.notify(args); + /** Widgets **/ + this.plotWidget = new PlotWidget({ + width : 650, + height : 490 }); - this.onSaved = new Event(this); - this.onRemoveAdditive = new Event(this); + /** Selected frames to be displayed **/ + this.selectedItems = { + frames : [], + averages : [], + subtractions : [] + }; + } -BufferForm.prototype.getBuffer = function() { - this.buffer.name = Ext.getCmp("buffer_name").getValue(); - this.buffer.acronym = Ext.getCmp("buffer_acronym").getValue(); - this.buffer.comments = Ext.getCmp("buffer_comments").getValue(); - this.buffer.ph = Ext.getCmp("buffer_ph").getValue(); - this.buffer.composition = Ext.getCmp("buffer_composition").getValue(); - this.buffer.bufferhasadditive3VOs = this.additiveGrid.getAdditives(); - return this.buffer; +DataReductionForm.prototype._parseSelectedItemsToIds = function(selectedArray) { + var ids = []; + if (selectedArray != null) { + for (var i = 0; i < selectedArray.length; i++) { + ids.push(selectedArray[i].id); + } + } + return ids; }; -BufferForm.prototype._getTopPanel = function() { +DataReductionForm.prototype.onSelectionChanged = function(columnName, selected) { + if (selected != null) { + if (columnName == "Frames") { + this.selectedItems.frames = selected; + this.selectedItems.subtractions = []; + } + if (columnName == "Averages") { + this.selectedItems.averages = selected; + } + if (columnName == "Subtractions") { + this.selectedItems.frames = []; + this.selectedItems.subtractions = selected; + } + } + this.plotWidget.refresh(this._parseSelectedItemsToIds(this.selectedItems.frames), this._parseSelectedItemsToIds(this.selectedItems.subtractions)); +}; + +DataReductionForm.prototype._getFramesExtPanel = function(store, columnName, height) { + var _this = this; + + var selModel = Ext.create('Ext.selection.RowModel', { + allowDeselect : true, + mode : 'MULTI', + listeners : { + selectionchange : function(sm, selections) { + var selected = []; + for (var i = 0; i < selections.length; i++) { + selected.push(selections[i].raw); + } + _this.onSelectionChanged(columnName, selected); + } + } + }); + + return Ext.create('Ext.grid.Panel', { + store : store, + margin : 10, + height : height, + width : 200, + selModel : selModel, + columns : [ { + text : columnName, + dataIndex : 'fileName', + flex : 1 + } ], + viewConfig : { + } + }); +}; + +DataReductionForm.prototype._getFramesPanel = function() { + var fields = [ 'fileName', 'type', 'id' ]; + + this.framesStore = Ext.create('Ext.data.Store', { + fields : fields, + sorters : 'fileName' + }); + + this.averagesStore = Ext.create('Ext.data.Store', { + fields : fields + }); + + this.subtractionStore = Ext.create('Ext.data.Store', { + fields : fields + }); + + var gridFrames = this._getFramesExtPanel(this.framesStore, "Frames", 375); + var gridAvgs = this._getFramesExtPanel(this.averagesStore, "Averages", 125); + var subtractionAvgs = this._getFramesExtPanel(this.subtractionStore, "Subtractions", 75); + return { xtype : 'container', - layout : 'hbox', - border : 0, - frame : true, + layout : 'vbox', + items : [ gridFrames, subtractionAvgs ] + }; +}; + +DataReductionForm.prototype._getImageContainer = function(name, help) { + var html = "
" + name + "
" + return { + xtype : 'container', + layout : 'vbox', items : [ { - xtype : 'container', - layout : 'hbox', - items : [ { - xtype : 'container', - flex : 1, - border : false, - layout : 'anchor', - defaultType : 'textfield', - items : [ { - xtype : 'requiredtext', - id : 'buffer_name', - fieldLabel : 'Name', - name : 'name', - width : '200px', - value : this.buffer.name - }, { - xtype : 'requiredtext', - id : 'buffer_acronym', - fieldLabel : 'Acronym', - name : 'acronym', - width : '200px', - value : this.buffer.acronym - } ] - } ] + html : html, + margin : "5 0 0 0", + height : 95, + width : 100 }, { - xtype : 'container', - flex : 1, - layout : 'anchor', - defaultType : 'textfield', - margin : '0 0 0 10', - items : [ { - id : 'buffer_ph', - fieldLabel : 'pH', - name : 'ph', - value : this.buffer.ph, - xtype : 'numberfield', - width : 200, - minValue : 0, - maxValue : 15 - }, { - xtype : 'requiredtext', - id : 'buffer_composition', - fieldLabel : 'Composition', - name : 'composition', - width : 200, - value : this.buffer.composition - } ] + xtype : 'label', + forId : 'myFieldId', + text : help, + margin : '5 0 0 0', + cls : "inline-help" } ] - }; + } }; -BufferForm.prototype.getPanel = function(buffer) { - this.buffer = buffer; - this.panel = Ext.createWidget({ +DataReductionForm.prototype._getItems = function() { + var _this = this; + return [ { xtype : 'container', - layout : 'vbox', - style : { - padding : '10px' - }, - fieldDefaults : { - labelAlign : 'left', - labelWidth : 50 + layout : 'hbox', + items : [ + this._getFramesPanel(), + this.plotWidget.getPanel(), + { + xtype : 'panel', + width : 110, + frame : true, + margin : "10 5 5 5", + border : 0, + layout : 'vbox', + items : [ this._getImageContainer("scattering", "Scattering"), this._getImageContainer("guinier", "Guinier Region"), + this._getImageContainer("kratky", "Kratky Plot"), this._getImageContainer("gnom", "P(r) distribution") ] + } ] + } ] +}; - }, - items : [ this._getTopPanel(), { - id : 'buffer_comments', - xtype : 'textareafield', - name : 'comments', - fieldLabel : 'Comments', - width : '100%', - value : buffer.comments - }, this.additiveGrid.getPanel(buffer) ] +DataReductionForm.prototype._getButtons = function() { + return []; +}; + +DataReductionForm.prototype.getPanel = function() { + var _this = this; + this.panel = Ext.create('Ext.form.Panel', { + width : this.width, + height : this.height, + border : 0, + items : this._getItems(), + buttons : this._getButtons(), + listeners : { + afterrender : function() { + _this._populate(); + } + } }); return this.panel; }; -BufferForm.prototype.input = function() { - return { - buffer : { - "bufferId" : 422, - "proposalId" : 10, - "safetyLevelId" : null, - "name" : "B1", - "acronym" : "B1", - "ph" : null, - "composition" : null, - "bufferhasadditive3VOs" : [], - "comments" : null +/** Populates could be call when the DOM is not filled yet **/ +DataReductionForm.prototype._populate = function() { +}; + +/** It populates the form * */ +DataReductionForm.prototype.refresh = function(subtractions) { + if (subtractions != null) { + for (var i = 0; i < subtractions.length; i++) { + /** Loading frame grids **/ + var subtraction = subtractions[i]; + var averages = [ { + fileName : BUI.getFileName(subtraction.bufferAverageFilePath), + type : 'bufferAvg', + id : subtraction.subtractionId + }, { + fileName : BUI.getFileName(subtraction.sampleAverageFilePath), + type : 'sampleAvg', + id : subtraction.subtractionId + } + + ]; + this.averagesStore.loadData(averages, true); + this.subtractionStore.loadData([ { + fileName : BUI.getFileName(subtraction.substractedFilePath), + type : 'SUBTRACTION', + id : subtraction.subtractionId + } ], true); + + var frames = []; + /** Buffers **/ + if (subtraction.bufferOneDimensionalFiles != null) { + if (subtraction.bufferOneDimensionalFiles.frametolist3VOs != null) { + for (var j = 0; j < subtraction.bufferOneDimensionalFiles.frametolist3VOs.length; j++) { + var frametolist3VO = subtraction.bufferOneDimensionalFiles.frametolist3VOs[j]; + if (frametolist3VO.frame3VO != null) { + frames.push({ + fileName : BUI.getFileName(frametolist3VO.frame3VO.filePath), + type : 'BUFFER', + id : frametolist3VO.frame3VO.frameId + }); + } + } + } + } + /** Samples **/ + if (subtraction.sampleOneDimensionalFiles != null) { + if (subtraction.sampleOneDimensionalFiles.frametolist3VOs != null) { + for (var j = 0; j < subtraction.sampleOneDimensionalFiles.frametolist3VOs.length; j++) { + var frametolist3VO = subtraction.sampleOneDimensionalFiles.frametolist3VOs[j]; + if (frametolist3VO.frame3VO != null) { + frames.push({ + fileName : BUI.getFileName(frametolist3VO.frame3VO.filePath), + type : 'SAMPLE', + id : frametolist3VO.frame3VO.frameId + }); + } + } + } + } + + this.framesStore.loadData(frames, true); + + /** Loading images **/ + this._displayImage("scattering", subtraction.subtractionId); + this._displayImage("kratky", subtraction.subtractionId); + this._displayImage("guinier", subtraction.subtractionId); + this._displayImage("gnom", subtraction.subtractionId); } - }; + } }; -BufferForm.prototype.test = function(targetId) { - var bufferForm = new BufferForm(); - var panel = bufferForm.getPanel(bufferForm.input().buffer); +DataReductionForm.prototype._displayImage = function(name, subtractionId) { + var url = BUI.getURL() + '&type=' + name + '&subtractionId=' + subtractionId; + var event = "OnClick= window.open('" + url + "')"; + document.getElementById(this.id + "_" + name).innerHTML = ''; +}; + +DataReductionForm.prototype.input = function() { + return {}; +}; + +/** It populates the form **/ +DataReductionForm.prototype.test = function(targetId) { + var macromoleculeForm = new DataReductionForm(); + var panel = macromoleculeForm.getPanel(); panel.render(targetId); }; - -/** - * @showTitle - * - * #onSaved - * #onAddPlates - * #onRemovePlates - **/ -function CaseForm(args) { - this.width = 700; - this.showTitle = true; + +function PlotWidget(args) { + this.width = 600; + this.height = 600; + this.id = BUI.id(); + + this.linear = false; + + this.margin = "10 0 0 0"; + /** for each stat the max and minimum value when it is scaled in order to show correctly in the legend **/ + this.ranges = {}; if (args != null) { - if (args.showTitle != null) { - this.showTitle = args.showTitle; + if (args.width != null) { + this.width = args.width; + } + if (args.height != null) { + this.height = args.height; + } + if (args.margin != null) { + this.margin = args.margin; } } - var _this = this; - this.stockSolutionGrid = new StockSolutionGrid({ - width : this.width - 10, - minHeight : 300, - height : 300, - tbar : true, - showTitle : true, - isPackedVisible : false, - btnAddExisting : true, - btnRemoveVisible : false, - btnUnpackVisible : true - }); +} - /** When selecting existing stock solutions **/ - this.stockSolutionGrid.onStockSolutionSelected.attach(function(sender, stockSolutions) { - if (stockSolutions != null) { - for ( var i = 0; i < stockSolutions.length; i++) { - _this.saveStockSolution(stockSolutions[i]); - } +PlotWidget.prototype.getMenu = function() { + var _this = this; + /** Actions buttons **/ + var actions = []; + actions.push("->"); + actions.push({ + text : "Export as Image", + scope : this, + icon : '../images/save.gif', + handler : function(item, pressed) { + var largeImage = document.createElement("img"); + largeImage.style.display = 'block'; + largeImage.style.width = 200 + "px"; + largeImage.style.height = 200 + "px"; + largeImage.setAttribute('src', Dygraph.Export.asCanvas(this.dygraphObject.dygraph).toDataURL()); + window.open(Dygraph.Export.asCanvas(this.dygraphObject.dygraph).toDataURL(), 'Image', ''); } }); - /** it can be because it has been added a new one or removed **/ - this.stockSolutionGrid.onProposalChanged.attach(function(sender, stockSolution) { - if (stockSolution != null) { - _this.saveStockSolution(stockSolution); - } else { - BIOSAXS.proposal.onInitialized.attach(function() { - _this.stockSolutionGrid.refresh(BIOSAXS.proposal.getStockSolutionsByDewarId(_this.dewar.dewarId)); + return actions; +}; - _this.stockSolutionGrid.grid.setLoading(false); - }); - BIOSAXS.proposal.init(); +/** Looks for the maximum value and then divide everything but that value **/ +PlotWidget.prototype.scaledData = function(data) { + for (var i = 0; i < data.length; i++) { + var values = this.getMaxAndMinValue(data[i]); + data[i] = this.divideValuesByMax(data[i], values.max); + this.ranges[data[i].label] = values; + } + return data; +}; + +/** Given a stat float[] and a max number it will divide each value by max **/ +PlotWidget.prototype.divideValuesByMax = function(stat, max) { + for (var j = 0; j < stat.data.length; j++) { + if (max != 0) { + stat.data[j] = Number(stat.data[j]) / max; + stat.std[j] = Number(stat.std[j]) / max; + } + } + return stat; +}; +/** returns max value of a stat **/ +PlotWidget.prototype.getMaxAndMinValue = function(stat) { + var max = 0; + var min = stat.data[0]; + for (var j = 0; j < stat.data.length; j++) { + if (Number(stat.data[j]) > max) { + max = Number(stat.data[j]); + } + if (Number(stat.std[j]) > max) { + max = Number(stat.std[j]); + } + if (Number(stat.data[j]) < min) { + min = Number(stat.data[j]); } + } + return { + max : Number(max), + min : Number(min) + }; +}; +PlotWidget.prototype._renderDygraph = function(parsed, colors, labels) { + this.dygraphObject = new StdDevDyGraph(this.id, { + width : this.width - 20, + height : this.height - 40, + xlabel : "", }); - this.onSaved = new Event(this); - this.onAddPlates = new Event(this); - this.onRemovePlates = new Event(this); -} + this.dygraphObject.draw(parsed, colors, labels); -CaseForm.prototype.saveStockSolution = function(stockSolution) { - var _this = this; - var adapter = new BiosaxsDataAdapter(); - this.stockSolutionGrid.grid.setLoading("ISPyB: setting case to Stock solution"); - adapter.onSuccess.attach(function(sender, stockSolution) { - BIOSAXS.proposal.onInitialized.attach(function() { - _this.stockSolutionGrid.refresh(BIOSAXS.proposal.getStockSolutionsByDewarId(_this.dewar.dewarId)); - _this.stockSolutionGrid.grid.setLoading(false); - }); - BIOSAXS.proposal.init(); - }); - adapter.onError.attach(function(sender, data) { - _this.stockSolutionGrid.grid.setLoading(false); - BUI.showError(data); - }); - stockSolution.boxId = _this.dewar.dewarId; - adapter.saveStockSolution(stockSolution); }; -CaseForm.prototype.fillStores = function() { - var _this = this; - this.panel.setLoading("Loading Labcontacts from database"); - - var proposal = BUI.getProposal(); - proposal.onDataRetrieved.attach(function(sender, data) { - _this.labContactForSendingStore.loadData(data, false); - _this.labContactForReturnStore.loadData(data, false); - _this.panel.setLoading(false); +PlotWidget.prototype.getPanel = function() { + this.panel = Ext.create('Ext.panel.Panel', { + width : this.width, + height : this.height, + margin : this.margin, + tbar : this.getMenu(), + items : [ { + html : "", + id : this.id, + width : this.width, + height : this.height, + padding : 10, + margin : "0 0 0 -30", + border : 0 + } ] }); - proposal.getLabContactsByProposalId(); - -}; -CaseForm.prototype.refresh = function(dewar) { - this.setDewar(dewar); - this.stockSolutionGrid.refresh(BIOSAXS.proposal.getStockSolutionsByDewarId(dewar.dewarId)); + return this.panel; }; -CaseForm.prototype.getDewar = function() { - this.dewar.code = Ext.getCmp("dewar_code").getValue(); - this.dewar.comments = Ext.getCmp("dewar_comments").getValue(); - this.dewar.trackingNumberFromSynchrotron = Ext.getCmp("dewar_trackingNumberFromSynchrotron").getValue(); - this.dewar.trackingNumberToSynchrotron = Ext.getCmp("dewar_trackingNumberToSynchrotron").getValue(); - this.dewar.transportValue = Ext.getCmp("dewar_transportValue").getValue(); - this.dewar.storageLocation = Ext.getCmp("dewar_storageLocation").getValue(); - this.dewar.firstExperimentId = this.macromoleculeCombo.getValue(); - return this.dewar; -}; +PlotWidget.prototype.getPoint = function(y, error) { + var minus = y - error; + var max = y + error; -CaseForm.prototype.setDewar = function(dewar) { - this.dewar = dewar; - Ext.getCmp("dewar_code").setValue(this.dewar.code); - Ext.getCmp("dewar_dewarStatus").setText(new String(this.dewar.dewarStatus).toUpperCase()); - Ext.getCmp("dewar_comments").setValue(this.dewar.comments); - Ext.getCmp("dewar_trackingNumberFromSynchrotron").setValue(this.dewar.trackingNumberFromSynchrotron); - Ext.getCmp("dewar_trackingNumberToSynchrotron").setValue(this.dewar.trackingNumberToSynchrotron); - Ext.getCmp("dewar_transportValue").setValue(this.dewar.transportValue); - Ext.getCmp("dewar_storageLocation").setValue(this.dewar.storageLocation); - if (dewar.sessionVO != null) { - this.macromoleculeCombo.setValue(dewar.sessionVO.sessionId); + if (this.linear) { + return [ Math.abs(minus), y, Math.abs(max) ]; + } + if ((minus != 0) && (max != 0)) { + return [ Math.log(Math.abs(minus)), Math.log(y), Math.log(Math.abs(max)) ]; + } else { + return [ Math.log(y), Math.log(y), Math.log(y) ]; } -}; -CaseForm.prototype.getSessionCombo = function() { - this.macromoleculeCombo = BIOSAXS_COMBOMANAGER.getComboSessions(BIOSAXS.proposal.getSessions(), { - labelWidth : 100, - margin : '5 0 00 0', - width : 250 - }); - return this.macromoleculeCombo; }; -CaseForm.prototype.getInformationPanel = function() { - if (this.panel == null) { - this.informationPanel = Ext.create('Ext.form.Panel', { - width : this.width - 10, - border : 0, - items : [ { - xtype : 'container', - margin : "2 2 2 2", - collapsible : false, - defaultType : 'textfield', - layout : 'anchor', - items : [ { - xtype : 'container', - layout : 'hbox', - items : [ { - xtype : 'requiredtext', - fieldLabel : 'Code', - allowBlank : false, - name : 'code', - id : 'dewar_code', - anchor : '50%' - }, { - xtype : 'label', - margin : '0 0 0 20', - readOnly : true, - id : 'dewar_dewarStatus', - anchor : '50%' - } ] - }, this.getSessionCombo(), { - margin : '5 0 0 0', - xtype : 'textareafield', - name : 'comments', - id : 'dewar_comments', - width : this.width - 50, - fieldLabel : 'Comments' - } ] - }, { - xtype : 'fieldset', - title : 'Courier Accounts Details for Return', - collapsible : false, - layout : 'anchor', - defaults : { - anchor : '100%' - }, - items : [ { - xtype : 'container', - layout : 'hbox', - items : [ { - xtype : 'textfield', - labelWidth : 200, - width : 280, - fieldLabel : 'Track Number From Synchrotron', - id : 'dewar_trackingNumberFromSynchrotron' - }, { - xtype : 'numberfield', - width : 190, - labelWidth : 110, - margin : '0 0 0 30', - fieldLabel : 'Transport Value', - id : 'dewar_transportValue' +PlotWidget.prototype.getDataPlot = function(frames, subtractions, models, fits, rbms) { + var files = []; + var labels = [ "Intensity" ]; + if (frames != null) { + for (var i = 0; i < frames.length; i++) { + files.push(frames[i].data); + labels.push(frames[i].fileName); + } + } + function splitData(data, column, errorColumn, name){ + var result = [] + for (var j = 0; j < data.length; j++) { + console.log(data[j][column]); + result.push([j,data[j][column],data[j][errorColumn]]);//[0, data[i][column],0]]); + } + files.push(result); + labels.push(name); + } + + if (subtractions != null) { + for (var i = 0; i < subtractions.length; i++) { + /** For subtraction **/ + files.push(subtractions[i].subtraction.data); + labels.push(subtractions[i].subtraction.fileName); + /** For sample average **/ +// files.push(subtractions[i].sampleAvg.data); +// labels.push(subtractions[i].sampleAvg.fileName); + /** For buffer average **/ +// files.push(subtractions[i].bufferAvg.data); +// labels.push(subtractions[i].bufferAvg.fileName); + } + } + + if (models != null) { + for (var i = 0; i < models.length; i++) { + for ( var key in models[i]) { + splitData(models[i][key].fir.data, 1, 2, "Intensity"); + splitData(models[i][key].fir.data, 3, 3, "Fit"); + } + } + } + + if (fits != null) { + for (var i = 0; i < fits.length; i++) { + for ( var key in fits[i]) { + + /** adding fit file to be plotted **/ + if (fits[i][key].fit.data[0].length == 3){ + splitData(fits[i][key].fit.data, 1, 1, "Intensity"); + splitData(fits[i][key].fit.data, 2, 2, "Fit"); + } + + /** s, Iexp(s), err, Ifit(s). **/ + if (fits[i][key].fit.data[0].length == 4){ + splitData(fits[i][key].fit.data, 1, 2, "Intensity"); + splitData(fits[i][key].fit.data, 3, 3, "Fit"); + } + + if (fits[i][key].fit.data[0].length == 5){ + /** X Intensity Fit Error Residues **/ + + splitData(fits[i][key].fit.data, 1, 3, "Intensity"); + splitData(fits[i][key].fit.data, 2, 2, "Fit"); + } + } + } + } + + var dataPoints = []; + if (files.length > 0) { + for (var i = 0; i < files[0].length; i++) { + dataPoints.push([ files[0][i][0], this.getPoint(files[0][i][1], files[0][i][2]) ]); + } + if (files.length > 1) { + for (var i = 1; i < files.length; i++) { + for (var j = 0; j < dataPoints.length; j++) { + if (files[i][j] != null){ + dataPoints[j].push(this.getPoint(files[i][j][1], files[i][j][2])); } - - ] - }, { - xtype : 'container', - layout : 'hbox', - margin : '10 0 0 0', - items : [ - - { - xtype : 'textfield', - labelWidth : 200, - width : 280, - fieldLabel : 'Track Number To Synchrotron', - id : 'dewar_trackingNumberToSynchrotron' - }, { - xtype : 'textfield', - margin : '0 0 0 30', - width : 190, - labelWidth : 110, - fieldLabel : 'Storage Location', - id : 'dewar_storageLocation' + else{ + dataPoints[j].push([0,0,0]); } + } + } + } + } - ] - } ] - } ] - }); + return { + dataPoints : dataPoints, + labels : labels } +}; - return this.informationPanel; +PlotWidget.prototype.refresh = function(frames, subtractions, models, fits, rbms, colors) { + + var _this = this; + this.panel.setLoading("Reading Files"); + + var dataAdapter = new BiosaxsDataAdapter(); + dataAdapter.onSuccess.attach(function(sender, data) { + _this.panel.setLoading("Rendering"); + var parsed = _this.getDataPlot(data.frames, data.subtractions, data.models, data.fits, rbms); + _this._renderDygraph(parsed.dataPoints, colors, parsed.labels); + _this.panel.setLoading(false); + }); + dataAdapter.onError.attach(function(sender, data) { + _this.panel.setLoading(false); + }); + dataAdapter.getDataPlot(frames, subtractions, models, fits, rbms); }; -CaseForm.prototype.getPanel = function(dewar) { - this.dewar = dewar; - this.panel = Ext.createWidget({ - xtype : 'container', - layout : 'vbox', - width : this.width, - border : 0, - items : [ { - items : { - xtype : "container", - layout : "vbox", - margin : "5 5 5 5", - items : [ this.getInformationPanel(dewar), this.stockSolutionGrid.getPanel() ] +PlotWidget.prototype.input = function() { + return DATADOC.getHPLCData(); +}; - } - } ] + +/** + * Fit form + * + * @witdh + * @height + */ +function FitForm(args) { + this.id = BUI.id(); + this.width = null; + this.height = null; + + if (args != null) { + if (args.width != null) { + this.width = args.width; + } + if (args.height != null) { + this.height = args.height; + } + } + + var _this = this; + + /** Widgets **/ + this.fitGrid = new FitStructureToExperimentDataGrid({ + width : null, + height : 200 }); - this.refresh(dewar); - return this.panel; + this.fitGrid.onSelected.attach(function(sender, fits) { + var modelsIdList = []; + for ( var i in fits) { + modelsIdList.push(fits[i].fitStructureToExperimentalDataId); + } + _this._renderPlot(_this.subtractions[0].subtractionId, modelsIdList); +// var adapter = new BiosaxsDataAdapter(); +// adapter.onSuccess.attach(function(sender, data) { +// +// }); +// adapter.getScatteringCurveByFrameIdsList([], [], [], ids); + }); + + + /** Dygraph Widget that plots fir files**/ + this.plotWidget = new PlotWidget({ + width : 745, + height : 300, + margin : "10 0 10 10" + }); +} +FitForm.prototype._renderPlot = function(subtractionId, modelsIdList) { + /** Trying to plot tje subtraction and the models **/ + try { + var colors = [ "#669900", "#0000FF" ]; + + this.plotWidget.refresh([], [ ], [], modelsIdList, [], colors); + } catch (e) { + console.log(e); + } }; -CaseForm.prototype.input = function() { - return { - dewar : { - "dewarId" : 305861, - "code" : "ESRF-TEST", - "comments" : "comments", - "storageLocation" : "FRIDGE", - "dewarStatus" : "opened", - "timeStamp" : null, - "isStorageDewar" : null, - "barCode" : "ESRF305861", - "customsValue" : null, - "transportValue" : null, - "trackingNumberToSynchrotron" : "3333", - "trackingNumberFromSynchrotron" : "224466", - "type" : "Dewar", - "sessionVO" : { - "sessionId" : 31697, - "expSessionPk" : null, - "projectCode" : null, - "startDate" : "2012 07 21", - "endDate" : "2012 07 23", - "beamlineName" : "BM29", - "scheduled" : 1, - "nbShifts" : 2, - "comments" : null, - "beamlineOperator" : "PERNOT P", - "usedFlag" : null, - "sessionTitle" : null, - "structureDeterminations" : null, - "dewarTransport" : null, - "databackupFrance" : null, - "databackupEurope" : null, - "visit_number" : null, - "operatorSiteNumber" : "14061", - "timeStamp" : "2012 04 25" +FitForm.prototype._getItems = function() { + var _this = this; + return [ { + xtype : 'label', + forId : 'myFieldId', + text : 'INLINE HELP: To be updated', + margin : '15 0 20 10', + cls : "inline-help" + }, this.fitGrid.getPanel(), this.plotWidget.getPanel() ] + +}; + +FitForm.prototype._getButtons = function() { + return []; +}; + +FitForm.prototype.getPanel = function() { + var _this = this; + this.panel = Ext.create('Ext.form.Panel', { + width : this.width, + height : this.height, + margin : 10, + border : 1, + defaultType : 'textfield', + items : this._getItems(), + buttons : this._getButtons(), + listeners : { + afterrender : function() { + _this._populate(); + } - }, - proposal : { - "assemblies" : [], - "sessions" : [ { - "sessionId" : 31697, - "expSessionPk" : null, - "projectCode" : null, - "startDate" : "2012 07 21", - "endDate" : "2012 07 23", - "beamlineName" : "BM29", - "scheduled" : 1, - "nbShifts" : 2, - "comments" : null, - "beamlineOperator" : "PERNOT P", - "usedFlag" : null, - "sessionTitle" : null, - "structureDeterminations" : null, - "dewarTransport" : null, - "databackupFrance" : null, - "databackupEurope" : null, - "visit_number" : null, - "operatorSiteNumber" : "14061", - "timeStamp" : "2012 04 25" - } ], - "labcontacts" : [ { - "labContactId" : 787, - "personVO" : { - "personId" : 304252, - "personUUID" : null, - "familyName" : "KIM", - "givenName" : "Henry", - "title" : null, - "emailAddress" : "henry-sung.kim@ibs.fr", - "phoneNumber" : "", - "login" : "", - "passwd" : "", - "faxNumber" : "" - }, - "cardName" : "KIM-Institut de Bio", - "defaultCourrierCompany" : "22", - "courierAccount" : "", - "billingReference" : "", - "dewarAvgCustomsValue" : 0, - "dewarAvgTransportValue" : 0 - } ], - "buffers" : [ { - "bufferId" : 811, - "proposalId" : 3124, - "safetyLevelId" : null, - "name" : "EDTA", - "acronym" : "EDTA", - "ph" : null, - "composition" : "", - "bufferhasadditive3VOs" : [], - "comments" : "" - }, { - "bufferId" : 810, - "proposalId" : 3124, - "safetyLevelId" : null, - "name" : "HEPES", - "acronym" : "HEPES", - "ph" : null, - "composition" : "", - "bufferhasadditive3VOs" : [], - "comments" : "" - } ], - "shippings" : [ { - "shippingId" : 304107, - "shippingName" : "TEST", - "deliveryAgentAgentName" : null, - "deliveryAgentShippingDate" : null, - "deliveryAgentDeliveryDate" : null, - "deliveryAgentAgentCode" : null, - "deliveryAgentFlightCode" : null, - "shippingStatus" : "opened", - "timeStamp" : "2013 09 25", - "laboratoryId" : null, - "isStorageShipping" : null, - "creationDate" : "2013 09 25", - "comments" : "test", - "sendingLabContactVO" : { - "labContactId" : 787, - "personVO" : { - "personId" : 304252, - "personUUID" : null, - "familyName" : "KIM", - "givenName" : "Henry", - "title" : null, - "emailAddress" : "henry-sung.kim@ibs.fr", - "phoneNumber" : "", - "login" : "", - "passwd" : "", - "faxNumber" : "" - }, - "cardName" : "KIM-Institut de Bio", - "defaultCourrierCompany" : "22", - "courierAccount" : "", - "billingReference" : "", - "dewarAvgCustomsValue" : 0, - "dewarAvgTransportValue" : 0 - }, - "returnLabContactVO" : { - "labContactId" : 787, - "personVO" : { - "personId" : 304252, - "personUUID" : null, - "familyName" : "KIM", - "givenName" : "Henry", - "title" : null, - "emailAddress" : "henry-sung.kim@ibs.fr", - "phoneNumber" : "", - "login" : "", - "passwd" : "", - "faxNumber" : "" - }, - "cardName" : "KIM-Institut de Bio", - "defaultCourrierCompany" : "22", - "courierAccount" : "", - "billingReference" : "", - "dewarAvgCustomsValue" : 0, - "dewarAvgTransportValue" : 0 - }, - "returnCourier" : null, - "dateOfShippingToUser" : null, - "shippingType" : "DewarTracking", - "dewarVOs" : [ { - "dewarId" : 305861, - "code" : "ESRF-TEST", - "comments" : "comments", - "storageLocation" : "FRIDGE", - "dewarStatus" : "opened", - "timeStamp" : null, - "isStorageDewar" : null, - "barCode" : "ESRF305861", - "customsValue" : null, - "transportValue" : null, - "trackingNumberToSynchrotron" : "3333", - "trackingNumberFromSynchrotron" : "224466", - "type" : "Dewar", - "sessionVO" : { - "sessionId" : 31697, - "expSessionPk" : null, - "projectCode" : null, - "startDate" : "2012 07 21", - "endDate" : "2012 07 23", - "beamlineName" : "BM29", - "scheduled" : 1, - "nbShifts" : 2, - "comments" : null, - "beamlineOperator" : "PERNOT P", - "usedFlag" : null, - "sessionTitle" : null, - "structureDeterminations" : null, - "dewarTransport" : null, - "databackupFrance" : null, - "databackupEurope" : null, - "visit_number" : null, - "operatorSiteNumber" : "14061", - "timeStamp" : "2012 04 25" - } - } ] - } ], - "macromolecules" : [ { - "macromoleculeId" : 5933, - "safetylevelId" : null, - "proposalId" : 3124, - "name" : "A", - "acronym" : "A", - "molecularMass" : "", - "extintionCoefficient" : "", - "sequence" : null, - "creationDate" : null, - "comments" : "", - "macromoleculeregion3VOs" : [], - "stoichiometry3VOsForHostMacromoleculeId" : [], - "structure3VOs" : [] - } ], - "stockSolutions" : [ { - "stockSolutionId" : 6, - "proposalId" : 3124, - "macromoleculeId" : 5933, - "bufferId" : 811, - "instructionSet3VO" : null, - "boxId" : 305861, - "storageTemperature" : "20", - "volume" : "300", - "concentration" : "1.2", - "comments" : "Buffer EDTA with A", - "name" : "A_EDTA_1.2", - "samples" : [] - } ] } + }); + return this.panel; +}; - }; +/** Populates could be call when the DOM is not filled yet **/ +FitForm.prototype._populate = function() { }; -CaseForm.prototype.test = function(targetId) { - var caseForm = new CaseForm(); - BIOSAXS.proposal = new Proposal(caseForm.input().proposal); - var panel = caseForm.getPanel(caseForm.input().dewar); - panel.render(targetId); +/** It populates the form * */ +FitForm.prototype.refresh = function(subtractions) { + this.subtractions = subtractions; + this.fitGrid.refresh(subtractions); +}; + +FitForm.prototype.input = function() { + return {}; +}; + +/** It populates the form **/ +FitForm.prototype.test = function(targetId) { + var macromoleculeForm = new FitForm(); + var panel = macromoleculeForm.getPanel(); + panel.render(targetId); }; -/** - * Example form - * - * @witdh - * @height - */ -function ExampleForm(args) { - this.id = BUI.id(); - this.width = 700; - this.height = 500; - - if (args != null) { - if (args.width != null) { - this.width = args.width; - } - if (args.height != null) { - this.height = args.height; - } - } -} - -ExampleForm.prototype._getItems = function() { - return [{ - fieldLabel : 'First Name', - name : 'first', - allowBlank : false - }, { - fieldLabel : 'Last Name', - name : 'last', - allowBlank : false - } ]; -}; - -ExampleForm.prototype._getItems = function() { - return [ ]; -}; - -ExampleForm.prototype._getButtons = function() { - return [ ]; -}; - -ExampleForm.prototype.getPanel = function() { - this.panel = Ext.create('Ext.form.Panel', { - width : this.width, - height : this.height, - margin : 10, - border : 1, - defaultType : 'textfield', - items : this._getItems(), - buttons : this._getButtons() - }); - return this.panel; -}; - -/** It populates the form **/ -ExampleForm.prototype.refresh = function(macromolecule) { -}; - -function ExperimentForm(args) { +/** + * Example form + * + * @witdh + * @height + */ +function RigidBodyModelingResultForm(args) { this.id = BUI.id(); + this.width = null; + this.height = null; if (args != null) { + if (args.width != null) { + this.width = args.width; + } + if (args.height != null) { + this.height = args.height; + } } - this.onSaved = new Event(this); -} - -ExperimentForm.prototype._getItems = function(experiment) { - this.experiment = experiment; - var typeCombo = Ext.create('Ext.form.ComboBox', { - id : this.id + 'type', - fieldLabel : 'Type', - store : [ "STATIC", "CALIBRATION", "HPLC" ], - queryMode : 'local', - labelWidth : 120, - displayField : 'name', - valueField : 'name', - value : experiment.json.type, - disabled : (experiment.json.type == 'TEMPLATE') + var _this = this; + /** Widgets **/ + this.rigidModelGrid = new RigidModelGrid({ + width : null, + height : 200 }); - var items = []; - - - if (experiment.json.type == "HPLC" ){ - var typeMacromolecule = BIOSAXS_COMBOMANAGER.getComboMacromoleculeByMacromolecules(BIOSAXS.proposal.getMacromolecules(), {labelWidth : 120, width: "120px"}); - if (experiment.getHPLCMacromolecule() != null){ - typeMacromolecule.setValue(experiment.getHPLCMacromolecule().macromoleculeId); - items.push(typeMacromolecule); + this.rigidModelGrid.onSelected.attach(function(sender, fits){ + var ids = []; + for ( var i in fits) { + ids.push(fits[i].fitStructureToExperimentalDataId); } - } - - - items.push(typeCombo, { - id : this.id + 'name', - xtype : 'textfield', - fieldLabel : 'Name', - labelWidth : 120, - width : '100%', - value : experiment.json.name - }, { - id : this.id + 'comments', - xtype : 'textareafield', - name : 'comments', - fieldLabel : 'Comments', - labelWidth : 120, - height : 120, - width : '100%', - value : experiment.json.comments + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, data){ +// debugger + + }); + + adapter.getScatteringCurveByFrameIdsList([], [], [], ids); }); - return items; + +} + +RigidBodyModelingResultForm.prototype._getItems = function() { + var _this = this; + return [ { + xtype : 'label', + forId : 'myFieldId', + text : 'INLINE HELP: To be updated', + margin : '15 0 20 10', + cls : "inline-help" + }, this.rigidModelGrid.getPanel() ] + }; -ExperimentForm.prototype.getPanel = function(experiment) { + +RigidBodyModelingResultForm.prototype._getButtons = function() { + return []; +}; + +RigidBodyModelingResultForm.prototype.getPanel = function() { var _this = this; - - this.panel = Ext.createWidget({ - xtype : 'container', - layout : 'vbox', - border : 0, - style : { - padding : '10px' - }, - fieldDefaults : { - labelAlign : 'left', - labelWidth : 50 - }, - items : this._getItems(experiment) + this.panel = Ext.create('Ext.form.Panel', { + width : this.width, + height : this.height, + margin : 10, + border : 1, + defaultType : 'textfield', + items : this._getItems(), + buttons : this._getButtons(), + listeners : { + afterrender : function() { + _this._populate(); + + } + } }); return this.panel; }; -ExperimentForm.prototype.input = function() { - return new ExperimentHeaderForm().input(); +/** Populates could be call when the DOM is not filled yet **/ +RigidBodyModelingResultForm.prototype._populate = function() { }; -ExperimentForm.prototype.test = function(targetId) { - var experimentForm = new ExperimentForm(); - var panel = experimentForm.getPanel(new Experiment(experimentForm.input().experiment)); +/** It populates the form * */ +RigidBodyModelingResultForm.prototype.refresh = function(subtractions) { + this.rigidModelGrid.refresh(subtractions); +}; + +RigidBodyModelingResultForm.prototype.input = function() { + return {}; +}; + +/** It populates the form **/ +RigidBodyModelingResultForm.prototype.test = function(targetId) { + var macromoleculeForm = new RigidBodyModelingResultForm(); + var panel = macromoleculeForm.getPanel(); panel.render(targetId); }; /** - * Shows the header for the experiments changing the color and parameters depending on experiment type + * Example form * + * @witdh + * @height */ -function ExperimentHeaderForm(args) { +function SuperpositionForm(args) { this.id = BUI.id(); - this.backgroundColor = '#FFFFFF'; -} + this.width = null; + this.height = null; -ExperimentHeaderForm.prototype.getHTMLSource = function(experiment) { - var html = BUI.createFormLabel("Name :", experiment.json.name, 75, 400, this.backgroundColor); - if (experiment.json.type == "HPLC") { - if (experiment.getHPLCMacromolecule() != null){ - html = html + BUI.createFormLabel("Molecule :", experiment.getHPLCMacromolecule().acronym, 75, 400, this.backgroundColor); + if (args != null) { + if (args.width != null) { + this.width = args.width; + } + if (args.height != null) { + this.height = args.height; } } - else{ - html = html + BUI.createFormLabel("Type :", experiment.json.type, 75, 400, this.backgroundColor); - } - - html = html + BUI.createFormLabel("Date :", experiment.json.creationDate, 75, 400, this.backgroundColor); - return html; -}; -ExperimentHeaderForm.prototype.getHTMLDownload = function(experiment) { - var bgcolor = "background-color:" + this.backgroundColor + ";"; - var html = "
"; - if ((experiment.json.type == "CALIBATION") || (experiment.json.type == "STATIC")) { - html = html + " Download Source File
"; - html = html - + ""; - } - if (experiment.json.type == "TEMPLATE") { - html = html - + " Download Source File"; - } + var _this = this; - if (experiment.json.type == "HPLC") { - html = html + " Download h5 File"; - } + /** Widgets **/ + this.superpositionGrid = new SuperpositionGrid({ + width : null, + height : 200 + }); - return html; -}; + this.superpositionGrid.onSelected.attach(function(sender, superpositions) { + var ids = []; + for ( var i in superpositions) { + ids.push(superpositions[i].superpositionId); + } +// _this._renderAbinitio(ids); +// _this.aprioriPDBViewer.refresh(); + _this._renderAligned(ids); + // getAlignedPDBContentBySuperpositionList + }); -ExperimentHeaderForm.prototype.getTopPanel = function(experiment) { - return { - xtype : 'container', - layout : 'hbox', - items : [ { - margin : "0 0 0 0", - width : 475, - border : 0, - html : this.getHTMLSource(experiment) - }, { - margin : "0 0 0 0", - width : 475, - border : 0, - html : this.getHTMLDownload(experiment) - } ] - }; + /** PDB viewer **/ +// this.abinitioPDBViewer = new PDBViewer({ +// width : 860 / 2, +// height : 300 / 2, +// margin : 0 +// }); +// +// /** PDB viewer **/ +// this.aprioriPDBViewer = new StructurePDBViewer({ +// width : 860 / 2, +// height : 300 / 2, +// margin : 0 +// }); + + this.alignedPDBViewer = new AlignedSuperpositionPDBViewer({ + width : 860 , + height : 300 + }); + +} + +SuperpositionForm.prototype._renderAligned = function(modelsIdList) { + /** Trying to plot the PDB file **/ + try { + this.alignedPDBViewer.refresh(modelsIdList); + } catch (e) { + console.log(e); + } }; -ExperimentHeaderForm.prototype.getButton = function(experiment) { - var _this = this; - return Ext.create('Ext.Button', { - text : 'EDIT', - minWidth : '100', - margin : '10 0 0 30', - handler : function() { - var experimentWindow = new ExperimentWindow(); - experimentWindow.onSaved.attach(function(sender, data) { - _this.experiment.json.name = data.name; - _this.experiment.json.type = data.type; - _this.experiment.json.comments = data.comments; - _this.panel.remove(_this.panel.items.items[0]); - _this.panel.remove(_this.panel.items.items[0]); - _this.panel.insert(_this.getTopPanel(_this.experiment)); - _this.panel.insert(_this.getBottomPanel(_this.experiment)); +SuperpositionForm.prototype._renderAbinitio = function(modelsIdList) { + /** Trying to plot the PDB file **/ + try { + var viz = []; + for (var i = 0; i < modelsIdList.length; i++) { + viz.push({ + modelId : modelsIdList[i], + color : "0xFF6600", + opacity : 0.8 }); - experimentWindow.show(experiment); } - }); + this.abinitioPDBViewer.refresh(viz); + } catch (e) { + console.log(e); + } }; -ExperimentHeaderForm.prototype.getBottomPanel = function(experiment) { - return { +SuperpositionForm.prototype._getItems = function() { + var _this = this; + return [ { + xtype : 'label', + forId : 'myFieldId', + text : 'INLINE HELP: To be updated', + margin : '15 0 20 10', + cls : "inline-help" + }, this.superpositionGrid.getPanel(), { xtype : 'container', layout : 'hbox', - margin : '10 0 0 0', - items : [ this.getComments(experiment), this.getButton(experiment) ] - }; + items : [ +// { +// xtype : 'container', +// layout : 'vbox', +// items : [ this.abinitioPDBViewer.getPanel(), this.aprioriPDBViewer.getPanel() +// +// ] +// }, + + this.alignedPDBViewer.getPanel() ] + } ] + }; -ExperimentHeaderForm.prototype.getComments = function(experiment) { - return { - xtype : 'textareafield', - labelStyle : 'font-weight: bold;', - name : 'comments', - fieldLabel : 'Comments', - labelWidth : 70, - height : 40, - minWidth : '450', - readOnly : true, - value : experiment.json.comments - }; +SuperpositionForm.prototype._getButtons = function() { + return []; }; -ExperimentHeaderForm.prototype.getPanel = function(experiment) { - this.experiment = experiment; - - if (experiment.json.type == 'CALIBRATION') { - this.backgroundColor = '#EFFBFB'; - } - if (experiment.json.type == 'TEMPLATE') { - this.backgroundColor = '#E0F8E6'; - } +SuperpositionForm.prototype.getPanel = function() { + var _this = this; + this.panel = Ext.create('Ext.form.Panel', { + width : this.width, + height : this.height, + margin : 10, + border : 1, + defaultType : 'textfield', + items : this._getItems(), + buttons : this._getButtons(), + listeners : { + afterrender : function() { + _this._populate(); - this.panel = Ext.create('Ext.container.Container', { - frame : false, - layout : 'vbox', - padding : 5, - bodyPadding : '5 5 0 0', - width : 890, - margin : '0 0 10 0', - height : 120, - style : { - borderColor : '#99bce8', - borderStyle : 'solid', - borderWidth : '1px', - 'background-color' : this.backgroundColor - }, - fieldDefaults : { - msgTarget : 'side', - labelWidth : 100 - }, - items : [ this.getTopPanel(experiment), this.getBottomPanel(experiment) ] + } + } }); return this.panel; }; -ExperimentHeaderForm.prototype.input = function() { - return { - experiment : DATADOC.getExperiment_10() - }; +/** Populates could be call when the DOM is not filled yet **/ +SuperpositionForm.prototype._populate = function() { }; -ExperimentHeaderForm.prototype.test = function(targetId) { - var experimentHeaderForm = new ExperimentHeaderForm(); - var panel = experimentHeaderForm.getPanel(new Experiment(experimentHeaderForm.input().experiment)); - panel.render(targetId); +/** It populates the form * */ +SuperpositionForm.prototype.refresh = function(subtractions) { + this.subtractions = subtractions; + this.superpositionGrid.refresh(subtractions); +}; + +SuperpositionForm.prototype.input = function() { + return {}; +}; +/** It populates the form **/ +SuperpositionForm.prototype.test = function(targetId) { + var macromoleculeForm = new SuperpositionForm(); + var panel = macromoleculeForm.getPanel(); + panel.render(targetId); }; /** - * Macromolecule form with the general parameters of a macromolecule + * Example form * * @witdh * @height - * - * #onSave button save has been clicked - * #onClose button close has been clicked */ -function MacromoleculeForm(args) { +function AssemblyForm(args) { this.id = BUI.id(); this.width = 700; this.height = 500; @@ -7053,6751 +6059,7745 @@ function MacromoleculeForm(args) { this.height = args.height; } } - - /** Events **/ - this.onSave = new Event(this); - this.onClose = new Event(this); -} -/** Type : is the Ext type then requiredtext or textfield * */ -MacromoleculeForm.prototype._getFieldTextWithHelp = function(type, fieldLabel, fieldName, help) { - return Ext.create('Ext.container.Container', { - margin : "10 0 0 10", - items : [ { - xtype : type, - fieldLabel : fieldLabel, - name : fieldName, - id : this.id + fieldName - }, { - xtype : 'label', - forId : 'myFieldId', - text : help, - margin : "5 0 0 105", - cls : "inline-help" - } ] - }); -}; + this.molarityGrid = new MolarityGrid({height : this.height - 50}); +} -MacromoleculeForm.prototype._getNumericWithHelp = function(type, fieldLabel, fieldName, help) { - return Ext.create('Ext.container.Container', { - margin : "0 0 0 10", - items : [ { - xtype : type, - fieldLabel : fieldLabel, - name : fieldName, - id : this.id + fieldName, - decimalPrecision : 6, - width : 220 - }, { - xtype : 'label', - forId : 'myFieldId', - text : help, - margin : "5 0 0 10", - cls : "inline-help" - } ] - }); +AssemblyForm.prototype._getItems = function() { + return [ { + xtype : 'label', + forId : 'myFieldId', + text : 'List of previously defined macromolecules present in the assembly. This information will be used for additional cross-checks where possible', + margin : '15 0 20 10', + cls : "inline-help" + }, this.molarityGrid.getPanel() ]; }; -MacromoleculeForm.prototype._getButtons = function() { - var _this = this; - return [ { - text : 'Save', - handler : function() { - _this._save(); - } - },{ - text : 'Close', - handler : function() { - _this.onClose.notify(); - - } - } ]; +AssemblyForm.prototype._getButtons = function() { + return []; }; -/** It persits the macromolecule in the database **/ -MacromoleculeForm.prototype._persist = function(macromoleculeId, acronym, name, molecularMass, extintionCoefficient, comments, refractiveIndex, solventViscosity) { - - /** Checking not duplicated acronym **/ - if (((macromoleculeId == null) && (BIOSAXS.proposal.getMacromoleculeByAcronym(acronym) != null))== true){ - BUI.showError("Duplicated acronym"); - return; - } - - - if (macromoleculeId == null){ - /** new macromolecule **/ - this.macromolecule = {}; - this.macromolecule.macromoleculeId = null; - } - else{ - this.macromolecule.macromoleculeId = macromoleculeId; - } - - this.macromolecule["acronym"] = acronym; - this.macromolecule["name"] = name; - this.macromolecule["molecularMass"] = molecularMass; - this.macromolecule["extintionCoefficient"] = extintionCoefficient; - this.macromolecule["comments"] = comments; - this.macromolecule["symmetry"] = Ext.getCmp(this.id + 'comboSym').getValue(); - this.macromolecule["refractiveIndex"] = refractiveIndex; - this.macromolecule["solventViscosity"] = solventViscosity; - - var _this = this; - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, proposal) { - /** Updating the proposal global object **/ - BIOSAXS.proposal.setItems(proposal); - _this.panel.setLoading(false); - - var saved = BIOSAXS.proposal.getMacromoleculeByAcronym(acronym); - /** Refreshing to check that everything is ok **/ - _this.refresh(saved); - _this.onSave.notify(saved); +AssemblyForm.prototype.getPanel = function() { + this.panel = Ext.create('Ext.form.Panel', { + width : this.width, + height : this.height, + margin : 10, + border : 0, + defaultType : 'textfield', + items : this._getItems(), + buttons : this._getButtons() }); - - this.panel.setLoading("Saving Macromolecule") - adapter.saveMacromolecule(this.macromolecule); + return this.panel; }; -/** Save the macromolecule in the DB **/ -MacromoleculeForm.prototype._save = function() { - - var _this = this; - - var acronym = this._getField("acronym"); - var name = this._getField("name"); - var molecularMass = this._getField("molecularMass"); - var extintionCoefficient = this._getField("extintionCoefficient"); - var comments = this._getField("comments"); - - var refractiveIndex = this._getField("refractiveIndex"); - var solventViscosity = this._getField("solventViscosity"); - - /** Checking required fields **/ - if (name == "") { - BUI.showError("Name field is mandatory"); - return; - } - if (acronym == "") { - BUI.showError("Acroynm field is mandatory"); - return; - } - if (this.macromolecule != null){ - /** Checking if it is a new macromolecule **/ - if (this.macromolecule.macromoleculeId == null){ - /** Check if the acronym exists already **/ - this._persist(null, acronym, name, molecularMass, extintionCoefficient, comments, refractiveIndex, solventViscosity); - } - else{ - /** It is an update **/ - this._persist(this.macromolecule.macromoleculeId, acronym, name, molecularMass, extintionCoefficient, comments, refractiveIndex, solventViscosity); - } - } - else{ - /** It is a new macromolecule **/ - this._persist(null, acronym, name, molecularMass, extintionCoefficient, comments, refractiveIndex, solventViscosity); - } +/** It populates the form **/ +AssemblyForm.prototype.refresh = function(macromolecule) { + this.macromolecule = macromolecule; + this.molarityGrid.refresh(macromolecule); }; +AssemblyForm.prototype.input = function() { + return {}; +}; -MacromoleculeForm.prototype._getItems = function() { +AssemblyForm.prototype.test = function(targetId) { + var assemblyForm = new AssemblyForm(); + + var panel = assemblyForm.getPanel(); + panel.render(targetId); +}; +/** + * Edit the information of a buffer + * + * #onSaved + * #onRemoveAdditive + */ +function BufferForm() { var _this = this; - /** Symmetry combo box **/ - var symmetry = Ext.create('Ext.data.Store', { - fields : [ 's' ], - data : this._getSymmetries() - }); - this.symmetryComboBox = Ext.create('Ext.form.ComboBox', { - fieldLabel : 'Symmetry', - store : symmetry, - id : this.id + 'comboSym', - queryMode : 'local', - displayField : 's', - valueField : 's', - value : "P1", - margin : "0 0 0 30", - width : 220 + this.additiveGrid = new AdditiveGrid(); + this.additiveGrid.onRemoveButtonClicked.attach(function(sender, args) { + _this.onRemoveAdditive.notify(args); }); - - return [this._getFieldTextWithHelp("requiredtext", "Name", "name", "Long name. i.e: Bovine serum albumin"), - this._getFieldTextWithHelp("requiredtext", "Acronym", "acronym", "Acronym will be used in the files and analisys. i.e: BSA"), - this._getFieldTextWithHelp("textfield", "Mol. Mass (Da)", "molecularMass", "Atomic mass estimation measured in Da"), - { - xtype : 'container', - layout : 'hbox', - margin : "10 0 0 0", - items :[ - this._getNumericWithHelp("numberfield", "Extinction coef.", "extintionCoefficient", ""), - this.symmetryComboBox - ] - }, - { - xtype : 'container', - layout : 'hbox', - margin : "5 0 0 0", - items :[ - this._getNumericWithHelp("numberfield", "Refractive Index", "refractiveIndex", "How radiation propagates through the medium"), - this._getNumericWithHelp("numberfield", "Solvent Viscosity", "solventViscosity", "") - ] - }, - { - id : this.id + "comments", - xtype : 'textareafield', - name : 'comments', - margin : '35 0 0 10', - fieldLabel : 'Comments', - width : this.width - 100 - } ]; + + this.onSaved = new Event(this); + this.onRemoveAdditive = new Event(this); +} + +BufferForm.prototype.getBuffer = function() { + this.buffer.name = Ext.getCmp("buffer_name").getValue(); + this.buffer.acronym = Ext.getCmp("buffer_acronym").getValue(); + this.buffer.comments = Ext.getCmp("buffer_comments").getValue(); + this.buffer.ph = Ext.getCmp("buffer_ph").getValue(); + this.buffer.composition = Ext.getCmp("buffer_composition").getValue(); + this.buffer.bufferhasadditive3VOs = this.additiveGrid.getAdditives(); + return this.buffer; }; -MacromoleculeForm.prototype._getSymmetries = function() { - return [ { - "s" : "P1" - }, { - "s" : "P2" - }, { - "s" : "P3" - }, { - "s" : "P4" - }, { - "s" : "P5" - }, { - "s" : "P6" - }, { - "s" : "P32" - }, { - "s" : "P42" - }, { - "s" : "P52" - }, { - "s" : "P62" - }, { - "s" : "P222" - } ] +BufferForm.prototype._getTopPanel = function() { + return { + xtype : 'container', + layout : 'hbox', + border : 0, + frame : true, + items : [ { + xtype : 'container', + layout : 'hbox', + items : [ { + xtype : 'container', + flex : 1, + border : false, + layout : 'anchor', + defaultType : 'textfield', + items : [ { + xtype : 'requiredtext', + id : 'buffer_name', + fieldLabel : 'Name', + name : 'name', + width : '200px', + value : this.buffer.name + }, { + xtype : 'requiredtext', + id : 'buffer_acronym', + fieldLabel : 'Acronym', + name : 'acronym', + width : '200px', + value : this.buffer.acronym + } ] + } ] + }, { + xtype : 'container', + flex : 1, + layout : 'anchor', + defaultType : 'textfield', + margin : '0 0 0 10', + items : [ { + id : 'buffer_ph', + fieldLabel : 'pH', + name : 'ph', + value : this.buffer.ph, + xtype : 'numberfield', + width : 200, + minValue : 0, + maxValue : 15 + }, { + xtype : 'requiredtext', + id : 'buffer_composition', + fieldLabel : 'Composition', + name : 'composition', + width : 200, + value : this.buffer.composition + } ] + } ] + }; }; -MacromoleculeForm.prototype.getPanel = function() { - this.panel = Ext.create('Ext.form.Panel', { - width : this.width, - height : this.height, - margin : 10, - border : 1, - defaultType : 'textfield', - items : this._getItems(), - buttons : this._getButtons() +BufferForm.prototype.getPanel = function(buffer) { + this.buffer = buffer; + this.panel = Ext.createWidget({ + xtype : 'container', + layout : 'vbox', + style : { + padding : '10px' + }, + fieldDefaults : { + labelAlign : 'left', + labelWidth : 50 + + }, + items : [ this._getTopPanel(), { + id : 'buffer_comments', + xtype : 'textareafield', + name : 'comments', + fieldLabel : 'Comments', + width : '100%', + value : buffer.comments + }, this.additiveGrid.getPanel(buffer) ] }); return this.panel; }; - -/** Populates each text field by field name and value **/ -MacromoleculeForm.prototype._populateField = function(fieldName, value) { - if (value != null){ - Ext.getCmp(this.id + fieldName).setValue(value); - } +BufferForm.prototype.input = function() { + return { + buffer : { + "bufferId" : 422, + "proposalId" : 10, + "safetyLevelId" : null, + "name" : "B1", + "acronym" : "B1", + "ph" : null, + "composition" : null, + "bufferhasadditive3VOs" : [], + "comments" : null + } + }; }; -/** Gets the value of a textfield **/ -MacromoleculeForm.prototype._getField = function(fieldName) { - return Ext.getCmp(this.id + fieldName).getValue(); +BufferForm.prototype.test = function(targetId) { + var bufferForm = new BufferForm(); + var panel = bufferForm.getPanel(bufferForm.input().buffer); + panel.render(targetId); }; - - -/** It populates the form **/ -MacromoleculeForm.prototype.refresh = function(macromolecule) { - this.macromolecule = macromolecule; - if (macromolecule != null){ - this._populateField("name", macromolecule.name); - this._populateField("acronym", macromolecule.acronym); - this._populateField("extintionCoefficient", macromolecule.extintionCoefficient); - this._populateField("molecularMass", macromolecule.molecularMass); - this._populateField("comments", macromolecule.comments); - this._populateField("refractiveIndex", macromolecule.refractiveIndex); - this._populateField("solventViscosity", macromolecule.solventViscosity); - if (macromolecule.symmetry != null){ - Ext.getCmp(this.id + 'comboSym').setValue(macromolecule.symmetry); + +/** + * @showTitle + * + * #onSaved + * #onAddPlates + * #onRemovePlates + **/ +function CaseForm(args) { + this.width = 700; + this.showTitle = true; + if (args != null) { + if (args.showTitle != null) { + this.showTitle = args.showTitle; } } -}; - -MacromoleculeForm.prototype.input = function() { - return {}; -}; - - -/** It populates the form **/ -MacromoleculeForm.prototype.test = function(targetId) { - var macromoleculeForm = new MacromoleculeForm(); - macromoleculeForm.onClose.attach(function(sender){ - alert("Click on close"); + var _this = this; + this.stockSolutionGrid = new StockSolutionGrid({ + width : this.width - 10, + minHeight : 300, + height : 300, + tbar : true, + showTitle : true, + isPackedVisible : false, + btnAddExisting : true, + btnRemoveVisible : false, + btnUnpackVisible : true }); - var panel = macromoleculeForm.getPanel(); - panel.render(targetId); -}; + /** When selecting existing stock solutions **/ + this.stockSolutionGrid.onStockSolutionSelected.attach(function(sender, stockSolutions) { + if (stockSolutions != null) { + for ( var i = 0; i < stockSolutions.length; i++) { + _this.saveStockSolution(stockSolutions[i]); + } + } + }); - + /** it can be because it has been added a new one or removed **/ + this.stockSolutionGrid.onProposalChanged.attach(function(sender, stockSolution) { + if (stockSolution != null) { + _this.saveStockSolution(stockSolution); + } else { + BIOSAXS.proposal.onInitialized.attach(function() { + _this.stockSolutionGrid.refresh(BIOSAXS.proposal.getStockSolutionsByDewarId(_this.dewar.dewarId)); + _this.stockSolutionGrid.grid.setLoading(false); + }); + BIOSAXS.proposal.init(); -function ModelVisualizerForm(args){ - this.id =BUI.id(); - this.width = 600; - this.height = 400; - if (args!= null){ - if (args.width != null){ - this.width = args.width; - } - if (args.height != null){ - this.height = args.height; } - } -}; -ModelVisualizerForm.prototype._getFirHTML = function(modelId, width, height, type, desc) { - var html = ""; - html = html + ''; - html = html + ''; - html = html + ''; - html = html + '
dammin.' + type + '
' + desc + '
'; - return html; -}; - -ModelVisualizerForm.prototype.getItems = function(modelPanel){ - _this = this; - var height = _this.height/2 - 60; - var width = _this.width/2 - 10; - - return Ext.create('Ext.container.Container', { - layout: { - type: 'vbox', // Arrange child items vertically - }, - items: [ - modelPanel, - { - xtype : 'container', - layout: { - type: 'hbox', // Arrange child items vertically - }, - items : [{ - html : this._getFirHTML("id", width, height, "fir", "Fit of the simulated scattering curve versus a smoothed experimental data (spline interpolation)"), - height :height, - width : width, - padding: 2 - }, - { - html : this._getFirHTML("id", width, height, "fit", "Fit of the simulated scattering curve versus the experimental data."), - height : height, - width : width, - padding: 2 - }] - } - - ] }); -}; -ModelVisualizerForm.prototype.refresh = function(models){ - var input = []; -// var colors = ["008000", "F0A804", "0000FF", "800080", "C0C0C0"]; - for (var i = 0; i < models.length; i++) { - console.log(BUI.rainbow(models.length, i).replace("#", "0x")); - input.push({ - color: BUI.rainbow(models.length, i).replace("#", "0x"), - modelId: models[i].modelId, - opacity: 0.8, - radius: 3, - title: BUI.getFileNameByPath(models[i].pdbFile), - type: "SHAPEDETERMINATIONMODEL" - - }); - } - - this.panel.removeAll(); - this.panel.add( - _this.getItems( - new PDBViewer({ - width : this.width - 10, - height : (this.height/2) - 10, - title : "" - }).draw(input) - ) - ); - + this.onSaved = new Event(this); + this.onAddPlates = new Event(this); + this.onRemovePlates = new Event(this); +} + +CaseForm.prototype.saveStockSolution = function(stockSolution) { + var _this = this; var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, data) { - var splitted = data.toString().split("\n"); - var array = []; - for ( var i = 0; i < splitted.length; i++) { - var line = splitted[i].trim(); - var line_splited = line.split(/\s*[\s,]\s*/); - if (line_splited.length == 4) { - array.push([ Number(line_splited[0]), BUI.getStvArray(line_splited[1], line_splited[2]), BUI.getStvArray(line_splited[3], 0) ]); - } - } - - var id = (_this.id + "firid"); - var dygraphWidget = new StdDevDyGraph(id, { - width : (_this.width/2) - 10, - height :(_this.height/2) -110, - xlabel : 'q(nm-1)' + this.stockSolutionGrid.grid.setLoading("ISPyB: setting case to Stock solution"); + adapter.onSuccess.attach(function(sender, stockSolution) { + BIOSAXS.proposal.onInitialized.attach(function() { + _this.stockSolutionGrid.refresh(BIOSAXS.proposal.getStockSolutionsByDewarId(_this.dewar.dewarId)); + _this.stockSolutionGrid.grid.setLoading(false); }); - dygraphWidget.draw(array, [ "#FF0000", "#0000FF", "#FF00FF" ], [ 's', 'I(exp)', 'I(sim)' ]); + BIOSAXS.proposal.init(); }); - - adapter.getModelFile("FIR", models[0].modelId); - - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, data) { - var splitted = data.toString().split("\n"); - var array = []; - for ( var i = 0; i < splitted.length; i++) { - var line = splitted[i].trim(); - var line_splited = line.split(/\s*[\s,]\s*/); - if (line_splited.length == 3) { - array.push([ Number(line_splited[0]), BUI.getStvArray(line_splited[1], 0), BUI.getStvArray(line_splited[2], 0) ]); - } - } - - var id = (_this.id + "fitid"); - - var dygraphWidget = new StdDevDyGraph(id, { - width : (_this.width/2) - 10, - height : (_this.height/2) - 110, - xlabel : 'q(nm-1)' - }); - dygraphWidget.draw(array, [ "#FF0000", "#0000FF" ], [ "s", "I(exp)", "I(sim)" ]); + adapter.onError.attach(function(sender, data) { + _this.stockSolutionGrid.grid.setLoading(false); + BUI.showError(data); }); - adapter.getModelFile("FIT", models[0].modelId); + stockSolution.boxId = _this.dewar.dewarId; + adapter.saveStockSolution(stockSolution); }; -ModelVisualizerForm.prototype.getPanel = function(modelList){ - _this = this; - this.modelList = modelList; - this.panel = Ext.create('Ext.Panel', { - title: 'Results', - width: this.width, - height: this.height, - layout: { - type: 'vbox', // Arrange child items vertically -// align: 'stretch' // Each takes up full width - }, - items: [ - - ], - listeners : { - afterrender : function(grid, eOpts) { -// alert(_this.modelList) - } - } +CaseForm.prototype.fillStores = function() { + var _this = this; + this.panel.setLoading("Loading Labcontacts from database"); + + var proposal = BUI.getProposal(); + proposal.onDataRetrieved.attach(function(sender, data) { + _this.labContactForSendingStore.loadData(data, false); + _this.labContactForReturnStore.loadData(data, false); + _this.panel.setLoading(false); }); - - return this.panel; + proposal.getLabContactsByProposalId(); }; +CaseForm.prototype.refresh = function(dewar) { + this.setDewar(dewar); + this.stockSolutionGrid.refresh(BIOSAXS.proposal.getStockSolutionsByDewarId(dewar.dewarId)); +}; + +CaseForm.prototype.getDewar = function() { + this.dewar.code = Ext.getCmp("dewar_code").getValue(); + this.dewar.comments = Ext.getCmp("dewar_comments").getValue(); + this.dewar.trackingNumberFromSynchrotron = Ext.getCmp("dewar_trackingNumberFromSynchrotron").getValue(); + this.dewar.trackingNumberToSynchrotron = Ext.getCmp("dewar_trackingNumberToSynchrotron").getValue(); + this.dewar.transportValue = Ext.getCmp("dewar_transportValue").getValue(); + this.dewar.storageLocation = Ext.getCmp("dewar_storageLocation").getValue(); + this.dewar.firstExperimentId = this.macromoleculeCombo.getValue(); + return this.dewar; +}; + +CaseForm.prototype.setDewar = function(dewar) { + this.dewar = dewar; + Ext.getCmp("dewar_code").setValue(this.dewar.code); + Ext.getCmp("dewar_dewarStatus").setText(new String(this.dewar.dewarStatus).toUpperCase()); + Ext.getCmp("dewar_comments").setValue(this.dewar.comments); + Ext.getCmp("dewar_trackingNumberFromSynchrotron").setValue(this.dewar.trackingNumberFromSynchrotron); + Ext.getCmp("dewar_trackingNumberToSynchrotron").setValue(this.dewar.trackingNumberToSynchrotron); + Ext.getCmp("dewar_transportValue").setValue(this.dewar.transportValue); + Ext.getCmp("dewar_storageLocation").setValue(this.dewar.storageLocation); + if (dewar.sessionVO != null) { + this.macromoleculeCombo.setValue(dewar.sessionVO.sessionId); + } +}; + +CaseForm.prototype.getSessionCombo = function() { + this.macromoleculeCombo = BIOSAXS_COMBOMANAGER.getComboSessions(BIOSAXS.proposal.getSessions(), { + labelWidth : 100, + margin : '5 0 00 0', + width : 250 + }); + return this.macromoleculeCombo; +}; + +CaseForm.prototype.getInformationPanel = function() { + if (this.panel == null) { + this.informationPanel = Ext.create('Ext.form.Panel', { + width : this.width - 10, + border : 0, + items : [ { + xtype : 'container', + margin : "2 2 2 2", + collapsible : false, + defaultType : 'textfield', + layout : 'anchor', + items : [ { + xtype : 'container', + layout : 'hbox', + items : [ { + xtype : 'requiredtext', + fieldLabel : 'Code', + allowBlank : false, + name : 'code', + id : 'dewar_code', + anchor : '50%' + }, { + xtype : 'label', + margin : '0 0 0 20', + readOnly : true, + id : 'dewar_dewarStatus', + anchor : '50%' + } ] + }, this.getSessionCombo(), { + margin : '5 0 0 0', + xtype : 'textareafield', + name : 'comments', + id : 'dewar_comments', + width : this.width - 50, + fieldLabel : 'Comments' + } ] + }, { + xtype : 'fieldset', + title : 'Courier Accounts Details for Return', + collapsible : false, + layout : 'anchor', + defaults : { + anchor : '100%' + }, + items : [ { + xtype : 'container', + layout : 'hbox', + items : [ { + xtype : 'textfield', + labelWidth : 200, + width : 280, + fieldLabel : 'Track Number From Synchrotron', + id : 'dewar_trackingNumberFromSynchrotron' + }, { + xtype : 'numberfield', + width : 190, + labelWidth : 110, + margin : '0 0 0 30', + fieldLabel : 'Transport Value', + id : 'dewar_transportValue' + } + + ] + }, { + xtype : 'container', + layout : 'hbox', + margin : '10 0 0 0', + items : [ + + { + xtype : 'textfield', + labelWidth : 200, + width : 280, + fieldLabel : 'Track Number To Synchrotron', + id : 'dewar_trackingNumberToSynchrotron' + }, { + xtype : 'textfield', + margin : '0 0 0 30', + width : 190, + labelWidth : 110, + fieldLabel : 'Storage Location', + id : 'dewar_storageLocation' + } + + ] + } ] + } ] + }); + } + + return this.informationPanel; +}; + +CaseForm.prototype.getPanel = function(dewar) { + this.dewar = dewar; + this.panel = Ext.createWidget({ + xtype : 'container', + layout : 'vbox', + width : this.width, + border : 0, + items : [ { + items : { + xtype : "container", + layout : "vbox", + margin : "5 5 5 5", + items : [ this.getInformationPanel(dewar), this.stockSolutionGrid.getPanel() ] + + } + } ] + }); + + this.refresh(dewar); + return this.panel; + +}; + +CaseForm.prototype.input = function() { + return { + dewar : { + "dewarId" : 305861, + "code" : "ESRF-TEST", + "comments" : "comments", + "storageLocation" : "FRIDGE", + "dewarStatus" : "opened", + "timeStamp" : null, + "isStorageDewar" : null, + "barCode" : "ESRF305861", + "customsValue" : null, + "transportValue" : null, + "trackingNumberToSynchrotron" : "3333", + "trackingNumberFromSynchrotron" : "224466", + "type" : "Dewar", + "sessionVO" : { + "sessionId" : 31697, + "expSessionPk" : null, + "projectCode" : null, + "startDate" : "2012 07 21", + "endDate" : "2012 07 23", + "beamlineName" : "BM29", + "scheduled" : 1, + "nbShifts" : 2, + "comments" : null, + "beamlineOperator" : "PERNOT P", + "usedFlag" : null, + "sessionTitle" : null, + "structureDeterminations" : null, + "dewarTransport" : null, + "databackupFrance" : null, + "databackupEurope" : null, + "visit_number" : null, + "operatorSiteNumber" : "14061", + "timeStamp" : "2012 04 25" + } + }, + proposal : { + "assemblies" : [], + "sessions" : [ { + "sessionId" : 31697, + "expSessionPk" : null, + "projectCode" : null, + "startDate" : "2012 07 21", + "endDate" : "2012 07 23", + "beamlineName" : "BM29", + "scheduled" : 1, + "nbShifts" : 2, + "comments" : null, + "beamlineOperator" : "PERNOT P", + "usedFlag" : null, + "sessionTitle" : null, + "structureDeterminations" : null, + "dewarTransport" : null, + "databackupFrance" : null, + "databackupEurope" : null, + "visit_number" : null, + "operatorSiteNumber" : "14061", + "timeStamp" : "2012 04 25" + } ], + "labcontacts" : [ { + "labContactId" : 787, + "personVO" : { + "personId" : 304252, + "personUUID" : null, + "familyName" : "KIM", + "givenName" : "Henry", + "title" : null, + "emailAddress" : "henry-sung.kim@ibs.fr", + "phoneNumber" : "", + "login" : "", + "passwd" : "", + "faxNumber" : "" + }, + "cardName" : "KIM-Institut de Bio", + "defaultCourrierCompany" : "22", + "courierAccount" : "", + "billingReference" : "", + "dewarAvgCustomsValue" : 0, + "dewarAvgTransportValue" : 0 + } ], + "buffers" : [ { + "bufferId" : 811, + "proposalId" : 3124, + "safetyLevelId" : null, + "name" : "EDTA", + "acronym" : "EDTA", + "ph" : null, + "composition" : "", + "bufferhasadditive3VOs" : [], + "comments" : "" + }, { + "bufferId" : 810, + "proposalId" : 3124, + "safetyLevelId" : null, + "name" : "HEPES", + "acronym" : "HEPES", + "ph" : null, + "composition" : "", + "bufferhasadditive3VOs" : [], + "comments" : "" + } ], + "shippings" : [ { + "shippingId" : 304107, + "shippingName" : "TEST", + "deliveryAgentAgentName" : null, + "deliveryAgentShippingDate" : null, + "deliveryAgentDeliveryDate" : null, + "deliveryAgentAgentCode" : null, + "deliveryAgentFlightCode" : null, + "shippingStatus" : "opened", + "timeStamp" : "2013 09 25", + "laboratoryId" : null, + "isStorageShipping" : null, + "creationDate" : "2013 09 25", + "comments" : "test", + "sendingLabContactVO" : { + "labContactId" : 787, + "personVO" : { + "personId" : 304252, + "personUUID" : null, + "familyName" : "KIM", + "givenName" : "Henry", + "title" : null, + "emailAddress" : "henry-sung.kim@ibs.fr", + "phoneNumber" : "", + "login" : "", + "passwd" : "", + "faxNumber" : "" + }, + "cardName" : "KIM-Institut de Bio", + "defaultCourrierCompany" : "22", + "courierAccount" : "", + "billingReference" : "", + "dewarAvgCustomsValue" : 0, + "dewarAvgTransportValue" : 0 + }, + "returnLabContactVO" : { + "labContactId" : 787, + "personVO" : { + "personId" : 304252, + "personUUID" : null, + "familyName" : "KIM", + "givenName" : "Henry", + "title" : null, + "emailAddress" : "henry-sung.kim@ibs.fr", + "phoneNumber" : "", + "login" : "", + "passwd" : "", + "faxNumber" : "" + }, + "cardName" : "KIM-Institut de Bio", + "defaultCourrierCompany" : "22", + "courierAccount" : "", + "billingReference" : "", + "dewarAvgCustomsValue" : 0, + "dewarAvgTransportValue" : 0 + }, + "returnCourier" : null, + "dateOfShippingToUser" : null, + "shippingType" : "DewarTracking", + "dewarVOs" : [ { + "dewarId" : 305861, + "code" : "ESRF-TEST", + "comments" : "comments", + "storageLocation" : "FRIDGE", + "dewarStatus" : "opened", + "timeStamp" : null, + "isStorageDewar" : null, + "barCode" : "ESRF305861", + "customsValue" : null, + "transportValue" : null, + "trackingNumberToSynchrotron" : "3333", + "trackingNumberFromSynchrotron" : "224466", + "type" : "Dewar", + "sessionVO" : { + "sessionId" : 31697, + "expSessionPk" : null, + "projectCode" : null, + "startDate" : "2012 07 21", + "endDate" : "2012 07 23", + "beamlineName" : "BM29", + "scheduled" : 1, + "nbShifts" : 2, + "comments" : null, + "beamlineOperator" : "PERNOT P", + "usedFlag" : null, + "sessionTitle" : null, + "structureDeterminations" : null, + "dewarTransport" : null, + "databackupFrance" : null, + "databackupEurope" : null, + "visit_number" : null, + "operatorSiteNumber" : "14061", + "timeStamp" : "2012 04 25" + } + } ] + } ], + "macromolecules" : [ { + "macromoleculeId" : 5933, + "safetylevelId" : null, + "proposalId" : 3124, + "name" : "A", + "acronym" : "A", + "molecularMass" : "", + "extintionCoefficient" : "", + "sequence" : null, + "creationDate" : null, + "comments" : "", + "macromoleculeregion3VOs" : [], + "stoichiometry3VOsForHostMacromoleculeId" : [], + "structure3VOs" : [] + } ], + "stockSolutions" : [ { + "stockSolutionId" : 6, + "proposalId" : 3124, + "macromoleculeId" : 5933, + "bufferId" : 811, + "instructionSet3VO" : null, + "boxId" : 305861, + "storageTemperature" : "20", + "volume" : "300", + "concentration" : "1.2", + "comments" : "Buffer EDTA with A", + "name" : "A_EDTA_1.2", + "samples" : [] + } ] + } + + }; +}; + +CaseForm.prototype.test = function(targetId) { + var caseForm = new CaseForm(); + BIOSAXS.proposal = new Proposal(caseForm.input().proposal); + var panel = caseForm.getPanel(caseForm.input().dewar); + panel.render(targetId); +}; -/** - * Example form - * - * @witdh - * @height - */ -function MolarityForm(args) { - this.id = BUI.id(); - this.width = 700; - this.height = 500; - - if (args != null) { - if (args.width != null) { - this.width = args.width; - } - if (args.height != null) { - this.height = args.height; - } - } - - this.onSave = new Event(this); - this.onClose = new Event(this); -} - - -MolarityForm.prototype._getNumericWithHelp = function(type, fieldLabel, fieldName, help) { - return Ext.create('Ext.container.Container', { - margin : "10 0 0 10", - items : [ { - xtype : type, - fieldLabel : fieldLabel, - name : fieldName, - id : this.id + fieldName, - decimalPrecision : 6 - }, { - xtype : 'label', - forId : 'myFieldId', - text : help, - margin : "5 0 0 105", - cls : "inline-help" - } ] - }); -}; - - -MolarityForm.prototype._getItems = function() { - this.macromoleculeCombo = BIOSAXS_COMBOMANAGER.getComboMacromoleculeByMacromolecules(this.getMacromuleculesCandidates(this.macromolecule), { - width : 250, - labelWidth : 100, - margin : 10 - }); - - return [ { - xtype : 'container', - flex : 1, - margin : '10 0 0 10', - border : 0, - layout : 'anchor', - defaultType : 'requiredtext', - items : [ this.macromoleculeCombo, - this._getNumericWithHelp("textfield", "Ratio", "ratio", "Number in assymmetric units") - ] - } ]; -}; - -MolarityForm.prototype._persist = function() { - var _this = this; - var macromoleculeId = this.macromoleculeCombo.getValue(); - var ratio = Ext.getCmp(this.id + "ratio").getValue(); - var comments = "Not used yet"; - var dataAdapter = new BiosaxsDataAdapter(); - this.panel.setLoading("Saving"); - dataAdapter.onSuccess.attach(function(sender, args) { - _this.onSave.notify(); - }); - dataAdapter.saveStoichiometry(this.macromolecule.macromoleculeId, macromoleculeId, ratio, comments); -}; - -MolarityForm.prototype._getButtons = function() { - var _this = this; - - function onClose() { - _this.onClose.notify(); - } - - return [ { - text : 'Save', - handler : function() { - _this._persist(); - } - }, { - text : 'Cancel', - handler : function() { - onClose(); - } - } ]; -}; - -MolarityForm.prototype.getPanel = function() { - this.panel = Ext.create('Ext.form.Panel', { -// width : null, - height : this.height, - margin : 2, - border : 1, - defaultType : 'requiredtext', - items : this._getItems(), - buttons : this._getButtons() - }); - return this.panel; -}; - -/** macromolecules contains all macromolecules except this one **/ -MolarityForm.prototype.getMacromuleculesCandidates = function(macromolecule) { - var macromolecules = []; - if ( BIOSAXS.proposal.macromolecules){ - for (var i = 0; i < BIOSAXS.proposal.macromolecules.length; i++) { - var m = BIOSAXS.proposal.macromolecules[i]; - if (this.macromolecule != null){ - if (m.macromoleculeId != this.macromolecule.macromoleculeId) { - macromolecules.push(m); - } - } - } - } - return macromolecules; -}; - - -/** It populates the form **/ -MolarityForm.prototype.refresh = function(macromolecule) { - this.macromolecule = macromolecule; - -}; - - -MolarityForm.prototype.input = function() { - return {}; -}; - - -/** It populates the form **/ -MolarityForm.prototype.test = function(targetId) { - var macromoleculeForm = new MolarityForm(); - var panel = macromoleculeForm.getPanel(); - panel.render(targetId); -}; - -///** -// * -// * @witdh -// * @height -// */ -//function MacromoleculeForm(args) { -// this.id = BUI.id(); -// this.width = 700; -// this.height = 500; -// -// if (args != null) { -// if (args.width != null) { -// this.width = args.width; -// } -// if (args.height != null) { -// this.height = args.height; -// } -// } -// -// this.onClose = new Event(this); -//} -// -//MacromoleculeForm.prototype.getMacromolecule = function() { -// this.macromolecule.name = Ext.getCmp(this.panel.getItemId()).getValues().name; -// this.macromolecule.acronym = Ext.getCmp(this.panel.getItemId()).getValues().acronym; -// this.macromolecule.comments = Ext.getCmp(this.panel.getItemId()).getValues().comments; -// this.macromolecule.extintionCoefficient = Ext.getCmp(this.panel.getItemId()).getValues().extintionCoefficient; -// this.macromolecule.molecularMass = Ext.getCmp(this.panel.getItemId()).getValues().molecularMass; -// return this.macromolecule; -//}; -// -//MacromoleculeForm.prototype.setMacromolecule = function(macromolecule) { -// this.pdbStore.loadData(macromolecule.structure3VOs); -// -//}; -// -//MacromoleculeForm.prototype.getForm = function(macromolecule) { -// this.panel = Ext.createWidget('form', { -// frame : false, -// border : 0, -// padding : 15, -// width : 550, -// height : 350, -// items : [ { -// xtype : 'container', -// layout : 'hbox', -// items : [ { -// xtype : 'container', -// flex : 1, -// border : false, -// layout : 'anchor', -// defaultType : 'requiredtext', -// items : [ { -// fieldLabel : 'Name', -// name : 'name', -// anchor : '95%', -// tooltip : "Name of the macromolecule", -// value : macromolecule.name -// }, { -// fieldLabel : 'Acronym', -// name : 'acronym', -// anchor : '95%', -// value : macromolecule.acronym -// } ] -// }, { -// xtype : 'container', -// flex : 1, -// layout : 'anchor', -// defaultType : 'textfield', -// items : [ { -// xtype : 'numberfield', -// fieldLabel : 'Mol. Mass (Da)', -// name : 'molecularMass', -// value : macromolecule.molecularMass, -// decimalPrecision : 6 -// }, { -// xtype : 'numberfield', -// fieldLabel : 'Extinction coef.', -// name : 'extintionCoefficient', -// value : macromolecule.extintionCoefficient, -// decimalPrecision : 6 -// } ] -// } ] -// }, { -// xtype : 'textareafield', -// name : 'comments', -// fieldLabel : 'Comments', -// value : macromolecule.comments, -// width : this.width - 10 -// }, -// -// { -// html : BUI.getWarningHTML("The macromolecule is unsaved. Save the macromolecule to get access to other tabs"), -// margin : "150 10 10 10", -// id : this.id + "unsavedWarning", -// hidden : !(!macromolecule.macromoleculeId) -// } -// -// ] -// }); -// return this.panel; -//}; -// -//MacromoleculeForm.prototype.save = function() { -// var _this = this; -// -// var adapter = new BiosaxsDataAdapter(); -// adapter.onSuccess.attach(function(sender, proposal) { -// BIOSAXS.proposal.setItems(proposal); -// _this.panel.setLoading(false); -// -// Ext.getCmp(_this.id + "assembly").enable() -// Ext.getCmp(_this.id + "advanced").enable(); -// Ext.getCmp(_this.id + "unsavedWarning").hide(); -// }); -// -// if (this.getMacromolecule().name == "") { -// BUI.showError("Name field is mandatory"); -// return; -// } -// if (this.getMacromolecule().acronym == "") { -// BUI.showError("Acroynm field is mandatory"); -// return; -// } -// -// /** Check if acronym is unique * */ -// if (this.getMacromolecule().macromoleculeId == null) { -// if (BIOSAXS.proposal.getMacromoleculeByAcronym(this.getMacromolecule().acronym) == null) { -// this.panel.setLoading("ISPyB: Saving Macromolecule") -// adapter.saveMacromolecule(this.getMacromolecule()); -// } else { -// alert("There is already an existing macromolecule with the same acronym"); -// -// } -// } else { -// this.panel.setLoading("ISPyB: Saving Macromolecule") -// adapter.saveMacromolecule(this.getMacromolecule()); -// } -// -//}; -// -//MacromoleculeForm.prototype.getPanel = function(macromolecule) { -// var _this = this; -// this.macromolecule = macromolecule; -// return Ext.createWidget('tabpanel', { -// height : this.height, -// margin : 5, -// plain : true, -// style : { -// padding : 5 -// }, -// items : [ { -// tabConfig : { -// title : "General", -// disabled : false -// }, -// items : [ this.getForm(macromolecule) ], -// bbar : [ "->", { -// text : 'Save', -// cls : 'btn-with-border', -// style : { -// -// border : 1 -// }, -// handler : function() { -// _this.save(); -// } -// }, { -// text : 'Close', -// cls : 'btn-with-border', -// handler : function() { -// _this.onClose.notify(); -// } -// } ] -// }, { -// tabConfig : { -// id : this.id + "assembly", -// title : "Assembly", -// tooltip : 'Description of subunits present in the macromolecule', -// // hidden : (!macromolecule.macromoleculeId), -// disabled : (!macromolecule.macromoleculeId) -// }, -// items : [ this.getMolarityGrid(macromolecule) ] -// }, { -// tabConfig : { -// id : this.id + "advanced", -// title : "Advanced Modeling", -// // hidden : (!macromolecule.macromoleculeId), -// tooltip : 'Definition of the description contacts and symetries', -// disabled : (!macromolecule.macromoleculeId) -// }, -// items : [ this.getRigidBodyForm(macromolecule) ] -// } ] -// }); -//}; -// -//MacromoleculeForm.prototype.getRigidBodyForm = function(macromolecule) { -// var _this = this; -// -// // [P1, P2, P3, P4 ,P5 ,P6 ,32, P42, P52, P62] + P222 -// var symmetry = Ext.create('Ext.data.Store', { -// fields : [ 's' ], -// data : [ { -// "s" : "P1" -// }, { -// "s" : "P2" -// }, { -// "s" : "P3" -// }, { -// "s" : "P4" -// }, { -// "s" : "P5" -// }, { -// "s" : "P6" -// }, { -// "s" : "P32" -// }, { -// "s" : "P42" -// }, { -// "s" : "P52" -// }, { -// "s" : "P62" -// }, { -// "s" : "P222" -// } ] -// }); -// -// if (macromolecule.symmetry == null) { -// macromolecule.symmetry = "P1"; -// } -// var comboBox = Ext.create('Ext.form.ComboBox', { -// fieldLabel : 'Symmetry', -// store : symmetry, -// id : 'comboSym', -// queryMode : 'local', -// displayField : 's', -// valueField : 's', -// value : macromolecule.symmetry, -// margin : "10 0 0 0", -// listeners : { -// change : function(combo, newValue, oldValue, eOpts) { -// macromolecule.symmetry = newValue; -// } -// } -// }); -// -// this.rigidBodyPanel = Ext.createWidget('form', { -// frame : false, -// border : 0, -// padding : 10, -// width : 550, -// height : 400, -// items : [ { -// xtype : 'container', -// layout : 'hbox', -// items : [ { -// xtype : 'container', -// border : false, -// layout : 'hbox', -// items : [ { -// xtype : 'label', -// forId : 'myFieldId', -// text : 'Contact Desc:', -// width : 105, -// margin : '0 0 0 0' -// }, { -// xtype : 'textfield', -// hideLabel : true, -// id : "contactsDescriptionFilePath", -// margin : '0 0 0 0', -// width : 300, -// value : macromolecule.contactsDescriptionFilePath -// }, { -// text : 'Upload', -// xtype : 'button', -// margin : "0 0 0 20", -// width : 100, -// handler : function() { -// _this._openUploadDialog(macromolecule.macromoleculeId, "CONTACTS", "Upload Contact Description File"); -// } -// } ] -// } ] -// }, -// -// comboBox, { -// xtype : 'checkbox', -// margin : '10 0 0 5', -// boxLabel : "I want rigid body modeling run on this stuff", -// checked : true, -// width : 300 -// }, _this.getPDBGrid(macromolecule) ] -// }); -// return this.rigidBodyPanel; -//}; -// -//MacromoleculeForm.prototype.update = function() { -// var _this = this; -// BIOSAXS.proposal.onInitialized.attach(function() { -// if (BIOSAXS.proposal != null) { -// var macromolecules = BIOSAXS.proposal.macromolecules; -// for (var i = 0; i < macromolecules.length; i++) { -// if (macromolecules[i].macromoleculeId == _this.macromolecule.macromoleculeId) { -// _this.macromolecule = macromolecules[i]; -// _this.setMacromolecule(_this.macromolecule); -// _this.molarityStore.loadData(_this.parseMolarityData(_this.macromolecule)); -// _this.pdbGrid.setLoading(false); -// Ext.getCmp("contactsDescriptionFilePath").setValue(_this.macromolecule.contactsDescriptionFilePath) -// _this.molarityGrid.setLoading(false); -// -// } -// } -// } -// }); -// this.molarityGrid.setLoading("Updating"); -// this.pdbGrid.setLoading("Updating"); -// BIOSAXS.proposal.init(); -//}; -// -///******************************************************************************* -// * MOLARITY GRID -// ******************************************************************************/ -// -//MacromoleculeForm.prototype.parseMolarityData = function(macromolecule) { -// var data = []; -// if (macromolecule.stoichiometry != null) { -// for (var i = 0; i < macromolecule.stoichiometry.length; i++) { -// data.push({ -// ratio : macromolecule.stoichiometry[i].ratio, -// acronym : macromolecule.stoichiometry[i].macromolecule3VO.acronym, -// comments : macromolecule.stoichiometry[i].macromolecule3VO.comments, -// stoichiometryId : macromolecule.stoichiometry[i].stoichiometryId, -// name : macromolecule.stoichiometry[i].macromolecule3VO.name -// }); -// } -// } -// return data; -//}; -// -//MacromoleculeForm.prototype.getMolarityGrid = function(macromolecule) { -// var _this = this; -// -// this.molarityStore = Ext.create('Ext.data.Store', { -// fields : [ 'acronym', 'ratio', 'comments', 'stoichiometryId', 'name' ], -// data : this.parseMolarityData(macromolecule), -// sorters : { -// property : 'ratio', -// direction : 'DESC' -// } -// }); -// -// this.molarityGrid = Ext.create('Ext.grid.Panel', { -// store : this.molarityStore, -// height : 350, -// padding : 5, -// columns : [ -// -// { -// text : 'Subunit', -// columns : [ { -// text : "Acronym", -// width : 100, -// hidden : false, -// dataIndex : 'acronym', -// sortable : true -// }, { -// text : "Name", -// width : 100, -// hidden : false, -// dataIndex : 'name', -// sortable : true -// }, { -// text : "Comments", -// width : 100, -// dataIndex : 'comments', -// sortable : true -// } ] -// }, { -// text : "Number
in assymmetric units", -// width : 150, -// dataIndex : 'ratio', -// tooltip : 'Number of times this subunit is present in the macromolecule', -// sortable : true -// }, { -// id : this.id + 'MOLARITY_REMOVE', -// flex : 0.1, -// sortable : false, -// renderer : function(value, metaData, record, rowIndex, colIndex, store) { -// return BUI.getRedButton('REMOVE'); -// } -// } ], -// listeners : { -// cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { -// if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'MOLARITY_REMOVE') { -// var dataAdapter = new BiosaxsDataAdapter(); -// dataAdapter.onSuccess.attach(function() { -// _this.molarityGrid.setLoading(false); -// _this.update(); -// }); -// dataAdapter.removeStoichiometry(record.data.stoichiometryId); -// _this.molarityGrid.setLoading("Removing Structure"); -// } -// } -// }, -// buttons : [ { -// text : 'Add molarity', -// handler : function() { -// -// function onClose() { -// w.destroy(); -// _this.update(); -// } -// var w = Ext.create('Ext.window.Window', { -// title : 'Molarity', -// height : 300, -// width : 500, -// modal : true, -// buttons : [ { -// text : 'Save', -// handler : function() { -// var macromoleculeId = (_this.macromoleculeCombo.getValue()); -// var ratio = Ext.getCmp(_this.id + "ratio").getValue(); -// var comments = ""; -// var dataAdapter = new BiosaxsDataAdapter(); -// dataAdapter.onSuccess.attach(function(sender, args) { -// _this.update(); -// w.destroy(); -// }); -// dataAdapter.saveStoichiometry(_this.macromolecule.macromoleculeId, macromoleculeId, ratio, comments); -// } -// }, { -// text : 'Cancel', -// handler : function() { -// onClose(); -// } -// } ], -// items : [ _this.getMolarityForm(macromolecule) ], -// listeners : { -// onEsc : function() { -// onClose(); -// }, -// close : function() { -// onClose(); -// } -// } -// }).show(); -// } -// } ] -// }); -// return this.molarityGrid; -//}; -// -///******************************************************************************* -// * PDB GRID -// ******************************************************************************/ -// -//MacromoleculeForm.prototype._openUploadDialog = function(macromoleculeId, type, title) { -// var _this = this; -// function onClose() { -// w.destroy(); -// _this.update(); -// } -// -// var w = Ext.create('Ext.window.Window', { -// title : title, -// height : 200, -// width : 400, -// modal : true, -// buttons : [ { -// text : 'Close', -// handler : function() { -// onClose(); -// } -// } ], -// layout : 'fit', -// items : { -// html : "" -// }, -// listeners : { -// onEsc : function() { -// onClose(); -// }, -// close : function() { -// onClose(); -// } -// } -// }).show(); -//}; -// -//MacromoleculeForm.prototype._getPlugins = function() { -// var _this = this; -// var plugins = []; -// // if (this.updateRowEnabled) { -// plugins.push(Ext.create('Ext.grid.plugin.RowEditing', { -// clicksToEdit : 1, -// listeners : { -// validateedit : function(grid, e) { -// /** Comments are always updatable* */ -// e.record.raw.symmetry = e.newValues.symmetry; -// e.record.raw.multiplicity = e.newValues.multiplicity; -// -// var adapter = new BiosaxsDataAdapter(); -// adapter.onSuccess.attach(function(sender, measurement) { -// // _this.grid.setLoading(false); -// }); -// adapter.onError.attach(function() { -// alert("Error"); -// // _this.grid.setLoading(false); -// }); -// -// // _this.grid.setLoading(); -// adapter.saveStructure(e.record.raw); -// } -// } -// })); -// // } -// return plugins; -//}; -// -//MacromoleculeForm.prototype.getPDBGrid = function(macromolecule) { -// var _this = this; -// -// var data = macromolecule.structure3VOs; -// -// // /** Getting PDB from subunits **/ -// // if (macromolecule != null){ -// // if (macromolecule.stoichiometry != null){ -// // for (var i =0; i < macromolecule.stoichiometry.length; i++){ -// // var stoichiometry = macromolecule.stoichiometry[i]; -// // if (stoichiometry.macromolecule3VO != null){ -// // if (stoichiometry.macromolecule3VO.structure3VOs != null){ -// // for (var j = 0; j < stoichiometry.macromolecule3VO.structure3VOs.length; -// // j++) { -// // var structure = stoichiometry.macromolecule3VO.structure3VOs[j]; -// // structure["isSubunit"] = stoichiometry.macromolecule3VO.acronym; -// // data.push(structure) -// // } -// // } -// // } -// // } -// // } -// // } -// -// this.pdbStore = Ext.create('Ext.data.Store', { -// fields : [ 'filePath', 'structureId', 'structureType', 'symmetry', 'structureId', 'name', 'multiplicity' ], -// data : macromolecule.structure3VOs, -// groupField : 'structureType', -// sorters : { -// property : 'structureId', -// direction : 'DESC' -// } -// }); -// -// var groupingFeature = Ext.create('Ext.grid.feature.Grouping', { -// groupHeaderTpl : Ext.create('Ext.XTemplate', -// "
{name:this.formatName}
", { -// formatName : function(name) { -// return name; -// } -// }), -// hideGroupedHeader : true, -// startCollapsed : false -// }); -// -// this.pdbGrid = Ext.create('Ext.grid.Panel', { -// margin : "15 0 0 5", -// height : 250, -// store : this.pdbStore, -// plugins : _this._getPlugins(), -// buttons : [ { -// // text : 'Add PDB file', -// text : 'Add Modeling Option', -// handler : function() { -// _this._openUploadDialog(macromolecule.macromoleculeId, "PDB", 'Upload PDB File'); -// } -// } -// -// ], -// columns : [ -// { -// text : "structureId", -// flex : 0.2, -// hidden : true, -// dataIndex : 'structureId', -// sortable : true -// }, -// { -// text : "File", -// flex : 0.5, -// dataIndex : 'filePath', -// sortable : true, -// hidden : true -// }, -// { -// text : "PDB", -// flex : 0.4, -// dataIndex : 'name', -// sortable : true -// }, -// { -// text : "Symmetry", -// flex : 0.4, -// dataIndex : 'symmetry', -// sortable : true, -// editor : { -// xtype : 'combobox', -// typeAhead : true, -// triggerAction : 'all', -// selectOnTab : true, -// store : [ [ "P1", "P1" ], [ "P2", "P2" ], [ "P3", "P3" ], [ "P4", "P4" ], [ "P5", "P5" ], [ "P6", "P6" ], [ "P32", "P32" ], [ "P42", "P42" ], -// [ "P52", "P52" ], [ "P62", "P62" ], [ "P222", "P222" ] ], -// } -// }, { -// text : "Multiplicity", -// flex : 0.4, -// dataIndex : 'multiplicity', -// sortable : true, -// editor : { -// xtype : 'textfield' -// } -// -// }, { -// text : "Subunit", -// flex : 0.2, -// dataIndex : 'isSubunit', -// sortable : true, -// hidden : true -// }, { -// text : "Type", -// flex : 0.2, -// dataIndex : 'structureType', -// sortable : true, -// hidden : true -// }, -// -// { -// id : this.id + 'REMOVE', -// flex : 0.2, -// hidden : true, -// sortable : false, -// renderer : function(value, metaData, record, rowIndex, colIndex, store) { -// return BUI.getRedButton('REMOVE'); -// } -// }, ], -// -// listeners : { -// itemdblclick : function(dataview, record, item, e) { -// _this._editExperiment(record.raw.experimentId); -// }, -// cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { -// -// if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'REMOVE') { -// var dataAdapter = new BiosaxsDataAdapter(); -// dataAdapter.onSuccess.attach(function() { -// _this.pdbGrid.setLoading(false); -// _this.update(); -// }); -// dataAdapter.removeStructure(record.data.structureId); -// _this.pdbGrid.setLoading("Removing PDB file"); -// } -// -// } -// }, -// viewConfig : { -// getRowClass : function(record, rowIdx, params, store) { -// if (record.raw.isSubunit != null) { -// return "blue-row"; -// } -// } -// } -// }); -// -// return this.pdbGrid; -//}; -// -//MacromoleculeForm.prototype.getMolarityForm = function(macromolecule) { -// var _this = this; -// var data = []; -// for (var i = 0; i < BIOSAXS.proposal.macromolecules.length; i++) { -// var m = BIOSAXS.proposal.macromolecules[i]; -// if (m.macromoleculeId != macromolecule.macromoleculeId) { -// data.push(m); -// } -// -// } -// this.macromoleculeCombo = BIOSAXS_COMBOMANAGER.getComboMacromoleculeByMacromolecules(data, { -// width : 250, -// labelWidth : 100, -// margin : 10 -// }); -// -// return Ext.createWidget('form', { -// -// frame : false, -// border : 0, -// // padding : 15, -// width : 550, -// height : 350, -// items : [ { -// xtype : 'container', -// flex : 1, -// border : false, -// layout : 'anchor', -// defaultType : 'requiredtext', -// items : [ this.macromoleculeCombo, { -// xtype : 'numberfield', -// name : 'Ratio', -// id : _this.id + "ratio", -// fieldLabel : 'Number in assymmetric units', -// value : 1, -// decimalPrecision : 6, -// margin : 10 -// }, { -// xtype : 'textareafield', -// name : 'comments', -// fieldLabel : 'Comments', -// margin : 10, -// width : 400, -// value : "" -// -// } ] -// } ] -// }); -// -//}; -// -///******************************************************************************* -// * JAVASCRIPT DOC -// ******************************************************************************/ -//MacromoleculeForm.prototype.input = function() { -// return { -// macromolecule : DATADOC.getMacromolecule_10() -// }; -//}; -// -//MacromoleculeForm.prototype.test = function(targetId) { -// var macromoleculeForm = new MacromoleculeForm(); -// var panel = macromoleculeForm.getPanel(macromoleculeForm.input().macromolecule); -// panel.render(targetId); -//}; - -function ResultSummaryForm() { - this.radiationDamageThreshold = BUI.getRadiationDamageThreshold(); - this.qualityThreshold = BUI.getQualityThreshold(); - - /** Data clusters with bufers **/ - this.clusterByBuffers = false; - - /** Visualization are groupeb by concenttrations. Ex: 4.5mg/ml and 4.7mg/ml will be clusterized together **/ - this.collapseConcentrations = true; - - this.summaryHeight = 350; - this.id = BUI.id(); - - var _this = this; - - this.qualityControlResultsWidget = new QualityControlResultsWidget({ - qualityThreshold : this.radiationDamageThreshold, - radiationDamageThreshold : this.qualityThreshold - - }); - this.qualityControlResultsWidget.onQualityChanged.attach(function(sender, value) { - _this.qualityThreshold = value; - _this.thresholdsChanged(); - }); - - this.qualityControlResultsWidget.onRadiationDatamageChanged.attach(function(sender, value) { - _this.radiationDamageThreshold = value; - _this.thresholdsChanged(); - - }); - this.concentrationHTMLTableWidget = new ConcentrationHTMLTableWidget({ - height : 250, - showBufferColumns : this.clusterByBuffers - }); - - this.plot = new BoxWhiskerGraph({ - targetId : _this.id + '_boxPlot', - height : 350, - width : 450, - maxBoxWidth : 25 - }); - - this.rangePlot = new RangeWhiskerGraph({ - targetId : _this.id + '_rangePlot', - height : 350, - width : 450, - maxBoxWidth : 25 - }); -} - -ResultSummaryForm.prototype.thresholdsChanged = function() { - var parsedData = this.prepareData(this.rawData); - - var filtered = JSON.parse(JSON.stringify(parsedData)); - filtered.concentration.concentrations = []; - for ( var conc in parsedData.concentration.concentrations) { - if (parsedData.concentration.concentrations[conc].calculation.rgGnom.validNumber > 0) { - filtered.concentration.concentrations.push(parsedData.concentration.concentrations[conc]); - } - } - - this.plotWhisker(filtered); - this.plotRange(filtered); - - this.concentrationHTMLTableWidget.refresh(parsedData); -}; - -/** Given a frame object (object returned by analyzeFrames()) and depending of the threshold indicates if a datacollection has radiation damage **/ -ResultSummaryForm.prototype.hasRadiationDamage = function(frameObject) { - var bb = frameObject.bufferBeforeFramesMerged / frameObject.framesCount; - var ba = frameObject.bufferAfterFramesMerged / frameObject.framesCount; - var mol = frameObject.framesMerge / frameObject.framesCount; - return !((bb >= this.radiationDamageThreshold) && (ba >= this.radiationDamageThreshold) && (mol >= this.radiationDamageThreshold)); -}; - -/** Return (frameObject) an object with the information about the frames for a data collection**/ -ResultSummaryForm.prototype.analyzeFrames = function(dataCollectionRow) { - return { - bufferAfterFramesMerged : dataCollectionRow.bufferAfterFramesMerged, - bufferBeforeFramesMerged : dataCollectionRow.bufferBeforeFramesMerged, - framesCount : dataCollectionRow.framesCount, - framesMerge : dataCollectionRow.framesMerge - }; -}; - -ResultSummaryForm.prototype.detectDataCollectionWarnings = function(dataCollectionRow) { - var frameObject = this.analyzeFrames(dataCollectionRow); - var warnings = { - count : 0, - type : [] - }; - - if (this.hasRadiationDamage(frameObject)) { - warnings.count = warnings.count + 1; - warnings.type.push("RADIATION DAMAGE"); - } - - if (Number(dataCollectionRow.quality) < this.qualityThreshold) { - warnings.count = 1;//warnings.count + 1; - warnings.type.push("Quality <" + this.qualityThreshold); - } - return warnings; -}; - -/** Return array composed by {concentration} objects **/ -ResultSummaryForm.prototype.getConcentrations = function(data) { - var concentrationsId = {}; - - for ( var i = 0; i < data.length; i++) { - var warning = this.detectDataCollectionWarnings(data[i]); - var id = data[i].conc;// + "_" + data[i].bufferId; - if (this.clusterByBuffers) { - id = data[i].conc + "_" + data[i].bufferId; - } - - if (concentrationsId[id] == null) { - concentrationsId[id] = { - id : id, - concentration : Number(data[i].conc).toFixed(2), - bufferId : data[i].bufferId, - bufferAcronym : data[i].bufferAcronym, - rgGuinier : [], - i0Guinier : [], - rgGnom : [], - dMax : [], - quality : [], - frames : [], - frames_warning : warning.count - }; - } else { - concentrationsId[id].frames_warning = concentrationsId[id].frames_warning + warning.count; - } - - concentrationsId[id].frames.push(data[i]); - - if (warning.count == 0) { - concentrationsId[id].rgGuinier.push(data[i].rgGuinier); - concentrationsId[id].i0Guinier.push(data[i].I0); - concentrationsId[id].quality.push(data[i].quality); - concentrationsId[id].rgGnom.push(data[i].rgGnom); - concentrationsId[id].dMax.push(data[i].dmax); - } - - } - var concentrations = []; - for ( var item in concentrationsId) { - if (concentrationsId.hasOwnProperty(item)) { - concentrations.push({ - concentration : concentrationsId[item].concentration, - id : item, - bufferId : Number(concentrationsId[item].bufferId).toFixed(2), - bufferAcronym : concentrationsId[item].bufferAcronym, - rgGuinier : concentrationsId[item].rgGuinier, - /** Frames **/ - frames : concentrationsId[item].frames, - frames_warning : concentrationsId[item].frames_warning, - /** Calculation **/ - calculation : { - rgGuinier : BUI.getStandardDeviation(concentrationsId[item].rgGuinier), - i0Guinier : BUI.getStandardDeviation(concentrationsId[item].i0Guinier), - quality : BUI.getStandardDeviation(concentrationsId[item].quality), - rgGnom : BUI.getStandardDeviation(concentrationsId[item].rgGnom), - dMax : BUI.getStandardDeviation(concentrationsId[item].dMax) - - } - }); - } - } - - return { - concentrations : concentrations - }; -}; - -ResultSummaryForm.prototype.prepareData = function(data) { - /** Return array composed by {acronym, bufferId} objects **/ - function getBuffers(data) { - var buffersId = {}; - for ( var i = 0; i < data.length; i++) { - buffersId[data[i].bufferId] = data[i].acronym; - } - var buffers = []; - for ( var id in buffersId) { - if (buffersId.hasOwnProperty(id)) { - buffers.push({ - acronym : buffersId[id], - bufferId : id - }); - } - } - return buffers; - } - - /** Get a string with all the concentrations **/ - function getConcentrationString(parseConcentrations) { - var concentrations = []; - for ( var i = 0; i < parseConcentrations.concentrations.length; i++) { - concentrations.push(parseConcentrations.concentrations[i].concentration + " mg/ml "); - } - return concentrations.toString(); - } - - var parseConcentrations = this.getConcentrations(data); - - return { - dataCollectionCount : data.length, - buffers : getBuffers(data), - concentration : parseConcentrations, - concentrationLabel : getConcentrationString(parseConcentrations) - }; -}; - -ResultSummaryForm.prototype.getDataForWhisker = function(data) { - var clusters = []; - - var concentrations = {}; - var i = 0; - var conc = 0; - if (this.collapseConcentrations) { - for (i = 0; i < data.concentration.concentrations.length; i++) { - conc = data.concentration.concentrations[i]; - var concentration = Number(conc.concentration).toFixed(0); - if (concentrations[concentration] == null) { - concentrations[concentration] = {}; - concentrations[concentration].concentration = concentration; - concentrations[concentration].calculation = {}; - concentrations[concentration].calculation.rgGuinier = {}; - concentrations[concentration].calculation.rgGuinier.values = []; - concentrations[concentration].calculation.rgGuinier.values = conc.calculation.rgGuinier.values; - concentrations[concentration].calculation.rgGnom = {}; - concentrations[concentration].calculation.rgGnom.values = []; - concentrations[concentration].calculation.rgGnom.values = conc.calculation.rgGnom.values; - } else { - concentrations[concentration].calculation.rgGuinier.values = concentrations[concentration].calculation.rgGuinier.values - .concat(conc.calculation.rgGuinier.values); - concentrations[concentration].calculation.rgGnom.values = concentrations[concentration].calculation.rgGnom.values - .concat(conc.calculation.rgGnom.values); - } - } - - /** From object to array **/ - var array = []; - for ( var key in concentrations) { - if (concentrations.hasOwnProperty(key)) { - array.push(concentrations[key]); - } - } - data.concentration.concentrations = array; - } - - for (i = 0; i < data.concentration.concentrations.length; i++) { - conc = data.concentration.concentrations[i]; - clusters.push({ - name : conc.concentration + "mg/ml", - concentration : Number(conc.concentration), - x : Number(conc.concentration), - classes : [] - }); - clusters[clusters.length - 1].classes.push({ - name : "Guinier", - color : '#9A2EFE', - values : conc.calculation.rgGuinier.values - - }); - clusters[clusters.length - 1].classes.push({ - name : "P(r)", - color : '#2E64FE', - values : conc.calculation.rgGnom.values - - }); - } - return { - clusters : clusters.sort(function(a, b) { - return a.concentration - b.concentration; - }) - }; -}; - -ResultSummaryForm.prototype.getDataForWhiskerQuality = function(data) { - var clusters = []; - - for ( var i = 0; i < data.concentration.concentrations.length; i++) { - var conc = data.concentration.concentrations[i]; - clusters.push({ - name : conc.concentration + "mg/ml", - classes : [] - }); - clusters[clusters.length - 1].classes.push({ - name : "Quality", - values : conc.calculation.quality.values - - }); - } - - return { - clusters : clusters - }; -}; - -ResultSummaryForm.prototype.plotQualityWhisker = function(parsedData) { - this.qualityPlot.refresh(this.getDataForWhiskerQuality(parsedData)); -}; - -ResultSummaryForm.prototype.plotWhisker = function(parsedData) { - this.plot.refresh(this.getDataForWhisker(parsedData)); -}; - -ResultSummaryForm.prototype.plotRange = function(parsedData) { - this.rangePlot.refresh(this.getDataForWhisker(parsedData)); -}; - -ResultSummaryForm.prototype.getPanel = function(data) { - var _this = this; - _this.rawData = data; - var parsedData = this.prepareData(data); - - this.panel = Ext - .createWidget( - 'form', - { - bodyPadding : 20, - frame : false, - border : 0, - width : this.width, - height : this.summaryHeight + 1000, - items : [ { - xtype : 'container', - layout : 'hbox', - items : [ { - xtype : 'container', - flex : 1, - border : false, - layout : 'anchor', - defaultType : 'textfield', - items : [ - _this.qualityControlResultsWidget.getPanel(), - { - xtype : 'fieldset', - width : 950, - margin : "20, 0 0 0", - height : this.summaryHeight + 500, - items : [ - { - html : "
Concentration Analysis
", - border : 0, - width : 900 - - }, - { - html : this.concentrationHTMLTableWidget.getPanel(parsedData), - border : 0, - width : 900, - margin : "10, 0 0 10" - - }, - { - xtype : 'container', - layout : 'vbox', - border : 5, - items : [ - { - html : "
Rg based on Guinier and Gnom
", - border : 8, - width : 900, - margin : "20 0 0 10" - - }, { - xtype : 'container', - layout : 'hbox', - items : [ { - xtype : 'container', - layout : 'vbox', - items : [ { - html : "
", - border : 0, - width : this.plot.width, - padding : "10 0 0 20", - listeners : { - afterrender : function() { - _this.plotWhisker(parsedData); - - } - } - }, { - html : "
Concentration (mg/ml)
", - width : 450, - border : 0, - margin : "-50 0 0 0" - - } ] - }, { - xtype : 'container', - layout : 'vbox', - items : [ { - html : "
", - border : 0, - width : this.rangePlot.width, - padding : "10 0 0 10", - listeners : { - afterrender : function() { - _this.plotRange(parsedData); - } - } - }, { - html : "
Concentration (mg/ml)
", - width : 450, - border : 0, - margin : "-50 0 0 0" - - } ] - } ] - } ] - } ] - } ] - } - - ] - } ] - }); - return this.panel; - -}; - -ResultSummaryForm.prototype.input = function() { - return { - data : DATADOC.getData_3367() - }; -}; - -ResultSummaryForm.prototype.test = function(targetId) { - var resultSummaryForm = new ResultSummaryForm(); - var panel = resultSummaryForm.getPanel(resultSummaryForm.input().data); - panel.render(targetId); - -}; - -/** - * Example form - * - * @witdh - * @height - */ -function RigibBodyModelingForm(args) { - this.id = BUI.id(); - this.width = 700; - this.height = 500; - - if (args != null) { - if (args.width != null) { - this.width = args.width; - } - if (args.height != null) { - this.height = args.height; - } - } - - var _this = this; - this.rigidBodyGrid = new AprioriRigidBodyGrid(); - - this.rigidBodyGrid.onUploadFile.attach(function(sender, type, title){ - _this._openUploadDialog(_this.macromolecule.macromoleculeId, type, title); - }); - - this.rigidBodyGrid.onRemove.attach(function(sender, type, title){ - _this._update(); - }); - - this.onSave = new Event(this); - -} - -RigibBodyModelingForm.prototype._getItems = function() { - var _this = this; - - - return [ { - xtype : 'label', - forId : 'myFieldId', - text : 'Information for model fit, mixture analysis and rigid body modeling', - margin : '15 0 20 10', - cls : "inline-help" - }, - this.rigidBodyGrid.getPanel(), - { - xtype : 'label', - forId : 'myFieldId', - text : 'Distance restraints may be imposed on the model using contacts conditions file (OPTIONAL)', - margin : '25 0 5 10', - cls : "inline-help" - }, - { - xtype : 'container', - layout : 'hbox', - items : [ { - xtype : 'container', - border : false, - layout : 'hbox', - items : [ { - xtype : 'label', - forId : 'myFieldId', - text : 'Contact Description File: (Optional)', - width : 150, - margin : '10 0 0 10' - }, { - id : this.id + "contactsDescriptionFilePath", - xtype : 'textfield', - hideLabel : true, - margin : '10 0 0 0', - width : 200, - disabled: true - }, { - text : 'Upload', - xtype : 'button', - margin : "10 0 0 10", - width : 80, - handler : function() { - - _this._openUploadDialog(_this.macromolecule.macromoleculeId, "CONTACTS", "Upload Contact Description File"); - } - }, { - text : 'Remove', - id : _this.id + "_remove", - xtype : 'button', - margin : "10 0 0 10", - width : 80, - handler : function() { - _this.macromolecule.contactsDescriptionFilePath = null; - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, proposal) { - /** Updating the proposal global object **/ - BIOSAXS.proposal.setItems(proposal); - _this.panel.setLoading(false); - - var saved = BIOSAXS.proposal.getMacromoleculeByAcronym(_this.macromolecule.acronym); - /** Refreshing to check that everything is ok **/ - _this.refresh(saved); - _this.onSave.notify(saved); - }); - - _this.panel.setLoading("Saving Macromolecule") - adapter.saveMacromolecule(_this.macromolecule); - } - } ] - } ] - }, { - xtype : 'panel', - html : "Go to SASREF manual for further information", - margin : "10 0 0 160", - border : 0 - - }, - { - xtype : 'checkbox', - margin : '10 0 0 5', - boxLabel : "I want rigid body modeling run on this stuff", - checked : true, - width : 300 - } ] - -}; - -/** Because update is a jsp page we don't know if the user has uploaded a file or not then we need to refresh **/ -RigibBodyModelingForm.prototype._update = function(macromoleculeId, type, title) { - var _this = this; - BIOSAXS.proposal.onInitialized.attach(function() { - if (BIOSAXS.proposal != null) { - _this.refresh(BIOSAXS.proposal.getMacromoleculeById(_this.macromolecule.macromoleculeId)); - _this.panel.setLoading(false); - } - }); - this.panel.setLoading(); - BIOSAXS.proposal.init(); -}; - -RigibBodyModelingForm.prototype._openUploadDialog = function(macromoleculeId, type, title) { - var _this = this; - function onClose() { - w.destroy(); - _this._update(); - - } - - var w = Ext.create('Ext.window.Window', { - title : title, - height : 200, - width : 400, - modal : true, - buttons : [ { - text : 'Close', - handler : function() { - onClose(); - } - } ], - layout : 'fit', - items : { - html : "" - }, - listeners : { - onEsc : function() { - onClose(); - }, - close : function() { - onClose(); - } - } - }).show(); -}; - -RigibBodyModelingForm.prototype._getButtons = function() { - return []; -}; - -RigibBodyModelingForm.prototype.getPanel = function() { - var _this = this; - this.panel = Ext.create('Ext.form.Panel', { - width : this.width, - height : this.height, - margin : 10, - border : 1, - defaultType : 'textfield', - items : this._getItems(), - buttons : this._getButtons(), - listeners : { - afterrender : function(){ - _this._populate(); - - } - } - }); - return this.panel; -}; - -/** Populates could be call when the DOM is not filled yet **/ -RigibBodyModelingForm.prototype._populate = function() { - if (this.macromolecule != null){ - if (Ext.getCmp(this.id + "contactsDescriptionFilePath") != null){ - if (this.macromolecule.contactsDescriptionFilePath != null){ - Ext.getCmp(this.id + "contactsDescriptionFilePath").setValue(this.macromolecule.contactsDescriptionFilePath); - Ext.getCmp(this.id + "_remove").enable(); - } - else{ - Ext.getCmp(this.id + "_remove").disable(); - Ext.getCmp(this.id + "contactsDescriptionFilePath").setValue(""); - - } - } - } -}; - -/** It populates the form * */ -RigibBodyModelingForm.prototype.refresh = function(macromolecule) { - this.macromolecule = macromolecule; - this.rigidBodyGrid.refresh(macromolecule); - this._populate(); -}; - -RigibBodyModelingForm.prototype.input = function() { - return {}; -}; - - -/** It populates the form **/ -RigibBodyModelingForm.prototype.test = function(targetId) { - var macromoleculeForm = new RigibBodyModelingForm(); - var panel = macromoleculeForm.getPanel(); - panel.render(targetId); -}; - - -/** - * Same form as MX part - * - * @creationMode if true a create button appears instead of save - * @showTitle true or false - */ -function ShipmentForm(args) { - this.id = BUI.id(); - - this.creationMode = false; - this.showTitle = true; - if (args != null) { - if (args.creationMode != null) { - this.creationMode = args.creationMode; - } - if (args.showTitle != null) { - this.showTitle = args.showTitle; - } - } - - this.onSaved = new Event(this); -} - -ShipmentForm.prototype.fillStores = function() { - this.panel.setLoading("Loading Labcontacts from database"); - this.labContactForSendingStore.loadData(BIOSAXS.proposal.getLabcontacts(), false); - this.labContactForReturnStore.loadData(BIOSAXS.proposal.getLabcontacts(), false); - this.panel.setLoading(false); - if (this.shipment != null) { - this.setShipment(this.shipment); - } -}; - -ShipmentForm.prototype.draw = function(targetId) { - this.getPanel().render(targetId); -}; - -ShipmentForm.prototype.setShipment = function(shipment) { - this.shipment = shipment; - var _this = this; - Ext.getCmp(_this.id + "shippingName").setValue(shipment.json.shippingName); - Ext.getCmp(_this.id + "shippingStatus").setValue(shipment.json.shippingStatus); - Ext.getCmp(_this.id + "comments").setValue(shipment.json.comments); - if (shipment.json.sendingLabContactVO != null) { - this.labContactsSendingCombo.setValue(shipment.json.sendingLabContactVO.labContactId); - } - if (shipment.json.returnLabContactVO != null) { - this.labContactsReturnCombo.setValue(shipment.json.returnLabContactVO.labContactId); - } - -}; - -ShipmentForm.prototype._saveShipment = function() { - var _this = this; - var shippingId = null; - if (this.shipment != null) { - shippingId = this.shipment.json.shippingId; - } - var json = { - shippingId : shippingId, - name : Ext.getCmp(_this.id + "shippingName").getValue(), - status : Ext.getCmp(_this.id + "shippingStatus").getValue(), - sendingLabContactId : Ext.getCmp(_this.id + "shipmentform_sendingLabContactId").getValue(), - returnLabContactId : Ext.getCmp(_this.id + "returnLabContactId").getValue(), - returnCourier : Ext.getCmp(_this.id + "returnCourier").getValue(), - courierAccount : Ext.getCmp(_this.id + "courierAccount").getValue(), - BillingReference : Ext.getCmp(_this.id + "BillingReference").getValue(), - dewarAvgCustomsValue : Ext.getCmp(_this.id + "dewarAvgCustomsValue").getValue(), - dewarAvgTransportValue : Ext.getCmp(_this.id + "dewarAvgTransportValue").getValue(), - comments : Ext.getCmp(_this.id + "comments").getValue() - }; - - var dataAdapter = new BiosaxsDataAdapter(); - dataAdapter.onSuccess.attach(function(sender, shipment) { - window.location = BUI.getShippingURL(shipment.shippingId); - }); - - dataAdapter.onError.attach(function(sender, error) { - _this.onError.notify(error); - }); - - /** Cheking params **/ - if (json.name == "") { - BUI.showError("Name field is mandatory"); - return; - } - - if (json.sendingLabContactId == null) { - BUI.showError("Lab contact for sending field is mandatory"); - return; - } - - if (json.returnLabContactId == null) { - BUI.showError("Lab contact for return field is mandatory"); - return; - } - - dataAdapter.createShipment(json.shippingId, json.name, json.status, json.comments, json.sendingLabContactId, json.returnLabContactId, - json.returnCourier, json.courierAccount, json.BillingReference, json.dewarAvgCustomsValue, json.dewarAvgTransportValue); - -}; - -ShipmentForm.prototype.getPanel = function(shipment) { - var _this = this; - this.shipment = shipment; - var required = '*'; - var buttons = []; - - if (_this.creationMode) { - buttons.push({ - text : 'Create', - scope : this, - handler : function() { - _this._saveShipment(); - } - }); - } else { - buttons.push({ - text : 'Save', - scope : this, - handler : function() { - _this._saveShipment(); - } - }); - - } - - this.labContactForSendingStore = Ext.create('Ext.data.Store', { - fields : [ 'cardName', 'labContactId' ] - }); - - this.labContactForReturnStore = Ext.create('Ext.data.Store', { - fields : [ 'cardName', 'labContactId' ] - }); - - // Create the combo box, attached to the states data store - this.labContactsSendingCombo = Ext.create('Ext.form.ComboBox', { - id : _this.id + "shipmentform_sendingLabContactId", - fieldLabel : 'Lab contact for sending', - afterLabelTextTpl : required, - store : this.labContactForSendingStore, - queryMode : 'local', - labelWidth : 200, - displayField : 'cardName', - valueField : 'labContactId' - }); - - this.labContactsReturnCombo = Ext.create('Ext.form.ComboBox', { - id : _this.id + "returnLabContactId", - fieldLabel : 'If No, Lab-Contact for Return', - afterLabelTextTpl : required, - store : this.labContactForReturnStore, - queryMode : 'local', - labelWidth : 200, - displayField : 'cardName', - valueField : 'labContactId', - listeners : { - change : function(x, newValue) { - for ( var i = 0; i < x.getStore().data.items.length; i++) { - if (x.getStore().data.items[i].raw.labContactId == newValue) { - Ext.getCmp(_this.id + "returnCourier").setValue(x.getStore().data.items[i].raw.defaultCourrierCompany); - Ext.getCmp(_this.id + "courierAccount").setValue(x.getStore().data.items[i].raw.courierAccount); - Ext.getCmp(_this.id + "BillingReference").setValue(x.getStore().data.items[i].raw.billingReference); - Ext.getCmp(_this.id + "dewarAvgCustomsValue").setValue(x.getStore().data.items[i].raw.dewarAvgCustomsValue); - Ext.getCmp(_this.id + "dewarAvgTransportValue").setValue(x.getStore().data.items[i].raw.dewarAvgTransportValue); - } - } - } - } - }); - - if (this.panel == null) { - this.panel = Ext.create('Ext.form.Panel', { - bodyPadding : 5, - width : 600, - border : 1, - items : [ { - xtype : 'fieldset', - title : 'Details', - collapsible : false, - defaultType : 'textfield', - layout : 'anchor', - defaults : { - anchor : '100%' - }, - items : [ { - xtype : 'requiredtext', - fieldLabel : 'Shipment Label', - allowBlank : false, - name : 'shippingName', - id : _this.id + 'shippingName', - value : '', - anchor : '50%' - }, { - - xtype : 'textareafield', - name : 'comments', - id : _this.id + 'comments', - fieldLabel : 'Comments', - value : '' - }, { - fieldLabel : 'Status', - readOnly : true, - id : _this.id + 'shippingStatus', - value : 'Opened', - anchor : '50%' - } ] - }, { - xtype : 'fieldset', - title : 'Lab-Contacts', - collapsible : false, - defaultType : 'textfield', - layout : 'anchor', - defaults : { - anchor : '100%' - }, - items : [ this.labContactsSendingCombo, this.labContactsReturnCombo ] - }, { - border : 0, - html : BUI.getWarningHTML("These informations are relevant for all shipments") - }, { - xtype : 'fieldset', - title : 'Courier accounts details for return', - collapsible : false, - layout : 'anchor', - defaults : { - anchor : '100%' - }, - items : [ { - xtype : 'textfield', - labelWidth : 400, - fieldLabel : 'Courier company for return (if ESRF sends a dewar back)', - id : _this.id + 'returnCourier', - value : '' - }, { - xtype : 'textfield', - labelWidth : 400, - fieldLabel : 'Courier account', - id : _this.id + 'courierAccount', - value : '' - }, { - xtype : 'textfield', - labelWidth : 400, - fieldLabel : 'Billing reference', - id : _this.id + 'BillingReference', - value : '' - }, { - xtype : 'numberfield', - labelWidth : 400, - fieldLabel : 'Average Customs value of a dewar (Euro)', - id : _this.id + 'dewarAvgCustomsValue', - value : '' - }, { - xtype : 'numberfield', - labelWidth : 400, - fieldLabel : 'Average Transport value of a dewar (Euro)', - id : _this.id + 'dewarAvgTransportValue', - value : '' - } ] - } ], - buttons : buttons - }); - } - this.fillStores(); - if (this.showTitle) { - this.panel.setTitle('Create a new Shipment'); - } - return this.panel; -}; - -ShipmentForm.prototype.input = function() { - return { - proposal : DATADOC.getProposal_10() - - }; -}; - -ShipmentForm.prototype.test = function(targetId) { - var shipmentForm = new ShipmentForm({ - creationMode : true - - }); - BIOSAXS.proposal = new Proposal(shipmentForm.input().proposal); - shipmentForm.getPanel().render(targetId); -}; - -/** - * #onSaved - */ -function StockSolutionForm(args) { - this.id = BUI.id(); - this.actions = []; - - if (args != null) { - if (args.actions != null) { - this.actions = args.actions; - } - } - this.onSaved = new Event(this); -} - -StockSolutionForm.prototype.getStockSolution = function() { - if (this.stockSolution != null) { - this.stockSolution.concentration = Ext.getCmp(this.id + "stockSolution_concentration").getValue(); - this.stockSolution.storageTemperature = Ext.getCmp(this.id + "stockSolution_storageTemperature").getValue(); - this.stockSolution.volume = Ext.getCmp(this.id + "stockSolution_volume").getValue(); - this.stockSolution.comments = Ext.getCmp(this.id + "stockSolution_comments").getValue(); - this.stockSolution.name = Ext.getCmp(this.id + "stockSolution_name").getValue(); - this.stockSolution.bufferId = this.bufferCombo.getValue(); - - if (this.macromoleculeCombo.getValue() != null) { - this.stockSolution.macromoleculeId = this.macromoleculeCombo.getValue(); - } else { - this.stockSolution.macromolecule3VO = null; - } - - } else { - return { - concentration : Ext.getCmp(this.id + "stockSolution_concentration").getValue(), - storageTemperature : Ext.getCmp(this.id + "stockSolution_storageTemperature").getValue(), - volume : Ext.getCmp(this.id + "stockSolution_volume").getValue(), - comments : Ext.getCmp(this.id + "stockSolution_comments").getValue(), - name : Ext.getCmp(this.id + "stockSolution_name").getValue(), - bufferId : this.bufferCombo.getValue(), - macromoleculeId : this.macromoleculeCombo.getValue() - }; - } - return this.stockSolution; -}; - -StockSolutionForm.prototype.setStockSolution = function(stockSolution) { - if (stockSolution != null) { - if (stockSolution.macromoleculeId != null) { - this.macromoleculeCombo.setValue(stockSolution.macromoleculeId); - } - this.bufferCombo.setValue(stockSolution.bufferId); - Ext.getCmp(this.id + "stockSolution_concentration").setValue(this.stockSolution.concentration); - Ext.getCmp(this.id + "stockSolution_storageTemperature").setValue(this.stockSolution.storageTemperature); - Ext.getCmp(this.id + "stockSolution_volume").setValue(this.stockSolution.volume); - Ext.getCmp(this.id + "stockSolution_name").setValue(this.stockSolution.name); - Ext.getCmp(this.id + "stockSolution_comments").setValue(this.stockSolution.comments); - } -}; - -StockSolutionForm.prototype.getBufferCombo = function() { - this.bufferCombo = BIOSAXS_COMBOMANAGER.getComboBuffers(BIOSAXS.proposal.getBuffers(), { - labelWidth : 120, - margin : '0 0 10 0', - width : 220 - - }); - return this.bufferCombo; -}; - -StockSolutionForm.prototype.getMacromoleculeCombo = function() { - this.macromoleculeCombo = BIOSAXS_COMBOMANAGER.getComboMacromoleculeByMacromolecules(BIOSAXS.proposal.getMacromolecules(), { - labelWidth : 120, - margin : '0 0 10 0', - width : 220 - - }); - return this.macromoleculeCombo; -}; - -StockSolutionForm.prototype.refresh = function() { -}; - -StockSolutionForm.prototype._getTopPanel = function() { - return { - xtype : 'container', - layout : 'hbox', - border : 0, - items : [ { - xtype : 'container', - layout : 'hbox', - items : [ { - xtype : 'container', - flex : 1, - border : false, - layout : 'anchor', - defaultType : 'textfield', - items : [ - - this.getMacromoleculeCombo(), { - xtype : 'requiredtext', - id : this.id + 'stockSolution_name', - fieldLabel : 'Acronym', - labelWidth : 120, - width : 250 - }, { - xtype : 'requiredtext', - id : this.id + 'stockSolution_concentration', - fieldLabel : 'Conc. (mg/ml)', - labelWidth : 120, - width : 250 - }, - - { - id : this.id + 'stockSolution_storageTemperature', - fieldLabel : 'Storage Temp.(C)', - labelWidth : 120, - width : 250 - }, { - xtype : 'requiredtext', - id : this.id + 'stockSolution_volume', - fieldLabel : 'Volume in Well (µl)', - labelWidth : 120, - width : 250 - } ] - } ] - }, { - xtype : 'container', - flex : 1, - layout : 'anchor', - defaultType : 'textfield', - margin : '0 0 0 10', - items : [ this.getBufferCombo() ] - } ] - }; - -}; - -StockSolutionForm.prototype.getPanel = function(stockSolution) { - this.stockSolution = stockSolution; - this.panel = Ext.createWidget({ - xtype : 'container', - layout : 'vbox', - border : 0, - style : { - padding : '10px' - }, - fieldDefaults : { - labelAlign : 'left', - labelWidth : 50 - }, - items : [ this._getTopPanel(stockSolution), { - id : this.id + 'stockSolution_comments', - xtype : 'textareafield', - name : 'comments', - fieldLabel : 'Comments', - labelWidth : 120, - width : '100%' - } ] - }); - - this.setStockSolution(stockSolution); - return this.panel; -}; - -StockSolutionForm.prototype.input = function() { - return { - stock : { - "stockSolutionId" : 6, - "proposalId" : 3124, - "macromoleculeId" : 5933, - "bufferId" : 811, - "instructionSet3VO" : null, - "boxId" : 305861, - "storageTemperature" : "20", - "volume" : "300", - "concentration" : "1.2", - "comments" : "Buffer EDTA with A", - "name" : "A_EDTA_1.2", - "samples" : [], - "buffer" : "EDTA", - "macromolecule" : "A" - }, - proposal : new MeasurementGrid().input().proposal - }; -}; - -StockSolutionForm.prototype.test = function(targetId) { - var stockSolutionForm = new StockSolutionForm(); - BIOSAXS.proposal = new Proposal(stockSolutionForm.input().proposal); - var panel = stockSolutionForm.getPanel(new Shipment(stockSolutionForm.input().stock)); - panel.render(targetId); -}; - - -BoxWhiskerGraph.prototype.cleanArray = GenericGraph.prototype.cleanArray; -BoxWhiskerGraph.prototype.plotAxes = GenericGraph.prototype.plotAxes; -BoxWhiskerGraph.prototype.plotRuler = GenericGraph.prototype.plotRuler; -BoxWhiskerGraph.prototype.drawSVGVerticalText = GenericGraph.prototype.drawSVGVerticalText; -BoxWhiskerGraph.prototype.getClassColor = GenericGraph.prototype.getClassColor; -BoxWhiskerGraph.prototype.getDimensions = GenericGraph.prototype.getDimensions; -BoxWhiskerGraph.prototype.calculate = GenericGraph.prototype.calculate; -BoxWhiskerGraph.prototype.getMedian = GenericGraph.prototype.getMedian; -BoxWhiskerGraph.prototype.pointToPixel = GenericGraph.prototype.pointToPixel; -BoxWhiskerGraph.prototype.maxValueWhisker = GenericGraph.prototype.maxValueWhisker; -BoxWhiskerGraph.prototype.refresh = GenericGraph.prototype.refresh; - -/** - * Subclass of GenericGraph - * - * @maxBoxWidth - */ -function BoxWhiskerGraph(args){ - this.maxBoxWidth = 25; - - if (args == null){ - args = new Object(); - } - args["plotHorizontalByCluster"] = true; - - GenericGraph.call(this, args); - - if (args.maxBoxWidth != null){ - this.maxBoxWidth = args.maxBoxWidth; - } -}; - - - -/** -There are several different methods for calculating quartiles.[1] This calculator uses a method described by Moore and McCabe to find quartile values. -The same method also used by The TI-83 to calculate quartile values. -With this method, the first quartile is the median of the numbers below the median, the third quartile is the median of the numbers above the median. - -http://www.miniwebtool.com/quartile-calculator/ -http://www.alcula.com/calculators/statistics/box-plot/ -**/ -BoxWhiskerGraph.prototype.getQ1 = function(array){ - array = array.slice(0, array.length/2); - return this.getMedian(array); -}; - -BoxWhiskerGraph.prototype.getQ3 = function(array){ - array = array.slice((array.length + 1)/2); - return this.getMedian(array); - -}; - -BoxWhiskerGraph.prototype.getQ2 = function(array){ - return this.getMedian(array); -}; - -BoxWhiskerGraph.prototype.getBelowOutliers = function(belowLimit, array){ - var points = new Array(); - for (var i = 0; i < array.length; i++){ - if (array[i] <= belowLimit){ - points.push(array[i]); - } - } - return points; -}; - -BoxWhiskerGraph.prototype.getAboveOutliers = function(aboveLimit, array){ - var points = new Array(); - for (var i = 0; i < array.length; i++){ - if (array[i] >= aboveLimit){ - points.push(array[i]); - } - } - return points; -}; - - -BoxWhiskerGraph.prototype.minValueWhisker = function(belowLimit, q1, array){ - var points = new Array(); - - for (var i = 0; i < array.length; i++){ - if ((array[i] < q1) && (array[i]) > belowLimit){ - points.push(array[i]); - } - } - - if (points.length > 0){ - points.sort(function(a, b){return a - b;}); - return points[0]; - } - return null; -}; - -BoxWhiskerGraph.prototype.isNumber = function(value){ - if (value=="") return false; - - var d = parseInt(value); - if (!isNaN(d)) return true; else return false; - -}; - -BoxWhiskerGraph.prototype.plotBoxWhisker = function(boxProperties){ - if (this.maxBoxWidth != null){ - if (boxProperties.width > this.maxBoxWidth){ - boxProperties.x = boxProperties.x + (boxProperties.width/2) - (this.maxBoxWidth/2); - boxProperties.width = this.maxBoxWidth; - } - - } - - if (this.plotPoints){ - for(var i = 0; i < boxProperties.values.length; i++){ - var value = boxProperties.values[i]; - var toPixel = this.pointToPixel(value, boxProperties); - SVG.drawCircle(boxProperties.x + (boxProperties.width/2) + 2, toPixel, this.pointRadius, this.svg, [["fill", "green"], ["fill-opacity", this.fillOpacityPoint],['stroke-opacity', this.strokeOpacityPoint], ["stroke", "black"]]); - } - } - - - var boxColor = this.getClassColor(boxProperties.name); - var result = this.calculate(boxProperties.values); - /** Q1 **/ - - if (this.isNumber(result.Q1)){ - SVG.drawLine(boxProperties.x, this.pointToPixel(result.Q1, boxProperties), boxProperties.x + boxProperties.width, this.pointToPixel(result.Q1, boxProperties), this.svg, [["stroke", boxColor]]); - } - /** Q2 **/ - if (this.isNumber(result.Q2)){ - SVG.drawLine(boxProperties.x, this.pointToPixel(result.Q2, boxProperties), boxProperties.x + boxProperties.width, this.pointToPixel(result.Q2, boxProperties), this.svg, [["stroke", "blue"], ["stroke-width", "2"]]); - } - - /** Q3 **/ - if (this.isNumber(result.Q3)){ - SVG.drawLine(boxProperties.x, this.pointToPixel(result.Q3, boxProperties), boxProperties.x + boxProperties.width, this.pointToPixel(result.Q3, boxProperties), this.svg, [["stroke", boxColor]]); - } - - /** Concenting Q1 and Q3 **/ - if (this.isNumber(result.Q1)&&this.isNumber(result.Q3)){ - SVG.drawLine(boxProperties.x, this.pointToPixel(result.Q1, boxProperties), boxProperties.x, this.pointToPixel(result.Q3, boxProperties), this.svg, [["stroke", boxColor]]); - SVG.drawLine(boxProperties.x + boxProperties.width, this.pointToPixel(result.Q1, boxProperties), boxProperties.x + boxProperties.width, this.pointToPixel(result.Q3, boxProperties), this.svg, [["stroke", boxColor]]); - } - - /** min-whisker **/ - if (result["min-whisker"] != null){ - if (this.isNumber(result.Q1)){ - SVG.drawLine(boxProperties.x + (boxProperties.width/2) + 2, this.pointToPixel(result.Q1, boxProperties), boxProperties.x + (boxProperties.width/2) + 2, this.pointToPixel(result["min-whisker"], boxProperties), this.svg, [["stroke", boxColor]]); - } - SVG.drawLine(boxProperties.x,this.pointToPixel(result["min-whisker"], boxProperties), boxProperties.x + boxProperties.width, this.pointToPixel(result["min-whisker"], boxProperties) , this.svg, [["stroke", boxColor]]); - - } - - /** max-whisker **/ - if (result["max-whisker"] != null){ - if (this.isNumber(result.Q3)){ - SVG.drawLine(boxProperties.x + (boxProperties.width/2) + 2, this.pointToPixel(result.Q3, boxProperties), boxProperties.x + (boxProperties.width/2) + 2, this.pointToPixel(result["max-whisker"], boxProperties), this.svg, [["stroke", boxColor]]); - } - SVG.drawLine(boxProperties.x , this.pointToPixel(result["max-whisker"], boxProperties), boxProperties.x + boxProperties.width, this.pointToPixel(result["max-whisker"], boxProperties), this.svg, [["stroke", boxColor]]); - - } - - /** outliners **/ - if (result["above-outliers"] != null){ - for (var point in result["above-outliers"]){ - SVG.drawCircle(boxProperties.x + (boxProperties.width/2) + 2, this.pointToPixel(result["above-outliers"][point], boxProperties), this.pointRadius, this.svg, [["fill", "red"], ["fill-opacity", "1"]]); - //SVG.drawText(boxProperties.x + (boxProperties.width/2) - 2, this.pointToPixel(result["above-outliers"][point], boxProperties) + 2, "x",this.svg, [["fill", "black"], ["font-size", "x-small"]]); - } - } - if (result["below-outliers"] != null){ - for (var point in result["below-outliers"]){ - SVG.drawCircle(boxProperties.x + (boxProperties.width/2) + 2, this.pointToPixel(result["below-outliers"][point], boxProperties), this.pointRadius, this.svg, [["fill", "red"], ["fill-opacity", "1"]]); - //SVG.drawText(boxProperties.x + (boxProperties.width/2) - 2, this.pointToPixel(result["below-outliers"][point], boxProperties) + 2, "x",this.svg, [["fill", "black"], ["font-size", "x-small"]]); - } - } -}; - - - -BoxWhiskerGraph.prototype.plotClusterTitles = function(properties){ - /** Cluster Titles **/ - var posX = this.left + this.rulerWidth; - var data = this.data; - for(var i = 0; i < data.clusters.length; i++ ){ - /** title for the clusters **/ - posX = posX + this.interClustersSpace; - - /** Drawing title of classes **/ - var cluster = data.clusters[i]; - var posXClasses = posX; - for(var j = 0; j < cluster.classes.length; j++){ - //SVG.drawRectangle(posXClasses, this.height - (this.bottom + this.clusterTitleHeight + this.rulerHeight), properties.classWidth, this.rulerHeight, this.svg, [["fill", "green"]]); -// SVG.drawText(posXClasses + 1/6*properties.classWidth, this.height - (this.bottom + this.clusterTitleHeight + (this.rulerHeight*1/4)), cluster.classes[j].name, this.svg, [["style", "font-size:xx-small"]]); - var color = cluster.classes[j].color; - this.drawSVGVerticalText(posXClasses + 1/2*(properties.classWidth), this.height - (this.bottom - this.clusterTitleHeight + (this.rulerHeight)), cluster.classes[j].name , [["style", "font-size:x-small;"], ["fill", color]]); - - // SVG.drawRectangle(posXClasses, this.height - (this.bottom + this.clusterTitleHeight), properties.classWidth, 2, this.svg, [["fill", "black"]]); - posXClasses = posXClasses + properties.classWidth + this.interClassesSpace; - } - - var clusterTitleSpace = (data.clusters[i].classes.length * properties.classWidth) + ((data.clusters[i].classes.length - 1) * this.interClassesSpace); - //SVG.drawRectangle(posX, this.height - (this.bottom + this.clusterTitleHeight), clusterTitleSpace, this.clusterTitleHeight, this.svg, [["fill", "pink"]]); - SVG.drawRectangle(posX, this.height - (this.clusterTitleHeight+this.bottom), clusterTitleSpace, 1, this.svg, []); - -// var transform = ["translate", "(" + posX + 1/3*clusterTitleSpace +", " + this.height - (this.bottom + (this.clusterTitleHeight*1/4)) +")"], ["transform", "rotate(180)"]; -// var transform = ["transform", "translate(" + (posX + 1/3*clusterTitleSpace) +", " + (this.height - (this.bottom + (this.clusterTitleHeight*1/4))) + "), rotate(-90) "]; -// this.drawSVGVerticalText(posX + 1/3*clusterTitleSpace, this.height - (this.bottom + (this.clusterTitleHeight*1/4)),data.clusters[i].name, [["style", "font-size:x-small"]]); - SVG.drawText(posX , this.height - (this.bottom + (this.clusterTitleHeight*1/4)),data.clusters[i].name ,this.svg, [["style", "font-size:x-small"]]); - posX = posX + clusterTitleSpace; - } -}; - -BoxWhiskerGraph.prototype.plotWhisters = function(data, properties){ - var colors = ["yellow", "orange", "green"]; - var posX = this.left + this.rulerWidth; - for(var i = 0; i < data.clusters.length; i++ ){ - var cluster = data.clusters[i]; - /** inter cluster space **/ - //SVG.drawRectangle(posX, this.top, this.interClustersSpace, this.height - this.top - this.bottom - this.clusterTitleHeight - this.rulerHeight, this.svg, [["fill", "black"]]); - posX = posX + this.interClustersSpace; - - for (var j = 0; j < cluster.classes.length; j ++){ - - //SVG.drawRectangle(posX, this.top, properties.classWidth, this.height - this.top - this.bottom - this.clusterTitleHeight - this.rulerHeight, this.svg, [["fill", colors[j]]]); - this.plotBoxWhisker( - { - name : cluster.classes[j].name, - values : cluster.classes[j].values, - minPoint : properties.minPoint, - maxPoint : properties.maxPoint, - x : posX, - y : this.top, - width : properties.classWidth, - height : this.height - this.top - this.bottom - this.clusterTitleHeight - this.rulerHeight - - - } - ); - posX = posX + properties.classWidth; - //SVG.drawRectangle(posX, this.top, this.interClassesSpace, this.height - this.top - this.bottom - this.clusterTitleHeight - this.rulerHeight, this.svg, [["fill", "black"]]); - if (j < cluster.classes.length - 1){ - posX = posX + this.interClassesSpace; - } - } - } -}; - -BoxWhiskerGraph.prototype.draw = function(targetId, data){ - this.targetId = targetId; - var properties = (this.getDimensions(data)); - this.svg = SVG.createSVGCanvas(document.getElementById(this.targetId), [["width", this.width], ["height", this.height]]); - this.plotAxes(properties); - this.plotClusterTitles(properties); - this.plotWhisters(data,properties); -}; - -BoxWhiskerGraph.prototype.input = function(){ - return DATADOC.getBoxWhikerData(); -}; - -BoxWhiskerGraph.prototype.test = function(targetId){ - var plot = new BoxWhiskerGraph( - { - targetId : targetId, - height : 350, - width : 450, - maxBoxWidth : 25 - } - ); - plot.refresh(this.input()); -}; - - - - - - - -function DataSet(){ - this.json = null; - -}; - - -DataSet.prototype.loadFromJSON = function(json){ - if(json != null) { - if(this.validate(json)) { - this.json = json; - } - } -}; - - -DataSet.prototype.toJSON = function(json){ - return this.json; -}; - - -/** Abstract method to be override on childs classes **/ -DataSet.prototype.validate = function(json){ - if (true){ - return true; - } - /*else{ - throw "Data validation failed"; - }*/ -}; - - - - -Edge.prototype.getName = GraphItem.prototype.getName; -Edge.prototype.setName = GraphItem.prototype.setName; -Edge.prototype.getId = GraphItem.prototype.getId; - -function Edge(id, name, nodeSource, nodeTarget, args){ - GraphItem.prototype.constructor.call(this, id, name, args); - - this.sourceNode = nodeSource; - this.targetNode = nodeTarget; - -}; - -Edge.prototype.toJSON = function(){ - return {"index": this.id,"sourceIndex":this.sourceNode.getId(),"targetIndex":this.targetNode.getId(),"args":this.args}; -}; - -Edge.prototype.getNodeSource = function(){ - return this.sourceNode; -}; - -Edge.prototype.getNodeTarget = function(){ - return this.targetNode; -}; - -Edge.prototype.remove = function(){ - //Remove edge object in the nodes - this.getNodeSource().removeEdge(this); - this.getNodeTarget().removeEdge(this); - - this.deleted.notify(this); -}; - - -EdgeGraphFormatter.prototype.setProperties = ItemGraphFormatter.prototype.setProperties; -EdgeGraphFormatter.prototype.getDefault = ItemGraphFormatter.prototype.getDefault; -EdgeGraphFormatter.prototype.getSelected = ItemGraphFormatter.prototype.getSelected; -EdgeGraphFormatter.prototype.getOver = ItemGraphFormatter.prototype.getOver; -EdgeGraphFormatter.prototype.getDragging = ItemGraphFormatter.prototype.getDragging; -EdgeGraphFormatter.prototype.getId = ItemGraphFormatter.prototype.getId; -EdgeGraphFormatter.prototype.toJSON = ItemGraphFormatter.prototype.toJSON; -EdgeGraphFormatter.prototype.loadFromJSON = ItemGraphFormatter.prototype.loadFromJSON; -EdgeGraphFormatter.prototype._setEvents = ItemGraphFormatter.prototype._setEvents; - -function EdgeGraphFormatter(id, defaultFormat, selectedFormat, overFormat, dragginFormat){ - ItemGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, dragginFormat); - - this.getDefault().args.type = "EdgeGraphFormatter"; -}; - - - - -LineEdgeGraphFormatter.prototype.setProperties = EdgeGraphFormatter.prototype.setProperties; -LineEdgeGraphFormatter.prototype.getDefault = EdgeGraphFormatter.prototype.getDefault; -LineEdgeGraphFormatter.prototype.getSelected = EdgeGraphFormatter.prototype.getSelected; -LineEdgeGraphFormatter.prototype.getOver = EdgeGraphFormatter.prototype.getOver; -LineEdgeGraphFormatter.prototype.getDragging = EdgeGraphFormatter.prototype.getDragging; -LineEdgeGraphFormatter.prototype.getId = EdgeGraphFormatter.prototype.getId; -LineEdgeGraphFormatter.prototype.toJSON = EdgeGraphFormatter.prototype.toJSON; -LineEdgeGraphFormatter.prototype.loadFromJSON = EdgeGraphFormatter.prototype.loadFromJSON; -LineEdgeGraphFormatter.prototype._setEvents = EdgeGraphFormatter.prototype._setEvents; - -function LineEdgeGraphFormatter(id, defaultFormat, selectedFormat, overFormat, dragginFormat){ - EdgeGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, dragginFormat); - this.args.type = "LineEdgeGraphFormatter"; -}; - -/** DIRECTED **/ -DirectedLineEdgeGraphFormatter.prototype.setProperties = EdgeGraphFormatter.prototype.setProperties; -DirectedLineEdgeGraphFormatter.prototype.getDefault = EdgeGraphFormatter.prototype.getDefault; -DirectedLineEdgeGraphFormatter.prototype.getSelected = EdgeGraphFormatter.prototype.getSelected; -DirectedLineEdgeGraphFormatter.prototype.getOver = EdgeGraphFormatter.prototype.getOver; -DirectedLineEdgeGraphFormatter.prototype.getDragging = EdgeGraphFormatter.prototype.getDragging; -DirectedLineEdgeGraphFormatter.prototype.getId = EdgeGraphFormatter.prototype.getId; -DirectedLineEdgeGraphFormatter.prototype.toJSON = EdgeGraphFormatter.prototype.toJSON; -DirectedLineEdgeGraphFormatter.prototype.loadFromJSON = EdgeGraphFormatter.prototype.loadFromJSON; -DirectedLineEdgeGraphFormatter.prototype._setEvents = EdgeGraphFormatter.prototype._setEvents; - -function DirectedLineEdgeGraphFormatter(id, defaultFormat, selectedFormat, overFormat, dragginFormat){ - EdgeGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, dragginFormat); - this.args.type = "DirectedLineEdgeGraphFormatter"; - this.args.arrowSize = "3"; -}; -DirectedLineEdgeGraphFormatter.prototype.getArrowSize = function(){ - return this.args.arrowSize; -}; - -OdirectedLineEdgeGraphFormatter.prototype.setProperties = EdgeGraphFormatter.prototype.setProperties; -OdirectedLineEdgeGraphFormatter.prototype.getDefault = EdgeGraphFormatter.prototype.getDefault; -OdirectedLineEdgeGraphFormatter.prototype.getSelected = EdgeGraphFormatter.prototype.getSelected; -OdirectedLineEdgeGraphFormatter.prototype.getOver = EdgeGraphFormatter.prototype.getOver; -OdirectedLineEdgeGraphFormatter.prototype.getDragging = EdgeGraphFormatter.prototype.getDragging; -OdirectedLineEdgeGraphFormatter.prototype.getId = EdgeGraphFormatter.prototype.getId; -OdirectedLineEdgeGraphFormatter.prototype.toJSON = EdgeGraphFormatter.prototype.toJSON; -OdirectedLineEdgeGraphFormatter.prototype.loadFromJSON = EdgeGraphFormatter.prototype.loadFromJSON; -OdirectedLineEdgeGraphFormatter.prototype._setEvents = EdgeGraphFormatter.prototype._setEvents; - -function OdirectedLineEdgeGraphFormatter(id, defaultFormat, selectedFormat, overFormat, dragginFormat){ - EdgeGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, dragginFormat); - this.args.type = "DirectedLineEdgeGraphFormatter"; - this.args.arrowSize = "3"; -}; - -OdirectedLineEdgeGraphFormatter.prototype.getArrowSize = function(){ - return this.args.arrowSize; -}; - - -/** CUT (inhibition) **/ -CutDirectedLineEdgeGraphFormatter.prototype.setProperties = EdgeGraphFormatter.prototype.setProperties; -CutDirectedLineEdgeGraphFormatter.prototype.getDefault = EdgeGraphFormatter.prototype.getDefault; -CutDirectedLineEdgeGraphFormatter.prototype.getSelected = EdgeGraphFormatter.prototype.getSelected; -CutDirectedLineEdgeGraphFormatter.prototype.getOver = EdgeGraphFormatter.prototype.getOver; -CutDirectedLineEdgeGraphFormatter.prototype.getDragging = EdgeGraphFormatter.prototype.getDragging; -CutDirectedLineEdgeGraphFormatter.prototype.getId = EdgeGraphFormatter.prototype.getId; -CutDirectedLineEdgeGraphFormatter.prototype.toJSON = EdgeGraphFormatter.prototype.toJSON; -CutDirectedLineEdgeGraphFormatter.prototype.loadFromJSON = EdgeGraphFormatter.prototype.loadFromJSON; -CutDirectedLineEdgeGraphFormatter.prototype._setEvents = EdgeGraphFormatter.prototype._setEvents; - -function CutDirectedLineEdgeGraphFormatter(id, defaultFormat, selectedFormat, overFormat, dragginFormat){ - EdgeGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, dragginFormat); - this.args.type = "CutDirectedLineEdgeGraphFormatter"; -}; - - - - -BezierEdgeGraphFormatter.prototype.setProperties = EdgeGraphFormatter.prototype.setProperties; -BezierEdgeGraphFormatter.prototype.setProperties = EdgeGraphFormatter.prototype.setProperties; -BezierEdgeGraphFormatter.prototype.getDefault = EdgeGraphFormatter.prototype.getDefault; -BezierEdgeGraphFormatter.prototype.getSelected = EdgeGraphFormatter.prototype.getSelected; -BezierEdgeGraphFormatter.prototype.getOver = EdgeGraphFormatter.prototype.getOver; -BezierEdgeGraphFormatter.prototype.getDragging = EdgeGraphFormatter.prototype.getDragging; -BezierEdgeGraphFormatter.prototype.getId = EdgeGraphFormatter.prototype.getId; -BezierEdgeGraphFormatter.prototype.toJSON = EdgeGraphFormatter.prototype.toJSON; -BezierEdgeGraphFormatter.prototype.loadFromJSON = EdgeGraphFormatter.prototype.loadFromJSON; -BezierEdgeGraphFormatter.prototype._setEvents = EdgeGraphFormatter.prototype._setEvents; - -function BezierEdgeGraphFormatter(id, defaultFormat, selectedFormat, overFormat, dragginFormat){ - EdgeGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, dragginFormat); - this.args.type = "BezierEdgeGraphFormatter"; -}; - - - -DotDirectedLineEdgeGraphFormatter.prototype.setProperties = EdgeGraphFormatter.prototype.setProperties; -DotDirectedLineEdgeGraphFormatter.prototype.getDefault = EdgeGraphFormatter.prototype.getDefault; -DotDirectedLineEdgeGraphFormatter.prototype.getSelected = EdgeGraphFormatter.prototype.getSelected; -DotDirectedLineEdgeGraphFormatter.prototype.getOver = EdgeGraphFormatter.prototype.getOver; -DotDirectedLineEdgeGraphFormatter.prototype.getDragging = EdgeGraphFormatter.prototype.getDragging; -DotDirectedLineEdgeGraphFormatter.prototype.getId = EdgeGraphFormatter.prototype.getId; -DotDirectedLineEdgeGraphFormatter.prototype.toJSON = EdgeGraphFormatter.prototype.toJSON; -DotDirectedLineEdgeGraphFormatter.prototype.loadFromJSON = EdgeGraphFormatter.prototype.loadFromJSON; -DotDirectedLineEdgeGraphFormatter.prototype._setEvents = EdgeGraphFormatter.prototype._setEvents; - -function DotDirectedLineEdgeGraphFormatter(id, defaultFormat, selectedFormat, overFormat, dragginFormat){ - EdgeGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, dragginFormat); - this.args.type = "DotDirectedLineEdgeGraphFormatter"; -}; - - -OdotDirectedLineEdgeGraphFormatter.prototype.setProperties = EdgeGraphFormatter.prototype.setProperties; -OdotDirectedLineEdgeGraphFormatter.prototype.getDefault = EdgeGraphFormatter.prototype.getDefault; -OdotDirectedLineEdgeGraphFormatter.prototype.getSelected = EdgeGraphFormatter.prototype.getSelected; -OdotDirectedLineEdgeGraphFormatter.prototype.getOver = EdgeGraphFormatter.prototype.getOver; -OdotDirectedLineEdgeGraphFormatter.prototype.getDragging = EdgeGraphFormatter.prototype.getDragging; -OdotDirectedLineEdgeGraphFormatter.prototype.getId = EdgeGraphFormatter.prototype.getId; -OdotDirectedLineEdgeGraphFormatter.prototype.toJSON = EdgeGraphFormatter.prototype.toJSON; -OdotDirectedLineEdgeGraphFormatter.prototype.loadFromJSON = EdgeGraphFormatter.prototype.loadFromJSON; -OdotDirectedLineEdgeGraphFormatter.prototype._setEvents = EdgeGraphFormatter.prototype._setEvents; - -function OdotDirectedLineEdgeGraphFormatter(id, defaultFormat, selectedFormat, overFormat, dragginFormat){ - EdgeGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, dragginFormat); - this.args.type = "OdotDirectedLineEdgeGraphFormatter"; -}; - - - -function GraphDataset(){ - DataSet.prototype.constructor.call(this); - this.edges = new Object(); - this.vertices = new Object(); - this.verticesIndex = new Object(); - - //Events - this.newVertex = new Event(this); - this.vertexNameChanged = new Event(this); - this.vertexDeleted = new Event(this); - - this.newEdge = new Event(this); - this.edgeNameChanged = new Event(this); - this.edgeDeleted = new Event(this); - - this.json = new Object(); - this.json.vertices = new Array(); - this.json.edges = new Array(); - this.json.relations = new Array(); -}; - -GraphDataset.prototype.loadFromJSON = DataSet.prototype.loadFromJSON; -GraphDataset.prototype.toJSON = DataSet.prototype.toJSON; -GraphDataset.prototype.validate = DataSet.prototype.validate; - -/** Devuelve el numero de edges incidentes sobre el nodo con mas edges **/ -GraphDataset.prototype.getMaxClass = function(){ - var maxClassNode = 0; - for ( var node in this.vertices) { - if (this.vertices[node].getEdgesCount() > maxClassNode){ - maxClassNode = this.vertices[node].getEdgesCount(); - } - } - return maxClassNode; -}; - -/** Devuelve el numero de edges incidentes sobre el nodo con mas edges **/ -GraphDataset.prototype.getMinClass = function(){ - var minClassNode = Math.min(); - for ( var node in this.vertices) { - if (this.vertices[node].getEdgesCount() < minClassNode){ - minClassNode = this.vertices[node].getEdgesCount(); - } - } - return minClassNode; -}; - -GraphDataset.prototype.getVertexByName = function(nodeName){ - var results = new Array(); - - for (var vertexId in this.verticesIndex[nodeName]){ - var vertexByid = this.getVertexById(this.verticesIndex[nodeName][vertexId]); - results.push(vertexByid); - //* añadido nuevo porque fallaba el anterior codigo - return vertexByid - } - - if (results <= 1){ - return this.getVertexById(this.verticesIndex[nodeName]); - } - else{ - return results; - } -}; - -GraphDataset.prototype.getVertexById = function(id){ - return this.vertices[id]; -}; - -GraphDataset.prototype.toSIF = function(){ - var sifDataAdapter = new SifFileDataAdapter(); - return sifDataAdapter.toSIF(this); -}; - -GraphDataset.prototype.toSIFID = function(){ - var sifDataAdapter = new SifFileDataAdapter(); - return sifDataAdapter.toSIFID(this); -}; - -GraphDataset.prototype.toDOT = function(){ - var dotFileDataAdapter = new DotFileDataAdapter(); - return dotFileDataAdapter.toDOT(this); -}; - -GraphDataset.prototype.toDOTID = function(){ - var dotFileDataAdapter = new DotFileDataAdapter(); - return dotFileDataAdapter.toDOTID(this); -}; - -GraphDataset.prototype._addNode = function(nodeName, args){ - return new Vertex(this._getVerticesCount()-1, nodeName, args); -}; - -GraphDataset.prototype.addNode = function(nodeName, args){ - this.json.vertices.push(nodeName); - this._addVerticesIndex(nodeName, this._getVerticesCount() - 1); - var vertex = this._addNode(nodeName, args); - this.vertices[this._getVerticesCount()-1] = vertex; - this._setNodeEvents(vertex); - this.newVertex.notify(vertex); -}; - -GraphDataset.prototype._addVerticesIndex = function(nodeName, id){ - if (this.verticesIndex[nodeName] == null){ - this.verticesIndex[nodeName] = new Array(); - } - this.verticesIndex[nodeName].push(id); -}; - -GraphDataset.prototype.addEdge = function(edgeName, nodeSourceId, nodeTargetId, args){ - this.json.edges.push(edgeName); - var nodeSource = this.getVertexById(nodeSourceId); - var nodeTarget = this.getVertexById(nodeTargetId); - var index = this.getEdgesCount() - 1; - this.edges[index] = new Edge(index, edgeName, nodeSource, nodeTarget, args); - this.json.relations.push({"index": index, "sourceIndex": nodeSourceId, "targetIndex": nodeTargetId, "args": args }); - - nodeSource.addEdge(this.edges[index]); - nodeTarget.addEdge(this.edges[index]); - this._setEdgeEvents(this.edges[index]); - this.newEdge.notify(this.edges[index]); -}; - -GraphDataset.prototype.getVertices = function(){ - return this.vertices; -}; - -GraphDataset.prototype.getEdges = function(){ - return this.edges; -}; - -GraphDataset.prototype.getEdgeById = function(edgeId){ - return this.edges[edgeId]; -}; - -GraphDataset.prototype._getVerticesCount = function(){ - return this.json.vertices.length; -}; - - -GraphDataset.prototype.getVerticesCount = function(){ - var count = 0; - for ( var vertex in this.getVertices()) { - count ++; - } - return count; -}; - - -GraphDataset.prototype.getEdgesCount = function(){ - return this.json.edges.length; -}; - -GraphDataset.prototype.init = function(){ - this.edges = new Object(); - this.vertices = new Object(); -}; - -GraphDataset.prototype._setNodeEvents = function(node){ - var _this = this; - //NODE EVENTS - node.deleted.attach(function (sender, node){ - _this._removeNode(node); - }); - - node.nameChanged.attach(function (sender, args){ - var item = args.item; - var newName = item.name; - var indexes = _this.verticesIndex[args.previousName]; - for(var i = 0; i < indexes.length; i++){ - if(indexes[i] == item.id) - indexes.splice(i,1); - } - if(indexes.length == 0){ - delete _this.verticesIndex[args.previousName]; - } - _this._addVerticesIndex(newName, item.id); - _this.json.vertices[item.id] = newName; - _this.vertexNameChanged.notify(args); - }); -}; - -GraphDataset.prototype._setEdgeEvents = function(edge){ - var _this = this; - //EDGE EVENTS - edge.nameChanged.attach(function (sender, edge){ - _this.edgeNameChanged.notify(edge); - - }); - - edge.deleted.attach(function (sender, edge){ - _this._removeEdge(edge); - }); -}; - -GraphDataset.prototype._connectVerticesByName = function(nodeNameSource, nodeNameTarget){ - var source = this.getVertexByName(nodeNameSource); - var target = this.getVertexByName(nodeNameTarget); - - if ((source != null)&&(target!=null)){ - this.addEdge(source.getName() +"_" + target.getName(), source.getId(), target.getId(), {}); - } - else{ - if (source == null){ - console.log("No encontrado: " + nodeNameSource) - } - if (target == null){ - console.log("No encontrado: " + nodeNameTarget) - } - } -}; - -GraphDataset.prototype.loadFromJSON = function(json){ - var json = json; - this.init(); - this.json = new Object(); - this.json.vertices = new Array(); - this.json.edges = new Array(); - this.json.relations = new Array(); - - for ( var i = 0; i < json.nodes.length; i++) { - if (json.nodes[i] != null){ - var name = json.nodes[i]; - this.addNode(name); - } - else{ - this.json.vertices.push(null); - } - } - - for ( var i = 0; i < json.edges.length; i++) { - if (json.edges[i] != null){ - if (json.relations[i] != null){ - var name = json.edges[i]; - this.addEdge(name, json.relations[i].sourceIndex, json.relations[i].targetIndex, json.relations[i].args); - } - } - else{ - this.json.edges.push(null); - this.json.relations.push(null); - } - } -}; - -GraphDataset.prototype.prettyPrint = function(){ - for ( var node in this.vertices) { - console.log(this.vertices[node].getName() ); - for ( var j = 0; j < this.vertices[node].getEdgesIn().length; j++) { - console.log(" --> " + this.vertices[node].getEdgesIn()[j].getNodeTarget().getName() ); - } - } -}; - -GraphDataset.prototype._removeEdge = function(edge){ - this.json.edges[edge.getId()] = null; - this.json.relations[edge.getId()] = null; - - delete this.edges[edge.getId()]; - this.edgeDeleted.notify(edge); - - -}; - -GraphDataset.prototype._removeNode = function(node){ - this.json.vertices[node.getId()] = null; - delete this.vertices[node.getId()]; - this.vertexDeleted.notify(node); -}; - -GraphDataset.prototype.toJSON = function(){ - var json = new Object(); - var nodes = new Array(); - json.nodes = this.json.vertices; //nodes; - json.edges = this.json.edges; //edges; - json.relations = this.json.relations; - return json; -}; - -GraphDataset.prototype.clone = function(){ - var dsDataset = new GraphDataset(); - dsDataset.loadFromJSON(this.toJSON()); - return dsDataset; -}; -//GraphDataset.prototype.test = function(){ -// this.loadFromJSON(this.toJSON()); -//}; - -function labels(){ - var names = new Array(); - - var dataset = interactomeViewer.graphEditorWidget.dataset; - var layout = interactomeViewer.graphEditorWidget.layout; - - for ( var vertexId in interactomeViewer.graphEditorWidget.dataset.getVertices()) { - names.push(interactomeViewer.graphEditorWidget.dataset.getVertexById(vertexId).getName()); - } - - var sorted = (names.sort()); - console.log(sorted) - var distance = 0.01; - var altura = 0.6; - for ( var i = 0; i < names.length; i++) { - var id =dataset.getVertexByName(names[i]).getId(); - - layout.getNodeById(id).setCoordenates(distance, altura); - - - distance = parseFloat(distance) + parseFloat(0.03); - - altura = parseFloat(altura) + parseFloat(0.02); - - if (parseFloat(altura) == 0.9800000000000003){ - - altura = 0.6; - distance = distance - 0.51; - } - - } - - -}; - -function GraphItem(id, name, args){ - this.id = id; - this.name = name; - this.type = "NONE"; - - this.args = new Object(); - - - if (args!=null){ - this.args = args; - if (args.type !=null){ - this.type = args.type; - } - } - - //Events - this.nameChanged = new Event(this); - this.deleted = new Event(this); -} - -GraphItem.prototype.getName = function(){ - return this.name; -}; - -GraphItem.prototype.getId = function(){ - return this.id; -}; - -GraphItem.prototype.setName = function(name){ - var oldName = this.getName(); - this.name = name; - this.nameChanged.notify({"item": this, "previousName" : oldName}); -}; - - - - - -function LayoutDataset(){ - this.dataset = null; - this.vertices = new Object(); - this.changed = new Event(this); - - - this.args = new Object(); - - //RANDOM, CIRCLE - this.args.type = "CIRCLE"; -}; - -LayoutDataset.prototype.loadFromJSON = function(dataset, json){ - var _this = this; - this.vertices = new Object(); - this.dataset = dataset; //new GraphDataset(); - for ( var vertex in json) { - this.vertices[vertex] = new NodeLayout(vertex, json[vertex].x, json[vertex].y); - this.vertices[vertex].changed.attach(function (sender, item){ - _this.changed.notify(item); - }); - } - this._attachDatasetEvents(); -}; - - -LayoutDataset.prototype.toJSON = function(){ - var serialize = new Object(); - for ( var vertex in this.vertices) { - serialize[vertex] = new Object(); - serialize[vertex].x = this.vertices[vertex].x; - serialize[vertex].y = this.vertices[vertex].y; - } - serialize.dataset = new Object(); - serialize.dataset =this.dataset.toJSON(); - return serialize; -}; - -LayoutDataset.prototype.dataBind = function(graphDataset){ - this.dataset = graphDataset; - this._attachDatasetEvents(); - this._calculateLayout(); -}; - -LayoutDataset.prototype._removeVertex = function(vertexId){ - delete this.vertices[vertexId]; -}; - -LayoutDataset.prototype._attachDatasetEvents = function(){ - var _this = this; - - this.dataset.vertexDeleted.attach(function (sender, item){ - _this._removeVertex(item.getId()); - }); - - this.dataset.newVertex.attach(function (sender, item){ - _this.vertices[item.getId()] = new NodeLayout(item.getId(), 0.5, 0.5); - _this.vertices[item.getId()].changed.attach(function (sender, item){ - _this.changed.notify(item); - }); - }); -}; - -LayoutDataset.prototype.getType = function(){ - return this.args.type; -}; - -LayoutDataset.prototype._calculateLayoutVertices = function(type, count){ - - if (type == "CIRCLE"){ - var radius = 0.4; - var centerX = 0.5; - var centerY = 0.5; - var verticesCoordinates = new Array(); - for(var i = 0; i < count; i++){ - x = centerX + radius * Math.sin(i * 2 * Math.PI/count); - y = centerY + radius * Math.cos(i * 2 * Math.PI/count); - verticesCoordinates.push({'x':x,'y':y}); - } - return verticesCoordinates; - } -}; - - -LayoutDataset.prototype._calculateLayout = function(){ - var _this = this; - if (this.getType() == "RANDOM"){ - for ( var vertex in this.dataset.getVertices()) { - if (this.vertices[vertex] == null){ - this.vertices[vertex] = new NodeLayout(vertex, 0, 0); - } - this.vertices[vertex].setCoordinates(Math.random(), Math.random()); - this.vertices[vertex].changed.attach(function (sender, item){ - _this.changed.notify(item); - }); - } - } - - if ( this.getType() == "CIRCLE"){ - - var count = this.dataset._getVerticesCount(); - var verticesCoordinates = this._calculateLayoutVertices(this.getType(), count); - - var aux = 0; - for ( var vertex in this.dataset.getVertices()) { - if (this.vertices[vertex] == null){ - this.vertices[vertex] = new NodeLayout(vertex, 0, 0); - } - this.vertices[vertex].setCoordinates(verticesCoordinates[aux].x, verticesCoordinates[aux].y);//{"x":, "y":}; - aux++; - this.vertices[vertex].changed.attach(function (sender, item){ - _this.changed.notify(item); - }); - } - } - - - if (this.getType() == "SQUARE"){ - - var count = this.dataset._getVerticesCount(); - var xMin = 0.1; - var xMax = 0.9; - var yMin = 0.1; - var yMax = 0.9; - - var rows = Math.sqrt(count); - var step = (xMax - xMin) / rows; - - var verticesCoordinates = new Array(); - for(var i = 0; i < rows; i ++){ - for ( var j = 0; j < rows; j++) { - x = i * step + xMin; - y = j * step + yMin; - verticesCoordinates.push({'x':x,'y':y}); - } - } - - var aux = 0; - for ( var vertex in this.dataset.getVertices()) { - if (this.vertices[vertex] == null){ - this.vertices[vertex] = new NodeLayout(vertex, 0, 0); - } - this.vertices[vertex].setCoordinates(verticesCoordinates[aux].x, verticesCoordinates[aux].y);//{"x":, "y":}; - aux++; - this.vertices[vertex].changed.attach(function (sender, item){ - _this.changed.notify(item); - }); - } - } - -}; - -LayoutDataset.prototype.getNodeById = function(id){ - return this.vertices[id]; -}; - -LayoutDataset.prototype.getVerticesByArea = function(x1, y1, x2, y2){ - var vertices = new Array(); - for ( var vertex in this.dataset.getVertices()) { - if ((this.vertices[vertex].x >= x1)&&(this.vertices[vertex].x <= x2)){ - if ((this.vertices[vertex].y >= y1)&&(this.vertices[vertex].y <= y2)){ - vertices.push(this.vertices[vertex]); - } - } - } - return vertices; -}; - - - - -LayoutDataset.prototype.getLayout = function(type){ - - if (type == "CIRCLE"){ - this.args.type = "CIRCLE"; - this._calculateLayout(); - return; - } - - if (type == "SQUARE"){ - this.args.type = "SQUARE"; - this._calculateLayout(); - return; - } - - if (type == "RANDOM"){ - this.args.type = "RANDOM"; - this._calculateLayout(); - return; - } - - - var dotText = this.dataset.toDOTID(); - var url = "http://bioinfo.cipf.es/utils/ws/rest/network/layout/"+type+".coords"; - var _this = this; - - $.ajax({ - async: true, - type: "POST", - url: url, - dataType: "text", - data: { - dot :dotText - }, - cache: false, - success: function(data){ - var response = JSON.parse(data); - for ( var vertexId in response) { - _this.vertices[vertexId].setCoordinates(response[vertexId].x, response[vertexId].y); - } - } - }); - -// $.ajax({ -// async: true, -// type: "POST", -// url: url, -// dataType: "script", -// data: { -// dot :dotText -// }, -// cache: false, -// success: function(data){ -// var response = JSON.parse(data); -// for ( var vertexId in response) { -// _this.vertices[vertexId].setCoordinates(response[vertexId].x, response[vertexId].y); -// } -// } -// }); - -}; - -function NodeLayout(id, x, y, args){ - this.id = id; - this.x = x; - this.y = y; - this.changed = new Event(this); -}; - -NodeLayout.prototype.getId = function(id){ - return this.id; -}; - -NodeLayout.prototype.setCoordinates = function(x, y){ - this.x = x; - this.y = y; - this.changed.notify(this); -}; - - - -function NetworkDataSetFormatter(vertexFormatProperties, defaultEdgeProperties, args){ - DataSet.prototype.constructor.call(this); - - this.args = new Object(); - - this.vertices = new Object(); - this.edges = new Object(); - this.dataset = null; - this.maxClass = 0; - this.minClass = 0; - - /** Coordenates with default Setting */ - this.args.width = 1100; - this.args.height = 500; - - /** Optional parameters */ - this.args.backgroundColor = "#FFFFFF"; - this.args.backgroundImage = null; - this.args.backgroundImageHeight = null; - this.args.backgroundImageWidth = null; - this.args.backgroundImageX = null; - this.args.backgroundImageY = null; - - - this.args.balanceNodes = false; - this.args.nodesMaxSize = 3; - this.args.nodesMinSize = 0.5; - - - /** Zoom **/ - this.args.zoomScale = 1; - this.args.zoomScaleStepFactor = 0.4; - - if (args != null){ - if (args.backgroundImage != null){ - this.args.backgroundImage = args.backgroundImage; - } - - if (args.width != null){ - this.args.width = args.width; - } - - if (args.height != null){ - this.args.height = args.height; - this.args.svgHeight = args.height; - } - - if (args.svgHeight != null){ - this.args.svgHeight = args.svgHeight; - } - - if (args.backgroundColor != null){ - this.args.backgroundColor = args.backgroundColor; - } - - if(args.balanceNodes != null){ - this.args.balanceNodes = args.balanceNodes; - } - - if(args.nodesMaxSize != null){ - this.args.nodesMaxSize = args.nodesMaxSize; - } - if(args.nodesMinSize != null){ - this.args.nodesMinSize = args.nodesMinSize; - } - } - - this.args.defaultEdgeProperties = {"fill":"#000000", "radius":"1", "stroke":"#000000", "size":1, "title":{"fontSize":10, "fontColor":"#000000"}}; - this.args.vertexFormatProperties = { "fill":"#000000", "stroke":"#000000", "stroke-opacity":"#000000"}; - - if (vertexFormatProperties!= null){ - this.args.vertexFormatProperties = vertexFormatProperties; - } - if (defaultEdgeProperties!= null){ - this.args.defaultEdgeProperties = defaultEdgeProperties; - } - - /** Events **/ - this.changed = new Event(this); - this.edgeChanged = new Event(this); - this.resized = new Event(this); - this.backgroundImageChanged= new Event(this); - this.backgroundColorChanged= new Event(this); -}; - -NetworkDataSetFormatter.prototype.loadFromJSON = function(dataset, json){ - this.args = new Object(); - this.vertices = new Object(); - this.args = json; - this._setDataset(dataset); - var _this = this; - - for ( var vertex in json.vertices) { - this.addVertex(vertex, json.vertices[vertex]); - } - - for ( var edgeId in json.edges) { - this.addEdge(edgeId, json.edges[edgeId]); - } -}; - - -NetworkDataSetFormatter.prototype.toJSON = function(){ - - -// this.args.vertices = new Object(); -// this.args.edges = new Object(); -// -// for ( var vertex in this.vertices) { -// this.args.vertices[vertex] = this.getVertexById(vertex).toJSON(); -// } -// for ( var edge in this.edges) { -// this.args.edges[edge] = this.getEdgeById(edge).toJSON(); -// } -// -// return (this.args); - - - var serialize = new Object(); - serialize = JSON.parse(JSON.stringify(this.args)); - serialize.vertices = new Object(); - serialize.edges = new Object(); - - for ( var vertex in this.vertices) { - serialize.vertices[vertex] = this.getVertexById(vertex).toJSON(); - } - for ( var edge in this.edges) { - serialize.edges[edge] = this.getEdgeById(edge).toJSON(); - } - - return (serialize); -}; - - - -NetworkDataSetFormatter.prototype._getNodeSize = function(nodeId){ - if (this.isVerticesBalanced()){ - var total = this.maxClass - this.minClass; - if (total == 0){ total = 1;} - var nodeConnection = this.dataset.getVertexById(nodeId).getEdges().length; - return ((nodeConnection*this.args.nodesMaxSize)/total) + this.args.nodesMinSize; - } - return this.getVertexById(nodeId).getDefault().getSize(); -}; - -NetworkDataSetFormatter.prototype._recalculateSize = function(){ - if (this.isVerticesBalanced()){ - this.maxClass = this.dataset.getMaxClass(); - this.minClass = this.dataset.getMinClass(); - for ( var vertexIdAll in this.vertices) { - var size = this._getNodeSize(vertexIdAll); - this.vertices[vertexIdAll].getDefault().setSize(size); - this.vertices[vertexIdAll].getSelected().setSize(size); - this.vertices[vertexIdAll].getOver().setSize(size); - } - } -}; - - -NetworkDataSetFormatter.prototype.addVertex = function(vertexId, json){ - - - if (json == null){ - this.vertices[vertexId] = new CircleVertexGraphFormatter(vertexId, this.args.vertexFormatProperties.defaultFormat, this.args.vertexFormatProperties.selected, this.args.vertexFormatProperties.over, this.args.vertexFormatProperties.dragging); - } - else{ - - /** Cargo los attributos desde el json **/ - if (json.type == null){ - this.vertices[vertexId] = new CircleVertexGraphFormatter(vertexId, this.args.vertexFormatProperties.defaultFormat, this.args.vertexFormatProperties.selected, this.args.vertexFormatProperties.over, this.args.vertexFormatProperties.dragging); - } - - if ((json.type == "SquareVertexGraphFormatter")||(json.type == "SquareVertexNetworkFormatter")){ - this.vertices[vertexId] = new SquareVertexGraphFormatter(vertexId); - this.vertices[vertexId].loadFromJSON(json); - } - - if ((json.type == "CircleVertexGraphFormatter")||(json.type == "CircleVertexNetworkFormatter")){ - this.vertices[vertexId] = new CircleVertexGraphFormatter(vertexId); - this.vertices[vertexId].loadFromJSON(json); - } - - if ((json.type == "EllipseVertexGraphFormatter")||(json.type == "EllipseVertexNetworkFormatter")){ - this.vertices[vertexId] = new EllipseVertexGraphFormatter(vertexId); - this.vertices[vertexId].loadFromJSON(json); - } - - if ((json.type == "RectangleVertexGraphFormatter")||(json.type == "RectangleVertexNetworkFormatter")){ - this.vertices[vertexId] = new RectangleVertexGraphFormatter(vertexId); - this.vertices[vertexId].loadFromJSON(json); - } - - if ((json.type == "RoundedVertexGraphFormatter")||(json.type == "RoundedVertexNetworkFormatter")){ - this.vertices[vertexId] = new RoundedVertexGraphFormatter(vertexId); - this.vertices[vertexId].loadFromJSON(json); - } - - } - - - var _this = this; - this.vertices[vertexId].stateChanged.attach(function (sender, item){ - _this.changed.notify(item); - }); - - var size = this._getNodeSize(vertexId); - this.vertices[vertexId].defaultFormat.args.size = size; - this.vertices[vertexId].selected.args.size = size; - this.vertices[vertexId].over.args.size = size; - -}; - -NetworkDataSetFormatter.prototype.addEdge = function(edgeId, json){ - - /** Es un edge nuevo que le doy los atributos por defecto **/ - if (json == null){ - if (this.dataset.getEdgeById(edgeId).getNodeSource().getId() == this.dataset.getEdgeById(edgeId).getNodeTarget().getId()){ - this.edges[edgeId] = new BezierEdgeGraphFormatter(edgeId, this.args.defaultEdgeProperties.defaultFormat, this.args.defaultEdgeProperties.selected, this.args.defaultEdgeProperties.over, this.args.defaultEdgeProperties.dragging); - }else{ - this.edges[edgeId] = new DirectedLineEdgeGraphFormatter(edgeId, this.args.defaultEdgeProperties.defaultFormat, this.args.defaultEdgeProperties.selected, this.args.defaultEdgeProperties.over, this.args.defaultEdgeProperties.dragging); - } - } - else{ - /** Cargo los attributos desde el json **/ - if (json.type == null){ - this.edges[edgeId] = new LineEdgeGraphFormatter(edgeId, this.args.defaultEdgeProperties.defaultFormat, this.args.defaultEdgeProperties.selected, this.args.defaultEdgeProperties.over, this.args.defaultEdgeProperties.dragging); - } - - if ((json.type == "LineEdgeGraphFormatter")||(json.type == "LineEdgeNetworkFormatter")){ - this.edges[edgeId] = new LineEdgeGraphFormatter(edgeId); - this.edges[edgeId].loadFromJSON(json); - } - if ((json.type == "DirectedLineEdgeGraphFormatter")||(json.type == "DirectedLineEdgeNetworkFormatter")){ - this.edges[edgeId] = new DirectedLineEdgeGraphFormatter(edgeId); - this.edges[edgeId].loadFromJSON(json); - } - - if ((json.type == "BezierEdgeGraphFormatter")||(json.type == "BezierEdgeNetworkFormatter")){ - this.edges[edgeId] = new BezierEdgeGraphFormatter(edgeId); - this.edges[edgeId].loadFromJSON(json); - } - - - if ((json.type == "CutDirectedLineEdgeGraphFormatter")||(json.type == "CutDirectedLineEdgeNetworkFormatter")){ - this.edges[edgeId] = new CutDirectedLineEdgeGraphFormatter(edgeId); - this.edges[edgeId].loadFromJSON(json); - } - - if ((json.type == "DotDirectedLineEdgeGraphFormatter")||(json.type == "DotDirectedLineEdgeNetworkFormatter")){ - this.edges[edgeId] = new DotDirectedLineEdgeGraphFormatter(edgeId); - this.edges[edgeId].loadFromJSON(json); - } - if ((json.type == "OdotDirectedLineEdgeGraphFormatter")||(json.type == "OdotDirectedLineEdgeNetworkFormatter")){ - this.edges[edgeId] = new OdotDirectedLineEdgeGraphFormatter(edgeId); - this.edges[edgeId].loadFromJSON(json); - } - - if ((json.type == "OdirectedLineEdgeGraphFormatter")||(json.type == "OdirectedLineEdgeNetworkFormatter")){ - this.edges[edgeId] = new OdirectedLineEdgeGraphFormatter(edgeId); - this.edges[edgeId].loadFromJSON(json); - } - } - - var _this = this; - this.edges[edgeId].stateChanged.attach(function (sender, item){ - _this.edgeChanged.notify(item); - }); - - this._recalculateSize(); -}; - -NetworkDataSetFormatter.prototype._setDataset = function(dataset){ - this.dataset = dataset; - this.maxClass = dataset.getMaxClass(); - this.minClass = dataset.getMinClass(); - this._attachDatasetEvents(); -}; - -NetworkDataSetFormatter.prototype.changeEdgeType = function(edgeId, type){ - if ((type == "LineEdgeGraphFormatter")||(type == "LineEdgeNetworkFormatter")){ - this.edges[edgeId] = new LineEdgeGraphFormatter(edgeId, this.args.defaultEdgeProperties.defaultFormat, this.args.defaultEdgeProperties.selected, this.args.defaultEdgeProperties.over, this.args.defaultEdgeProperties.dragging); - - } - if ((type == "DirectedLineEdgeGraphFormatter")||(type == "DirectedLineEdgeNetworkFormatter")){ - this.edges[edgeId] = new DirectedLineEdgeGraphFormatter(edgeId, this.args.defaultEdgeProperties.defaultFormat, this.args.defaultEdgeProperties.selected, this.args.defaultEdgeProperties.over, this.args.defaultEdgeProperties.dragging); - } - - if ((type == "CutDirectedLineEdgeGraphFormatter")||(type == "CutDirectedLineEdgeNetworkFormatter")){ - this.edges[edgeId] = new CutDirectedLineEdgeGraphFormatter(edgeId, this.args.defaultEdgeProperties.defaultFormat, this.args.defaultEdgeProperties.selected, this.args.defaultEdgeProperties.over, this.args.defaultEdgeProperties.dragging); - } - - if ((type == "DotDirectedLineEdgeGraphFormatter")||(type == "DotDirectedLineEdgeNetworkFormatter")){ - this.edges[edgeId] = new DotDirectedLineEdgeGraphFormatter(edgeId, this.args.defaultEdgeProperties.defaultFormat, this.args.defaultEdgeProperties.selected, this.args.defaultEdgeProperties.over, this.args.defaultEdgeProperties.dragging); - } - - if ((type == "OdotDirectedLineEdgeGraphFormatter")||(type == "OdotDirectedLineEdgeNetworkFormatter")){ - this.edges[edgeId] = new OdotDirectedLineEdgeGraphFormatter(edgeId, this.args.defaultEdgeProperties.defaultFormat, this.args.defaultEdgeProperties.selected, this.args.defaultEdgeProperties.over, this.args.defaultEdgeProperties.dragging); - } - - if ((type == "OdirectedLineEdgeGraphFormatter")||(type == "OdirectedLineEdgeNetworkFormatter")){ - this.edges[edgeId] = new OdirectedLineEdgeGraphFormatter(edgeId, this.args.defaultEdgeProperties.defaultFormat, this.args.defaultEdgeProperties.selected, this.args.defaultEdgeProperties.over, this.args.defaultEdgeProperties.dragging); - } - - - - var _this = this; - this.edges[edgeId].stateChanged.attach(function (sender, item){ - _this.edgeChanged.notify(item); - }); - _this.edgeChanged.notify(this.edges[edgeId]); -}; -/* -classe = "SquareNetworkNodeFormatter"; -} -if (value == "circle"){ - classe = "CircleNetworkNodeFormatter"; - **/ -NetworkDataSetFormatter.prototype.changeNodeType = function(vertexId, type){ - var defaultFormat = JSON.parse(JSON.stringify(this.vertices[vertexId].getDefault())); - var selectedFormat = JSON.parse(JSON.stringify(this.vertices[vertexId].getSelected())); - var defaultFormat = JSON.parse(JSON.stringify(this.vertices[vertexId].getDefault())); - var defaultFormat = JSON.parse(JSON.stringify(this.vertices[vertexId].getDefault())); - - if ((type == "SquareVertexGraphFormatter")||(type == "SquareVertexNetworkFormatter")){ - this.vertices[vertexId] = new SquareVertexGraphFormatter(vertexId,defaultFormat, this.args.vertexFormatProperties.selected, this.args.vertexFormatProperties.over, this.args.vertexFormatProperties.dragging); - } - - if ((type == "CircleVertexGraphFormatter")||(type == "CircleVertexNetworkFormatter")){ - this.vertices[vertexId] = new CircleVertexGraphFormatter(vertexId, defaultFormat, this.args.vertexFormatProperties.selected, this.args.vertexFormatProperties.over, this.args.vertexFormatProperties.dragging); - } - - if ((type == "EllipseVertexGraphFormatter")||(type == "EllipseVertexNetworkFormatter")){ - this.vertices[vertexId] = new EllipseVertexGraphFormatter(vertexId, defaultFormat, this.args.vertexFormatProperties.selected, this.args.vertexFormatProperties.over, this.args.vertexFormatProperties.dragging); - } - - if ((type == "RectangleVertexGraphFormatter")||(type == "RectangleVertexNetworkFormatter")){ - this.vertices[vertexId] = new RectangleVertexGraphFormatter(vertexId, defaultFormat, this.args.vertexFormatProperties.selected, this.args.vertexFormatProperties.over, this.args.vertexFormatProperties.dragging); - } - - if ((type == "RoundedVertexGraphFormatter")||(type == "RoundedVertexNetworkFormatter")){ - this.vertices[vertexId] = new RoundedVertexGraphFormatter(vertexId, defaultFormat, this.args.vertexFormatProperties.selected, this.args.vertexFormatProperties.over, this.args.vertexFormatProperties.dragging); - } - - if ((type == "OctagonVertexGraphFormatter")||(type == "OctagonVertexNetworkhFormatter")){ - this.vertices[vertexId] = new OctagonVertexGraphFormatter(vertexId, defaultFormat, this.args.vertexFormatProperties.selected, this.args.vertexFormatProperties.over, this.args.vertexFormatProperties.dragging); - } - - - var _this = this; - this.vertices[vertexId].stateChanged.attach(function (sender, item){ - _this.changed.notify(item); - }); - _this.changed.notify(this.vertices[vertexId]); -}; - - - -NetworkDataSetFormatter.prototype.dataBind = function(networkDataSet){ - var _this = this; - this._setDataset(networkDataSet); - - for ( var vertex in this.dataset.getVertices()) { - this.addVertex(vertex); - } - - for ( var edge in this.dataset.getEdges()) { - this.addEdge(edge); - } -}; - -NetworkDataSetFormatter.prototype._removeEdge = function(edgeId){ - delete this.edges[edgeId]; -}; - -NetworkDataSetFormatter.prototype._removeVertex = function(vertexId){ - delete this.vertices[vertexId]; -}; - -NetworkDataSetFormatter.prototype._attachDatasetEvents = function(id){ - var _this = this; - this.dataset.vertexDeleted.attach(function (sender, item){ - _this._removeVertex(item.getId()); - }); - - this.dataset.edgeDeleted.attach(function (sender, item){ - _this._removeEdge(item.getId()); - }); - - this.dataset.newVertex.attach(function (sender, item){ - _this.addVertex(item.getId()); - }); - - this.dataset.newEdge.attach(function (sender, item){ - _this.addEdge(item.getId()); - }); -}; - - -NetworkDataSetFormatter.prototype.getVertexById = function(id){ - return this.vertices[id]; -}; - -NetworkDataSetFormatter.prototype.getEdgeById = function(id){ - return this.edges[id]; -}; - -NetworkDataSetFormatter.prototype.makeLabelsBigger = function(){ -for ( var vertexId in this.vertices) { - var fontSize = this.vertices[vertexId].getDefault().getFontSize() + 2; - this.vertices[vertexId].getDefault().setFontSize(fontSize); - } -}; - -NetworkDataSetFormatter.prototype.makeLabelsSmaller = function(){ - for ( var vertexId in this.vertices) { - var fontSize = this.vertices[vertexId].getDefault().getFontSize() - 2; - this.vertices[vertexId].getDefault().setFontSize(fontSize); - } -}; - - -NetworkDataSetFormatter.prototype.resize = function(width, height){ - this.args.width = width; - this.args.height = height; - this.resized.notify(); -}; - -/** ZOOM GETTERS **/ -NetworkDataSetFormatter.prototype.getZoomScaleStepFactor = function(){return this.args.zoomScaleStepFactor;}; -NetworkDataSetFormatter.prototype.setZoomScaleStepFactor = function(scaleFactor){this.args.zoomScaleStepFactor = scaleFactor;}; -NetworkDataSetFormatter.prototype.getZoomScale = function(){return this.args.zoomScale;}; -NetworkDataSetFormatter.prototype.setZoomScale = function(scale){this.args.zoomScale = scale;}; - -NetworkDataSetFormatter.prototype.getNodesMaxSize = function(){return this.args.nodesMaxSize;}; -NetworkDataSetFormatter.prototype.getNodesMinSize = function(){return this.args.nodesMinSize;}; - -/** SIZE SETTERS AND GETTERS **/ -NetworkDataSetFormatter.prototype.isVerticesBalanced = function(){return this.args.balanceNodes;}; -NetworkDataSetFormatter.prototype.getWidth = function(){return this.args.width;}; -NetworkDataSetFormatter.prototype.getHeight = function(){return this.args.height;}; - -/** OPTIONAL PARAMETERS GETTERS AND SETTERS **/ -NetworkDataSetFormatter.prototype.getBackgroundImage = function(){return this.args.backgroundImage;}; -NetworkDataSetFormatter.prototype.setBackgroundImage = function(value){this.args.backgroundImage = value; this.backgroundImageChanged.notify(this);}; - - -NetworkDataSetFormatter.prototype.getBackgroundImageWidth = function(){return this.args.backgroundImageWidth;}; -NetworkDataSetFormatter.prototype.setBackgroundImageWidth = function(value){this.args.backgroundImageWidth = value; this.backgroundImageChanged.notify(this);}; - -NetworkDataSetFormatter.prototype.getBackgroundImageHeight = function(){return this.args.backgroundImageHeight;}; -NetworkDataSetFormatter.prototype.setBackgroundImageHeight = function(value){this.args.backgroundImageHeight = value; this.backgroundImageChanged.notify(this);}; - -NetworkDataSetFormatter.prototype.getBackgroundImageX = function(){return this.args.backgroundImageX;}; -NetworkDataSetFormatter.prototype.setBackgroundImageX = function(value){this.args.backgroundImageX = value; this.backgroundImageChanged.notify(this);}; - -NetworkDataSetFormatter.prototype.getBackgroundImageY = function(){return this.args.backgroundImageX;}; -NetworkDataSetFormatter.prototype.setBackgroundImageY = function(value){this.args.backgroundImageY = value; this.backgroundImageChanged.notify(this);}; - - - -NetworkDataSetFormatter.prototype.getBackgroundColor = function(){return this.args.backgroundColor;}; -NetworkDataSetFormatter.prototype.setBackgroundColor = function(value){this.args.backgroundColor = value; this.backgroundColorChanged.notify(this);}; - -NetworkDataSetFormatter.prototype.getWidth = function(){return this.args.width;}; -NetworkDataSetFormatter.prototype.setWidth = function(value){this.args.width = value;}; - -NetworkDataSetFormatter.prototype.getHeight = function(){return this.args.height;}; -NetworkDataSetFormatter.prototype.setHeight = function(value){this.args.height = value;}; - - - - -Vertex.prototype.getName = GraphItem.prototype.getName; -Vertex.prototype.setName = GraphItem.prototype.setName; -Vertex.prototype.getId = GraphItem.prototype.getId; - -function Vertex(id, name, args){ - GraphItem.prototype.constructor.call(this, id, name, args); - this.edgesIn= new Array(); - this.edgesOut= new Array(); -}; - -Vertex.prototype.getEdges = function(){ - return this.edgesIn.concat(this.edgesOut); -}; - -Vertex.prototype.getEdgesCount = function(){ - return this.getEdges().length; -}; - -Vertex.prototype.getEdgesIn = function(){ - return this.edgesIn; -}; - -Vertex.prototype.getEdgesOut = function(){ - return this.edgesOut; -}; - -Vertex.prototype.addEdge = function(edge){ - if (edge.getNodeSource().getId() == this.id){ - this.edgesIn.push(edge); - } - else{ - this.edgesOut.push(edge); - } -}; - -Vertex.prototype.removeEdge = function(edge){ - for ( var i = 0; i < this.getEdgesIn().length; i++) { - var edgeIn = this.edgesIn[i]; - if (edgeIn.getId() == edge.getId()){ - this.edgesIn.splice(i, 1); - } - } - for ( var i = 0; i < this.getEdgesOut().length; i++) { - var edgeIn = this.edgesOut[i]; - if (edgeIn.getId() == edge.getId()){ - this.edgesOut.splice(i, 1); - } - } -}; - -Vertex.prototype.remove = function(){ - var edges = this.getEdges(); - for ( var i = 0; i < edges.length; i++) { - var edge = edges[i]; - edge.remove(); - } - this.deleted.notify(this); -}; - - - - - -VertexGraphFormatter.prototype.getDefault = ItemGraphFormatter.prototype.getDefault; -VertexGraphFormatter.prototype.getSelected = ItemGraphFormatter.prototype.getSelected; -VertexGraphFormatter.prototype.getOver = ItemGraphFormatter.prototype.getOver; -VertexGraphFormatter.prototype.getDragging = ItemGraphFormatter.prototype.getDragging; -VertexGraphFormatter.prototype.getId = ItemGraphFormatter.prototype.getId; -VertexGraphFormatter.prototype.toJSON = ItemGraphFormatter.prototype.toJSON; -VertexGraphFormatter.prototype.loadFromJSON = ItemGraphFormatter.prototype.loadFromJSON; -VertexGraphFormatter.prototype._setEvents = ItemGraphFormatter.prototype._setEvents; - - -function VertexGraphFormatter(defaultFormat, selectedFormat, overFormat, draggingFormat){ - ItemGraphFormatter.prototype.constructor.call(this, defaultFormat, selectedFormat, overFormat, draggingFormat); -}; - - -CircleVertexGraphFormatter.prototype.getDefault = VertexGraphFormatter.prototype.getDefault; -CircleVertexGraphFormatter.prototype.getSelected = VertexGraphFormatter.prototype.getSelected; -CircleVertexGraphFormatter.prototype.getOver = VertexGraphFormatter.prototype.getOver; -CircleVertexGraphFormatter.prototype.getDragging = VertexGraphFormatter.prototype.getDragging; -CircleVertexGraphFormatter.prototype.getId = VertexGraphFormatter.prototype.getId; -CircleVertexGraphFormatter.prototype.toJSON = VertexGraphFormatter.prototype.toJSON; -CircleVertexGraphFormatter.prototype.loadFromJSON = VertexGraphFormatter.prototype.loadFromJSON; -CircleVertexGraphFormatter.prototype._setEvents = VertexGraphFormatter.prototype._setEvents; - -function CircleVertexGraphFormatter(id, defaultFormat, selectedFormat, overFormat, draggingFormat){ - VertexGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, draggingFormat); - this.args.type = "CircleVertexGraphFormatter"; - if (defaultFormat != null){ - if (defaultFormat.radius != null){ - this.defaultFormat.args.radius = defaultFormat.radius; - } - } - - if (selectedFormat != null){ - if (selectedFormat.radius != null){ - this.selected.args.radius = selectedFormat.radius; - } - } - - if (overFormat != null){ - if (overFormat.radius != null){ - this.over.args.radius = overFormat.radius; - } - } - - if (draggingFormat != null){ - if (draggingFormat.radius != null){ - this.dragging.args.draggingFormat = draggingFormat.radius; - } - } -}; - - - -SquareVertexGraphFormatter.prototype.getDefault = VertexGraphFormatter.prototype.getDefault; -SquareVertexGraphFormatter.prototype.getSelected = VertexGraphFormatter.prototype.getSelected; -SquareVertexGraphFormatter.prototype.getOver = VertexGraphFormatter.prototype.getOver; -SquareVertexGraphFormatter.prototype.getDragging = VertexGraphFormatter.prototype.getDragging; -SquareVertexGraphFormatter.prototype.getId = VertexGraphFormatter.prototype.getId; -SquareVertexGraphFormatter.prototype.toJSON = VertexGraphFormatter.prototype.toJSON; -SquareVertexGraphFormatter.prototype.loadFromJSON = VertexGraphFormatter.prototype.loadFromJSON; -SquareVertexGraphFormatter.prototype._setEvents = VertexGraphFormatter.prototype._setEvents; - -function SquareVertexGraphFormatter(id, defaultFormat, selectedFormat, overFormat, draggingFormat){ - VertexGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, draggingFormat); - this.args.type = "SquareVertexGraphFormatter"; - if (defaultFormat != null){ - if (defaultFormat.radius != null){ - this.defaultFormat.args.radius = defaultFormat.radius; - } - } - - if (selectedFormat != null){ - if (selectedFormat.radius != null){ - this.selected.args.radius = selectedFormat.radius; - } - } - - if (overFormat != null){ - if (overFormat.radius != null){ - this.over.args.radius = overFormat.radius; - } - } - - if (draggingFormat != null){ - if (draggingFormat.radius != null){ - this.dragging.args.draggingFormat = draggingFormat.radius; - } - } -}; - - -EllipseVertexGraphFormatter.prototype.getDefault = VertexGraphFormatter.prototype.getDefault; -EllipseVertexGraphFormatter.prototype.getSelected = VertexGraphFormatter.prototype.getSelected; -EllipseVertexGraphFormatter.prototype.getOver = VertexGraphFormatter.prototype.getOver; -EllipseVertexGraphFormatter.prototype.getDragging = VertexGraphFormatter.prototype.getDragging; -EllipseVertexGraphFormatter.prototype.getId = VertexGraphFormatter.prototype.getId; -EllipseVertexGraphFormatter.prototype.toJSON = VertexGraphFormatter.prototype.toJSON; -EllipseVertexGraphFormatter.prototype.loadFromJSON = VertexGraphFormatter.prototype.loadFromJSON; -EllipseVertexGraphFormatter.prototype._setEvents = VertexGraphFormatter.prototype._setEvents; - -function EllipseVertexGraphFormatter(id, defaultFormat, selectedFormat, overFormat, draggingFormat){ - VertexGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, draggingFormat); - this.args.type = "EllipseVertexGraphFormatter"; - if (defaultFormat != null){ - if (defaultFormat.radius != null){ - this.defaultFormat.args.radius = defaultFormat.radius; - } - } - - if (selectedFormat != null){ - if (selectedFormat.radius != null){ - this.selected.args.radius = selectedFormat.radius; - } - } - - if (overFormat != null){ - if (overFormat.radius != null){ - this.over.args.radius = overFormat.radius; - } - } - - if (draggingFormat != null){ - if (draggingFormat.radius != null){ - this.dragging.args.draggingFormat = draggingFormat.radius; - } - } -}; - - - -RectangleVertexGraphFormatter.prototype.getDefault = ItemGraphFormatter.prototype.getDefault; -RectangleVertexGraphFormatter.prototype.getSelected = ItemGraphFormatter.prototype.getSelected; -RectangleVertexGraphFormatter.prototype.getOver = ItemGraphFormatter.prototype.getOver; -RectangleVertexGraphFormatter.prototype.getDragging = ItemGraphFormatter.prototype.getDragging; -RectangleVertexGraphFormatter.prototype.getId = ItemGraphFormatter.prototype.getId; -RectangleVertexGraphFormatter.prototype.toJSON = ItemGraphFormatter.prototype.toJSON; -RectangleVertexGraphFormatter.prototype.loadFromJSON = ItemGraphFormatter.prototype.loadFromJSON; -RectangleVertexGraphFormatter.prototype._setEvents = ItemGraphFormatter.prototype._setEvents; - -function RectangleVertexGraphFormatter(id, defaultFormat, selectedFormat, overFormat, draggingFormat){ - VertexGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, draggingFormat); - this.args.type = "RectangleVertexGraphFormatter"; - if (defaultFormat != null){ - if (defaultFormat.radius != null){ - this.defaultFormat.args.radius = defaultFormat.radius; - } - } - - if (selectedFormat != null){ - if (selectedFormat.radius != null){ - this.selected.args.radius = selectedFormat.radius; - } - } - - if (overFormat != null){ - if (overFormat.radius != null){ - this.over.args.radius = overFormat.radius; - } - } - - if (draggingFormat != null){ - if (draggingFormat.radius != null){ - this.dragging.args.draggingFormat = draggingFormat.radius; - } - } -}; - - - -RoundedVertexGraphFormatter.prototype.getDefault = ItemGraphFormatter.prototype.getDefault; -RoundedVertexGraphFormatter.prototype.getSelected = ItemGraphFormatter.prototype.getSelected; -RoundedVertexGraphFormatter.prototype.getOver = ItemGraphFormatter.prototype.getOver; -RoundedVertexGraphFormatter.prototype.getDragging = ItemGraphFormatter.prototype.getDragging; -RoundedVertexGraphFormatter.prototype.getId = ItemGraphFormatter.prototype.getId; -RoundedVertexGraphFormatter.prototype.toJSON = ItemGraphFormatter.prototype.toJSON; -RoundedVertexGraphFormatter.prototype.loadFromJSON = ItemGraphFormatter.prototype.loadFromJSON; -RoundedVertexGraphFormatter.prototype._setEvents = ItemGraphFormatter.prototype._setEvents; - -function RoundedVertexGraphFormatter(id, defaultFormat, selectedFormat, overFormat, draggingFormat){ - VertexGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, draggingFormat); - this.args.type = "RoundedVertexGraphFormatter"; - if (defaultFormat != null){ - if (defaultFormat.radius != null){ - this.defaultFormat.args.radius = defaultFormat.radius; - } - } - - if (selectedFormat != null){ - if (selectedFormat.radius != null){ - this.selected.args.radius = selectedFormat.radius; - } - } - - if (overFormat != null){ - if (overFormat.radius != null){ - this.over.args.radius = overFormat.radius; - } - } - - if (draggingFormat != null){ - if (draggingFormat.radius != null){ - this.dragging.args.draggingFormat = draggingFormat.radius; - } - } -}; - - - -OctagonVertexGraphFormatter.prototype.getDefault = ItemGraphFormatter.prototype.getDefault; -OctagonVertexGraphFormatter.prototype.getSelected = ItemGraphFormatter.prototype.getSelected; -OctagonVertexGraphFormatter.prototype.getOver = ItemGraphFormatter.prototype.getOver; -OctagonVertexGraphFormatter.prototype.getDragging = ItemGraphFormatter.prototype.getDragging; -OctagonVertexGraphFormatter.prototype.getId = ItemGraphFormatter.prototype.getId; -OctagonVertexGraphFormatter.prototype.toJSON = ItemGraphFormatter.prototype.toJSON; -OctagonVertexGraphFormatter.prototype.loadFromJSON = ItemGraphFormatter.prototype.loadFromJSON; -OctagonVertexGraphFormatter.prototype._setEvents = ItemGraphFormatter.prototype._setEvents; - -function OctagonVertexGraphFormatter(id, defaultFormat, selectedFormat, overFormat, draggingFormat){ - VertexGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, draggingFormat); - this.args.type = "OctagonVertexGraphFormatter"; - if (defaultFormat != null){ - if (defaultFormat.radius != null){ - this.defaultFormat.args.radius = defaultFormat.radius; - } - } - - if (selectedFormat != null){ - if (selectedFormat.radius != null){ - this.selected.args.radius = selectedFormat.radius; - } - } - - if (overFormat != null){ - if (overFormat.radius != null){ - this.over.args.radius = overFormat.radius; - } - } - - if (draggingFormat != null){ - if (draggingFormat.radius != null){ - this.dragging.args.draggingFormat = draggingFormat.radius; - } - } -}; - - - -var Colors = new function() -{ - this.hashColor = []; - this.getColorByScoreArrayValue = function (arrayScore) - { - var array = new Array(); - - for (var i = 0; i< arrayScore.length; i++) - { - - var color = this.getColorByScoreValue(arrayScore[i]) - array.push( color); - - } - return array; - }; - - this.getHexStringByScoreArrayValue = function (arrayScore) - { - var arrayColor = this.getColorByScoreArrayValue(arrayScore); - var arrayHex = new Array(); - for (var i = 0; i< arrayColor.length; i++) - { - arrayHex.push( arrayColor[i].HexString()); - } - return arrayHex; - }; - - this.getColorByScoreValue = function (score) - { - - var truncate = score.toString().substr(0,4); - if (this.hashColor[truncate]!=null) - { - return this.hashColor[truncate]; - } - - - if(isNaN(score)) { - return Colors.ColorFromRGB(0,0,0); - } - var value; - - var from, to; - if(score < 0.5) { - from = Colors.ColorFromRGB(0,0,255); - to = Colors.ColorFromRGB(255,255,255); - value = (score * 2); - } else { - from = Colors.ColorFromRGB(255,255,255); - to = Colors.ColorFromRGB(255,0,0); - value = (score * 2) - 1; - } - - var x = value; - var y = 1.0 - value; - var color = Colors.ColorFromRGB(y * from.Red() + x * to.Red(), y * from.Green() + x * to.Green(), y * from.Blue() + x * to.Blue()); - - this.hashColor[truncate] = color; - - return color; - }; - - this.ColorFromHSV = function(hue, sat, val) - { - var color = new Color(); - color.SetHSV(hue,sat,val); - return color; - }; - - this.ColorFromRGB = function(r, g, b) - { - var color = new Color(); - color.SetRGB(r,g,b); - return color; - }; - - this.ColorFromHex = function(hexStr) - { - var color = new Color(); - color.SetHexString(hexStr); - return color; - }; - - function Color() { - //Stored as values between 0 and 1 - var red = 0; - var green = 0; - var blue = 0; - - //Stored as values between 0 and 360 - var hue = 0; - - //Strored as values between 0 and 1 - var saturation = 0; - var value = 0; - - this.SetRGB = function(r, g, b) - { - red = r/255.0; - green = g/255.0; - blue = b/255.0; - calculateHSV(); - }; - - this.Red = function() { return Math.round(red*255); }; - - this.Green = function() { return Math.round(green*255); }; - - this.Blue = function() { return Math.round(blue*255); }; - - this.SetHSV = function(h, s, v) - { - hue = h; - saturation = s; - value = v; - calculateRGB(); - }; - - this.Hue = function() - { return hue; }; - - this.Saturation = function() - { return saturation; }; - - this.Value = function() - { return value; }; - - this.SetHexString = function(hexString) - { - if(hexString == null || typeof(hexString) != "string") - { - this.SetRGB(0,0,0); - return; - } - - if (hexString.substr(0, 1) == '#') - hexString = hexString.substr(1); - - if(hexString.length != 6) - { - this.SetRGB(0,0,0); - return; - } - - var r = parseInt(hexString.substr(0, 2), 16); - var g = parseInt(hexString.substr(2, 2), 16); - var b = parseInt(hexString.substr(4, 2), 16); - if (isNaN(r) || isNaN(g) || isNaN(b)) - { - this.SetRGB(0,0,0); - return; - } - - this.SetRGB(r,g,b); - }; - - this.HexString = function() - { - - var rStr = this.Red().toString(16); - if (rStr.length == 1) - rStr = '0' + rStr; - var gStr = this.Green().toString(16); - if (gStr.length == 1) - gStr = '0' + gStr; - var bStr = this.Blue().toString(16); - if (bStr.length == 1) - bStr = '0' + bStr; - return ('#' + rStr + gStr + bStr).toUpperCase(); - }; - - this.Complement = function() - { - var newHue = (hue >= 180) ? hue - 180 : hue + 180; - var newVal = (value * (saturation - 1) + 1); - var newSat = (value*saturation) / newVal; - var newColor = new Color(); - newColor.SetHSV(newHue, newSat, newVal); - return newColor; - } ; - - function calculateHSV() - { - var max = Math.max(Math.max(red, green), blue); - var min = Math.min(Math.min(red, green), blue); - - value = max; - - saturation = 0; - if(max != 0) - saturation = 1 - min/max; - - hue = 0; - if(min == max) - return; - - var delta = (max - min); - if (red == max) - hue = (green - blue) / delta; - else if (green == max) - hue = 2 + ((blue - red) / delta); - else - hue = 4 + ((red - green) / delta); - hue = hue * 60; - if(hue < 0) - hue += 360; - } - - function calculateRGB() - { - red = value; - green = value; - blue = value; - - if(value == 0 || saturation == 0) - return; - - var tHue = (hue / 60); - var i = Math.floor(tHue); - var f = tHue - i; - var p = value * (1 - saturation); - var q = value * (1 - saturation * f); - var t = value * (1 - saturation * (1 - f)); - switch(i) - { - case 0: - red = value; green = t; blue = p; - break; - case 1: - red = q; green = value; blue = p; - break; - case 2: - red = p; green = value; blue = t; - break; - case 3: - red = p; green = q; blue = value; - break; - case 4: - red = t; green = p; blue = value; - break; - default: - red = value; green = p; blue = q; - break; - } - } - } -} -(); -/* - * Clase gestiona la insercción, eliminación de los elementos en el DOM - * - * Vital hacerla SIEMPRE compatible hacia atrás - * - * Last update: 28-10-2010 - * - */ - - -var DOM = {}; - -DOM.createNewElement = function(type, nodeParent, attributes) -{ - - var node = document.createElement(type); - for (var i=0; i0) - { - parent.removeChild(parent.childNodes[0]); - - } -}; - -DOM.select = function(targetID) -{ - return document.getElementById(targetID); -// return $("#"+targetID); -}; -var Geometry = -{ - - /** From tow points obtains the angles formed with the cartesian side **/ - getAngleBetweenTwoPoints : function(x1, y1, x2, y2){ - //var m = (y1 - y2)/ (x1 - x2); - return Math.atan2(y2-y1, x2-x1); - }, - - getAdjacentSideOfRectangleRight : function(angle, hypotenuse){ - return Math.abs(Math.cos(angle)*hypotenuse); - }, - - getOppositeSideOfRectangleRight : function(angle, hypotenuse){ - return Math.abs(Math.sin(angle)*hypotenuse); - }, - - toDegree: function(radian){ - return radian*180/Math.PI; - - } - - -}; - -var SVG = -{ - svgns : 'http://www.w3.org/2000/svg', - xlinkns : "http://www.w3.org/1999/xlink", - - createSVGCanvas: function(parentNode, attributes) - { - - attributes.push( ['xmlns', SVG.svgns], ['xmlns:xlink', 'http://www.w3.org/1999/xlink']); - var svg = document.createElementNS("http://www.w3.org/2000/svg", "svg"); - this._setProperties(svg, attributes); - parentNode.appendChild( svg); - return svg; - - }, - - createRectangle : function (x, y, width, height, attributes){ - var rect = document.createElementNS(this.svgns, "rect"); - rect.setAttribute("x",x); - rect.setAttribute("y",y); - rect.setAttribute("width",width); - rect.setAttribute("height",height); - SVG._setProperties(rect, attributes); - return rect; - }, - - drawImage64 : function (x, y, width, height, base64, svgNode, attributes) { - var node = SVG.createImage64(x, y, width, height, base64, attributes); - svgNode.appendChild(node); - return node; - }, - - createImage64 : function (x, y, width, height, base64, attributes) { - var img = document.createElementNS(this.svgns, "image"); - img.setAttribute("x",x); - img.setAttribute("y",y); - img.setAttribute("width",width); - img.setAttribute("height",height); - img.setAttribute("xlink:href",base64); - SVG._setProperties(img, attributes); - return img; - }, - - createLine: function (x1, y1, x2, y2, attributes){ - var line = document.createElementNS(this.svgns,"line"); - line.setAttribute("x1",x1); - line.setAttribute("y1",y1); - line.setAttribute("x2", x2); - line.setAttribute("y2", y2); - SVG._setProperties(line, attributes); - return line; - }, - - createClip: function (id, nodeToClip, attributes){ - var clip = document.createElementNS(this.svgns,"clipPath"); - clip.setAttribute("id",id); - clip.appendChild(nodeToClip); - return clip; - }, - - drawClip : function (id, nodeToClip, svgNode) { - var node = SVG.createClip(id, nodeToClip); - svgNode.appendChild(node); - return node; - }, - - drawRectangle : function (cx, cy, width, height, svgNode, attributes) { - try{ - var node = SVG.createRectangle(cx, cy, width, height, attributes); - svgNode.appendChild(node); - } - catch(e){ - - console.log("-------------------- "); - console.log("Error on drawRectangle " + e); - console.log(attributes); - console.log("-------------------- "); - } - return node; - }, - - createEllipse : function (x, y, rx, ry, attributes){ - var rect = document.createElementNS(this.svgns, "ellipse"); - rect.setAttribute("cx",x); - rect.setAttribute("cy",y); - rect.setAttribute("rx",rx); - rect.setAttribute("ry",ry); - SVG._setProperties(rect, attributes); - return rect; - }, - - drawEllipse : function (cx, cy, rx, ry, svgNode, attributes) { - var node = SVG.createEllipse(cx, cy, rx, ry, attributes); - svgNode.appendChild(node); - return node; - }, - - drawImage : function (x, y, canvasSVG, attributes) { - var image = document.createElementNS(this.svgns, "image"); - image.setAttribute("x",x); - image.setAttribute("y",y); - canvasSVG.appendChild(image); - SVG._setProperties(image, attributes); - }, - - drawLine : function (x1, y1, x2, y2, nodeSVG, attributes) { - try{ - var line = SVG.createLine(x1, y1, x2, y2, attributes); - nodeSVG.appendChild(line); - }catch(e){ - } - return line; - }, - - - drawPath: function (d, nodeSVG, attributes) { - var path = SVG.createPath(d, attributes); - nodeSVG.appendChild(path); - return path; - }, - - createPoligon : function (points, attributes){ - var poligon = document.createElementNS(this.svgns, "polygon"); - poligon.setAttribute("points",points); - SVG._setProperties(poligon, attributes); - return poligon; - }, - - drawPoligon : function (points, canvasSVG, attributes){ - var poligon = SVG.createPoligon(points, attributes); - canvasSVG.appendChild(poligon); - return poligon; - }, - // - - createPath : function (d, attributes){ - var path = document.createElementNS(this.svgns, "path"); - path.setAttribute("d",d); - SVG._setProperties(path, attributes); - return path; - }, - - drawCircle : function (x, y, radio, canvasSVG, attributes) { - - var newText = document.createElementNS(this.svgns,"circle"); - newText.setAttribute("cx",x); - newText.setAttribute("cy",y); - newText.setAttribute("r",radio); - - canvasSVG.appendChild(newText); - attributes["cursor"] = "pointer"; - this._setProperties(newText, attributes); - return newText; - }, - - - _setProperties: function(node, attributes) - { - if (attributes instanceof Array){ - for (var i=0; i< attributes.length; i++) - { - node.setAttribute(attributes[i][0], attributes[i][1]); - } - } - else{ - for ( var key in attributes){ - node.setAttribute(key, attributes[key]); - } - } - }, - -/* drawPath: function(pointsArray, canvasSVG, attributes){ - var path = document.createElementNS(this.svgns,"polyline"); - path.setAttribute ('id', id); - - var d= pointsArray[0].x+ " "+ pointsArray[0].y; - for (var i=1; i< pointsArray.length; i++) - { - d=d+" "+pointsArray[i].x+" "+pointsArray[i].y; - } - path.setAttribute ('points', d); - canvasSVG.appendChild(path); - },*/ - - createText : function (x, y, text, attributes) { - var node = document.createElementNS(this.svgns,"text"); - node.setAttributeNS(null , "x",x); - node.setAttributeNS(null, "y",y); - - var textNode = document.createTextNode(text); - node.appendChild(textNode); - - this._setProperties(node, attributes); - return node; - }, - - drawText : function (x, y, text, canvasSVG, attributes) { - var text = SVG.createText(x, y, text, attributes); - canvasSVG.appendChild(text); - return text; - }, - - - - drawGroup: function(svgNode, attributes) - { - var group = SVG.createGroup(attributes); - svgNode.appendChild(group); - return group; - }, - - createGroup: function(attributes){ - var group = document.createElementNS(this.svgns,"g"); - this._setProperties(group, attributes); - return group; - } - -}; - - - -var CanvasToSVG = { - - convert: function(sourceCanvas, targetSVG, x, y, id, attributes) { - - var img = this._convert(sourceCanvas, targetSVG, x, y, id); - - for (var i=0; i< attributes.length; i++) - { - img.setAttribute(attributes[i][0], attributes[i][1]); - } - }, - - _convert: function(sourceCanvas, targetSVG, x, y, id) { - var svgNS = "http://www.w3.org/2000/svg"; - var xlinkNS = "http://www.w3.org/1999/xlink"; - // get base64 encoded png from Canvas - var image = sourceCanvas.toDataURL(); - - // must be careful with the namespaces - var svgimg = document.createElementNS(svgNS, "image"); - - svgimg.setAttribute('id', id); - - //svgimg.setAttribute('class', class); - //svgimg.setAttribute('xlink:href', image); - svgimg.setAttributeNS(xlinkNS, 'xlink:href', image); - - - - - svgimg.setAttribute('x', x ? x : 0); - svgimg.setAttribute('y', y ? y : 0); - svgimg.setAttribute('width', sourceCanvas.width); - svgimg.setAttribute('height', sourceCanvas.height); - //svgimg.setAttribute('cursor', 'pointer'); - svgimg.imageData = image; - - targetSVG.appendChild(svgimg); - return svgimg; - }, - - importSVG: function(sourceSVG, targetCanvas) { - svg_xml = sourceSVG;//(new XMLSerializer()).serializeToString(sourceSVG); - var ctx = targetCanvas.getContext('2d'); - - var img = new Image(); - img.src = "data:image/svg+xml;base64," + btoa(svg_xml); -// img.onload = function() { - ctx.drawImage(img, 0, 0); -// }; - } - -}; -/* -Graph.prototype.importSVG = function(sourceSVG, targetCanvas) { - sourceSVG = this._svg; - targetCanvas = document.createElementNS('canvas'); - // https://developer.mozilla.org/en/XMLSerializer - svg_xml = (new XMLSerializer()).serializeToString(sourceSVG); - var ctx = targetCanvas.getContext('2d'); - // this is just a JavaScript (HTML) image - var img = new Image(); - // http://en.wikipedia.org/wiki/SVG#Native_support - // https://developer.mozilla.org/en/DOM/window.btoa - img.src = "data:image/svg+xml;base64," + btoa(svg_xml); - img.onload = function() { - // after this, Canvas’ origin-clean is DIRTY - ctx.drawImage(img, 0, 0); - } -}; -*/ - -/* - -Normalizacion de datos para dibujar colores -Issues: - No sé como debería llamarse esta libreria - No sé si ya existe una funciçon en javascript que lo haga - - -*/ - - -var Normalizer = new function() -{ - this.normalizeArray = function (arrayData) - { - - return this.standardizeArray(this.normal(arrayData)); - -// var result = this._getMaxAndMin(arrayData); -// var min =result[0]; -// var max = result[1]; -// -// -// //los hacemos todos positivos -// for (var i = 0; i< arrayData.length; i++) -// { -// arrayData[i]= Math.abs(min) + parseFloat(arrayData[i]); -// } -// -// var result = this._getMaxAndMin(arrayData); -// var min =result[0]; -// var max = result[1]; -// -// -// var resultArray = new Array(); -// for (var i = 0; i< arrayData.length; i++) -// { -// resultArray.push(arrayData[i]*1/max); -// } -// return resultArray; - }; - - this.normal = function(arrayData){ - var mean = this._getMean(arrayData); - var deviation = this._getStdDeviation(arrayData); - var result = this._getMaxAndMin(arrayData); - var min = result[0]; - var max = result[1]; - - var resultArray = new Array(); - for (var i = 0; i< arrayData.length; i++) { - if (deviation!=0){ - resultArray.push((arrayData[i]-mean)/deviation); - }else{ - resultArray.push(arrayData[i]); - } - } - return resultArray; - }; - - this.standardizeArray = function(arrayData) - { - var result = this._getMaxAndMin(arrayData); - var min = result[0]; - var max = result[1]; - - var offset = ( min <= 0 ) ? Math.abs(min) : (-1 * min); - var resultArray = new Array(); - for (var i = 0; i< arrayData.length; i++) { - if(max + offset!=0){ - resultArray.push((arrayData[i] + offset) / (max + offset)); - }else{ - resultArray.push(arrayData[i]+offset); - } - } - return resultArray; - }; - - - this._getMean = function(arrayData) { - var sum = 0; - for (var i = 0; i< arrayData.length; i++) { - sum = sum + parseFloat(arrayData[i]); - } - return sum/arrayData.length; - }; - - this._getStdDeviation = function(arrayData) { - var mean = this._getMean(arrayData); - var acum = 0.0; - for(var i=0; i max) max = parseFloat(arrayData[i]); - } - - return [min, max]; - }; -}; -function GraphCanvas(componentID, targetNode, args) { - this.args = {}; - /** target */ - this.targetID = targetNode.id; - - /** id manage */ - this.id = componentID; - this.args.idGraph = this.id + "main"; - this.args.idBackgroundNode = this.id + "background"; - - this.args.idEdgesGraph = this.id + "edges"; - this.args.idNodesGraph = this.id + "vertices"; - this.args.idLabelGraph = this.id + "label"; - this.args.idBackground = this.id + "background"; - - /** Objects Graph **/ - this.dataset = null; - this.formatter = null; - this.layout = null; - - /** Drawing **/ - this.circleDefaultRadius = 2; - this.squareDefaultSide = this.circleDefaultRadius * 1.5; - - /** Directed Arrow **/ - this.arrowDefaultSize = this.circleDefaultRadius; - - /** Groups **/ - this.GraphGroup = null; - this.GraphNodeGroup = null; - this.GraphLabelGroup = null; - this.GraphBackground = null; - - /** SETTINGS FLAGS **/ - this.args.draggingCanvasEnabled = false; //Flag to set if the canvas can be dragged - this.args.multipleSelectionEnabled = false; - this.args.interactive = false; - this.args.labeled = false; - this.args.linkEnabled = false; - - /** If numberEdge > maxNumberEdgesMoving then only it will move edges when mouse up **/ - this.args.maxNumberEdgesMoving = 3; - this.args.maxNumberEdgesFiringEvents = 50; - - /** Linking edges **/ - this.args.linking = false; - this.linkStartX = 0; - this.linkStartY = 0; - this.linkSVGNode = null; - this.linkNodeSource = null; - this.linkNodeTarget = null; - - /** Dragging Control **/ - this.draggingElement = null; - this.dragging = false; - this.nMouseOffsetX = 0; - this.nMouseOffsetY = 0; - this.dragStartX = 0; - this.dragStartY = 0; - this.desplazamientoX = 0; - this.desplazamientoY = 0; - - /** Selection Control **/ - this.selecting = false; - this.selectorX = null; - this.selectorY = null; - this.selectorSVGNode = null; - - /** Node status **/ - this.args.isVertexSelected = {}; - this.args.selectedVertices = []; - - /** Edges status **/ - this.args.isEdgeSelected = {}; - //this.args.selectedEdges = []; - - if (args != null) { - if (args.multipleSelectionEnabled != null) { - this.args.multipleSelectionEnabled = args.multipleSelectionEnabled; - this.args.draggingCanvasEnabled = !(this.args.multipleSelectionEnabled); - } - if (args.draggingCanvasEnabled != null) { - this.args.draggingCanvasEnabled = args.draggingCanvasEnabled; - this.args.multipleSelectionEnabled = !(this.args.draggingCanvasEnabled); - } - if (args.interactive != null) { - this.args.interactive = args.interactive; - } - if (args.labeled != null) { - this.args.labeled = args.labeled; - } - - } - - /** Hashmap with the svg node labels **/ - this.svgLabels = {}; - - /** EVENTS **/ - this.onVertexOut = new Event(this); - this.onVertexOver = new Event(this); - this.onVertexSelect = new Event(this); - this.onEdgeSelect = new Event(this); - this.onCanvasClicked = new Event(this); - this.onVertexUp = new Event(this); -} - -GraphCanvas.prototype.showLabels = function(value) { - this.args.labeled = value; - this.removeLabels(); - if (value) { - this.renderLabels(); - } -}; - -GraphCanvas.prototype.getSelectedVertices = function() { - return this.args.selectedVertices; -}; - -GraphCanvas.prototype.getSelectedEdges = function() { - var selected = []; - for ( var selectedEdge in this.args.isEdgeSelected) { - selected.push(selectedEdge); - } - return selected; -}; - -GraphCanvas.prototype.createSVGDom = function(targetID, id, width, height, backgroundColor) { - var container = document.getElementById(targetID); - this._svg = SVG.createSVGCanvas(container, [ - [ "style", "background-color:" + backgroundColor + ";" ], [ "id", id ], [ "dragx", 0 ], [ "dragy", 0 ], - [ "height", this.getFormatter().getHeight() ], [ "width", this.getFormatter().getWidth() ] ]); - return this._svg; -}; - -/** MULTIPLE SELECTION **/ -GraphCanvas.prototype.isMultipleSelectionEnabled = function() { - return this.args.multipleSelectionEnabled; -}; - -GraphCanvas.prototype.setMultipleSelection = function(value) { - this.args.multipleSelectionEnabled = value; - this.args.draggingCanvasEnabled = (!value); -}; - -GraphCanvas.prototype.setSelecting = function(value) { - this.selecting = value; -}; - -/** linking **/ -GraphCanvas.prototype.setLinking = function(value) { - this.args.linkEnabled = value; - this.selecting = !value; - this.dragging = !value; -}; - -/** CANVAS MOVING **/ -GraphCanvas.prototype.setDraggingCanvas = function(value) { - this.args.draggingCanvasEnabled = value; - this.args.multipleSelectionEnabled = !value; -}; - -GraphCanvas.prototype.isDraggingCanvasEnabled = function() { - return this.args.draggingCanvasEnabled; -}; -/** ZOOM **/ -GraphCanvas.prototype.getScale = function() { - return this.getFormatter().getZoomScale(); -}; - -GraphCanvas.prototype.setScale = function(scale) { - var graphNode = document.getElementById(this.args.idGraph); - graphNode.setAttribute("transform", graphNode.getAttribute("transform").replace("scale(" + this.getScale() + ")", "scale(" + scale + ")")); - this.getFormatter().setZoomScale(scale); -}; - -GraphCanvas.prototype.zoomIn = function() { - this.setScale(this.getScale() + this.getFormatter().getZoomScaleStepFactor()); -}; - -GraphCanvas.prototype.zoomOut = function() { - this.setScale(this.getScale() - this.getFormatter().getZoomScaleStepFactor()); - -}; - -/** SVG COORDENATES **/ -GraphCanvas.prototype.getSVGCoordenates = function(evt) { - var p = this._svg.createSVGPoint(); - p.x = evt.clientX; - p.y = evt.clientY; - - var m = this._svg.getScreenCTM(document.documentElement); - p = p.matrixTransform(m.inverse()); - return p; -}; - -/** SVG EVENTS **/ -GraphCanvas.prototype.mouseClick = function(event) { - if (event.button == 0) { - if (!this.args.interactive) { - return; - } - - if (this.isVertex(event.target)) { - this.clickNode(this.getVertexIdFromSVGId(event.target.id)); - } - /** Como el evento mouseClick viene despues del mouse up es aqui donde manejo el tema de deseccionar los elementos que estoy dragging **/ - if (this.dragging) { - this.dragging = false; - } - } -}; - -GraphCanvas.prototype.mouseMove = function(evt) { - if (this.selecting) { - this.clearLabels(); - - var width = (this.getSVGCoordenates(evt).x - this.selectorX); - var height = (this.getSVGCoordenates(evt).y - this.selectorY); - if ((width > 0) && (height > 0)) { - this.displaySelection(this.selectorX, this.selectorY, width, height); - } - if ((width > 0) && (height < 0)) { - this.displaySelection(this.selectorX, this.getSVGCoordenates(evt).y, width, Math.abs(height)); - } - if ((width < 0) && (height < 0)) { - this.displaySelection(this.getSVGCoordenates(evt).x, this.getSVGCoordenates(evt).y, Math.abs(width), Math.abs(height)); - } - if ((width < 0) && (height > 0)) { - this.displaySelection(this.selectorX + width, this.selectorY, Math.abs(width), Math.abs(height)); - } - - var x1 = (parseFloat(this.selectorSVGNode.getAttribute("x")) - DOM.select(this.id).getAttribute("dragx")) / this.getFormatter().getWidth(); - var y1 = (parseFloat(this.selectorSVGNode.getAttribute("y")) - DOM.select(this.id).getAttribute("dragy")) / this.getFormatter().getHeight(); - var x2 = (x1 + parseFloat(this.selectorSVGNode.getAttribute("width") / this.getFormatter().getWidth())); - var y2 = (y1 + parseFloat(this.selectorSVGNode.getAttribute("height") / this.getFormatter().getHeight())); - - this.deselectNodes(this.getLayout()); - var verticesSelected = this.getLayout().getVerticesByArea(x1 / this.getFormatter().getZoomScale(), y1 / this.getFormatter().getZoomScale(), - x2 / this.getFormatter().getZoomScale(), y2 / this.getFormatter().getZoomScale()); - for ( var i = 0; i < verticesSelected.length; i++) { - this.selectNode(verticesSelected[i].getId()); - this.renderLabel(verticesSelected[i].getId()); - } - - } - var p = null; - if (this.args.linking) { - p = this.getSVGCoordenates(evt); - if (this.linkSVGNode != null) { - this.linkSVGNode.setAttribute("x2", p.x - 2); - this.linkSVGNode.setAttribute("y2", p.y - 2); - } - } - - if (this.dragging) { - p = this.getSVGCoordenates(evt); - p.x -= this.nMouseOffsetX; - p.y -= this.nMouseOffsetY; - this.desplazamientoX = (this.getSVGCoordenates(evt).x - this.dragStartX);// + parseFloat(DOM.select(this.id).getAttribute("dragx")); - this.desplazamientoY = (this.getSVGCoordenates(evt).y - this.dragStartY);// + parseFloat(DOM.select(this.id).getAttribute("dragy")); - - if (this.draggingElement != null) { - /** Click sobre el recct del banground que provoca que mueva todo el canvas **/ - if (this.isNodeCanvas(this.draggingElement)) { - - p = this.getSVGCoordenates(evt); - p.x = this.desplazamientoX; - p.y = this.desplazamientoY; - - this.draggingElement.setAttribute("dragx", p.x); - this.draggingElement.setAttribute("dragy", p.y); - this.draggingElement = document.getElementById(this.args.idGraph); - this.draggingElement.setAttribute("transform", "translate(" + p.x + "," + p.y + "), scale(" + this.getScale() + ")"); - - DOM.select(this.id).setAttribute("dragx", p.x); - DOM.select(this.id).setAttribute("dragy", p.y); - - if (this.NodeSVGbackgroundImage != null) { - this.NodeSVGbackgroundImage.setAttribute("dragx", p.x); - this.NodeSVGbackgroundImage.setAttribute("dragy", p.y); - } - } else { - if (this.isVertex(this.draggingElement)) { - this.selectNode(this.getVertexIdFromSVGId(this.draggingElement.id)); - this.desplazamientoX = this.desplazamientoX / this.getFormatter().getZoomScale(); - this.desplazamientoY = this.desplazamientoY / this.getFormatter().getZoomScale(); - this.moveSelectedNodes(this.desplazamientoX, this.desplazamientoY); - - this.dragStartX = this.getSVGCoordenates(evt).x; - this.dragStartY = this.getSVGCoordenates(evt).y; - } else { - if (this.isNodeBackground(this.draggingElement)) { - - this.draggingElement.setAttribute("dragx", p.x); - this.draggingElement.setAttribute("dragy", p.y); - this.draggingElement = document.getElementById(this.args.idGraph); - this.draggingElement.setAttribute("transform", "translate(" + p.x + "," + p.y + "), scale(" + this.getScale() + ")"); - } else { - this.draggingElement.setAttribute("transform", "translate(" + p.x + "," + p.y + ")"); - } - } - } - } - } -}; - -GraphCanvas.prototype.moveSelectedNodes = function(offsetX, offsetY) { - for ( var i = 0; i < this.getSelectedVertices().length; i++) { - - var nodeId = this.getSelectedVertices()[i]; - var svgNodeId = this.getSVGNodeId(nodeId); - - var x = parseFloat(DOM.select(svgNodeId).getAttribute("dragx")) + parseFloat(offsetX);// - parseFloat(DOM.select(this.id).getAttribute("dragx")); - var y = parseFloat(DOM.select(svgNodeId).getAttribute("dragy")) + parseFloat(offsetY);// + parseFloat(DOM.select(this.id).getAttribute("dragy")); - - this._movingNode(DOM.select(svgNodeId), x, y); - } -}; - -GraphCanvas.prototype.mouseDown = function(evt) { - if (event.button == 0) { - - /** if !no interactive mouse events do anything **/ - if (!this.args.interactive) { - return; - } - - var p = this.getSVGCoordenates(evt); - - /** When click on canvas or background deselect all **/ - if (this.isNodeCanvas(evt.target) || this.isNodeBackground(evt.target)) { - this.deselectNodes(); - this.deselectEdges(); - this.onCanvasClicked.notify(); - } - - /** if I am linking vertices **/ - if (this.args.linkEnabled) { - - if (!this.args.linking) { - this.args.linking = true; - if (this.isVertex(evt.target)) { - this.linkStartX = p.x; - this.linkStartY = p.y; - this.linkSVGNode = SVG.drawLine(p.x, p.y, p.x, p.y, this._svg, { - "stroke" : "#FF0000" - }); - this.linkNodeSource = this.getVertexIdFromSVGId(evt.target.id); - } - } else { - this.linkNodeTarget = this.getVertexIdFromSVGId(evt.target.id); - this.args.linking = false; - this.args.linkEnabled = false; - if (this.isVertex(evt.target)) { - this.getDataset().addEdge(this.linkNodeSource + "_" + this.linkNodeTarget, this.linkNodeSource, this.linkNodeTarget, {}); - } - this.linkSVGNode.parentNode.removeChild(this.linkSVGNode); - } - return; - } - - /** Id is a vertex or the canvas **/ - if (this.isVertex(evt.target) || this.isNodeCanvas(evt.target) || this.isNodeBackground(evt.target)) { - this._startDragging(evt); - } - /** if i is edge **/ - if (this.isEdge(evt.target)) { - this.selectEdge(this.getEdgeIdFromSVGId(evt.target.getAttribute("id"))); - } - - if (this.args.multipleSelectionEnabled) { - if (!this.dragging) { - this.setSelecting(true); - this.selectorX = p.x; - this.selectorY = p.y; - this.displaySelection(p.x, p.y, 1, 1); - } - } - - } - if (event.button == 1) { - this.setLinking(false); - this.setMultipleSelection(false); - this.selecting = false; - - /** Id is a vertex or the canvas **/ - if (this.isVertex(evt.target) || this.isNodeCanvas(evt.target) || this.isNodeBackground(evt.target)) { - this._startDragging(evt); - } - } -}; - -GraphCanvas.prototype.mouseUp = function(event) { - if (!this.args.interactive) { - return; - } - - if (this.dragging) { - this._stopDragging(event); - if (this.isVertex(event.target)) { - var vertexId = this.getVertexIdFromSVGId(event.target.id); - if (this.getDataset().getVertexById(vertexId).getEdges().length >= this.args.maxNumberEdgesMoving) { - this.moveEdge(vertexId); - } - } - } - - if (this.selecting) { - this.setSelecting(false); - - var x1 = (parseFloat(this.selectorSVGNode.getAttribute("x")) - DOM.select(this.id).getAttribute("dragx")) / this.getFormatter().getWidth(); - var y1 = (parseFloat(this.selectorSVGNode.getAttribute("y")) - DOM.select(this.id).getAttribute("dragy")) / this.getFormatter().getHeight(); - var x2 = (x1 + parseFloat(this.selectorSVGNode.getAttribute("width") / this.formatter.getWidth())); - var y2 = (y1 + parseFloat(this.selectorSVGNode.getAttribute("height") / this.formatter.getHeight())); - - var verticesSelected = this.getLayout().getVerticesByArea(x1 / this.getFormatter().getZoomScale, y1 / this.getFormatter().getZoomScale, - x2 / this.getFormatter().getZoomScale, y2 / this.getFormatter().getZoomScale); - - for ( var i = 0; i < verticesSelected.length; i++) { - this.selectNode(verticesSelected[i].getId()); - } - - if (this.selectorSVGNode != null) { - this._svg.removeChild(this.selectorSVGNode); - } - - if (this.args.labeled) { - this.clearLabels(); - this.renderLabels(); - } - - this.selectorSVGNode = null; - // this.renderLabels(); - } -}; - -/** SELECTION **/ -GraphCanvas.prototype.displaySelection = function(x, y, width, height) { - if (this.selectorSVGNode != null) { - this.selectorSVGNode.setAttribute("x", x); - this.selectorSVGNode.setAttribute("y", y); - this.selectorSVGNode.setAttribute("width", width); - this.selectorSVGNode.setAttribute("height", height); - } else { - this.selectorSVGNode = SVG.drawRectangle(x, y, width, height, this._svg, { - "fill" : "red", - "stroke" : "black", - "opacity" : "0.2", - "stroke-opacity" : "1" - }); - } -}; - -/** DRAGGING **/ -GraphCanvas.prototype._startDragging = function(evt) { - if (!this.isDraggingCanvasEnabled()) { - if (this.isNodeCanvas(evt.target)) { - this.draggingElement = null; - } - } - - if (this.isVertex(evt.target) || (this.isNodeBackground(evt.target) && (this.isDraggingCanvasEnabled()))|| (this.isNodeCanvas(evt.target) && (this.isDraggingCanvasEnabled()))) { - this.clearLabels(); - this.draggingElement = evt.target; - this.dragging = true; - var p = this.getSVGCoordenates(evt); - - this.nMouseOffsetX = p.x - parseInt(evt.target.getAttribute("dragx")); - this.nMouseOffsetY = p.y - parseInt(evt.target.getAttribute("dragy")); - - if (this.isVertex(evt.target)) { - this.dragStartX = parseInt(this.draggingElement.getAttribute("dragx")) * this.getFormatter().getZoomScale() - + parseFloat(DOM.select(this.id).getAttribute("dragx")); - this.dragStartY = parseInt(this.draggingElement.getAttribute("dragy")) * this.getFormatter().getZoomScale() - + parseFloat(DOM.select(this.id).getAttribute("dragy")); - } else { - this.dragStartX = p.x - parseInt(this.draggingElement.getAttribute("dragx"));// + parseFloat(DOM.select(this.id).getAttribute("dragx")); - this.dragStartY = p.y - parseInt(this.draggingElement.getAttribute("dragy"));// + parseFloat(DOM.select(this.id).getAttribute("dragy")); - } - } -}; - -GraphCanvas.prototype._stopDragging = function(event) { - this.nMouseOffsetX = 0; - this.nMouseOffsetX = 0; - /** despues del evento up viene el evento click entonces acabo el dragging en el mouseclick **/ - this.dragging = false; - this.draggingElement = null; - this.renderLabels(); - - this.setLinking(false); - this.setMultipleSelection(true); - this.selecting = false; - -}; - -/** Move the edges of the vertex with the vertexId indicado **/ -GraphCanvas.prototype.moveEdge = function(vertexId) { - var x = this.getLayout().getNodeById(vertexId).x * this.getFormatter().getWidth(); - var y = this.getLayout().getNodeById(vertexId).y * this.getFormatter().getHeight(); - - /** Moving edges out **/ - for ( var i = 0; i < this.getDataset().getVertexById(vertexId).getEdgesOut().length; i++) { - var edgeId = this.getDataset().getVertexById(vertexId).getEdgesOut()[i].getId(); - var svgEdgeId = this.getSVGEdgeId(edgeId); - var edgeFormatter = this.getFormatter().getEdgeById(edgeId); - if (edgeFormatter instanceof LineEdgeGraphFormatter) { - DOM.select(svgEdgeId + "_shadow").setAttribute("x2", x); - DOM.select(svgEdgeId + "_shadow").setAttribute("y2", y); - DOM.select(svgEdgeId).setAttribute("x2", x); - DOM.select(svgEdgeId).setAttribute("y2", y); - } - - if ((edgeFormatter instanceof DirectedLineEdgeGraphFormatter) || (edgeFormatter instanceof OdirectedLineEdgeGraphFormatter) - || (edgeFormatter instanceof OdotDirectedLineEdgeGraphFormatter) || (edgeFormatter instanceof DotDirectedLineEdgeGraphFormatter) - || (edgeFormatter instanceof CutDirectedLineEdgeGraphFormatter)) { - this.removeEdge(edgeId); - this.renderEdge(edgeId); - } - } - - /** Moving edges in **/ - for ( var i = 0; i < this.getDataset().getVertexById(vertexId).getEdgesIn().length; i++) { - var edgeId = this.getDataset().getVertexById(vertexId).getEdgesIn()[i].getId(); - var svgEdgeId = this.getSVGEdgeId(edgeId); - var edgeFormatter = this.getFormatter().getEdgeById(edgeId); - if (edgeFormatter instanceof LineEdgeGraphFormatter) { - DOM.select(svgEdgeId).setAttribute("x1", x); - DOM.select(svgEdgeId).setAttribute("y1", y); - DOM.select(svgEdgeId + "_shadow").setAttribute("x1", x); - DOM.select(svgEdgeId + "_shadow").setAttribute("y1", y); - } - - if ((edgeFormatter instanceof DirectedLineEdgeGraphFormatter) || (edgeFormatter instanceof OdirectedLineEdgeGraphFormatter) - || (edgeFormatter instanceof OdotDirectedLineEdgeGraphFormatter) || (edgeFormatter instanceof DotDirectedLineEdgeGraphFormatter) - || (edgeFormatter instanceof CutDirectedLineEdgeGraphFormatter)) { - this.removeEdge(edgeId); - this.renderEdge(edgeId); - } - - if (edgeFormatter instanceof BezierEdgeGraphFormatter) { - var radius = this.getFormatter().getVertexById(vertexId).getDefault().getSize() * this.getFormatter().getNodesMaxSize(); - var d = this.calculateCoordenatesBezier(radius, x, y); - DOM.select(svgEdgeId).setAttribute("d", d); - } - } -}; - -GraphCanvas.prototype.moveNode = function(vertexId) { - var x = this.getLayout().getNodeById(vertexId).x * this.getFormatter().getWidth(); - var y = this.getLayout().getNodeById(vertexId).y * this.getFormatter().getHeight(); - var svgNodeElement = DOM.select(this.getSVGNodeId(vertexId)); - - svgNodeElement.setAttribute("dragx", x); - svgNodeElement.setAttribute("dragy", y); - svgNodeElement.setAttribute("transform", "translate(" + x + "," + y + ")"); - - if (this.getDataset().getVertexById(vertexId).getEdges().length < this.args.maxNumberEdgesMoving) { - this.moveEdge(vertexId); - } -}; - -GraphCanvas.prototype._movingNode = function(svgNodeElement, x, y) { - var vertexId = this.getVertexIdFromSVGId(svgNodeElement.getAttribute("id")); - this.getLayout().getNodeById(vertexId).setCoordinates(x / this.getFormatter().getWidth(), y / this.getFormatter().getHeight()); - this.desplazamientoX = 0; - this.desplazamientoY = 0; - this.removeLabel(vertexId); - this.renderLabel(vertexId); -}; - -/** INIT **/ -GraphCanvas.prototype.init = function() { - - this._svg = this.createSVGDom(this.targetID, this.id, this.getFormatter().getWidth(), this.getFormatter().getHeight(), this.getFormatter() - .getBackgroundColor()); - this.GraphGroup = SVG.drawGroup(this._svg, [ [ "id", this.args.idGraph ], [ "transform", "translate(0,0), scale(1)" ] ]); - this.GraphBackground = SVG.drawGroup(this.GraphGroup, [ [ "id", this.args.idBackground ] ]); - this.GraphEdgeGroup = SVG.drawGroup(this.GraphGroup, [ [ "id", this.args.idEdgesGraph ] ]); - this.GraphNodeGroup = SVG.drawGroup(this.GraphGroup, [ [ "id", this.args.idNodesGraph ] ]); - this.GraphLabelGroup = SVG.drawGroup(this.GraphGroup, [ [ "id", this.args.idLabelGraph ] ]); - - if ((this.getFormatter().getBackgroundImage() != null) && (this.getFormatter().getBackgroundImage() != "")) { - this.setBackgroundImage(this.getFormatter().getBackgroundImage()); - } - /** SVG Events listener */ - var _this = this; - this._svg.addEventListener("click", function(event) { - _this.mouseClick(event); - }, false); - this._svg.addEventListener("mousemove", function(event) { - _this.mouseMove(event, _this); - }, false); - this._svg.addEventListener("mousedown", function(event) { - _this.mouseDown(event, _this); - }, false); - this._svg.addEventListener("mouseup", function(event) { - _this.mouseUp(event, _this); - }, false); -}; - -/* - GraphCanvas.prototype.backgroungToSVG = function(){ - var _this = this; - var canvas = document.createElement('canvas'); - canvas.setAttribute("id", "canvas"); - canvas.width = this.formatter.getWidth(); - canvas.height = this.formatter.getHeight(); - - this._svg.parentNode.parentNode.appendChild(canvas); - var ctx = document.getElementById('canvas').getContext('2d'); - var img = new Image(); - - img.src = this.formatter.getBackgroundImage(); - ctx.drawImage(img,0,0 ,_this.formatter.getWidth(), _this.formatter.getHeight()); - - - img.onload = function() { - canvas.parentNode.removeChild(canvas); - } - - this.NodeSVGbackgroundImage.setAttribute("xlink:href", document.getElementById("canvas").toDataURL()); - this.NodeSVGbackgroundImage.removeAttribute("href"); - - // - - };*/ - -GraphCanvas.prototype.setBackgroundImage = function() { - if (this.NodeSVGbackgroundImage != null) { - this.NodeSVGbackgroundImage.parentNode.removeChild(this.NodeSVGbackgroundImage); - } - $('#' + this.targetID).svg(); - $('#' + this.targetID).svg("get"); - - $('#' + this.targetID).svg("get")._svg = document.getElementById(this.id); - - var svg = $('#' + this.targetID).svg("get"); - this.NodeSVGbackgroundImage = svg.image(0, 0, this.getFormatter().getWidth(), this.getFormatter().getHeight(), this.getFormatter() - .getBackgroundImage()); - this.NodeSVGbackgroundImage.setAttribute("id", this.args.idBackgroundNode); - - this.NodeSVGbackgroundImage.setAttribute("x", 0); - this.NodeSVGbackgroundImage.setAttribute("y", 0); - - this.NodeSVGbackgroundImage.setAttribute("dragx", 0); - this.NodeSVGbackgroundImage.setAttribute("dragy", 0); - - if (this.getFormatter().args.backgroundImageHeight != null) { - this.NodeSVGbackgroundImage.setAttribute("height", this.getFormatter().args.backgroundImageHeight); - } - if (this.getFormatter().args.backgroundImageWidth != null) { - this.NodeSVGbackgroundImage.setAttribute("width", this.getFormatter().args.backgroundImageWidth); - } - - if (this.getFormatter().args.backgroundImageX != null) { - this.NodeSVGbackgroundImage.setAttribute("x", this.getFormatter().args.backgroundImageX); - } - if (this.getFormatter().args.backgroundImageY != null) { - this.NodeSVGbackgroundImage.setAttribute("y", this.getFormatter().args.backgroundImageY); - } - - this.GraphBackground.appendChild(this.NodeSVGbackgroundImage); - this.NodeSVGbackgroundImage.removeAttribute("href"); - this.NodeSVGbackgroundImage.setAttribute("xlink:href", this.getFormatter().getBackgroundImage()); -}; - -GraphCanvas.prototype.removeBackgroundImage = function() { - if (this.NodeSVGbackgroundImage != null) { - this.NodeSVGbackgroundImage.parentNode.removeChild(this.NodeSVGbackgroundImage); - } -}; - -GraphCanvas.prototype._setBackgroundColor = function(color) { - var attributes = [ [ "fill", color ] ]; - SVG.drawRectangle(0, 0, this.getFormatter().getWidth(), this.getFormatter().getHeight(), this.GraphBackground, attributes); -}; - -/** Serialize **/ -GraphCanvas.prototype.toJSON = function() { - var json = {}; - json.dataset = {}; - json.formatter = {}; - json.layout = {}; - json.dataset = this.getDataset().toJSON(); - json.formatter = this.getFormatter().toJSON(); - json.layout = this.getLayout().toJSON(); - return json; -}; - -GraphCanvas.prototype.toHTML = function() { - //this.backgroungToSVG(); - var html = this._svg.parentElement.innerHTML; - - var start = html.indexOf("") + 6; - - return html.substr(start, end); -}; - -/** DRAW **/ -GraphCanvas.prototype.draw = function(graphdataset, graphformatter, graphlayout) { - this.setDataset(graphdataset); - this.setFormatter(graphformatter); - this.setLayout(graphlayout); - - var _this = this; - this.getFormatter().changed.attach(function(sender, item) { - _this.removeNode(item.getId()); - _this.renderNode(item.getId()); - if (_this.args.labeled) { - _this.removeLabel(item.getId()); - _this.renderLabel(item.getId()); - } - - }); - //TODO - this.getFormatter().edgeChanged.attach(function(sender, item) { - _this.removeEdge(item.getId()); - _this.renderEdge(item.getId()); - }); - - this.getFormatter().resized.attach(function(sender, item) { - _this.resize(_this.getFormatter().getWidth(), _this.getFormatter().getHeight()); - }); - - this.getFormatter().backgroundImageChanged.attach(function(sender, item) { - _this.setBackgroundImage(_this.getFormatter().getBackgroundImage()); - }); - - this.getFormatter().backgroundColorChanged.attach(function(sender, item) { - _this._setBackgroundColor(_this.getFormatter().getBackgroundColor()); - }); - - this.getLayout().changed.attach(function(sender, item) { - _this.moveNode(item.getId()); - _this.moveEdge(item.getId()); - if (_this.args.labeled) { - _this.removeLabel(item.getId()); - _this.renderLabel(item.getId()); - } - }); - - this.getDataset().newVertex.attach(function(sender, item) { - - _this.renderNode(item.getId()); - if (_this.args.labeled) { - _this.renderLabel(item.getId()); - } - }); - - this.getDataset().newEdge.attach(function(sender, item) { - _this.renderEdge(item.getId()); - }); - - this.getDataset().vertexDeleted.attach(function(sender, item) { - _this.removeNode(item.getId()); - if (_this.args.labeled) { - _this.removeLabel(item.getId()); - } - }); - - this.getDataset().edgeDeleted.attach(function(sender, item) { - _this.removeEdge(item.getId()); - }); - - this.getDataset().vertexNameChanged.attach(function(sender, args) { - if (_this.args.labeled) { - _this.removeLabel(args.item.getId()); - _this.removeLabel(args.item.getId()); - _this.renderLabel(args.item.getId()); - } - }); - this.init(); - this.render(); -}; - -GraphCanvas.prototype.render = function() { - for ( var id in this.getDataset().getVertices()) { - this.renderNode(id); - } - this.renderLabels(); - this.renderEdges(); -}; - -GraphCanvas.prototype.renderLabels = function() { - if (this.args.labeled) { - for ( var id in this.getDataset().getVertices()) { - this.renderLabel(id); - } - } -}; - -GraphCanvas.prototype.removeLabels = function() { - for ( var id in this.getDataset().getVertices()) { - this.removeLabel(id); - } -}; - -/** Utilities method for nodes **/ -GraphCanvas.prototype.isNodeCanvas = function(node) { - return ((node.id == this.args.idGraph) || (node.id == this.id)); -}; - -GraphCanvas.prototype.isNodeBackground = function(node) { - return ((node.id == this.args.idBackgroundNode)); -}; - -GraphCanvas.prototype.isVertex = function(node) { - if (node.getAttribute("id") != null) { - if (node.getAttribute("id").indexOf("_v_") != -1) { - return true; - } - } - return false; -}; - -GraphCanvas.prototype.isLabel = function(node) { - if (node.getAttribute("id") != null) { - if (node.getAttribute("id").indexOf("_l_") != -1) { - return true; - } - } - return false; -}; - -GraphCanvas.prototype.isEdge = function(node) { - if (node.getAttribute("id") != null) { - if (node.getAttribute("id").indexOf("_e_") != -1) { - return true; - } - } - return false; -}; - -/** Resize **/ -GraphCanvas.prototype.resize = function(width, height) { - // this._svg.setAttribute("width", width); - // this._svg.setAttribute("height", height); - if (this.NodeSVGbackgroundImage != null) { - this.NodeSVGbackgroundImage.setAttribute("width", width); - this.NodeSVGbackgroundImage.setAttribute("height", height); - } - - this._svg.setAttribute("width", width); - this._svg.setAttribute("height", height); - - this.clearCanvas(); - this.render(); -}; - -GraphCanvas.prototype.clearCanvas = function() { - DOM.removeChilds(this.GraphEdgeGroup.getAttribute("id")); - DOM.removeChilds(this.GraphNodeGroup.getAttribute("id")); - this.clearLabels(); -}; - -GraphCanvas.prototype.clearLabels = function() { - DOM.removeChilds(this.GraphLabelGroup.getAttribute("id")); -}; - -/** ID'S converter **/ -GraphCanvas.prototype.getSVGNodeId = function(nodeId) { - return this.id + "_v_" + nodeId; -}; - -GraphCanvas.prototype.getSVGEdgeId = function(edgeId) { - return this.id + "_e_" + edgeId; -}; - -GraphCanvas.prototype.getSVGArrowEdgeId = function(edgeId) { - return this.id + "_arrow_" + edgeId; -}; - -GraphCanvas.prototype.getSVGLabelId = function(edgeId) { - return this.id + "_l_" + edgeId; -}; - -GraphCanvas.prototype.blinkVertexById = function(vertexId) { - $("#" + this.getSVGNodeId(vertexId)).fadeIn().fadeOut().fadeIn().fadeOut().fadeIn().fadeOut(); -}; - -GraphCanvas.prototype.getVertexIdFromSVGId = function(svgVertexId) { - return svgVertexId.replace(this.id, "").replace("_v_", ""); -}; - -GraphCanvas.prototype.getEdgeIdFromSVGId = function(svgEdgeId) { - return svgEdgeId.replace(this.id, "").replace("_e_", ""); -}; - -/** VERTEX **/ -GraphCanvas.prototype.getVertexById = function(id) { - return document.getElementById(this.getSVGNodeId(id)); -}; - -GraphCanvas.prototype.renderNodes = function() { - for ( var id in this.getDataset().getVertices()) { - this.renderNode(id); - } -}; - -GraphCanvas.prototype.overNode = function(nodeId) { - if (!this.args.interactive) { - return; - } - /** If selected we don't change the format **/ - if (this.args.isVertexSelected[nodeId] == null) { - var args = this.getFormatter().getVertexById(nodeId).getOver(); - args.args["cursor"] = 'pointer'; - this.changeVertexFormat(nodeId, args); - } -}; - -GraphCanvas.prototype.outNode = function(nodeId) { - if (!this.args.interactive) { - return; - } - - /** If selected we don't change the format **/ - if (this.args.isVertexSelected[nodeId] == null) { - this.changeVertexFormat(nodeId, this.getFormatter().getVertexById(nodeId).getDefault()); - } -}; - -GraphCanvas.prototype.overLabel = function(nodeId) { - this.overNode(nodeId); - // this.svgLabels[nodeId].setAttribute("cursor", "pointer"); -}; - -GraphCanvas.prototype.outLabel = function(nodeId) { - this.outNode(nodeId); - // this.svgLabels[nodeId].setAttribute("cursor", ""); -}; - -GraphCanvas.prototype.clickNode = function(nodeId) { - if (!this.args.interactive) { - return; - } - - /** si el evento se dispara oprque estaba dragging entonces no activo nada **/ - if (this.args.isVertexSelected[nodeId] == null) { - this.selectNode(nodeId); - } else { - this.deselectNode(nodeId); - } -}; - -GraphCanvas.prototype.selectNode = function(nodeId) { - for ( var i = 0; i < this.args.selectedVertices.length; i++) { - var format = this.getFormatter().getVertexById(nodeId).getSelected(); - format.opacity = 0.2; - this.changeVertexFormat(nodeId, this.getFormatter().getVertexById(nodeId).getSelected()); - } - - if (this.args.isVertexSelected[nodeId] == null) { - var format = this.getFormatter().getVertexById(nodeId).getSelected(); - format.opacity = 1; - this.changeVertexFormat(nodeId, this.getFormatter().getVertexById(nodeId).getSelected()); - this.args.selectedVertices.push(nodeId); - this.args.isVertexSelected[nodeId] = this.args.selectedVertices.length - 1; - this.onVertexSelect.notify(nodeId); - } -}; - -GraphCanvas.prototype.selectAllEdges = function() { - this.deselectNodes(); - this.deselectEdges(); - - for ( var edgesId in this.getDataset().edges) { - this.selectEdge(edgesId); - } -}; - -GraphCanvas.prototype.selectAllNodes = function() { - this.deselectNodes(); - this.deselectEdges(); - - for ( var vertexId in this.getDataset().vertices) { - this.selectNode(vertexId); - } -}; - -GraphCanvas.prototype.selectAll = function() { - this.deselectNodes(); - this.deselectEdges(); - - for ( var vertexId in this.getDataset().vertices) { - this.selectNode(vertexId); - } - - for ( var edgesId in this.getDataset().edges) { - this.selectEdge(edgesId); - } -}; - -GraphCanvas.prototype.selectEdge = function(edgeId) { - if (this.args.isEdgeSelected[edgeId] == null) { - this.changeEdgeFormat(edgeId, this.getFormatter().getEdgeById(edgeId).getSelected()); - //this.args.selectedEdges.push(edgeId); - this.args.isEdgeSelected[edgeId] = true; //this.args.selectedEdges.length - 1; - this.onEdgeSelect.notify(edgeId); - } -}; - -GraphCanvas.prototype.selectEdges = function(edges) { - - for ( var i = 0; i < edges.length; i++) { - this.selectEdge(edges[i]); - } -}; - -GraphCanvas.prototype.deselectNode = function(nodeId) { - if (this.args.isVertexSelected[nodeId] != null) { - this.changeVertexFormat(nodeId, this.getFormatter().getVertexById(nodeId).getDefault()); - this.args.selectedVertices.splice(this.args.isVertexSelected[nodeId], 1); - var index = this.args.isVertexSelected[nodeId]; - delete this.args.isVertexSelected[nodeId]; - - for ( var vertex in this.args.isVertexSelected) { - if (this.args.isVertexSelected[vertex] > index) { - this.args.isVertexSelected[vertex] = this.args.isVertexSelected[vertex] - 1; - } - } - } -}; - -GraphCanvas.prototype.deselectNodes = function() { - var selected = JSON.parse(JSON.stringify(this.getSelectedVertices())); - for ( var i = 0; i < selected.length; i++) { - this.deselectNode(selected[i]); - } -}; -GraphCanvas.prototype.selectNodes = function(idNodes) { - - for ( var i = 0; i < idNodes.length; i++) { - this.selectNode(idNodes[i]); - } - - // for ( var vertex in this.args.isVertexSelected) { - // if (this.args.isVertexSelected[vertex] > index){ - // this.args.isVertexSelected[vertex] = this.args.isVertexSelected[vertex] - 1; - // } - // } - -}; - -GraphCanvas.prototype.changeVertexFormat = function(nodeId, format) { - var svgNode = DOM.select(this.getSVGNodeId(nodeId)); - if (svgNode != null) { - var properties = format.toJSON(); - for ( var item in properties) { - svgNode.setAttribute(item, properties[item]); - } - - if (this.getFormatter().getVertexById(nodeId) instanceof CircleVertexGraphFormatter) { - var transform = "translate(" + svgNode.getAttribute("dragx") + "," + svgNode.getAttribute("dragy") + "), scale(" + format.getSize() + ")"; - svgNode.setAttribute("transform", transform); - } - } -}; - -GraphCanvas.prototype.renderLabel = function(nodeId) { - var x = Math.ceil(this.getLayout().getNodeById(nodeId).x * this.getFormatter().getWidth()); - var y = Math.ceil(this.getLayout().getNodeById(nodeId).y * this.getFormatter().getHeight()); - - var svgAttributesNode = JSON.parse(JSON.stringify(this.getFormatter().getVertexById(nodeId).getDefault().toJSON().title)); - svgAttributesNode.id = this.getSVGLabelId(this.getDataset().getVertexById(nodeId).getId()); - svgAttributesNode.dx = (-1) * (this.getDataset().getVertexById(nodeId).getName().length * svgAttributesNode["font-size"]) / 4 - 4; - - svgAttributesNode.dy = parseFloat((this.getFormatter().getVertexById(nodeId).getDefault().getSize())) - + parseFloat(svgAttributesNode["font-size"]) + parseFloat(this.getFormatter().getVertexById(nodeId).getDefault().getStrokeWidth()) - 4; - svgAttributesNode.dragx = Math.ceil(this.getLayout().getNodeById(nodeId).x * this.getFormatter().getWidth()); - - var gragy = parseFloat(this.getFormatter().getVertexById(nodeId).getDefault().getSize()) - + Math.ceil(this.getLayout().getNodeById(nodeId).y * this.getFormatter().getHeight()); - svgAttributesNode.dragy = gragy; - svgAttributesNode.transform = "translate(" + svgAttributesNode.dragx + "," + svgAttributesNode.dragy + ")";//, scale("+this.formatter.getVertexById(nodeId).getDefault().getSize()+")"; - - var nodeSVG = SVG.drawText(0, 0, this.getDataset().getVertexById(nodeId).getName(), this.GraphLabelGroup, svgAttributesNode); - - this.svgLabels[nodeId] = nodeSVG; - - /** Events for the SVG node **/ - var _this = this; - if (nodeSVG != null) { - nodeSVG.addEventListener("mouseover", function() { - _this.overLabel(nodeId); - }, false); - nodeSVG.addEventListener("mouseout", function() { - _this.outLabel(nodeId); - }, false); - } - -}; - -GraphCanvas.prototype.removeLabel = function(labelId) { - if (DOM.select(this.getSVGLabelId(labelId)) != null) { - DOM.select(this.getSVGLabelId(labelId)).parentNode.removeChild(DOM.select(this.getSVGLabelId(labelId))); - } -}; - -GraphCanvas.prototype.renderNode = function(nodeId) { - var svgAttributesNode = JSON.parse(JSON.stringify(this.getFormatter().getVertexById(nodeId).getDefault().toJSON())); - svgAttributesNode.dragx = Math.ceil(this.getLayout().getNodeById(nodeId).x * this.getFormatter().getWidth()); - svgAttributesNode.dragy = Math.ceil(this.getLayout().getNodeById(nodeId).y * this.getFormatter().getHeight()); - svgAttributesNode.transform = "translate(" + svgAttributesNode.dragx + "," + svgAttributesNode.dragy + ")"; - svgAttributesNode.id = this.getSVGNodeId(nodeId); - /*svgAttributesNode["stroke-width"] = 3 ; - svgAttributesNode["stroke-opacity"] = 1 ; - svgAttributesNode["fill-opacity"] = svgAttributesNode["opacity"] ; - svgAttributesNode["opacity"] = 1 ;*/ - this.circleDefaultRadius = this.getFormatter().getVertexById(nodeId).getDefault().getSize(); - var nodeSVG; - - if (this.getFormatter().getVertexById(nodeId) instanceof CircleVertexGraphFormatter) { - nodeSVG = SVG.drawCircle(0, 0, this.circleDefaultRadius, this.GraphNodeGroup, svgAttributesNode); - } - - if (this.getFormatter().getVertexById(nodeId) instanceof SquareVertexGraphFormatter) { - //nodeSVG = SVG.drawRectangle(0 - (this.circleDefaultRadius) ,0 - (this.formatter.getVertexById(nodeId).getDefault().getSize()) , (this.getFormatter().getVertexById(nodeId).getDefault().getSize()*2), (this.getFormatter().getVertexById(nodeId).getDefault().getSize()*2), this.GraphNodeGroup, svgAttributesNode); - nodeSVG = SVG.drawRectangle(0 - (this.circleDefaultRadius), 0 - (this.circleDefaultRadius), (this.circleDefaultRadius * 2), - (this.circleDefaultRadius * 2), this.GraphNodeGroup, svgAttributesNode); - } - - if (this.getFormatter().getVertexById(nodeId) instanceof EllipseVertexGraphFormatter) { - nodeSVG = SVG.drawEllipse(0, 0, this.circleDefaultRadius * 1.5, this.circleDefaultRadius, this.GraphNodeGroup, svgAttributesNode); - } - - if (this.getFormatter().getVertexById(nodeId) instanceof RectangleVertexGraphFormatter) { - //nodeSVG = SVG.drawRectangle(0 - (this.circleDefaultRadius) ,0 - ((this.circleDefaultRadius*2)/2) , (this.circleDefaultRadius*2), (this.circleDefaultRadius), this.GraphNodeGroup, svgAttributesNode); - nodeSVG = SVG.drawRectangle(0 - (this.circleDefaultRadius * 1.5), 0 - (this.circleDefaultRadius), (this.circleDefaultRadius * 2 * 1.5), - (this.circleDefaultRadius * 2), this.GraphNodeGroup, svgAttributesNode); - - } - - if (this.getFormatter().getVertexById(nodeId) instanceof RoundedVertexGraphFormatter) { - svgAttributesNode.ry = 2;// this.formatter.getVertexById(nodeId).getDefault().getSize()/4; - svgAttributesNode.rx = 2;// this.formatter.getVertexById(nodeId).getDefault().getSize()/4; - nodeSVG = SVG.drawRectangle(0 - (this.circleDefaultRadius * 1.5), 0 - (this.circleDefaultRadius), (this.circleDefaultRadius * 2 * 1.5), - (this.circleDefaultRadius * 2), this.GraphNodeGroup, svgAttributesNode); - } - - // - - if (this.getFormatter().getVertexById(nodeId) instanceof OctagonVertexGraphFormatter) { - svgAttributesNode.ry = 2; - svgAttributesNode.rx = 2; - nodeSVG = SVG.drawRectangle(0 - (this.circleDefaultRadius * 1.5), 0 - (this.circleDefaultRadius), (this.circleDefaultRadius * 2 * 1.5), - (this.circleDefaultRadius * 2), this.GraphNodeGroup, svgAttributesNode); - } - - nodeSVG.internalId = nodeId; - // - var _this = this; - - /** Events for the SVG node **/ - if (nodeSVG != null) { - nodeSVG.addEventListener("mouseover", function() { - _this.onVertexOver.notify(nodeId); - _this.overNode(nodeId); - }, false); - nodeSVG.addEventListener("mouseout", function() { - _this.onVertexOut.notify(nodeId); - _this.outNode(nodeId); - }, false); - //nodeSVG.addEventListener("click", function(){_this.clickNode(nodeId);}, false); - // - nodeSVG.addEventListener("mouseup", function() { - _this.onVertexUp.notify(nodeId); - }, false); - } -}; - -GraphCanvas.prototype.removeNode = function(nodeId) { - DOM.select(this.getSVGNodeId(nodeId)).parentNode.removeChild(DOM.select(this.getSVGNodeId(nodeId))); - if (this.args.labeled) { - this.removeLabel(nodeId); - } -}; - -/** REMOVING **/ -GraphCanvas.prototype.removeSelected = function() { - /** El orden importa **/ - this.removeSelectedEdges(); - this.removeSelectedNode(); - -}; - -GraphCanvas.prototype.removeSelectedNode = function() { - var selected = JSON.parse(JSON.stringify(this.getSelectedVertices())); - this.deselectNodes(); - var sorted = selected.sort(function(a, b) { - return a - b - }); - for ( var i = 0; i < sorted.length; i++) { - if (this.getDataset().getVertexById(sorted[i]) != null) { - this.getDataset().getVertexById(sorted[i]).remove(); - } - } -}; - -/** EDGES **/ -GraphCanvas.prototype.removeEdge = function(edgeId) { - if (DOM.select(this.getSVGEdgeId(edgeId)) != null) { - DOM.select(this.getSVGEdgeId(edgeId)).parentNode.removeChild(DOM.select(this.getSVGEdgeId(edgeId))); - } - - if (DOM.select(this.getSVGEdgeId(edgeId) + "_shadow") != null) { - DOM.select(this.getSVGEdgeId(edgeId) + "_shadow").parentNode.removeChild(DOM.select(this.getSVGEdgeId(edgeId) + "_shadow")); - } - - if (DOM.select(this.getSVGArrowEdgeId(edgeId)) != null) { - DOM.select(this.getSVGArrowEdgeId(edgeId)).parentNode.removeChild(DOM.select(this.getSVGArrowEdgeId(edgeId))); - } -}; - -GraphCanvas.prototype.overEdge = function(edgeId) { - if ((!this.args.interactive) || this.dragging || this.selecting) { - return; - } - - /** If selected we don't change the format **/ - if (this.args.isEdgeSelected[edgeId] == null) { - var format = this.getFormatter().getEdgeById(edgeId).getOver(); - format.args["cursor"] = "pointer"; - this.changeEdgeFormat(edgeId, format); - } -}; - -GraphCanvas.prototype.outEdge = function(edgeId) { - if (!this.args.interactive) { - return; - } - - /** If selected we don't change the format **/ - if (this.args.isEdgeSelected[edgeId] == null) { - this.changeEdgeFormat(edgeId, this.getFormatter().getEdgeById(edgeId).getDefault()); - } -}; - -GraphCanvas.prototype.changeEdgeFormat = function(edgeId, format) { - var svgEdge = DOM.select(this.getSVGEdgeId(edgeId) + "_shadow"); - if (svgEdge != null) { - var properties = format.toJSON(); - for ( var item in properties) { - svgEdge.setAttribute(item, properties[item]); - } - } -}; - -GraphCanvas.prototype.deselectEdge = function(edgeID) { - if (this.args.isEdgeSelected[edgeID] != null) { - this.changeEdgeFormat(edgeID, this.getFormatter().getEdgeById(edgeID).getDefault()); - var index = this.args.isEdgeSelected[edgeID]; - delete this.args.isEdgeSelected[edgeID]; - } -}; - -GraphCanvas.prototype.deselectEdges = function() { - var selected = JSON.parse(JSON.stringify(this.getSelectedEdges())); - for ( var i = 0; i < selected.length; i++) { - this.deselectEdge(selected[i]); - } -}; - -GraphCanvas.prototype.removeSelectedEdges = function() { - var selected = JSON.parse(JSON.stringify(this.getSelectedEdges())); - this.deselectEdges(); - for ( var i = 0; i < selected.length; i++) { - if (this.getDataset().getEdgeById(selected[i]) != null) { - this.getDataset().getEdgeById(selected[i]).remove(); - } - } -}; - -GraphCanvas.prototype.renderEdge = function(edgeId) { - var svgAttributesEdge = this.getFormatter().getEdgeById(edgeId).getDefault().toJSON(); - var edge = this.getDataset().getEdgeById(edgeId); - - var svgNodeTarget = this.getVertexById(edge.getNodeTarget().getId()); - var svgNodeSource = this.getVertexById(edge.getNodeSource().getId()); - svgAttributesEdge.id = this.getSVGEdgeId(edge.getId()) + "_shadow"; - - var svgEdge = null; - - if (this.getFormatter().getEdgeById(edgeId) instanceof LineEdgeGraphFormatter) { - var coordenateSourceX = svgNodeSource.getAttribute("dragx"); - var coordenateSourceY = svgNodeSource.getAttribute("dragy"); - var coordenateTargetX = svgNodeTarget.getAttribute("dragx"); - var coordenateTargetY = svgNodeTarget.getAttribute("dragy"); - - SVG.drawLine(coordenateSourceX, coordenateSourceY, coordenateTargetX, coordenateTargetY, this.GraphEdgeGroup, svgAttributesEdge); - var attributesShadow = {}; - attributesShadow.id = this.getSVGEdgeId(edge.getId()); - attributesShadow["stroke-opacity"] = 0; - attributesShadow["stroke-width"] = 4; - attributesShadow["stroke"] = "black"; - svgEdge = SVG.drawLine(svgNodeSource.getAttribute("dragx"), svgNodeSource.getAttribute("dragy"), svgNodeTarget.getAttribute("dragx"), - svgNodeTarget.getAttribute("dragy"), this.GraphEdgeGroup, attributesShadow); - } - - if (this.getFormatter().getEdgeById(edgeId) instanceof BezierEdgeGraphFormatter) { - var nodeId = edge.getNodeTarget().getId(); - var nodeSize = this.formatter.getVertexById(nodeId).getDefault().getSize() * this.getFormatter().getNodesMaxSize(); - svgAttributesEdge.fill = "none"; - svgAttributesEdge.id = this.getSVGEdgeId(edgeId); - var d = this.calculateCoordenatesBezier(nodeSize, svgNodeSource.getAttribute("dragx"), svgNodeSource.getAttribute("dragy")); - svgEdge = SVG.drawPath(d, this.GraphEdgeGroup, svgAttributesEdge); - } - ; - - if ((this.getFormatter().getEdgeById(edgeId) instanceof DirectedLineEdgeGraphFormatter) - || (this.getFormatter().getEdgeById(edgeId) instanceof CutDirectedLineEdgeGraphFormatter) - || (this.getFormatter().getEdgeById(edgeId) instanceof DotDirectedLineEdgeGraphFormatter) - || (this.getFormatter().getEdgeById(edgeId) instanceof OdotDirectedLineEdgeGraphFormatter) - || (this.getFormatter().getEdgeById(edgeId) instanceof OdirectedLineEdgeGraphFormatter)) { - var coordenateSourceX = svgNodeSource.getAttribute("dragx"); - var coordenateSourceY = svgNodeSource.getAttribute("dragy"); - var coordenateTargetX = svgNodeTarget.getAttribute("dragx"); - var coordenateTargetY = svgNodeTarget.getAttribute("dragy"); - - var offset = parseFloat(this.getFormatter().getVertexById(this.getDataset().getEdgeById(edgeId).getNodeTarget().getId()).getDefault() - .getSize() - * this.circleDefaultRadius); - var point = this._calculateEdgePointerPosition(coordenateSourceX, coordenateSourceY, coordenateTargetX, coordenateTargetY, offset); - coordenateTargetX = point.x; - coordenateTargetY = point.y; - SVG.drawLine(coordenateSourceX, coordenateSourceY, coordenateTargetX, coordenateTargetY, this.GraphEdgeGroup, svgAttributesEdge); - - var attributesShadow = {}; - attributesShadow.id = this.getSVGEdgeId(edge.getId()); - attributesShadow["stroke-opacity"] = 0; - attributesShadow["stroke-width"] = 4; - attributesShadow["stroke"] = "black"; - svgEdge = SVG.drawLine(coordenateSourceX, coordenateSourceY, coordenateTargetX, coordenateTargetY, this.GraphEdgeGroup, attributesShadow); - } - - if (this.getFormatter().getEdgeById(edgeId) instanceof DirectedLineEdgeGraphFormatter - || (this.getFormatter().getEdgeById(edgeId) instanceof OdirectedLineEdgeGraphFormatter)) { - var coordenateSourceX = svgNodeSource.getAttribute("dragx"); - var coordenateSourceY = svgNodeSource.getAttribute("dragy"); - var coordenateTargetX = svgNodeTarget.getAttribute("dragx"); - var coordenateTargetY = svgNodeTarget.getAttribute("dragy"); - - var point = this._calculateEdgePointerPosition(coordenateSourceX, coordenateSourceY, coordenateTargetX, coordenateTargetY, offset); - coordenateTargetX = point.x; - coordenateTargetY = point.y; - - var angle = Geometry.toDegree(point.angle) + 90; - this.arrowDefaultSize = this.getFormatter().getEdgeById(edgeId).getArrowSize(); //getDefault().getArrowSize(); - var d = "-" + this.arrowDefaultSize + ",0 0,-" + parseFloat(this.arrowDefaultSize) * 2 + " " + this.arrowDefaultSize + ",0"; - - var attributes; - - if (this.getFormatter().getEdgeById(edgeId) instanceof DirectedLineEdgeGraphFormatter) { - attributes = [ - [ "fill", this.getFormatter().getEdgeById(edgeId).getDefault().getStroke() ], - [ "stroke", this.getFormatter().getEdgeById(edgeId).getDefault().getStroke() ], [ "id", this.getSVGArrowEdgeId(edgeId) ] ]; - } else { - attributes = [ - [ "fill", "#FFFFFF" ], [ "stroke", this.getFormatter().getEdgeById(edgeId).getDefault().getStroke() ], - [ "id", this.getSVGArrowEdgeId(edgeId) ] ]; - } - - var flechaSVGNode = SVG.drawPoligon(d, this.GraphEdgeGroup, attributes);//, ["transform", "rotate("+angle+"), translate(0,0)"]]); - flechaSVGNode.setAttribute("transform", " translate(" + coordenateTargetX + ", " + coordenateTargetY + "), rotate(" + angle + ")"); - } - ; - - if (this.getFormatter().getEdgeById(edgeId) instanceof CutDirectedLineEdgeGraphFormatter) { - var coordenateSourceX = svgNodeSource.getAttribute("dragx"); - var coordenateSourceY = svgNodeSource.getAttribute("dragy"); - var coordenateTargetX = svgNodeTarget.getAttribute("dragx"); - var coordenateTargetY = svgNodeTarget.getAttribute("dragy"); - - var point = this._calculateEdgePointerPosition(coordenateSourceX, coordenateSourceY, coordenateTargetX, coordenateTargetY, offset); - coordenateTargetX = point.x; - coordenateTargetY = point.y; - - var angle = Geometry.toDegree(point.angle) + 90; - - //this.arrowDefaultSize = 2; //getDefault().getArrowSize(); - var d = "-4,0 4,0 4,-2 -4,-2"; - - var flechaSVGNode = SVG.drawPoligon(d, this.GraphEdgeGroup, [ - [ "fill", this.getFormatter().getEdgeById(edgeId).getDefault().getStroke() ], - [ "stroke", this.getFormatter().getEdgeById(edgeId).getDefault().getStroke() ], [ "id", this.getSVGArrowEdgeId(edgeId) ] ]);//, ["transform", "rotate("+angle+"), translate(0,0)"]]); - flechaSVGNode.setAttribute("transform", " translate(" + coordenateTargetX + ", " + coordenateTargetY + "), rotate(" + angle + ")"); - } - ; - - if ((this.getFormatter().getEdgeById(edgeId) instanceof DotDirectedLineEdgeGraphFormatter) - || (this.getFormatter().getEdgeById(edgeId) instanceof OdotDirectedLineEdgeGraphFormatter)) { - var coordenateSourceX = svgNodeSource.getAttribute("dragx"); - var coordenateSourceY = svgNodeSource.getAttribute("dragy"); - var coordenateTargetX = svgNodeTarget.getAttribute("dragx"); - var coordenateTargetY = svgNodeTarget.getAttribute("dragy"); - var point = this._calculateEdgePointerPosition(coordenateSourceX, coordenateSourceY, coordenateTargetX, coordenateTargetY, offset); - coordenateTargetX = point.x; - coordenateTargetY = point.y; - var angle = Geometry.toDegree(point.angle) + 90; - // this.arrowDefaultSize = this.formatter.getEdgeById(edgeId).getArrowSize(); //getDefault().getArrowSize(); - var attributes = []; - if (this.getFormatter().getEdgeById(edgeId) instanceof OdotDirectedLineEdgeGraphFormatter) { - attributes = [ - [ "fill", "#FFFFFF" ], [ "stroke", this.getFormatter().getEdgeById(edgeId).getDefault().getStroke() ], - [ "id", this.getSVGArrowEdgeId(edgeId) ] ]; - } else { - attributes = [ - [ "fill", this.getFormatter().getEdgeById(edgeId).getDefault().getStroke() ], - [ "stroke", this.getFormatter().getEdgeById(edgeId).getDefault().getStroke() ], [ "id", this.getSVGArrowEdgeId(edgeId) ] ]; - } - var flechaSVGNode = SVG.drawCircle(0, 0, 4, this.GraphEdgeGroup, attributes); - flechaSVGNode.setAttribute("transform", " translate(" + coordenateTargetX + ", " + coordenateTargetY + "), rotate(" + angle + ")"); - } - ; - - var _this = this; - /** Events for the SVG edge **/ - if (svgEdge != null) { - if (this.getDataset().getEdgesCount() < this.args.maxNumberEdgesFiringEvents) { - svgEdge.addEventListener("mouseover", function() { - _this.overEdge(edgeId); - }, false); - svgEdge.addEventListener("mouseout", function() { - _this.outEdge(edgeId); - }, false); - } - } -}; - -GraphCanvas.prototype._calculateEdgePointerPosition = function(sourceX, sourceY, targetX, targetY, radius) { - var angle = Geometry.getAngleBetweenTwoPoints(sourceX, sourceY, targetX, targetY); - - /** Suponiendo el node source que este a la derecha **/ - if ((targetX - sourceX) < 0) { - var b = Geometry.getAdjacentSideOfRectangleRight(angle, radius); - targetX = parseFloat(targetX) + parseFloat(b); - arrowX = parseFloat(targetX) + parseFloat(b) + this.arrowDefaultSize / 2; - } else { - var b = Geometry.getAdjacentSideOfRectangleRight(angle, radius); - targetX = parseFloat(targetX) - parseFloat(b); - arrowX = parseFloat(targetX) - parseFloat(b) - this.arrowDefaultSize / 2; - } - - /** Suponiendo el node source que este a la arriba **/ - if ((targetY - sourceY) > 0) { - var a = Geometry.getOppositeSideOfRectangleRight(angle, radius); - targetY = parseFloat(targetY) - parseFloat(a); - arrowY = parseFloat(targetY) - parseFloat(a) - this.arrowDefaultSize / 2; - } else { - var a = Geometry.getOppositeSideOfRectangleRight(angle, radius); - targetY = parseFloat(targetY) + parseFloat(a); - arrowY = parseFloat(targetY) + parseFloat(a) - this.arrowDefaultSize / 2; - - } - - return { - "x" : arrowX, - "y" : arrowY, - "angle" : angle - }; -}; - -GraphCanvas.prototype.calculateCoordenatesBezier = function(nodeSize, x1, y1) { - var x11 = x1 - (nodeSize / 2); - var y11 = y1 - (nodeSize / 2); - - var x12 = parseFloat(x1) + parseFloat(nodeSize / 2); - var y12 = y1 - (nodeSize / 2); - - var curvePointX = (x12 - x11) / 2 + x11; - var curvePointY = y1 - (nodeSize * 2); - var d = "M" + x11 + "," + y11 + " T" + curvePointX + "," + curvePointY + " " + x12 + "," + y12; - return d; - -}; - -GraphCanvas.prototype.renderEdges = function() { - for ( var edge in this.getDataset().getEdges()) { - this.renderEdge(this.getDataset().getEdgeById(edge).getId()); - - } -}; - -GraphCanvas.prototype.getLastSelectedNode = function() { - var node = null; - if (this.getSelectedVertices().length > 0) { - var nodeId = this.getSelectedVertices()[this.getSelectedVertices().length - 1]; - node = this.getDataset().getVertexById(nodeId); - } - return node; -}; -/* - GraphCanvas.prototype.getNodeByNameAndIndex = function(node, index){ - var nodeId = this.getDataset().verticesIndex[node][index]; - var nodeItem = this.getDataset().getVertexById(nodeId); - return nodeItem; - }; - */ - -GraphCanvas.prototype.setDataset = function(dataset) { - this.dataset = dataset; -}; - -GraphCanvas.prototype.setFormatter = function(formatter) { - this.formatter = formatter; -}; - -GraphCanvas.prototype.setLayout = function(layout) { - this.layout = layout; -}; - -/** API **/ -GraphCanvas.prototype.getDataset = function() { - return this.dataset; -}; - -GraphCanvas.prototype.getFormatter = function() { - return this.formatter; -}; - -GraphCanvas.prototype.getLayout = function() { - return this.layout; -}; - -/** API DATASET **/ -GraphCanvas.prototype.addVertex = function(name, args) { - this.getDataset().addNode(name, args); -}; - -GraphCanvas.prototype.removeVertex = function(vertexId) { - this.getDataset().getVertexById(vertexId).remove(); -}; - -GraphCanvas.prototype.addEdge = function(edgeName, nodeSourceId, nodeTargetId, args) { - this.getDataset().addEdge(edgeName, nodeSourceId, nodeTargetId, args); -}; -/* - GraphCanvas.prototype.removeEdge = function(edgeId){ - this.getDataset().getEdgeById(edgeId).remove(); - }; - */ - -/** API FORMATTER **/ -GraphCanvas.prototype.getWidth = function() { - return this.getFormatter().getWidth(); -}; - -GraphCanvas.prototype.getHeight = function() { - return this.getFormatter().getHeight(); -}; - -GraphCanvas.prototype.getBackgroundImage = function() { - return this.getFormatter().getBackgroundImage(); -}; +/** + * Example form + * + * @witdh + * @height + */ +function ExampleForm(args) { + this.id = BUI.id(); + this.width = 700; + this.height = 500; + + if (args != null) { + if (args.width != null) { + this.width = args.width; + } + if (args.height != null) { + this.height = args.height; + } + } +} + +ExampleForm.prototype._getItems = function() { + return [{ + fieldLabel : 'First Name', + name : 'first', + allowBlank : false + }, { + fieldLabel : 'Last Name', + name : 'last', + allowBlank : false + } ]; +}; + +ExampleForm.prototype._getItems = function() { + return [ ]; +}; + +ExampleForm.prototype._getButtons = function() { + return [ ]; +}; + +ExampleForm.prototype.getPanel = function() { + this.panel = Ext.create('Ext.form.Panel', { + width : this.width, + height : this.height, + margin : 10, + border : 1, + defaultType : 'textfield', + items : this._getItems(), + buttons : this._getButtons() + }); + return this.panel; +}; + +/** It populates the form **/ +ExampleForm.prototype.refresh = function(macromolecule) { +}; -//GraphCanvas.prototype.setBackgroundImage = function(value){ -// this.getFormatter().setBackgroundImage(value); -//}; +function ExperimentForm(args) { + this.id = BUI.id(); + + if (args != null) { + } + + this.onSaved = new Event(this); +} + +ExperimentForm.prototype._getItems = function(experiment) { + this.experiment = experiment; + var typeCombo = Ext.create('Ext.form.ComboBox', { + id : this.id + 'type', + fieldLabel : 'Type', + store : [ "STATIC", "CALIBRATION", "HPLC" ], + queryMode : 'local', + labelWidth : 120, + displayField : 'name', + valueField : 'name', + value : experiment.json.type, + disabled : (experiment.json.type == 'TEMPLATE') + }); + + var items = []; + + + if (experiment.json.type == "HPLC" ){ + var typeMacromolecule = BIOSAXS_COMBOMANAGER.getComboMacromoleculeByMacromolecules(BIOSAXS.proposal.getMacromolecules(), {labelWidth : 120, width: "120px"}); + if (experiment.getHPLCMacromolecule() != null){ + typeMacromolecule.setValue(experiment.getHPLCMacromolecule().macromoleculeId); + items.push(typeMacromolecule); + } + } + + + items.push(typeCombo, { + id : this.id + 'name', + xtype : 'textfield', + fieldLabel : 'Name', + labelWidth : 120, + width : '100%', + value : experiment.json.name + }, { + id : this.id + 'comments', + xtype : 'textareafield', + name : 'comments', + fieldLabel : 'Comments', + labelWidth : 120, + height : 120, + width : '100%', + value : experiment.json.comments + }); + return items; +}; +ExperimentForm.prototype.getPanel = function(experiment) { + var _this = this; + + this.panel = Ext.createWidget({ + xtype : 'container', + layout : 'vbox', + border : 0, + style : { + padding : '10px' + }, + fieldDefaults : { + labelAlign : 'left', + labelWidth : 50 + }, + items : this._getItems(experiment) + }); + return this.panel; +}; + +ExperimentForm.prototype.input = function() { + return new ExperimentHeaderForm().input(); +}; + +ExperimentForm.prototype.test = function(targetId) { + var experimentForm = new ExperimentForm(); + var panel = experimentForm.getPanel(new Experiment(experimentForm.input().experiment)); + panel.render(targetId); +}; -GraphCanvas.prototype.getBackgroundColor = function() { - return this.getFormatter().getBackgroundColor(); -}; +/** + * Shows the header for the experiments changing the color and parameters depending on experiment type + * + */ +function ExperimentHeaderForm(args) { + this.id = BUI.id(); + this.backgroundColor = '#FFFFFF'; +} + +ExperimentHeaderForm.prototype.getHTMLSource = function(experiment) { + var html = BUI.createFormLabel("Name :", experiment.json.name, 75, 400, this.backgroundColor); + if (experiment.json.type == "HPLC") { + if (experiment.getHPLCMacromolecule() != null){ + html = html + BUI.createFormLabel("Molecule :", experiment.getHPLCMacromolecule().acronym, 75, 400, this.backgroundColor); + } + } + else{ + html = html + BUI.createFormLabel("Type :", experiment.json.type, 75, 400, this.backgroundColor); + } + + html = html + BUI.createFormLabel("Date :", experiment.json.creationDate, 75, 400, this.backgroundColor); + return html; +}; + +ExperimentHeaderForm.prototype.getHTMLDownload = function(experiment) { + var bgcolor = "background-color:" + this.backgroundColor + ";"; + var html = "
"; + if ((experiment.json.type == "CALIBATION") || (experiment.json.type == "STATIC")) { + html = html + " Download Source File
"; + html = html + + ""; + } + if (experiment.json.type == "TEMPLATE") { + html = html + + " Download Source File"; + } + + if (experiment.json.type == "HPLC") { + html = html + " Download h5 File"; + } + + return html; +}; + +ExperimentHeaderForm.prototype.getTopPanel = function(experiment) { + return { + xtype : 'container', + layout : 'hbox', + items : [ { + margin : "0 0 0 0", + width : 475, + border : 0, + html : this.getHTMLSource(experiment) + }, { + margin : "0 0 0 0", + width : 475, + border : 0, + html : this.getHTMLDownload(experiment) + } ] + }; +}; + +ExperimentHeaderForm.prototype.getButton = function(experiment) { + var _this = this; + return Ext.create('Ext.Button', { + text : 'EDIT', + minWidth : '100', + margin : '10 0 0 30', + handler : function() { + var experimentWindow = new ExperimentWindow(); + experimentWindow.onSaved.attach(function(sender, data) { + _this.experiment.json.name = data.name; + _this.experiment.json.type = data.type; + _this.experiment.json.comments = data.comments; + _this.panel.remove(_this.panel.items.items[0]); + _this.panel.remove(_this.panel.items.items[0]); + _this.panel.insert(_this.getTopPanel(_this.experiment)); + _this.panel.insert(_this.getBottomPanel(_this.experiment)); + }); + experimentWindow.show(experiment); + } + }); +}; + +ExperimentHeaderForm.prototype.getBottomPanel = function(experiment) { + return { + xtype : 'container', + layout : 'hbox', + margin : '10 0 0 0', + items : [ this.getComments(experiment), this.getButton(experiment) ] + }; +}; + +ExperimentHeaderForm.prototype.getComments = function(experiment) { + return { + xtype : 'textareafield', + labelStyle : 'font-weight: bold;', + name : 'comments', + fieldLabel : 'Comments', + labelWidth : 70, + height : 40, + minWidth : '450', + readOnly : true, + value : experiment.json.comments + }; +}; + +ExperimentHeaderForm.prototype.getPanel = function(experiment) { + this.experiment = experiment; + + if (experiment.json.type == 'CALIBRATION') { + this.backgroundColor = '#EFFBFB'; + } + if (experiment.json.type == 'TEMPLATE') { + this.backgroundColor = '#E0F8E6'; + } + + this.panel = Ext.create('Ext.container.Container', { + frame : false, + layout : 'vbox', + padding : 5, + bodyPadding : '5 5 0 0', + width : 890, + margin : '0 0 10 0', + height : 120, + style : { + borderColor : '#99bce8', + borderStyle : 'solid', + borderWidth : '1px', + 'background-color' : this.backgroundColor + }, + fieldDefaults : { + msgTarget : 'side', + labelWidth : 100 + }, + items : [ this.getTopPanel(experiment), this.getBottomPanel(experiment) ] + }); + return this.panel; +}; + +ExperimentHeaderForm.prototype.input = function() { + return { + experiment : DATADOC.getExperiment_10() + }; +}; + +ExperimentHeaderForm.prototype.test = function(targetId) { + var experimentHeaderForm = new ExperimentHeaderForm(); + var panel = experimentHeaderForm.getPanel(new Experiment(experimentHeaderForm.input().experiment)); + panel.render(targetId); + +}; -GraphCanvas.prototype.setBackgroundColor = function() { - this.getFormatter().setBackgroundColor(value); -}; +/** + * Macromolecule form with the general parameters of a macromolecule + * + * @witdh + * @height + * + * #onSave button save has been clicked + * #onClose button close has been clicked + */ +function MacromoleculeForm(args) { + this.id = BUI.id(); + this.width = 700; + this.height = 500; + + if (args != null) { + if (args.width != null) { + this.width = args.width; + } + if (args.height != null) { + this.height = args.height; + } + } + + /** Events **/ + this.onSave = new Event(this); + this.onClose = new Event(this); +} + +/** Type : is the Ext type then requiredtext or textfield * */ +MacromoleculeForm.prototype._getFieldTextWithHelp = function(type, fieldLabel, fieldName, help) { + return Ext.create('Ext.container.Container', { + margin : "10 0 0 10", + items : [ { + xtype : type, + fieldLabel : fieldLabel, + name : fieldName, + id : this.id + fieldName + }, { + xtype : 'label', + forId : 'myFieldId', + text : help, + margin : "5 0 0 105", + cls : "inline-help" + } ] + }); +}; + +MacromoleculeForm.prototype._getNumericWithHelp = function(type, fieldLabel, fieldName, help) { + return Ext.create('Ext.container.Container', { + margin : "0 0 0 10", + items : [ { + xtype : type, + fieldLabel : fieldLabel, + name : fieldName, + id : this.id + fieldName, + decimalPrecision : 6, + width : 220 + }, { + xtype : 'label', + forId : 'myFieldId', + text : help, + margin : "5 0 0 10", + cls : "inline-help" + } ] + }); +}; + +MacromoleculeForm.prototype._getButtons = function() { + var _this = this; + return [ { + text : 'Save', + handler : function() { + _this._save(); + } + },{ + text : 'Close', + handler : function() { + _this.onClose.notify(); + + } + } ]; +}; + +/** It persits the macromolecule in the database **/ +MacromoleculeForm.prototype._persist = function(macromoleculeId, acronym, name, molecularMass, extintionCoefficient, comments, refractiveIndex, solventViscosity) { + + /** Checking not duplicated acronym **/ + if (((macromoleculeId == null) && (BIOSAXS.proposal.getMacromoleculeByAcronym(acronym) != null))== true){ + BUI.showError("Duplicated acronym"); + return; + } + + + if (macromoleculeId == null){ + /** new macromolecule **/ + this.macromolecule = {}; + this.macromolecule.macromoleculeId = null; + } + else{ + this.macromolecule.macromoleculeId = macromoleculeId; + } + + this.macromolecule["acronym"] = acronym; + this.macromolecule["name"] = name; + this.macromolecule["molecularMass"] = molecularMass; + this.macromolecule["extintionCoefficient"] = extintionCoefficient; + this.macromolecule["comments"] = comments; + this.macromolecule["symmetry"] = Ext.getCmp(this.id + 'comboSym').getValue(); + this.macromolecule["refractiveIndex"] = refractiveIndex; + this.macromolecule["solventViscosity"] = solventViscosity; + + var _this = this; + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, proposal) { + /** Updating the proposal global object **/ + BIOSAXS.proposal.setItems(proposal); + _this.panel.setLoading(false); + + var saved = BIOSAXS.proposal.getMacromoleculeByAcronym(acronym); + /** Refreshing to check that everything is ok **/ + _this.refresh(saved); + _this.onSave.notify(saved); + }); + + this.panel.setLoading("Saving Macromolecule") + adapter.saveMacromolecule(this.macromolecule); +}; + +/** Save the macromolecule in the DB **/ +MacromoleculeForm.prototype._save = function() { + + var _this = this; + + var acronym = this._getField("acronym"); + var name = this._getField("name"); + var molecularMass = this._getField("molecularMass"); + var extintionCoefficient = this._getField("extintionCoefficient"); + var comments = this._getField("comments"); + + var refractiveIndex = this._getField("refractiveIndex"); + var solventViscosity = this._getField("solventViscosity"); + + /** Checking required fields **/ + if (name == "") { + BUI.showError("Name field is mandatory"); + return; + } + if (acronym == "") { + BUI.showError("Acroynm field is mandatory"); + return; + } + + if (this.macromolecule != null){ + /** Checking if it is a new macromolecule **/ + if (this.macromolecule.macromoleculeId == null){ + /** Check if the acronym exists already **/ + this._persist(null, acronym, name, molecularMass, extintionCoefficient, comments, refractiveIndex, solventViscosity); + } + else{ + /** It is an update **/ + this._persist(this.macromolecule.macromoleculeId, acronym, name, molecularMass, extintionCoefficient, comments, refractiveIndex, solventViscosity); + } + } + else{ + /** It is a new macromolecule **/ + this._persist(null, acronym, name, molecularMass, extintionCoefficient, comments, refractiveIndex, solventViscosity); + } +}; + + + +MacromoleculeForm.prototype._getItems = function() { + var _this = this; + /** Symmetry combo box **/ + var symmetry = Ext.create('Ext.data.Store', { + fields : [ 's' ], + data : this._getSymmetries() + }); + + this.symmetryComboBox = Ext.create('Ext.form.ComboBox', { + fieldLabel : 'Symmetry', + store : symmetry, + id : this.id + 'comboSym', + queryMode : 'local', + displayField : 's', + valueField : 's', + value : "P1", + margin : "0 0 0 30", + width : 220 + }); + + return [this._getFieldTextWithHelp("requiredtext", "Name", "name", "Long name. i.e: Bovine serum albumin"), + this._getFieldTextWithHelp("requiredtext", "Acronym", "acronym", "Acronym will be used in the files and analisys. i.e: BSA"), + this._getFieldTextWithHelp("textfield", "Mol. Mass (Da)", "molecularMass", "Atomic mass estimation measured in Da"), + { + xtype : 'container', + layout : 'hbox', + margin : "10 0 0 0", + items :[ + this._getNumericWithHelp("numberfield", "Extinction coef.", "extintionCoefficient", ""), + this.symmetryComboBox + ] + }, + { + xtype : 'container', + layout : 'hbox', + margin : "5 0 0 0", + items :[ + this._getNumericWithHelp("numberfield", "Refractive Index", "refractiveIndex", "How radiation propagates through the medium"), + this._getNumericWithHelp("numberfield", "Solvent Viscosity", "solventViscosity", "") + ] + }, + { + id : this.id + "comments", + xtype : 'textareafield', + name : 'comments', + margin : '35 0 0 10', + fieldLabel : 'Comments', + width : this.width - 100 + } ]; +}; + +MacromoleculeForm.prototype._getSymmetries = function() { + return [ { + "s" : "P1" + }, { + "s" : "P2" + }, { + "s" : "P3" + }, { + "s" : "P4" + }, { + "s" : "P5" + }, { + "s" : "P6" + }, { + "s" : "P32" + }, { + "s" : "P42" + }, { + "s" : "P52" + }, { + "s" : "P62" + }, { + "s" : "P222" + } ] +}; + +MacromoleculeForm.prototype.getPanel = function() { + this.panel = Ext.create('Ext.form.Panel', { + width : this.width, + height : this.height, + margin : 10, + border : 1, + defaultType : 'textfield', + items : this._getItems(), + buttons : this._getButtons() + }); + return this.panel; +}; + + +/** Populates each text field by field name and value **/ +MacromoleculeForm.prototype._populateField = function(fieldName, value) { + if (value != null){ + Ext.getCmp(this.id + fieldName).setValue(value); + } +}; + +/** Gets the value of a textfield **/ +MacromoleculeForm.prototype._getField = function(fieldName) { + return Ext.getCmp(this.id + fieldName).getValue(); +}; + + +/** It populates the form **/ +MacromoleculeForm.prototype.refresh = function(macromolecule) { + this.macromolecule = macromolecule; + if (macromolecule != null){ + this._populateField("name", macromolecule.name); + this._populateField("acronym", macromolecule.acronym); + this._populateField("extintionCoefficient", macromolecule.extintionCoefficient); + this._populateField("molecularMass", macromolecule.molecularMass); + this._populateField("comments", macromolecule.comments); + this._populateField("refractiveIndex", macromolecule.refractiveIndex); + this._populateField("solventViscosity", macromolecule.solventViscosity); + if (macromolecule.symmetry != null){ + Ext.getCmp(this.id + 'comboSym').setValue(macromolecule.symmetry); + } + } +}; + + +MacromoleculeForm.prototype.input = function() { + return {}; +}; + + +/** It populates the form **/ +MacromoleculeForm.prototype.test = function(targetId) { + var macromoleculeForm = new MacromoleculeForm(); + macromoleculeForm.onClose.attach(function(sender){ + alert("Click on close"); + }); + var panel = macromoleculeForm.getPanel(); + panel.render(targetId); +}; + + -//GraphCanvas.prototype.setEdgeFill = function(edgeId, value){ -// this.getFormatter().getEdgeById(edgeId).getDefault().setFill(value); -//}; -// -//GraphCanvas.prototype.getEdgeFill = function(edgeId){ -// return this.getFormatter().getEdgeById(edgeId).getDefault().getFill(); -//}; + + +function ModelVisualizerForm(args){ + this.id =BUI.id(); + this.width = 600; + this.height = 400; + if (args!= null){ + if (args.width != null){ + this.width = args.width; + } + if (args.height != null){ + this.height = args.height; + } + } +}; + +ModelVisualizerForm.prototype._getFirHTML = function(modelId, width, height, type, desc) { + var html = ""; + html = html + ''; + html = html + ''; + html = html + ''; + html = html + '
dammin.' + type + '
' + desc + '
'; + return html; +}; + +ModelVisualizerForm.prototype.getItems = function(modelPanel){ + _this = this; + var height = _this.height/2 - 60; + var width = _this.width/2 - 10; + + return Ext.create('Ext.container.Container', { + layout: { + type: 'vbox', // Arrange child items vertically + }, + items: [ + modelPanel, + { + xtype : 'container', + layout: { + type: 'hbox', // Arrange child items vertically + }, + items : [{ + html : this._getFirHTML("id", width, height, "fir", "Fit of the simulated scattering curve versus a smoothed experimental data (spline interpolation)"), + height :height, + width : width, + padding: 2 + }, + { + html : this._getFirHTML("id", width, height, "fit", "Fit of the simulated scattering curve versus the experimental data."), + height : height, + width : width, + padding: 2 + }] + } + + ] + }); +}; + +ModelVisualizerForm.prototype.refresh = function(models){ + var input = []; +// var colors = ["008000", "F0A804", "0000FF", "800080", "C0C0C0"]; + for (var i = 0; i < models.length; i++) { + console.log(BUI.rainbow(models.length, i).replace("#", "0x")); + input.push({ + color: BUI.rainbow(models.length, i).replace("#", "0x"), + modelId: models[i].modelId, + opacity: 0.8, + radius: 3, + title: BUI.getFileNameByPath(models[i].pdbFile), + type: "SHAPEDETERMINATIONMODEL" + + }); + } + + this.panel.removeAll(); + this.panel.add( + _this.getItems( + new PDBViewer({ + width : this.width - 10, + height : (this.height/2) - 10, + title : "" + }).draw(input) + ) + ); + + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, data) { + var splitted = data.toString().split("\n"); + var array = []; + for ( var i = 0; i < splitted.length; i++) { + var line = splitted[i].trim(); + var line_splited = line.split(/\s*[\s,]\s*/); + if (line_splited.length == 4) { + array.push([ Number(line_splited[0]), BUI.getStvArray(line_splited[1], line_splited[2]), BUI.getStvArray(line_splited[3], 0) ]); + } + } + + var id = (_this.id + "firid"); + var dygraphWidget = new StdDevDyGraph(id, { + width : (_this.width/2) - 10, + height :(_this.height/2) -110, + xlabel : 'q(nm-1)' + }); + dygraphWidget.draw(array, [ "#FF0000", "#0000FF", "#FF00FF" ], [ 's', 'I(exp)', 'I(sim)' ]); + }); + + adapter.getModelFile("FIR", models[0].modelId); + + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, data) { + var splitted = data.toString().split("\n"); + var array = []; + for ( var i = 0; i < splitted.length; i++) { + var line = splitted[i].trim(); + var line_splited = line.split(/\s*[\s,]\s*/); + if (line_splited.length == 3) { + array.push([ Number(line_splited[0]), BUI.getStvArray(line_splited[1], 0), BUI.getStvArray(line_splited[2], 0) ]); + } + } + + var id = (_this.id + "fitid"); + + var dygraphWidget = new StdDevDyGraph(id, { + width : (_this.width/2) - 10, + height : (_this.height/2) - 110, + xlabel : 'q(nm-1)' + }); + dygraphWidget.draw(array, [ "#FF0000", "#0000FF" ], [ "s", "I(exp)", "I(sim)" ]); + }); + adapter.getModelFile("FIT", models[0].modelId); +}; + +ModelVisualizerForm.prototype.getPanel = function(modelList){ + _this = this; + this.modelList = modelList; + this.panel = Ext.create('Ext.Panel', { + title: 'Results', + width: this.width, + height: this.height, + layout: { + type: 'vbox', // Arrange child items vertically +// align: 'stretch' // Each takes up full width + }, + items: [ + + ], + listeners : { + afterrender : function(grid, eOpts) { +// alert(_this.modelList) + } + } + }); + + return this.panel; + +}; + -/** VERTICES FORMATTER **/ -GraphCanvas.prototype.setVertexSize = function(vertexId, value) { - this.getFormatter().getVertexById(vertexId).getDefault().setSize(value); -}; +/** + * Example form + * + * @witdh + * @height + */ +function MolarityForm(args) { + this.id = BUI.id(); + this.width = 700; + this.height = 500; + + if (args != null) { + if (args.width != null) { + this.width = args.width; + } + if (args.height != null) { + this.height = args.height; + } + } + + this.onSave = new Event(this); + this.onClose = new Event(this); +} + + +MolarityForm.prototype._getNumericWithHelp = function(type, fieldLabel, fieldName, help) { + return Ext.create('Ext.container.Container', { + margin : "10 0 0 10", + items : [ { + xtype : type, + fieldLabel : fieldLabel, + name : fieldName, + id : this.id + fieldName, + decimalPrecision : 6 + }, { + xtype : 'label', + forId : 'myFieldId', + text : help, + margin : "5 0 0 105", + cls : "inline-help" + } ] + }); +}; + + +MolarityForm.prototype._getItems = function() { + this.macromoleculeCombo = BIOSAXS_COMBOMANAGER.getComboMacromoleculeByMacromolecules(this.getMacromuleculesCandidates(this.macromolecule), { + width : 250, + labelWidth : 100, + margin : 10 + }); + + return [ { + xtype : 'container', + flex : 1, + margin : '10 0 0 10', + border : 0, + layout : 'anchor', + defaultType : 'requiredtext', + items : [ this.macromoleculeCombo, + this._getNumericWithHelp("textfield", "Ratio", "ratio", "Number in assymmetric units") + ] + } ]; +}; + +MolarityForm.prototype._persist = function() { + var _this = this; + var macromoleculeId = this.macromoleculeCombo.getValue(); + var ratio = Ext.getCmp(this.id + "ratio").getValue(); + var comments = "Not used yet"; + var dataAdapter = new BiosaxsDataAdapter(); + this.panel.setLoading("Saving"); + dataAdapter.onSuccess.attach(function(sender, args) { + _this.onSave.notify(); + }); + dataAdapter.saveStoichiometry(this.macromolecule.macromoleculeId, macromoleculeId, ratio, comments); +}; + +MolarityForm.prototype._getButtons = function() { + var _this = this; + + function onClose() { + _this.onClose.notify(); + } + + return [ { + text : 'Save', + handler : function() { + _this._persist(); + } + }, { + text : 'Cancel', + handler : function() { + onClose(); + } + } ]; +}; + +MolarityForm.prototype.getPanel = function() { + this.panel = Ext.create('Ext.form.Panel', { +// width : null, + height : this.height, + margin : 2, + border : 1, + defaultType : 'requiredtext', + items : this._getItems(), + buttons : this._getButtons() + }); + return this.panel; +}; + +/** macromolecules contains all macromolecules except this one **/ +MolarityForm.prototype.getMacromuleculesCandidates = function(macromolecule) { + var macromolecules = []; + if ( BIOSAXS.proposal.macromolecules){ + for (var i = 0; i < BIOSAXS.proposal.macromolecules.length; i++) { + var m = BIOSAXS.proposal.macromolecules[i]; + if (this.macromolecule != null){ + if (m.macromoleculeId != this.macromolecule.macromoleculeId) { + macromolecules.push(m); + } + } + } + } + return macromolecules; +}; + + +/** It populates the form **/ +MolarityForm.prototype.refresh = function(macromolecule) { + this.macromolecule = macromolecule; + +}; + + +MolarityForm.prototype.input = function() { + return {}; +}; + + +/** It populates the form **/ +MolarityForm.prototype.test = function(targetId) { + var macromoleculeForm = new MolarityForm(); + var panel = macromoleculeForm.getPanel(); + panel.render(targetId); +}; -GraphCanvas.prototype.getVertexSize = function(vertexId) { - return this.getFormatter().getVertexById(vertexId).getDefault().getSize(); -}; +///** +// * +// * @witdh +// * @height +// */ +//function MacromoleculeForm(args) { +// this.id = BUI.id(); +// this.width = 700; +// this.height = 500; +// +// if (args != null) { +// if (args.width != null) { +// this.width = args.width; +// } +// if (args.height != null) { +// this.height = args.height; +// } +// } +// +// this.onClose = new Event(this); +//} +// +//MacromoleculeForm.prototype.getMacromolecule = function() { +// this.macromolecule.name = Ext.getCmp(this.panel.getItemId()).getValues().name; +// this.macromolecule.acronym = Ext.getCmp(this.panel.getItemId()).getValues().acronym; +// this.macromolecule.comments = Ext.getCmp(this.panel.getItemId()).getValues().comments; +// this.macromolecule.extintionCoefficient = Ext.getCmp(this.panel.getItemId()).getValues().extintionCoefficient; +// this.macromolecule.molecularMass = Ext.getCmp(this.panel.getItemId()).getValues().molecularMass; +// return this.macromolecule; +//}; +// +//MacromoleculeForm.prototype.setMacromolecule = function(macromolecule) { +// this.pdbStore.loadData(macromolecule.structure3VOs); +// +//}; +// +//MacromoleculeForm.prototype.getForm = function(macromolecule) { +// this.panel = Ext.createWidget('form', { +// frame : false, +// border : 0, +// padding : 15, +// width : 550, +// height : 350, +// items : [ { +// xtype : 'container', +// layout : 'hbox', +// items : [ { +// xtype : 'container', +// flex : 1, +// border : false, +// layout : 'anchor', +// defaultType : 'requiredtext', +// items : [ { +// fieldLabel : 'Name', +// name : 'name', +// anchor : '95%', +// tooltip : "Name of the macromolecule", +// value : macromolecule.name +// }, { +// fieldLabel : 'Acronym', +// name : 'acronym', +// anchor : '95%', +// value : macromolecule.acronym +// } ] +// }, { +// xtype : 'container', +// flex : 1, +// layout : 'anchor', +// defaultType : 'textfield', +// items : [ { +// xtype : 'numberfield', +// fieldLabel : 'Mol. Mass (Da)', +// name : 'molecularMass', +// value : macromolecule.molecularMass, +// decimalPrecision : 6 +// }, { +// xtype : 'numberfield', +// fieldLabel : 'Extinction coef.', +// name : 'extintionCoefficient', +// value : macromolecule.extintionCoefficient, +// decimalPrecision : 6 +// } ] +// } ] +// }, { +// xtype : 'textareafield', +// name : 'comments', +// fieldLabel : 'Comments', +// value : macromolecule.comments, +// width : this.width - 10 +// }, +// +// { +// html : BUI.getWarningHTML("The macromolecule is unsaved. Save the macromolecule to get access to other tabs"), +// margin : "150 10 10 10", +// id : this.id + "unsavedWarning", +// hidden : !(!macromolecule.macromoleculeId) +// } +// +// ] +// }); +// return this.panel; +//}; +// +//MacromoleculeForm.prototype.save = function() { +// var _this = this; +// +// var adapter = new BiosaxsDataAdapter(); +// adapter.onSuccess.attach(function(sender, proposal) { +// BIOSAXS.proposal.setItems(proposal); +// _this.panel.setLoading(false); +// +// Ext.getCmp(_this.id + "assembly").enable() +// Ext.getCmp(_this.id + "advanced").enable(); +// Ext.getCmp(_this.id + "unsavedWarning").hide(); +// }); +// +// if (this.getMacromolecule().name == "") { +// BUI.showError("Name field is mandatory"); +// return; +// } +// if (this.getMacromolecule().acronym == "") { +// BUI.showError("Acroynm field is mandatory"); +// return; +// } +// +// /** Check if acronym is unique * */ +// if (this.getMacromolecule().macromoleculeId == null) { +// if (BIOSAXS.proposal.getMacromoleculeByAcronym(this.getMacromolecule().acronym) == null) { +// this.panel.setLoading("ISPyB: Saving Macromolecule") +// adapter.saveMacromolecule(this.getMacromolecule()); +// } else { +// alert("There is already an existing macromolecule with the same acronym"); +// +// } +// } else { +// this.panel.setLoading("ISPyB: Saving Macromolecule") +// adapter.saveMacromolecule(this.getMacromolecule()); +// } +// +//}; +// +//MacromoleculeForm.prototype.getPanel = function(macromolecule) { +// var _this = this; +// this.macromolecule = macromolecule; +// return Ext.createWidget('tabpanel', { +// height : this.height, +// margin : 5, +// plain : true, +// style : { +// padding : 5 +// }, +// items : [ { +// tabConfig : { +// title : "General", +// disabled : false +// }, +// items : [ this.getForm(macromolecule) ], +// bbar : [ "->", { +// text : 'Save', +// cls : 'btn-with-border', +// style : { +// +// border : 1 +// }, +// handler : function() { +// _this.save(); +// } +// }, { +// text : 'Close', +// cls : 'btn-with-border', +// handler : function() { +// _this.onClose.notify(); +// } +// } ] +// }, { +// tabConfig : { +// id : this.id + "assembly", +// title : "Assembly", +// tooltip : 'Description of subunits present in the macromolecule', +// // hidden : (!macromolecule.macromoleculeId), +// disabled : (!macromolecule.macromoleculeId) +// }, +// items : [ this.getMolarityGrid(macromolecule) ] +// }, { +// tabConfig : { +// id : this.id + "advanced", +// title : "Advanced Modeling", +// // hidden : (!macromolecule.macromoleculeId), +// tooltip : 'Definition of the description contacts and symetries', +// disabled : (!macromolecule.macromoleculeId) +// }, +// items : [ this.getRigidBodyForm(macromolecule) ] +// } ] +// }); +//}; +// +//MacromoleculeForm.prototype.getRigidBodyForm = function(macromolecule) { +// var _this = this; +// +// // [P1, P2, P3, P4 ,P5 ,P6 ,32, P42, P52, P62] + P222 +// var symmetry = Ext.create('Ext.data.Store', { +// fields : [ 's' ], +// data : [ { +// "s" : "P1" +// }, { +// "s" : "P2" +// }, { +// "s" : "P3" +// }, { +// "s" : "P4" +// }, { +// "s" : "P5" +// }, { +// "s" : "P6" +// }, { +// "s" : "P32" +// }, { +// "s" : "P42" +// }, { +// "s" : "P52" +// }, { +// "s" : "P62" +// }, { +// "s" : "P222" +// } ] +// }); +// +// if (macromolecule.symmetry == null) { +// macromolecule.symmetry = "P1"; +// } +// var comboBox = Ext.create('Ext.form.ComboBox', { +// fieldLabel : 'Symmetry', +// store : symmetry, +// id : 'comboSym', +// queryMode : 'local', +// displayField : 's', +// valueField : 's', +// value : macromolecule.symmetry, +// margin : "10 0 0 0", +// listeners : { +// change : function(combo, newValue, oldValue, eOpts) { +// macromolecule.symmetry = newValue; +// } +// } +// }); +// +// this.rigidBodyPanel = Ext.createWidget('form', { +// frame : false, +// border : 0, +// padding : 10, +// width : 550, +// height : 400, +// items : [ { +// xtype : 'container', +// layout : 'hbox', +// items : [ { +// xtype : 'container', +// border : false, +// layout : 'hbox', +// items : [ { +// xtype : 'label', +// forId : 'myFieldId', +// text : 'Contact Desc:', +// width : 105, +// margin : '0 0 0 0' +// }, { +// xtype : 'textfield', +// hideLabel : true, +// id : "contactsDescriptionFilePath", +// margin : '0 0 0 0', +// width : 300, +// value : macromolecule.contactsDescriptionFilePath +// }, { +// text : 'Upload', +// xtype : 'button', +// margin : "0 0 0 20", +// width : 100, +// handler : function() { +// _this._openUploadDialog(macromolecule.macromoleculeId, "CONTACTS", "Upload Contact Description File"); +// } +// } ] +// } ] +// }, +// +// comboBox, { +// xtype : 'checkbox', +// margin : '10 0 0 5', +// boxLabel : "I want rigid body modeling run on this stuff", +// checked : true, +// width : 300 +// }, _this.getPDBGrid(macromolecule) ] +// }); +// return this.rigidBodyPanel; +//}; +// +//MacromoleculeForm.prototype.update = function() { +// var _this = this; +// BIOSAXS.proposal.onInitialized.attach(function() { +// if (BIOSAXS.proposal != null) { +// var macromolecules = BIOSAXS.proposal.macromolecules; +// for (var i = 0; i < macromolecules.length; i++) { +// if (macromolecules[i].macromoleculeId == _this.macromolecule.macromoleculeId) { +// _this.macromolecule = macromolecules[i]; +// _this.setMacromolecule(_this.macromolecule); +// _this.molarityStore.loadData(_this.parseMolarityData(_this.macromolecule)); +// _this.pdbGrid.setLoading(false); +// Ext.getCmp("contactsDescriptionFilePath").setValue(_this.macromolecule.contactsDescriptionFilePath) +// _this.molarityGrid.setLoading(false); +// +// } +// } +// } +// }); +// this.molarityGrid.setLoading("Updating"); +// this.pdbGrid.setLoading("Updating"); +// BIOSAXS.proposal.init(); +//}; +// +///******************************************************************************* +// * MOLARITY GRID +// ******************************************************************************/ +// +//MacromoleculeForm.prototype.parseMolarityData = function(macromolecule) { +// var data = []; +// if (macromolecule.stoichiometry != null) { +// for (var i = 0; i < macromolecule.stoichiometry.length; i++) { +// data.push({ +// ratio : macromolecule.stoichiometry[i].ratio, +// acronym : macromolecule.stoichiometry[i].macromolecule3VO.acronym, +// comments : macromolecule.stoichiometry[i].macromolecule3VO.comments, +// stoichiometryId : macromolecule.stoichiometry[i].stoichiometryId, +// name : macromolecule.stoichiometry[i].macromolecule3VO.name +// }); +// } +// } +// return data; +//}; +// +//MacromoleculeForm.prototype.getMolarityGrid = function(macromolecule) { +// var _this = this; +// +// this.molarityStore = Ext.create('Ext.data.Store', { +// fields : [ 'acronym', 'ratio', 'comments', 'stoichiometryId', 'name' ], +// data : this.parseMolarityData(macromolecule), +// sorters : { +// property : 'ratio', +// direction : 'DESC' +// } +// }); +// +// this.molarityGrid = Ext.create('Ext.grid.Panel', { +// store : this.molarityStore, +// height : 350, +// padding : 5, +// columns : [ +// +// { +// text : 'Subunit', +// columns : [ { +// text : "Acronym", +// width : 100, +// hidden : false, +// dataIndex : 'acronym', +// sortable : true +// }, { +// text : "Name", +// width : 100, +// hidden : false, +// dataIndex : 'name', +// sortable : true +// }, { +// text : "Comments", +// width : 100, +// dataIndex : 'comments', +// sortable : true +// } ] +// }, { +// text : "Number
in assymmetric units", +// width : 150, +// dataIndex : 'ratio', +// tooltip : 'Number of times this subunit is present in the macromolecule', +// sortable : true +// }, { +// id : this.id + 'MOLARITY_REMOVE', +// flex : 0.1, +// sortable : false, +// renderer : function(value, metaData, record, rowIndex, colIndex, store) { +// return BUI.getRedButton('REMOVE'); +// } +// } ], +// listeners : { +// cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { +// if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'MOLARITY_REMOVE') { +// var dataAdapter = new BiosaxsDataAdapter(); +// dataAdapter.onSuccess.attach(function() { +// _this.molarityGrid.setLoading(false); +// _this.update(); +// }); +// dataAdapter.removeStoichiometry(record.data.stoichiometryId); +// _this.molarityGrid.setLoading("Removing Structure"); +// } +// } +// }, +// buttons : [ { +// text : 'Add molarity', +// handler : function() { +// +// function onClose() { +// w.destroy(); +// _this.update(); +// } +// var w = Ext.create('Ext.window.Window', { +// title : 'Molarity', +// height : 300, +// width : 500, +// modal : true, +// buttons : [ { +// text : 'Save', +// handler : function() { +// var macromoleculeId = (_this.macromoleculeCombo.getValue()); +// var ratio = Ext.getCmp(_this.id + "ratio").getValue(); +// var comments = ""; +// var dataAdapter = new BiosaxsDataAdapter(); +// dataAdapter.onSuccess.attach(function(sender, args) { +// _this.update(); +// w.destroy(); +// }); +// dataAdapter.saveStoichiometry(_this.macromolecule.macromoleculeId, macromoleculeId, ratio, comments); +// } +// }, { +// text : 'Cancel', +// handler : function() { +// onClose(); +// } +// } ], +// items : [ _this.getMolarityForm(macromolecule) ], +// listeners : { +// onEsc : function() { +// onClose(); +// }, +// close : function() { +// onClose(); +// } +// } +// }).show(); +// } +// } ] +// }); +// return this.molarityGrid; +//}; +// +///******************************************************************************* +// * PDB GRID +// ******************************************************************************/ +// +//MacromoleculeForm.prototype._openUploadDialog = function(macromoleculeId, type, title) { +// var _this = this; +// function onClose() { +// w.destroy(); +// _this.update(); +// } +// +// var w = Ext.create('Ext.window.Window', { +// title : title, +// height : 200, +// width : 400, +// modal : true, +// buttons : [ { +// text : 'Close', +// handler : function() { +// onClose(); +// } +// } ], +// layout : 'fit', +// items : { +// html : "" +// }, +// listeners : { +// onEsc : function() { +// onClose(); +// }, +// close : function() { +// onClose(); +// } +// } +// }).show(); +//}; +// +//MacromoleculeForm.prototype._getPlugins = function() { +// var _this = this; +// var plugins = []; +// // if (this.updateRowEnabled) { +// plugins.push(Ext.create('Ext.grid.plugin.RowEditing', { +// clicksToEdit : 1, +// listeners : { +// validateedit : function(grid, e) { +// /** Comments are always updatable* */ +// e.record.raw.symmetry = e.newValues.symmetry; +// e.record.raw.multiplicity = e.newValues.multiplicity; +// +// var adapter = new BiosaxsDataAdapter(); +// adapter.onSuccess.attach(function(sender, measurement) { +// // _this.grid.setLoading(false); +// }); +// adapter.onError.attach(function() { +// alert("Error"); +// // _this.grid.setLoading(false); +// }); +// +// // _this.grid.setLoading(); +// adapter.saveStructure(e.record.raw); +// } +// } +// })); +// // } +// return plugins; +//}; +// +//MacromoleculeForm.prototype.getPDBGrid = function(macromolecule) { +// var _this = this; +// +// var data = macromolecule.structure3VOs; +// +// // /** Getting PDB from subunits **/ +// // if (macromolecule != null){ +// // if (macromolecule.stoichiometry != null){ +// // for (var i =0; i < macromolecule.stoichiometry.length; i++){ +// // var stoichiometry = macromolecule.stoichiometry[i]; +// // if (stoichiometry.macromolecule3VO != null){ +// // if (stoichiometry.macromolecule3VO.structure3VOs != null){ +// // for (var j = 0; j < stoichiometry.macromolecule3VO.structure3VOs.length; +// // j++) { +// // var structure = stoichiometry.macromolecule3VO.structure3VOs[j]; +// // structure["isSubunit"] = stoichiometry.macromolecule3VO.acronym; +// // data.push(structure) +// // } +// // } +// // } +// // } +// // } +// // } +// +// this.pdbStore = Ext.create('Ext.data.Store', { +// fields : [ 'filePath', 'structureId', 'structureType', 'symmetry', 'structureId', 'name', 'multiplicity' ], +// data : macromolecule.structure3VOs, +// groupField : 'structureType', +// sorters : { +// property : 'structureId', +// direction : 'DESC' +// } +// }); +// +// var groupingFeature = Ext.create('Ext.grid.feature.Grouping', { +// groupHeaderTpl : Ext.create('Ext.XTemplate', +// "
{name:this.formatName}
", { +// formatName : function(name) { +// return name; +// } +// }), +// hideGroupedHeader : true, +// startCollapsed : false +// }); +// +// this.pdbGrid = Ext.create('Ext.grid.Panel', { +// margin : "15 0 0 5", +// height : 250, +// store : this.pdbStore, +// plugins : _this._getPlugins(), +// buttons : [ { +// // text : 'Add PDB file', +// text : 'Add Modeling Option', +// handler : function() { +// _this._openUploadDialog(macromolecule.macromoleculeId, "PDB", 'Upload PDB File'); +// } +// } +// +// ], +// columns : [ +// { +// text : "structureId", +// flex : 0.2, +// hidden : true, +// dataIndex : 'structureId', +// sortable : true +// }, +// { +// text : "File", +// flex : 0.5, +// dataIndex : 'filePath', +// sortable : true, +// hidden : true +// }, +// { +// text : "PDB", +// flex : 0.4, +// dataIndex : 'name', +// sortable : true +// }, +// { +// text : "Symmetry", +// flex : 0.4, +// dataIndex : 'symmetry', +// sortable : true, +// editor : { +// xtype : 'combobox', +// typeAhead : true, +// triggerAction : 'all', +// selectOnTab : true, +// store : [ [ "P1", "P1" ], [ "P2", "P2" ], [ "P3", "P3" ], [ "P4", "P4" ], [ "P5", "P5" ], [ "P6", "P6" ], [ "P32", "P32" ], [ "P42", "P42" ], +// [ "P52", "P52" ], [ "P62", "P62" ], [ "P222", "P222" ] ], +// } +// }, { +// text : "Multiplicity", +// flex : 0.4, +// dataIndex : 'multiplicity', +// sortable : true, +// editor : { +// xtype : 'textfield' +// } +// +// }, { +// text : "Subunit", +// flex : 0.2, +// dataIndex : 'isSubunit', +// sortable : true, +// hidden : true +// }, { +// text : "Type", +// flex : 0.2, +// dataIndex : 'structureType', +// sortable : true, +// hidden : true +// }, +// +// { +// id : this.id + 'REMOVE', +// flex : 0.2, +// hidden : true, +// sortable : false, +// renderer : function(value, metaData, record, rowIndex, colIndex, store) { +// return BUI.getRedButton('REMOVE'); +// } +// }, ], +// +// listeners : { +// itemdblclick : function(dataview, record, item, e) { +// _this._editExperiment(record.raw.experimentId); +// }, +// cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { +// +// if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'REMOVE') { +// var dataAdapter = new BiosaxsDataAdapter(); +// dataAdapter.onSuccess.attach(function() { +// _this.pdbGrid.setLoading(false); +// _this.update(); +// }); +// dataAdapter.removeStructure(record.data.structureId); +// _this.pdbGrid.setLoading("Removing PDB file"); +// } +// +// } +// }, +// viewConfig : { +// getRowClass : function(record, rowIdx, params, store) { +// if (record.raw.isSubunit != null) { +// return "blue-row"; +// } +// } +// } +// }); +// +// return this.pdbGrid; +//}; +// +//MacromoleculeForm.prototype.getMolarityForm = function(macromolecule) { +// var _this = this; +// var data = []; +// for (var i = 0; i < BIOSAXS.proposal.macromolecules.length; i++) { +// var m = BIOSAXS.proposal.macromolecules[i]; +// if (m.macromoleculeId != macromolecule.macromoleculeId) { +// data.push(m); +// } +// +// } +// this.macromoleculeCombo = BIOSAXS_COMBOMANAGER.getComboMacromoleculeByMacromolecules(data, { +// width : 250, +// labelWidth : 100, +// margin : 10 +// }); +// +// return Ext.createWidget('form', { +// +// frame : false, +// border : 0, +// // padding : 15, +// width : 550, +// height : 350, +// items : [ { +// xtype : 'container', +// flex : 1, +// border : false, +// layout : 'anchor', +// defaultType : 'requiredtext', +// items : [ this.macromoleculeCombo, { +// xtype : 'numberfield', +// name : 'Ratio', +// id : _this.id + "ratio", +// fieldLabel : 'Number in assymmetric units', +// value : 1, +// decimalPrecision : 6, +// margin : 10 +// }, { +// xtype : 'textareafield', +// name : 'comments', +// fieldLabel : 'Comments', +// margin : 10, +// width : 400, +// value : "" +// +// } ] +// } ] +// }); +// +//}; +// +///******************************************************************************* +// * JAVASCRIPT DOC +// ******************************************************************************/ +//MacromoleculeForm.prototype.input = function() { +// return { +// macromolecule : DATADOC.getMacromolecule_10() +// }; +//}; +// +//MacromoleculeForm.prototype.test = function(targetId) { +// var macromoleculeForm = new MacromoleculeForm(); +// var panel = macromoleculeForm.getPanel(macromoleculeForm.input().macromolecule); +// panel.render(targetId); +//}; -GraphCanvas.prototype.setVertexStroke = function(vertexId, value) { - this.getFormatter().getVertexById(vertexId).getDefault().setStroke(value); -}; +function ResultSummaryForm() { + this.radiationDamageThreshold = BUI.getRadiationDamageThreshold(); + this.qualityThreshold = BUI.getQualityThreshold(); + + /** Data clusters with bufers **/ + this.clusterByBuffers = false; + + /** Visualization are groupeb by concenttrations. Ex: 4.5mg/ml and 4.7mg/ml will be clusterized together **/ + this.collapseConcentrations = true; + + this.summaryHeight = 350; + this.id = BUI.id(); + + var _this = this; + + this.qualityControlResultsWidget = new QualityControlResultsWidget({ + qualityThreshold : this.radiationDamageThreshold, + radiationDamageThreshold : this.qualityThreshold + + }); + this.qualityControlResultsWidget.onQualityChanged.attach(function(sender, value) { + _this.qualityThreshold = value; + _this.thresholdsChanged(); + }); + + this.qualityControlResultsWidget.onRadiationDatamageChanged.attach(function(sender, value) { + _this.radiationDamageThreshold = value; + _this.thresholdsChanged(); + + }); + this.concentrationHTMLTableWidget = new ConcentrationHTMLTableWidget({ + height : 250, + showBufferColumns : this.clusterByBuffers + }); + + this.plot = new BoxWhiskerGraph({ + targetId : _this.id + '_boxPlot', + height : 350, + width : 450, + maxBoxWidth : 25 + }); + + this.rangePlot = new RangeWhiskerGraph({ + targetId : _this.id + '_rangePlot', + height : 350, + width : 450, + maxBoxWidth : 25 + }); +} + +ResultSummaryForm.prototype.thresholdsChanged = function() { + var parsedData = this.prepareData(this.rawData); + + var filtered = JSON.parse(JSON.stringify(parsedData)); + filtered.concentration.concentrations = []; + for ( var conc in parsedData.concentration.concentrations) { + if (parsedData.concentration.concentrations[conc].calculation.rgGnom.validNumber > 0) { + filtered.concentration.concentrations.push(parsedData.concentration.concentrations[conc]); + } + } + + this.plotWhisker(filtered); + this.plotRange(filtered); + + this.concentrationHTMLTableWidget.refresh(parsedData); +}; + +/** Given a frame object (object returned by analyzeFrames()) and depending of the threshold indicates if a datacollection has radiation damage **/ +ResultSummaryForm.prototype.hasRadiationDamage = function(frameObject) { + var bb = frameObject.bufferBeforeFramesMerged / frameObject.framesCount; + var ba = frameObject.bufferAfterFramesMerged / frameObject.framesCount; + var mol = frameObject.framesMerge / frameObject.framesCount; + return !((bb >= this.radiationDamageThreshold) && (ba >= this.radiationDamageThreshold) && (mol >= this.radiationDamageThreshold)); +}; + +/** Return (frameObject) an object with the information about the frames for a data collection**/ +ResultSummaryForm.prototype.analyzeFrames = function(dataCollectionRow) { + return { + bufferAfterFramesMerged : dataCollectionRow.bufferAfterFramesMerged, + bufferBeforeFramesMerged : dataCollectionRow.bufferBeforeFramesMerged, + framesCount : dataCollectionRow.framesCount, + framesMerge : dataCollectionRow.framesMerge + }; +}; + +ResultSummaryForm.prototype.detectDataCollectionWarnings = function(dataCollectionRow) { + var frameObject = this.analyzeFrames(dataCollectionRow); + var warnings = { + count : 0, + type : [] + }; + + if (this.hasRadiationDamage(frameObject)) { + warnings.count = warnings.count + 1; + warnings.type.push("RADIATION DAMAGE"); + } + + if (Number(dataCollectionRow.quality) < this.qualityThreshold) { + warnings.count = 1;//warnings.count + 1; + warnings.type.push("Quality <" + this.qualityThreshold); + } + return warnings; +}; + +/** Return array composed by {concentration} objects **/ +ResultSummaryForm.prototype.getConcentrations = function(data) { + var concentrationsId = {}; + + for ( var i = 0; i < data.length; i++) { + var warning = this.detectDataCollectionWarnings(data[i]); + var id = data[i].conc;// + "_" + data[i].bufferId; + if (this.clusterByBuffers) { + id = data[i].conc + "_" + data[i].bufferId; + } + + if (concentrationsId[id] == null) { + concentrationsId[id] = { + id : id, + concentration : Number(data[i].conc).toFixed(2), + bufferId : data[i].bufferId, + bufferAcronym : data[i].bufferAcronym, + rgGuinier : [], + i0Guinier : [], + rgGnom : [], + dMax : [], + quality : [], + frames : [], + frames_warning : warning.count + }; + } else { + concentrationsId[id].frames_warning = concentrationsId[id].frames_warning + warning.count; + } + + concentrationsId[id].frames.push(data[i]); + + if (warning.count == 0) { + concentrationsId[id].rgGuinier.push(data[i].rgGuinier); + concentrationsId[id].i0Guinier.push(data[i].I0); + concentrationsId[id].quality.push(data[i].quality); + concentrationsId[id].rgGnom.push(data[i].rgGnom); + concentrationsId[id].dMax.push(data[i].dmax); + } + + } + var concentrations = []; + for ( var item in concentrationsId) { + if (concentrationsId.hasOwnProperty(item)) { + concentrations.push({ + concentration : concentrationsId[item].concentration, + id : item, + bufferId : Number(concentrationsId[item].bufferId).toFixed(2), + bufferAcronym : concentrationsId[item].bufferAcronym, + rgGuinier : concentrationsId[item].rgGuinier, + /** Frames **/ + frames : concentrationsId[item].frames, + frames_warning : concentrationsId[item].frames_warning, + /** Calculation **/ + calculation : { + rgGuinier : BUI.getStandardDeviation(concentrationsId[item].rgGuinier), + i0Guinier : BUI.getStandardDeviation(concentrationsId[item].i0Guinier), + quality : BUI.getStandardDeviation(concentrationsId[item].quality), + rgGnom : BUI.getStandardDeviation(concentrationsId[item].rgGnom), + dMax : BUI.getStandardDeviation(concentrationsId[item].dMax) + + } + }); + } + } + + return { + concentrations : concentrations + }; +}; + +ResultSummaryForm.prototype.prepareData = function(data) { + /** Return array composed by {acronym, bufferId} objects **/ + function getBuffers(data) { + var buffersId = {}; + for ( var i = 0; i < data.length; i++) { + buffersId[data[i].bufferId] = data[i].acronym; + } + var buffers = []; + for ( var id in buffersId) { + if (buffersId.hasOwnProperty(id)) { + buffers.push({ + acronym : buffersId[id], + bufferId : id + }); + } + } + return buffers; + } + + /** Get a string with all the concentrations **/ + function getConcentrationString(parseConcentrations) { + var concentrations = []; + for ( var i = 0; i < parseConcentrations.concentrations.length; i++) { + concentrations.push(parseConcentrations.concentrations[i].concentration + " mg/ml "); + } + return concentrations.toString(); + } + + var parseConcentrations = this.getConcentrations(data); + + return { + dataCollectionCount : data.length, + buffers : getBuffers(data), + concentration : parseConcentrations, + concentrationLabel : getConcentrationString(parseConcentrations) + }; +}; + +ResultSummaryForm.prototype.getDataForWhisker = function(data) { + var clusters = []; + + var concentrations = {}; + var i = 0; + var conc = 0; + if (this.collapseConcentrations) { + for (i = 0; i < data.concentration.concentrations.length; i++) { + conc = data.concentration.concentrations[i]; + var concentration = Number(conc.concentration).toFixed(0); + if (concentrations[concentration] == null) { + concentrations[concentration] = {}; + concentrations[concentration].concentration = concentration; + concentrations[concentration].calculation = {}; + concentrations[concentration].calculation.rgGuinier = {}; + concentrations[concentration].calculation.rgGuinier.values = []; + concentrations[concentration].calculation.rgGuinier.values = conc.calculation.rgGuinier.values; + concentrations[concentration].calculation.rgGnom = {}; + concentrations[concentration].calculation.rgGnom.values = []; + concentrations[concentration].calculation.rgGnom.values = conc.calculation.rgGnom.values; + } else { + concentrations[concentration].calculation.rgGuinier.values = concentrations[concentration].calculation.rgGuinier.values + .concat(conc.calculation.rgGuinier.values); + concentrations[concentration].calculation.rgGnom.values = concentrations[concentration].calculation.rgGnom.values + .concat(conc.calculation.rgGnom.values); + } + } + + /** From object to array **/ + var array = []; + for ( var key in concentrations) { + if (concentrations.hasOwnProperty(key)) { + array.push(concentrations[key]); + } + } + data.concentration.concentrations = array; + } + + for (i = 0; i < data.concentration.concentrations.length; i++) { + conc = data.concentration.concentrations[i]; + clusters.push({ + name : conc.concentration + "mg/ml", + concentration : Number(conc.concentration), + x : Number(conc.concentration), + classes : [] + }); + clusters[clusters.length - 1].classes.push({ + name : "Guinier", + color : '#9A2EFE', + values : conc.calculation.rgGuinier.values + + }); + clusters[clusters.length - 1].classes.push({ + name : "P(r)", + color : '#2E64FE', + values : conc.calculation.rgGnom.values + + }); + } + return { + clusters : clusters.sort(function(a, b) { + return a.concentration - b.concentration; + }) + }; +}; + +ResultSummaryForm.prototype.getDataForWhiskerQuality = function(data) { + var clusters = []; + + for ( var i = 0; i < data.concentration.concentrations.length; i++) { + var conc = data.concentration.concentrations[i]; + clusters.push({ + name : conc.concentration + "mg/ml", + classes : [] + }); + clusters[clusters.length - 1].classes.push({ + name : "Quality", + values : conc.calculation.quality.values + + }); + } + + return { + clusters : clusters + }; +}; + +ResultSummaryForm.prototype.plotQualityWhisker = function(parsedData) { + this.qualityPlot.refresh(this.getDataForWhiskerQuality(parsedData)); +}; + +ResultSummaryForm.prototype.plotWhisker = function(parsedData) { + this.plot.refresh(this.getDataForWhisker(parsedData)); +}; + +ResultSummaryForm.prototype.plotRange = function(parsedData) { + this.rangePlot.refresh(this.getDataForWhisker(parsedData)); +}; + +ResultSummaryForm.prototype.getPanel = function(data) { + var _this = this; + _this.rawData = data; + var parsedData = this.prepareData(data); + + this.panel = Ext + .createWidget( + 'form', + { + bodyPadding : 20, + frame : false, + border : 0, + width : this.width, + height : this.summaryHeight + 1000, + items : [ { + xtype : 'container', + layout : 'hbox', + items : [ { + xtype : 'container', + flex : 1, + border : false, + layout : 'anchor', + defaultType : 'textfield', + items : [ + _this.qualityControlResultsWidget.getPanel(), + { + xtype : 'fieldset', + width : 950, + margin : "20, 0 0 0", + height : this.summaryHeight + 500, + items : [ + { + html : "
Concentration Analysis
", + border : 0, + width : 900 + + }, + { + html : this.concentrationHTMLTableWidget.getPanel(parsedData), + border : 0, + width : 900, + margin : "10, 0 0 10" + + }, + { + xtype : 'container', + layout : 'vbox', + border : 5, + items : [ + { + html : "
Rg based on Guinier and Gnom
", + border : 8, + width : 900, + margin : "20 0 0 10" + + }, { + xtype : 'container', + layout : 'hbox', + items : [ { + xtype : 'container', + layout : 'vbox', + items : [ { + html : "
", + border : 0, + width : this.plot.width, + padding : "10 0 0 20", + listeners : { + afterrender : function() { + _this.plotWhisker(parsedData); + + } + } + }, { + html : "
Concentration (mg/ml)
", + width : 450, + border : 0, + margin : "-50 0 0 0" + + } ] + }, { + xtype : 'container', + layout : 'vbox', + items : [ { + html : "
", + border : 0, + width : this.rangePlot.width, + padding : "10 0 0 10", + listeners : { + afterrender : function() { + _this.plotRange(parsedData); + } + } + }, { + html : "
Concentration (mg/ml)
", + width : 450, + border : 0, + margin : "-50 0 0 0" + + } ] + } ] + } ] + } ] + } ] + } + + ] + } ] + }); + return this.panel; + +}; + +ResultSummaryForm.prototype.input = function() { + return { + data : DATADOC.getData_3367() + }; +}; + +ResultSummaryForm.prototype.test = function(targetId) { + var resultSummaryForm = new ResultSummaryForm(); + var panel = resultSummaryForm.getPanel(resultSummaryForm.input().data); + panel.render(targetId); + +}; -GraphCanvas.prototype.getVertexStroke = function(vertexId) { - return this.getFormatter().getVertexById(vertexId).getDefault().getStroke(); -}; +/** + * Example form + * + * @witdh + * @height + */ +function RigibBodyModelingForm(args) { + this.id = BUI.id(); + this.width = 700; + this.height = 500; + + if (args != null) { + if (args.width != null) { + this.width = args.width; + } + if (args.height != null) { + this.height = args.height; + } + } + + var _this = this; + this.rigidBodyGrid = new AprioriRigidBodyGrid(); + + this.rigidBodyGrid.onUploadFile.attach(function(sender, type, title){ + _this._openUploadDialog(_this.macromolecule.macromoleculeId, type, title); + }); + + this.rigidBodyGrid.onRemove.attach(function(sender, type, title){ + _this._update(); + }); + + this.onSave = new Event(this); + +} + +RigibBodyModelingForm.prototype._getItems = function() { + var _this = this; + + + return [ { + xtype : 'label', + forId : 'myFieldId', + text : 'Information for model fit, mixture analysis and rigid body modeling', + margin : '15 0 20 10', + cls : "inline-help" + }, + this.rigidBodyGrid.getPanel(), + { + xtype : 'label', + forId : 'myFieldId', + text : 'Distance restraints may be imposed on the model using contacts conditions file (OPTIONAL)', + margin : '25 0 5 10', + cls : "inline-help" + }, + { + xtype : 'container', + layout : 'hbox', + items : [ { + xtype : 'container', + border : false, + layout : 'hbox', + items : [ { + xtype : 'label', + forId : 'myFieldId', + text : 'Contact Description File: (Optional)', + width : 150, + margin : '10 0 0 10' + }, { + id : this.id + "contactsDescriptionFilePath", + xtype : 'textfield', + hideLabel : true, + margin : '10 0 0 0', + width : 200, + disabled: true + }, { + text : 'Upload', + xtype : 'button', + margin : "10 0 0 10", + width : 80, + handler : function() { + + _this._openUploadDialog(_this.macromolecule.macromoleculeId, "CONTACTS", "Upload Contact Description File"); + } + }, { + text : 'Remove', + id : _this.id + "_remove", + xtype : 'button', + margin : "10 0 0 10", + width : 80, + handler : function() { + _this.macromolecule.contactsDescriptionFilePath = null; + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, proposal) { + /** Updating the proposal global object **/ + BIOSAXS.proposal.setItems(proposal); + _this.panel.setLoading(false); + + var saved = BIOSAXS.proposal.getMacromoleculeByAcronym(_this.macromolecule.acronym); + /** Refreshing to check that everything is ok **/ + _this.refresh(saved); + _this.onSave.notify(saved); + }); + + _this.panel.setLoading("Saving Macromolecule") + adapter.saveMacromolecule(_this.macromolecule); + } + } ] + } ] + }, { + xtype : 'panel', + html : "Go to SASREF manual for further information", + margin : "10 0 0 160", + border : 0 + + }, + { + xtype : 'checkbox', + margin : '10 0 0 5', + boxLabel : "I want rigid body modeling run on this stuff", + checked : true, + width : 300 + } ] + +}; + +/** Because update is a jsp page we don't know if the user has uploaded a file or not then we need to refresh **/ +RigibBodyModelingForm.prototype._update = function(macromoleculeId, type, title) { + var _this = this; + BIOSAXS.proposal.onInitialized.attach(function() { + if (BIOSAXS.proposal != null) { + _this.refresh(BIOSAXS.proposal.getMacromoleculeById(_this.macromolecule.macromoleculeId)); + _this.panel.setLoading(false); + } + }); + this.panel.setLoading(); + BIOSAXS.proposal.init(); +}; + +RigibBodyModelingForm.prototype._openUploadDialog = function(macromoleculeId, type, title) { + var _this = this; + function onClose() { + w.destroy(); + _this._update(); + + } + + var w = Ext.create('Ext.window.Window', { + title : title, + height : 200, + width : 400, + modal : true, + buttons : [ { + text : 'Close', + handler : function() { + onClose(); + } + } ], + layout : 'fit', + items : { + html : "" + }, + listeners : { + onEsc : function() { + onClose(); + }, + close : function() { + onClose(); + } + } + }).show(); +}; + +RigibBodyModelingForm.prototype._getButtons = function() { + return []; +}; + +RigibBodyModelingForm.prototype.getPanel = function() { + var _this = this; + this.panel = Ext.create('Ext.form.Panel', { + width : this.width, + height : this.height, + margin : 10, + border : 1, + defaultType : 'textfield', + items : this._getItems(), + buttons : this._getButtons(), + listeners : { + afterrender : function(){ + _this._populate(); + + } + } + }); + return this.panel; +}; + +/** Populates could be call when the DOM is not filled yet **/ +RigibBodyModelingForm.prototype._populate = function() { + if (this.macromolecule != null){ + if (Ext.getCmp(this.id + "contactsDescriptionFilePath") != null){ + if (this.macromolecule.contactsDescriptionFilePath != null){ + Ext.getCmp(this.id + "contactsDescriptionFilePath").setValue(this.macromolecule.contactsDescriptionFilePath); + Ext.getCmp(this.id + "_remove").enable(); + } + else{ + Ext.getCmp(this.id + "_remove").disable(); + Ext.getCmp(this.id + "contactsDescriptionFilePath").setValue(""); + + } + } + } +}; + +/** It populates the form * */ +RigibBodyModelingForm.prototype.refresh = function(macromolecule) { + this.macromolecule = macromolecule; + this.rigidBodyGrid.refresh(macromolecule); + this._populate(); +}; + +RigibBodyModelingForm.prototype.input = function() { + return {}; +}; + + +/** It populates the form **/ +RigibBodyModelingForm.prototype.test = function(targetId) { + var macromoleculeForm = new RigibBodyModelingForm(); + var panel = macromoleculeForm.getPanel(); + panel.render(targetId); +}; + -GraphCanvas.prototype.setVertexStrokeOpacity = function(vertexId, value) { - this.getFormatter().getVertexById(vertexId).getDefault().setStrokeOpacity(value); -}; +/** + * Same form as MX part + * + * @creationMode if true a create button appears instead of save + * @showTitle true or false + */ +function ShipmentForm(args) { + this.id = BUI.id(); + + this.creationMode = false; + this.showTitle = true; + if (args != null) { + if (args.creationMode != null) { + this.creationMode = args.creationMode; + } + if (args.showTitle != null) { + this.showTitle = args.showTitle; + } + } + + this.onSaved = new Event(this); +} + +ShipmentForm.prototype.fillStores = function() { + this.panel.setLoading("Loading Labcontacts from database"); + this.labContactForSendingStore.loadData(BIOSAXS.proposal.getLabcontacts(), false); + this.labContactForReturnStore.loadData(BIOSAXS.proposal.getLabcontacts(), false); + this.panel.setLoading(false); + if (this.shipment != null) { + this.setShipment(this.shipment); + } +}; + +ShipmentForm.prototype.draw = function(targetId) { + this.getPanel().render(targetId); +}; + +ShipmentForm.prototype.setShipment = function(shipment) { + this.shipment = shipment; + var _this = this; + Ext.getCmp(_this.id + "shippingName").setValue(shipment.json.shippingName); + Ext.getCmp(_this.id + "shippingStatus").setValue(shipment.json.shippingStatus); + Ext.getCmp(_this.id + "comments").setValue(shipment.json.comments); + if (shipment.json.sendingLabContactVO != null) { + this.labContactsSendingCombo.setValue(shipment.json.sendingLabContactVO.labContactId); + } + if (shipment.json.returnLabContactVO != null) { + this.labContactsReturnCombo.setValue(shipment.json.returnLabContactVO.labContactId); + } + +}; + +ShipmentForm.prototype._saveShipment = function() { + var _this = this; + var shippingId = null; + if (this.shipment != null) { + shippingId = this.shipment.json.shippingId; + } + var json = { + shippingId : shippingId, + name : Ext.getCmp(_this.id + "shippingName").getValue(), + status : Ext.getCmp(_this.id + "shippingStatus").getValue(), + sendingLabContactId : Ext.getCmp(_this.id + "shipmentform_sendingLabContactId").getValue(), + returnLabContactId : Ext.getCmp(_this.id + "returnLabContactId").getValue(), + returnCourier : Ext.getCmp(_this.id + "returnCourier").getValue(), + courierAccount : Ext.getCmp(_this.id + "courierAccount").getValue(), + BillingReference : Ext.getCmp(_this.id + "BillingReference").getValue(), + dewarAvgCustomsValue : Ext.getCmp(_this.id + "dewarAvgCustomsValue").getValue(), + dewarAvgTransportValue : Ext.getCmp(_this.id + "dewarAvgTransportValue").getValue(), + comments : Ext.getCmp(_this.id + "comments").getValue() + }; + + var dataAdapter = new BiosaxsDataAdapter(); + dataAdapter.onSuccess.attach(function(sender, shipment) { + window.location = BUI.getShippingURL(shipment.shippingId); + }); + + dataAdapter.onError.attach(function(sender, error) { + _this.onError.notify(error); + }); + + /** Cheking params **/ + if (json.name == "") { + BUI.showError("Name field is mandatory"); + return; + } + + if (json.sendingLabContactId == null) { + BUI.showError("Lab contact for sending field is mandatory"); + return; + } + + if (json.returnLabContactId == null) { + BUI.showError("Lab contact for return field is mandatory"); + return; + } + + dataAdapter.createShipment(json.shippingId, json.name, json.status, json.comments, json.sendingLabContactId, json.returnLabContactId, + json.returnCourier, json.courierAccount, json.BillingReference, json.dewarAvgCustomsValue, json.dewarAvgTransportValue); + +}; + +ShipmentForm.prototype.getPanel = function(shipment) { + var _this = this; + this.shipment = shipment; + var required = '*'; + var buttons = []; + + if (_this.creationMode) { + buttons.push({ + text : 'Create', + scope : this, + handler : function() { + _this._saveShipment(); + } + }); + } else { + buttons.push({ + text : 'Save', + scope : this, + handler : function() { + _this._saveShipment(); + } + }); + + } + + this.labContactForSendingStore = Ext.create('Ext.data.Store', { + fields : [ 'cardName', 'labContactId' ] + }); + + this.labContactForReturnStore = Ext.create('Ext.data.Store', { + fields : [ 'cardName', 'labContactId' ] + }); + + // Create the combo box, attached to the states data store + this.labContactsSendingCombo = Ext.create('Ext.form.ComboBox', { + id : _this.id + "shipmentform_sendingLabContactId", + fieldLabel : 'Lab contact for sending', + afterLabelTextTpl : required, + store : this.labContactForSendingStore, + queryMode : 'local', + labelWidth : 200, + displayField : 'cardName', + valueField : 'labContactId' + }); + + this.labContactsReturnCombo = Ext.create('Ext.form.ComboBox', { + id : _this.id + "returnLabContactId", + fieldLabel : 'If No, Lab-Contact for Return', + afterLabelTextTpl : required, + store : this.labContactForReturnStore, + queryMode : 'local', + labelWidth : 200, + displayField : 'cardName', + valueField : 'labContactId', + listeners : { + change : function(x, newValue) { + for ( var i = 0; i < x.getStore().data.items.length; i++) { + if (x.getStore().data.items[i].raw.labContactId == newValue) { + Ext.getCmp(_this.id + "returnCourier").setValue(x.getStore().data.items[i].raw.defaultCourrierCompany); + Ext.getCmp(_this.id + "courierAccount").setValue(x.getStore().data.items[i].raw.courierAccount); + Ext.getCmp(_this.id + "BillingReference").setValue(x.getStore().data.items[i].raw.billingReference); + Ext.getCmp(_this.id + "dewarAvgCustomsValue").setValue(x.getStore().data.items[i].raw.dewarAvgCustomsValue); + Ext.getCmp(_this.id + "dewarAvgTransportValue").setValue(x.getStore().data.items[i].raw.dewarAvgTransportValue); + } + } + } + } + }); + + if (this.panel == null) { + this.panel = Ext.create('Ext.form.Panel', { + bodyPadding : 5, + width : 600, + border : 1, + items : [ { + xtype : 'fieldset', + title : 'Details', + collapsible : false, + defaultType : 'textfield', + layout : 'anchor', + defaults : { + anchor : '100%' + }, + items : [ { + xtype : 'requiredtext', + fieldLabel : 'Shipment Label', + allowBlank : false, + name : 'shippingName', + id : _this.id + 'shippingName', + value : '', + anchor : '50%' + }, { + + xtype : 'textareafield', + name : 'comments', + id : _this.id + 'comments', + fieldLabel : 'Comments', + value : '' + }, { + fieldLabel : 'Status', + readOnly : true, + id : _this.id + 'shippingStatus', + value : 'Opened', + anchor : '50%' + } ] + }, { + xtype : 'fieldset', + title : 'Lab-Contacts', + collapsible : false, + defaultType : 'textfield', + layout : 'anchor', + defaults : { + anchor : '100%' + }, + items : [ this.labContactsSendingCombo, this.labContactsReturnCombo ] + }, { + border : 0, + html : BUI.getWarningHTML("These informations are relevant for all shipments") + }, { + xtype : 'fieldset', + title : 'Courier accounts details for return', + collapsible : false, + layout : 'anchor', + defaults : { + anchor : '100%' + }, + items : [ { + xtype : 'textfield', + labelWidth : 400, + fieldLabel : 'Courier company for return (if ESRF sends a dewar back)', + id : _this.id + 'returnCourier', + value : '' + }, { + xtype : 'textfield', + labelWidth : 400, + fieldLabel : 'Courier account', + id : _this.id + 'courierAccount', + value : '' + }, { + xtype : 'textfield', + labelWidth : 400, + fieldLabel : 'Billing reference', + id : _this.id + 'BillingReference', + value : '' + }, { + xtype : 'numberfield', + labelWidth : 400, + fieldLabel : 'Average Customs value of a dewar (Euro)', + id : _this.id + 'dewarAvgCustomsValue', + value : '' + }, { + xtype : 'numberfield', + labelWidth : 400, + fieldLabel : 'Average Transport value of a dewar (Euro)', + id : _this.id + 'dewarAvgTransportValue', + value : '' + } ] + } ], + buttons : buttons + }); + } + this.fillStores(); + if (this.showTitle) { + this.panel.setTitle('Create a new Shipment'); + } + return this.panel; +}; + +ShipmentForm.prototype.input = function() { + return { + proposal : DATADOC.getProposal_10() + + }; +}; + +ShipmentForm.prototype.test = function(targetId) { + var shipmentForm = new ShipmentForm({ + creationMode : true + + }); + BIOSAXS.proposal = new Proposal(shipmentForm.input().proposal); + shipmentForm.getPanel().render(targetId); +}; -GraphCanvas.prototype.getVertexStrokeOpacity = function(vertexId) { - return this.getFormatter().getVertexById(vertexId).getDefault().getStrokeOpacity(); -}; +/** + * #onSaved + */ +function StockSolutionForm(args) { + this.id = BUI.id(); + this.actions = []; + + if (args != null) { + if (args.actions != null) { + this.actions = args.actions; + } + } + this.onSaved = new Event(this); +} + +StockSolutionForm.prototype.getStockSolution = function() { + if (this.stockSolution != null) { + this.stockSolution.concentration = Ext.getCmp(this.id + "stockSolution_concentration").getValue(); + this.stockSolution.storageTemperature = Ext.getCmp(this.id + "stockSolution_storageTemperature").getValue(); + this.stockSolution.volume = Ext.getCmp(this.id + "stockSolution_volume").getValue(); + this.stockSolution.comments = Ext.getCmp(this.id + "stockSolution_comments").getValue(); + this.stockSolution.name = Ext.getCmp(this.id + "stockSolution_name").getValue(); + this.stockSolution.bufferId = this.bufferCombo.getValue(); + + if (this.macromoleculeCombo.getValue() != null) { + this.stockSolution.macromoleculeId = this.macromoleculeCombo.getValue(); + } else { + this.stockSolution.macromolecule3VO = null; + } + + } else { + return { + concentration : Ext.getCmp(this.id + "stockSolution_concentration").getValue(), + storageTemperature : Ext.getCmp(this.id + "stockSolution_storageTemperature").getValue(), + volume : Ext.getCmp(this.id + "stockSolution_volume").getValue(), + comments : Ext.getCmp(this.id + "stockSolution_comments").getValue(), + name : Ext.getCmp(this.id + "stockSolution_name").getValue(), + bufferId : this.bufferCombo.getValue(), + macromoleculeId : this.macromoleculeCombo.getValue() + }; + } + return this.stockSolution; +}; + +StockSolutionForm.prototype.setStockSolution = function(stockSolution) { + if (stockSolution != null) { + if (stockSolution.macromoleculeId != null) { + this.macromoleculeCombo.setValue(stockSolution.macromoleculeId); + } + this.bufferCombo.setValue(stockSolution.bufferId); + Ext.getCmp(this.id + "stockSolution_concentration").setValue(this.stockSolution.concentration); + Ext.getCmp(this.id + "stockSolution_storageTemperature").setValue(this.stockSolution.storageTemperature); + Ext.getCmp(this.id + "stockSolution_volume").setValue(this.stockSolution.volume); + Ext.getCmp(this.id + "stockSolution_name").setValue(this.stockSolution.name); + Ext.getCmp(this.id + "stockSolution_comments").setValue(this.stockSolution.comments); + } +}; + +StockSolutionForm.prototype.getBufferCombo = function() { + this.bufferCombo = BIOSAXS_COMBOMANAGER.getComboBuffers(BIOSAXS.proposal.getBuffers(), { + labelWidth : 120, + margin : '0 0 10 0', + width : 220 + + }); + return this.bufferCombo; +}; + +StockSolutionForm.prototype.getMacromoleculeCombo = function() { + this.macromoleculeCombo = BIOSAXS_COMBOMANAGER.getComboMacromoleculeByMacromolecules(BIOSAXS.proposal.getMacromolecules(), { + labelWidth : 120, + margin : '0 0 10 0', + width : 220 + + }); + return this.macromoleculeCombo; +}; + +StockSolutionForm.prototype.refresh = function() { +}; + +StockSolutionForm.prototype._getTopPanel = function() { + return { + xtype : 'container', + layout : 'hbox', + border : 0, + items : [ { + xtype : 'container', + layout : 'hbox', + items : [ { + xtype : 'container', + flex : 1, + border : false, + layout : 'anchor', + defaultType : 'textfield', + items : [ + + this.getMacromoleculeCombo(), { + xtype : 'requiredtext', + id : this.id + 'stockSolution_name', + fieldLabel : 'Acronym', + labelWidth : 120, + width : 250 + }, { + xtype : 'requiredtext', + id : this.id + 'stockSolution_concentration', + fieldLabel : 'Conc. (mg/ml)', + labelWidth : 120, + width : 250 + }, + + { + id : this.id + 'stockSolution_storageTemperature', + fieldLabel : 'Storage Temp.(C)', + labelWidth : 120, + width : 250 + }, { + xtype : 'requiredtext', + id : this.id + 'stockSolution_volume', + fieldLabel : 'Volume in Well (µl)', + labelWidth : 120, + width : 250 + } ] + } ] + }, { + xtype : 'container', + flex : 1, + layout : 'anchor', + defaultType : 'textfield', + margin : '0 0 0 10', + items : [ this.getBufferCombo() ] + } ] + }; + +}; + +StockSolutionForm.prototype.getPanel = function(stockSolution) { + this.stockSolution = stockSolution; + this.panel = Ext.createWidget({ + xtype : 'container', + layout : 'vbox', + border : 0, + style : { + padding : '10px' + }, + fieldDefaults : { + labelAlign : 'left', + labelWidth : 50 + }, + items : [ this._getTopPanel(stockSolution), { + id : this.id + 'stockSolution_comments', + xtype : 'textareafield', + name : 'comments', + fieldLabel : 'Comments', + labelWidth : 120, + width : '100%' + } ] + }); + + this.setStockSolution(stockSolution); + return this.panel; +}; + +StockSolutionForm.prototype.input = function() { + return { + stock : { + "stockSolutionId" : 6, + "proposalId" : 3124, + "macromoleculeId" : 5933, + "bufferId" : 811, + "instructionSet3VO" : null, + "boxId" : 305861, + "storageTemperature" : "20", + "volume" : "300", + "concentration" : "1.2", + "comments" : "Buffer EDTA with A", + "name" : "A_EDTA_1.2", + "samples" : [], + "buffer" : "EDTA", + "macromolecule" : "A" + }, + proposal : new MeasurementGrid().input().proposal + }; +}; + +StockSolutionForm.prototype.test = function(targetId) { + var stockSolutionForm = new StockSolutionForm(); + BIOSAXS.proposal = new Proposal(stockSolutionForm.input().proposal); + var panel = stockSolutionForm.getPanel(new Shipment(stockSolutionForm.input().stock)); + panel.render(targetId); +}; -GraphCanvas.prototype.setVertexOpacity = function(vertexId, value) { - this.getFormatter().getVertexById(vertexId).getDefault().setOpacity(value); -}; + +BoxWhiskerGraph.prototype.cleanArray = GenericGraph.prototype.cleanArray; +BoxWhiskerGraph.prototype.plotAxes = GenericGraph.prototype.plotAxes; +BoxWhiskerGraph.prototype.plotRuler = GenericGraph.prototype.plotRuler; +BoxWhiskerGraph.prototype.drawSVGVerticalText = GenericGraph.prototype.drawSVGVerticalText; +BoxWhiskerGraph.prototype.getClassColor = GenericGraph.prototype.getClassColor; +BoxWhiskerGraph.prototype.getDimensions = GenericGraph.prototype.getDimensions; +BoxWhiskerGraph.prototype.calculate = GenericGraph.prototype.calculate; +BoxWhiskerGraph.prototype.getMedian = GenericGraph.prototype.getMedian; +BoxWhiskerGraph.prototype.pointToPixel = GenericGraph.prototype.pointToPixel; +BoxWhiskerGraph.prototype.maxValueWhisker = GenericGraph.prototype.maxValueWhisker; +BoxWhiskerGraph.prototype.refresh = GenericGraph.prototype.refresh; + +/** + * Subclass of GenericGraph + * + * @maxBoxWidth + */ +function BoxWhiskerGraph(args){ + this.maxBoxWidth = 25; + + if (args == null){ + args = new Object(); + } + args["plotHorizontalByCluster"] = true; + + GenericGraph.call(this, args); + + if (args.maxBoxWidth != null){ + this.maxBoxWidth = args.maxBoxWidth; + } +}; + + + +/** +There are several different methods for calculating quartiles.[1] This calculator uses a method described by Moore and McCabe to find quartile values. +The same method also used by The TI-83 to calculate quartile values. +With this method, the first quartile is the median of the numbers below the median, the third quartile is the median of the numbers above the median. + +http://www.miniwebtool.com/quartile-calculator/ +http://www.alcula.com/calculators/statistics/box-plot/ +**/ +BoxWhiskerGraph.prototype.getQ1 = function(array){ + array = array.slice(0, array.length/2); + return this.getMedian(array); +}; + +BoxWhiskerGraph.prototype.getQ3 = function(array){ + array = array.slice((array.length + 1)/2); + return this.getMedian(array); + +}; + +BoxWhiskerGraph.prototype.getQ2 = function(array){ + return this.getMedian(array); +}; + +BoxWhiskerGraph.prototype.getBelowOutliers = function(belowLimit, array){ + var points = new Array(); + for (var i = 0; i < array.length; i++){ + if (array[i] <= belowLimit){ + points.push(array[i]); + } + } + return points; +}; + +BoxWhiskerGraph.prototype.getAboveOutliers = function(aboveLimit, array){ + var points = new Array(); + for (var i = 0; i < array.length; i++){ + if (array[i] >= aboveLimit){ + points.push(array[i]); + } + } + return points; +}; + + +BoxWhiskerGraph.prototype.minValueWhisker = function(belowLimit, q1, array){ + var points = new Array(); + + for (var i = 0; i < array.length; i++){ + if ((array[i] < q1) && (array[i]) > belowLimit){ + points.push(array[i]); + } + } + + if (points.length > 0){ + points.sort(function(a, b){return a - b;}); + return points[0]; + } + return null; +}; + +BoxWhiskerGraph.prototype.isNumber = function(value){ + if (value=="") return false; + + var d = parseInt(value); + if (!isNaN(d)) return true; else return false; + +}; + +BoxWhiskerGraph.prototype.plotBoxWhisker = function(boxProperties){ + if (this.maxBoxWidth != null){ + if (boxProperties.width > this.maxBoxWidth){ + boxProperties.x = boxProperties.x + (boxProperties.width/2) - (this.maxBoxWidth/2); + boxProperties.width = this.maxBoxWidth; + } + + } + + if (this.plotPoints){ + for(var i = 0; i < boxProperties.values.length; i++){ + var value = boxProperties.values[i]; + var toPixel = this.pointToPixel(value, boxProperties); + SVG.drawCircle(boxProperties.x + (boxProperties.width/2) + 2, toPixel, this.pointRadius, this.svg, [["fill", "green"], ["fill-opacity", this.fillOpacityPoint],['stroke-opacity', this.strokeOpacityPoint], ["stroke", "black"]]); + } + } + + + var boxColor = this.getClassColor(boxProperties.name); + var result = this.calculate(boxProperties.values); + /** Q1 **/ + + if (this.isNumber(result.Q1)){ + SVG.drawLine(boxProperties.x, this.pointToPixel(result.Q1, boxProperties), boxProperties.x + boxProperties.width, this.pointToPixel(result.Q1, boxProperties), this.svg, [["stroke", boxColor]]); + } + /** Q2 **/ + if (this.isNumber(result.Q2)){ + SVG.drawLine(boxProperties.x, this.pointToPixel(result.Q2, boxProperties), boxProperties.x + boxProperties.width, this.pointToPixel(result.Q2, boxProperties), this.svg, [["stroke", "blue"], ["stroke-width", "2"]]); + } + + /** Q3 **/ + if (this.isNumber(result.Q3)){ + SVG.drawLine(boxProperties.x, this.pointToPixel(result.Q3, boxProperties), boxProperties.x + boxProperties.width, this.pointToPixel(result.Q3, boxProperties), this.svg, [["stroke", boxColor]]); + } + + /** Concenting Q1 and Q3 **/ + if (this.isNumber(result.Q1)&&this.isNumber(result.Q3)){ + SVG.drawLine(boxProperties.x, this.pointToPixel(result.Q1, boxProperties), boxProperties.x, this.pointToPixel(result.Q3, boxProperties), this.svg, [["stroke", boxColor]]); + SVG.drawLine(boxProperties.x + boxProperties.width, this.pointToPixel(result.Q1, boxProperties), boxProperties.x + boxProperties.width, this.pointToPixel(result.Q3, boxProperties), this.svg, [["stroke", boxColor]]); + } + + /** min-whisker **/ + if (result["min-whisker"] != null){ + if (this.isNumber(result.Q1)){ + SVG.drawLine(boxProperties.x + (boxProperties.width/2) + 2, this.pointToPixel(result.Q1, boxProperties), boxProperties.x + (boxProperties.width/2) + 2, this.pointToPixel(result["min-whisker"], boxProperties), this.svg, [["stroke", boxColor]]); + } + SVG.drawLine(boxProperties.x,this.pointToPixel(result["min-whisker"], boxProperties), boxProperties.x + boxProperties.width, this.pointToPixel(result["min-whisker"], boxProperties) , this.svg, [["stroke", boxColor]]); + + } + + /** max-whisker **/ + if (result["max-whisker"] != null){ + if (this.isNumber(result.Q3)){ + SVG.drawLine(boxProperties.x + (boxProperties.width/2) + 2, this.pointToPixel(result.Q3, boxProperties), boxProperties.x + (boxProperties.width/2) + 2, this.pointToPixel(result["max-whisker"], boxProperties), this.svg, [["stroke", boxColor]]); + } + SVG.drawLine(boxProperties.x , this.pointToPixel(result["max-whisker"], boxProperties), boxProperties.x + boxProperties.width, this.pointToPixel(result["max-whisker"], boxProperties), this.svg, [["stroke", boxColor]]); + + } + + /** outliners **/ + if (result["above-outliers"] != null){ + for (var point in result["above-outliers"]){ + SVG.drawCircle(boxProperties.x + (boxProperties.width/2) + 2, this.pointToPixel(result["above-outliers"][point], boxProperties), this.pointRadius, this.svg, [["fill", "red"], ["fill-opacity", "1"]]); + //SVG.drawText(boxProperties.x + (boxProperties.width/2) - 2, this.pointToPixel(result["above-outliers"][point], boxProperties) + 2, "x",this.svg, [["fill", "black"], ["font-size", "x-small"]]); + } + } + if (result["below-outliers"] != null){ + for (var point in result["below-outliers"]){ + SVG.drawCircle(boxProperties.x + (boxProperties.width/2) + 2, this.pointToPixel(result["below-outliers"][point], boxProperties), this.pointRadius, this.svg, [["fill", "red"], ["fill-opacity", "1"]]); + //SVG.drawText(boxProperties.x + (boxProperties.width/2) - 2, this.pointToPixel(result["below-outliers"][point], boxProperties) + 2, "x",this.svg, [["fill", "black"], ["font-size", "x-small"]]); + } + } +}; + + + +BoxWhiskerGraph.prototype.plotClusterTitles = function(properties){ + /** Cluster Titles **/ + var posX = this.left + this.rulerWidth; + var data = this.data; + for(var i = 0; i < data.clusters.length; i++ ){ + /** title for the clusters **/ + posX = posX + this.interClustersSpace; + + /** Drawing title of classes **/ + var cluster = data.clusters[i]; + var posXClasses = posX; + for(var j = 0; j < cluster.classes.length; j++){ + //SVG.drawRectangle(posXClasses, this.height - (this.bottom + this.clusterTitleHeight + this.rulerHeight), properties.classWidth, this.rulerHeight, this.svg, [["fill", "green"]]); +// SVG.drawText(posXClasses + 1/6*properties.classWidth, this.height - (this.bottom + this.clusterTitleHeight + (this.rulerHeight*1/4)), cluster.classes[j].name, this.svg, [["style", "font-size:xx-small"]]); + var color = cluster.classes[j].color; + this.drawSVGVerticalText(posXClasses + 1/2*(properties.classWidth), this.height - (this.bottom - this.clusterTitleHeight + (this.rulerHeight)), cluster.classes[j].name , [["style", "font-size:x-small;"], ["fill", color]]); + + // SVG.drawRectangle(posXClasses, this.height - (this.bottom + this.clusterTitleHeight), properties.classWidth, 2, this.svg, [["fill", "black"]]); + posXClasses = posXClasses + properties.classWidth + this.interClassesSpace; + } + + var clusterTitleSpace = (data.clusters[i].classes.length * properties.classWidth) + ((data.clusters[i].classes.length - 1) * this.interClassesSpace); + //SVG.drawRectangle(posX, this.height - (this.bottom + this.clusterTitleHeight), clusterTitleSpace, this.clusterTitleHeight, this.svg, [["fill", "pink"]]); + SVG.drawRectangle(posX, this.height - (this.clusterTitleHeight+this.bottom), clusterTitleSpace, 1, this.svg, []); + +// var transform = ["translate", "(" + posX + 1/3*clusterTitleSpace +", " + this.height - (this.bottom + (this.clusterTitleHeight*1/4)) +")"], ["transform", "rotate(180)"]; +// var transform = ["transform", "translate(" + (posX + 1/3*clusterTitleSpace) +", " + (this.height - (this.bottom + (this.clusterTitleHeight*1/4))) + "), rotate(-90) "]; +// this.drawSVGVerticalText(posX + 1/3*clusterTitleSpace, this.height - (this.bottom + (this.clusterTitleHeight*1/4)),data.clusters[i].name, [["style", "font-size:x-small"]]); + SVG.drawText(posX , this.height - (this.bottom + (this.clusterTitleHeight*1/4)),data.clusters[i].name ,this.svg, [["style", "font-size:x-small"]]); + posX = posX + clusterTitleSpace; + } +}; + +BoxWhiskerGraph.prototype.plotWhisters = function(data, properties){ + var colors = ["yellow", "orange", "green"]; + var posX = this.left + this.rulerWidth; + for(var i = 0; i < data.clusters.length; i++ ){ + var cluster = data.clusters[i]; + /** inter cluster space **/ + //SVG.drawRectangle(posX, this.top, this.interClustersSpace, this.height - this.top - this.bottom - this.clusterTitleHeight - this.rulerHeight, this.svg, [["fill", "black"]]); + posX = posX + this.interClustersSpace; + + for (var j = 0; j < cluster.classes.length; j ++){ + + //SVG.drawRectangle(posX, this.top, properties.classWidth, this.height - this.top - this.bottom - this.clusterTitleHeight - this.rulerHeight, this.svg, [["fill", colors[j]]]); + this.plotBoxWhisker( + { + name : cluster.classes[j].name, + values : cluster.classes[j].values, + minPoint : properties.minPoint, + maxPoint : properties.maxPoint, + x : posX, + y : this.top, + width : properties.classWidth, + height : this.height - this.top - this.bottom - this.clusterTitleHeight - this.rulerHeight + + + } + ); + posX = posX + properties.classWidth; + //SVG.drawRectangle(posX, this.top, this.interClassesSpace, this.height - this.top - this.bottom - this.clusterTitleHeight - this.rulerHeight, this.svg, [["fill", "black"]]); + if (j < cluster.classes.length - 1){ + posX = posX + this.interClassesSpace; + } + } + } +}; + +BoxWhiskerGraph.prototype.draw = function(targetId, data){ + this.targetId = targetId; + var properties = (this.getDimensions(data)); + this.svg = SVG.createSVGCanvas(document.getElementById(this.targetId), [["width", this.width], ["height", this.height]]); + this.plotAxes(properties); + this.plotClusterTitles(properties); + this.plotWhisters(data,properties); +}; + +BoxWhiskerGraph.prototype.input = function(){ + return DATADOC.getBoxWhikerData(); +}; + +BoxWhiskerGraph.prototype.test = function(targetId){ + var plot = new BoxWhiskerGraph( + { + targetId : targetId, + height : 350, + width : 450, + maxBoxWidth : 25 + } + ); + plot.refresh(this.input()); +}; + + + + + -GraphCanvas.prototype.getVertexOpacity = function(vertexId) { - return this.getFormatter().getVertexById(vertexId).getDefault().getOpacity(); -}; + +function DataSet(){ + this.json = null; + +}; + + +DataSet.prototype.loadFromJSON = function(json){ + if(json != null) { + if(this.validate(json)) { + this.json = json; + } + } +}; + + +DataSet.prototype.toJSON = function(json){ + return this.json; +}; + + +/** Abstract method to be override on childs classes **/ +DataSet.prototype.validate = function(json){ + if (true){ + return true; + } + /*else{ + throw "Data validation failed"; + }*/ +}; + + -GraphCanvas.prototype.setVertexFill = function(vertexId, color) { - this.getFormatter().getVertexById(vertexId).getDefault().setFill(color); -}; + +Edge.prototype.getName = GraphItem.prototype.getName; +Edge.prototype.setName = GraphItem.prototype.setName; +Edge.prototype.getId = GraphItem.prototype.getId; + +function Edge(id, name, nodeSource, nodeTarget, args){ + GraphItem.prototype.constructor.call(this, id, name, args); + + this.sourceNode = nodeSource; + this.targetNode = nodeTarget; + +}; + +Edge.prototype.toJSON = function(){ + return {"index": this.id,"sourceIndex":this.sourceNode.getId(),"targetIndex":this.targetNode.getId(),"args":this.args}; +}; + +Edge.prototype.getNodeSource = function(){ + return this.sourceNode; +}; + +Edge.prototype.getNodeTarget = function(){ + return this.targetNode; +}; + +Edge.prototype.remove = function(){ + //Remove edge object in the nodes + this.getNodeSource().removeEdge(this); + this.getNodeTarget().removeEdge(this); + + this.deleted.notify(this); +}; -GraphCanvas.prototype.getVertexFill = function(vertexId) { - return this.getFormatter().getVertexById(vertexId).getDefault().getFill(); -}; + +EdgeGraphFormatter.prototype.setProperties = ItemGraphFormatter.prototype.setProperties; +EdgeGraphFormatter.prototype.getDefault = ItemGraphFormatter.prototype.getDefault; +EdgeGraphFormatter.prototype.getSelected = ItemGraphFormatter.prototype.getSelected; +EdgeGraphFormatter.prototype.getOver = ItemGraphFormatter.prototype.getOver; +EdgeGraphFormatter.prototype.getDragging = ItemGraphFormatter.prototype.getDragging; +EdgeGraphFormatter.prototype.getId = ItemGraphFormatter.prototype.getId; +EdgeGraphFormatter.prototype.toJSON = ItemGraphFormatter.prototype.toJSON; +EdgeGraphFormatter.prototype.loadFromJSON = ItemGraphFormatter.prototype.loadFromJSON; +EdgeGraphFormatter.prototype._setEvents = ItemGraphFormatter.prototype._setEvents; + +function EdgeGraphFormatter(id, defaultFormat, selectedFormat, overFormat, dragginFormat){ + ItemGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, dragginFormat); + + this.getDefault().args.type = "EdgeGraphFormatter"; +}; + + + + +LineEdgeGraphFormatter.prototype.setProperties = EdgeGraphFormatter.prototype.setProperties; +LineEdgeGraphFormatter.prototype.getDefault = EdgeGraphFormatter.prototype.getDefault; +LineEdgeGraphFormatter.prototype.getSelected = EdgeGraphFormatter.prototype.getSelected; +LineEdgeGraphFormatter.prototype.getOver = EdgeGraphFormatter.prototype.getOver; +LineEdgeGraphFormatter.prototype.getDragging = EdgeGraphFormatter.prototype.getDragging; +LineEdgeGraphFormatter.prototype.getId = EdgeGraphFormatter.prototype.getId; +LineEdgeGraphFormatter.prototype.toJSON = EdgeGraphFormatter.prototype.toJSON; +LineEdgeGraphFormatter.prototype.loadFromJSON = EdgeGraphFormatter.prototype.loadFromJSON; +LineEdgeGraphFormatter.prototype._setEvents = EdgeGraphFormatter.prototype._setEvents; + +function LineEdgeGraphFormatter(id, defaultFormat, selectedFormat, overFormat, dragginFormat){ + EdgeGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, dragginFormat); + this.args.type = "LineEdgeGraphFormatter"; +}; + +/** DIRECTED **/ +DirectedLineEdgeGraphFormatter.prototype.setProperties = EdgeGraphFormatter.prototype.setProperties; +DirectedLineEdgeGraphFormatter.prototype.getDefault = EdgeGraphFormatter.prototype.getDefault; +DirectedLineEdgeGraphFormatter.prototype.getSelected = EdgeGraphFormatter.prototype.getSelected; +DirectedLineEdgeGraphFormatter.prototype.getOver = EdgeGraphFormatter.prototype.getOver; +DirectedLineEdgeGraphFormatter.prototype.getDragging = EdgeGraphFormatter.prototype.getDragging; +DirectedLineEdgeGraphFormatter.prototype.getId = EdgeGraphFormatter.prototype.getId; +DirectedLineEdgeGraphFormatter.prototype.toJSON = EdgeGraphFormatter.prototype.toJSON; +DirectedLineEdgeGraphFormatter.prototype.loadFromJSON = EdgeGraphFormatter.prototype.loadFromJSON; +DirectedLineEdgeGraphFormatter.prototype._setEvents = EdgeGraphFormatter.prototype._setEvents; + +function DirectedLineEdgeGraphFormatter(id, defaultFormat, selectedFormat, overFormat, dragginFormat){ + EdgeGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, dragginFormat); + this.args.type = "DirectedLineEdgeGraphFormatter"; + this.args.arrowSize = "3"; +}; +DirectedLineEdgeGraphFormatter.prototype.getArrowSize = function(){ + return this.args.arrowSize; +}; + +OdirectedLineEdgeGraphFormatter.prototype.setProperties = EdgeGraphFormatter.prototype.setProperties; +OdirectedLineEdgeGraphFormatter.prototype.getDefault = EdgeGraphFormatter.prototype.getDefault; +OdirectedLineEdgeGraphFormatter.prototype.getSelected = EdgeGraphFormatter.prototype.getSelected; +OdirectedLineEdgeGraphFormatter.prototype.getOver = EdgeGraphFormatter.prototype.getOver; +OdirectedLineEdgeGraphFormatter.prototype.getDragging = EdgeGraphFormatter.prototype.getDragging; +OdirectedLineEdgeGraphFormatter.prototype.getId = EdgeGraphFormatter.prototype.getId; +OdirectedLineEdgeGraphFormatter.prototype.toJSON = EdgeGraphFormatter.prototype.toJSON; +OdirectedLineEdgeGraphFormatter.prototype.loadFromJSON = EdgeGraphFormatter.prototype.loadFromJSON; +OdirectedLineEdgeGraphFormatter.prototype._setEvents = EdgeGraphFormatter.prototype._setEvents; + +function OdirectedLineEdgeGraphFormatter(id, defaultFormat, selectedFormat, overFormat, dragginFormat){ + EdgeGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, dragginFormat); + this.args.type = "DirectedLineEdgeGraphFormatter"; + this.args.arrowSize = "3"; +}; + +OdirectedLineEdgeGraphFormatter.prototype.getArrowSize = function(){ + return this.args.arrowSize; +}; + + +/** CUT (inhibition) **/ +CutDirectedLineEdgeGraphFormatter.prototype.setProperties = EdgeGraphFormatter.prototype.setProperties; +CutDirectedLineEdgeGraphFormatter.prototype.getDefault = EdgeGraphFormatter.prototype.getDefault; +CutDirectedLineEdgeGraphFormatter.prototype.getSelected = EdgeGraphFormatter.prototype.getSelected; +CutDirectedLineEdgeGraphFormatter.prototype.getOver = EdgeGraphFormatter.prototype.getOver; +CutDirectedLineEdgeGraphFormatter.prototype.getDragging = EdgeGraphFormatter.prototype.getDragging; +CutDirectedLineEdgeGraphFormatter.prototype.getId = EdgeGraphFormatter.prototype.getId; +CutDirectedLineEdgeGraphFormatter.prototype.toJSON = EdgeGraphFormatter.prototype.toJSON; +CutDirectedLineEdgeGraphFormatter.prototype.loadFromJSON = EdgeGraphFormatter.prototype.loadFromJSON; +CutDirectedLineEdgeGraphFormatter.prototype._setEvents = EdgeGraphFormatter.prototype._setEvents; + +function CutDirectedLineEdgeGraphFormatter(id, defaultFormat, selectedFormat, overFormat, dragginFormat){ + EdgeGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, dragginFormat); + this.args.type = "CutDirectedLineEdgeGraphFormatter"; +}; + + + + +BezierEdgeGraphFormatter.prototype.setProperties = EdgeGraphFormatter.prototype.setProperties; +BezierEdgeGraphFormatter.prototype.setProperties = EdgeGraphFormatter.prototype.setProperties; +BezierEdgeGraphFormatter.prototype.getDefault = EdgeGraphFormatter.prototype.getDefault; +BezierEdgeGraphFormatter.prototype.getSelected = EdgeGraphFormatter.prototype.getSelected; +BezierEdgeGraphFormatter.prototype.getOver = EdgeGraphFormatter.prototype.getOver; +BezierEdgeGraphFormatter.prototype.getDragging = EdgeGraphFormatter.prototype.getDragging; +BezierEdgeGraphFormatter.prototype.getId = EdgeGraphFormatter.prototype.getId; +BezierEdgeGraphFormatter.prototype.toJSON = EdgeGraphFormatter.prototype.toJSON; +BezierEdgeGraphFormatter.prototype.loadFromJSON = EdgeGraphFormatter.prototype.loadFromJSON; +BezierEdgeGraphFormatter.prototype._setEvents = EdgeGraphFormatter.prototype._setEvents; + +function BezierEdgeGraphFormatter(id, defaultFormat, selectedFormat, overFormat, dragginFormat){ + EdgeGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, dragginFormat); + this.args.type = "BezierEdgeGraphFormatter"; +}; + + + +DotDirectedLineEdgeGraphFormatter.prototype.setProperties = EdgeGraphFormatter.prototype.setProperties; +DotDirectedLineEdgeGraphFormatter.prototype.getDefault = EdgeGraphFormatter.prototype.getDefault; +DotDirectedLineEdgeGraphFormatter.prototype.getSelected = EdgeGraphFormatter.prototype.getSelected; +DotDirectedLineEdgeGraphFormatter.prototype.getOver = EdgeGraphFormatter.prototype.getOver; +DotDirectedLineEdgeGraphFormatter.prototype.getDragging = EdgeGraphFormatter.prototype.getDragging; +DotDirectedLineEdgeGraphFormatter.prototype.getId = EdgeGraphFormatter.prototype.getId; +DotDirectedLineEdgeGraphFormatter.prototype.toJSON = EdgeGraphFormatter.prototype.toJSON; +DotDirectedLineEdgeGraphFormatter.prototype.loadFromJSON = EdgeGraphFormatter.prototype.loadFromJSON; +DotDirectedLineEdgeGraphFormatter.prototype._setEvents = EdgeGraphFormatter.prototype._setEvents; + +function DotDirectedLineEdgeGraphFormatter(id, defaultFormat, selectedFormat, overFormat, dragginFormat){ + EdgeGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, dragginFormat); + this.args.type = "DotDirectedLineEdgeGraphFormatter"; +}; + + +OdotDirectedLineEdgeGraphFormatter.prototype.setProperties = EdgeGraphFormatter.prototype.setProperties; +OdotDirectedLineEdgeGraphFormatter.prototype.getDefault = EdgeGraphFormatter.prototype.getDefault; +OdotDirectedLineEdgeGraphFormatter.prototype.getSelected = EdgeGraphFormatter.prototype.getSelected; +OdotDirectedLineEdgeGraphFormatter.prototype.getOver = EdgeGraphFormatter.prototype.getOver; +OdotDirectedLineEdgeGraphFormatter.prototype.getDragging = EdgeGraphFormatter.prototype.getDragging; +OdotDirectedLineEdgeGraphFormatter.prototype.getId = EdgeGraphFormatter.prototype.getId; +OdotDirectedLineEdgeGraphFormatter.prototype.toJSON = EdgeGraphFormatter.prototype.toJSON; +OdotDirectedLineEdgeGraphFormatter.prototype.loadFromJSON = EdgeGraphFormatter.prototype.loadFromJSON; +OdotDirectedLineEdgeGraphFormatter.prototype._setEvents = EdgeGraphFormatter.prototype._setEvents; + +function OdotDirectedLineEdgeGraphFormatter(id, defaultFormat, selectedFormat, overFormat, dragginFormat){ + EdgeGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, dragginFormat); + this.args.type = "OdotDirectedLineEdgeGraphFormatter"; +}; + + -/** EDGES FORMATTER **/ -GraphCanvas.prototype.setEdgeSize = function(edgeId, value) { - this.getFormatter().getEdgeById(edgeId).getDefault().setSize(value); -}; +function GraphDataset(){ + DataSet.prototype.constructor.call(this); + this.edges = new Object(); + this.vertices = new Object(); + this.verticesIndex = new Object(); + + //Events + this.newVertex = new Event(this); + this.vertexNameChanged = new Event(this); + this.vertexDeleted = new Event(this); + + this.newEdge = new Event(this); + this.edgeNameChanged = new Event(this); + this.edgeDeleted = new Event(this); + + this.json = new Object(); + this.json.vertices = new Array(); + this.json.edges = new Array(); + this.json.relations = new Array(); +}; + +GraphDataset.prototype.loadFromJSON = DataSet.prototype.loadFromJSON; +GraphDataset.prototype.toJSON = DataSet.prototype.toJSON; +GraphDataset.prototype.validate = DataSet.prototype.validate; + +/** Devuelve el numero de edges incidentes sobre el nodo con mas edges **/ +GraphDataset.prototype.getMaxClass = function(){ + var maxClassNode = 0; + for ( var node in this.vertices) { + if (this.vertices[node].getEdgesCount() > maxClassNode){ + maxClassNode = this.vertices[node].getEdgesCount(); + } + } + return maxClassNode; +}; + +/** Devuelve el numero de edges incidentes sobre el nodo con mas edges **/ +GraphDataset.prototype.getMinClass = function(){ + var minClassNode = Math.min(); + for ( var node in this.vertices) { + if (this.vertices[node].getEdgesCount() < minClassNode){ + minClassNode = this.vertices[node].getEdgesCount(); + } + } + return minClassNode; +}; + +GraphDataset.prototype.getVertexByName = function(nodeName){ + var results = new Array(); + + for (var vertexId in this.verticesIndex[nodeName]){ + var vertexByid = this.getVertexById(this.verticesIndex[nodeName][vertexId]); + results.push(vertexByid); + //* añadido nuevo porque fallaba el anterior codigo + return vertexByid + } + + if (results <= 1){ + return this.getVertexById(this.verticesIndex[nodeName]); + } + else{ + return results; + } +}; + +GraphDataset.prototype.getVertexById = function(id){ + return this.vertices[id]; +}; + +GraphDataset.prototype.toSIF = function(){ + var sifDataAdapter = new SifFileDataAdapter(); + return sifDataAdapter.toSIF(this); +}; + +GraphDataset.prototype.toSIFID = function(){ + var sifDataAdapter = new SifFileDataAdapter(); + return sifDataAdapter.toSIFID(this); +}; + +GraphDataset.prototype.toDOT = function(){ + var dotFileDataAdapter = new DotFileDataAdapter(); + return dotFileDataAdapter.toDOT(this); +}; + +GraphDataset.prototype.toDOTID = function(){ + var dotFileDataAdapter = new DotFileDataAdapter(); + return dotFileDataAdapter.toDOTID(this); +}; + +GraphDataset.prototype._addNode = function(nodeName, args){ + return new Vertex(this._getVerticesCount()-1, nodeName, args); +}; + +GraphDataset.prototype.addNode = function(nodeName, args){ + this.json.vertices.push(nodeName); + this._addVerticesIndex(nodeName, this._getVerticesCount() - 1); + var vertex = this._addNode(nodeName, args); + this.vertices[this._getVerticesCount()-1] = vertex; + this._setNodeEvents(vertex); + this.newVertex.notify(vertex); +}; + +GraphDataset.prototype._addVerticesIndex = function(nodeName, id){ + if (this.verticesIndex[nodeName] == null){ + this.verticesIndex[nodeName] = new Array(); + } + this.verticesIndex[nodeName].push(id); +}; + +GraphDataset.prototype.addEdge = function(edgeName, nodeSourceId, nodeTargetId, args){ + this.json.edges.push(edgeName); + var nodeSource = this.getVertexById(nodeSourceId); + var nodeTarget = this.getVertexById(nodeTargetId); + var index = this.getEdgesCount() - 1; + this.edges[index] = new Edge(index, edgeName, nodeSource, nodeTarget, args); + this.json.relations.push({"index": index, "sourceIndex": nodeSourceId, "targetIndex": nodeTargetId, "args": args }); + + nodeSource.addEdge(this.edges[index]); + nodeTarget.addEdge(this.edges[index]); + this._setEdgeEvents(this.edges[index]); + this.newEdge.notify(this.edges[index]); +}; + +GraphDataset.prototype.getVertices = function(){ + return this.vertices; +}; + +GraphDataset.prototype.getEdges = function(){ + return this.edges; +}; + +GraphDataset.prototype.getEdgeById = function(edgeId){ + return this.edges[edgeId]; +}; + +GraphDataset.prototype._getVerticesCount = function(){ + return this.json.vertices.length; +}; + + +GraphDataset.prototype.getVerticesCount = function(){ + var count = 0; + for ( var vertex in this.getVertices()) { + count ++; + } + return count; +}; + + +GraphDataset.prototype.getEdgesCount = function(){ + return this.json.edges.length; +}; + +GraphDataset.prototype.init = function(){ + this.edges = new Object(); + this.vertices = new Object(); +}; + +GraphDataset.prototype._setNodeEvents = function(node){ + var _this = this; + //NODE EVENTS + node.deleted.attach(function (sender, node){ + _this._removeNode(node); + }); + + node.nameChanged.attach(function (sender, args){ + var item = args.item; + var newName = item.name; + var indexes = _this.verticesIndex[args.previousName]; + for(var i = 0; i < indexes.length; i++){ + if(indexes[i] == item.id) + indexes.splice(i,1); + } + if(indexes.length == 0){ + delete _this.verticesIndex[args.previousName]; + } + _this._addVerticesIndex(newName, item.id); + _this.json.vertices[item.id] = newName; + _this.vertexNameChanged.notify(args); + }); +}; + +GraphDataset.prototype._setEdgeEvents = function(edge){ + var _this = this; + //EDGE EVENTS + edge.nameChanged.attach(function (sender, edge){ + _this.edgeNameChanged.notify(edge); + + }); + + edge.deleted.attach(function (sender, edge){ + _this._removeEdge(edge); + }); +}; + +GraphDataset.prototype._connectVerticesByName = function(nodeNameSource, nodeNameTarget){ + var source = this.getVertexByName(nodeNameSource); + var target = this.getVertexByName(nodeNameTarget); + + if ((source != null)&&(target!=null)){ + this.addEdge(source.getName() +"_" + target.getName(), source.getId(), target.getId(), {}); + } + else{ + if (source == null){ + console.log("No encontrado: " + nodeNameSource) + } + if (target == null){ + console.log("No encontrado: " + nodeNameTarget) + } + } +}; + +GraphDataset.prototype.loadFromJSON = function(json){ + var json = json; + this.init(); + this.json = new Object(); + this.json.vertices = new Array(); + this.json.edges = new Array(); + this.json.relations = new Array(); + + for ( var i = 0; i < json.nodes.length; i++) { + if (json.nodes[i] != null){ + var name = json.nodes[i]; + this.addNode(name); + } + else{ + this.json.vertices.push(null); + } + } + + for ( var i = 0; i < json.edges.length; i++) { + if (json.edges[i] != null){ + if (json.relations[i] != null){ + var name = json.edges[i]; + this.addEdge(name, json.relations[i].sourceIndex, json.relations[i].targetIndex, json.relations[i].args); + } + } + else{ + this.json.edges.push(null); + this.json.relations.push(null); + } + } +}; + +GraphDataset.prototype.prettyPrint = function(){ + for ( var node in this.vertices) { + console.log(this.vertices[node].getName() ); + for ( var j = 0; j < this.vertices[node].getEdgesIn().length; j++) { + console.log(" --> " + this.vertices[node].getEdgesIn()[j].getNodeTarget().getName() ); + } + } +}; + +GraphDataset.prototype._removeEdge = function(edge){ + this.json.edges[edge.getId()] = null; + this.json.relations[edge.getId()] = null; + + delete this.edges[edge.getId()]; + this.edgeDeleted.notify(edge); + + +}; + +GraphDataset.prototype._removeNode = function(node){ + this.json.vertices[node.getId()] = null; + delete this.vertices[node.getId()]; + this.vertexDeleted.notify(node); +}; + +GraphDataset.prototype.toJSON = function(){ + var json = new Object(); + var nodes = new Array(); + json.nodes = this.json.vertices; //nodes; + json.edges = this.json.edges; //edges; + json.relations = this.json.relations; + return json; +}; + +GraphDataset.prototype.clone = function(){ + var dsDataset = new GraphDataset(); + dsDataset.loadFromJSON(this.toJSON()); + return dsDataset; +}; +//GraphDataset.prototype.test = function(){ +// this.loadFromJSON(this.toJSON()); +//}; + +function labels(){ + var names = new Array(); + + var dataset = interactomeViewer.graphEditorWidget.dataset; + var layout = interactomeViewer.graphEditorWidget.layout; + + for ( var vertexId in interactomeViewer.graphEditorWidget.dataset.getVertices()) { + names.push(interactomeViewer.graphEditorWidget.dataset.getVertexById(vertexId).getName()); + } + + var sorted = (names.sort()); + console.log(sorted) + var distance = 0.01; + var altura = 0.6; + for ( var i = 0; i < names.length; i++) { + var id =dataset.getVertexByName(names[i]).getId(); + + layout.getNodeById(id).setCoordenates(distance, altura); + + + distance = parseFloat(distance) + parseFloat(0.03); + + altura = parseFloat(altura) + parseFloat(0.02); + + if (parseFloat(altura) == 0.9800000000000003){ + + altura = 0.6; + distance = distance - 0.51; + } + + } + + +}; -GraphCanvas.prototype.getEdgeSize = function(edgeId) { - return this.getFormatter().getEdgeById(edgeId).getDefault().getSize(); -}; +function GraphItem(id, name, args){ + this.id = id; + this.name = name; + this.type = "NONE"; + + this.args = new Object(); + + + if (args!=null){ + this.args = args; + if (args.type !=null){ + this.type = args.type; + } + } + + //Events + this.nameChanged = new Event(this); + this.deleted = new Event(this); +} + +GraphItem.prototype.getName = function(){ + return this.name; +}; + +GraphItem.prototype.getId = function(){ + return this.id; +}; + +GraphItem.prototype.setName = function(name){ + var oldName = this.getName(); + this.name = name; + this.nameChanged.notify({"item": this, "previousName" : oldName}); +}; + + + + -GraphCanvas.prototype.setEdgeStroke = function(edgeId, value) { - this.getFormatter().getEdgeById(edgeId).getDefault().setStroke(value); -}; +function LayoutDataset(){ + this.dataset = null; + this.vertices = new Object(); + this.changed = new Event(this); + + + this.args = new Object(); + + //RANDOM, CIRCLE + this.args.type = "CIRCLE"; +}; + +LayoutDataset.prototype.loadFromJSON = function(dataset, json){ + var _this = this; + this.vertices = new Object(); + this.dataset = dataset; //new GraphDataset(); + for ( var vertex in json) { + this.vertices[vertex] = new NodeLayout(vertex, json[vertex].x, json[vertex].y); + this.vertices[vertex].changed.attach(function (sender, item){ + _this.changed.notify(item); + }); + } + this._attachDatasetEvents(); +}; + + +LayoutDataset.prototype.toJSON = function(){ + var serialize = new Object(); + for ( var vertex in this.vertices) { + serialize[vertex] = new Object(); + serialize[vertex].x = this.vertices[vertex].x; + serialize[vertex].y = this.vertices[vertex].y; + } + serialize.dataset = new Object(); + serialize.dataset =this.dataset.toJSON(); + return serialize; +}; + +LayoutDataset.prototype.dataBind = function(graphDataset){ + this.dataset = graphDataset; + this._attachDatasetEvents(); + this._calculateLayout(); +}; + +LayoutDataset.prototype._removeVertex = function(vertexId){ + delete this.vertices[vertexId]; +}; + +LayoutDataset.prototype._attachDatasetEvents = function(){ + var _this = this; + + this.dataset.vertexDeleted.attach(function (sender, item){ + _this._removeVertex(item.getId()); + }); + + this.dataset.newVertex.attach(function (sender, item){ + _this.vertices[item.getId()] = new NodeLayout(item.getId(), 0.5, 0.5); + _this.vertices[item.getId()].changed.attach(function (sender, item){ + _this.changed.notify(item); + }); + }); +}; + +LayoutDataset.prototype.getType = function(){ + return this.args.type; +}; + +LayoutDataset.prototype._calculateLayoutVertices = function(type, count){ + + if (type == "CIRCLE"){ + var radius = 0.4; + var centerX = 0.5; + var centerY = 0.5; + var verticesCoordinates = new Array(); + for(var i = 0; i < count; i++){ + x = centerX + radius * Math.sin(i * 2 * Math.PI/count); + y = centerY + radius * Math.cos(i * 2 * Math.PI/count); + verticesCoordinates.push({'x':x,'y':y}); + } + return verticesCoordinates; + } +}; + + +LayoutDataset.prototype._calculateLayout = function(){ + var _this = this; + if (this.getType() == "RANDOM"){ + for ( var vertex in this.dataset.getVertices()) { + if (this.vertices[vertex] == null){ + this.vertices[vertex] = new NodeLayout(vertex, 0, 0); + } + this.vertices[vertex].setCoordinates(Math.random(), Math.random()); + this.vertices[vertex].changed.attach(function (sender, item){ + _this.changed.notify(item); + }); + } + } + + if ( this.getType() == "CIRCLE"){ + + var count = this.dataset._getVerticesCount(); + var verticesCoordinates = this._calculateLayoutVertices(this.getType(), count); + + var aux = 0; + for ( var vertex in this.dataset.getVertices()) { + if (this.vertices[vertex] == null){ + this.vertices[vertex] = new NodeLayout(vertex, 0, 0); + } + this.vertices[vertex].setCoordinates(verticesCoordinates[aux].x, verticesCoordinates[aux].y);//{"x":, "y":}; + aux++; + this.vertices[vertex].changed.attach(function (sender, item){ + _this.changed.notify(item); + }); + } + } + + + if (this.getType() == "SQUARE"){ + + var count = this.dataset._getVerticesCount(); + var xMin = 0.1; + var xMax = 0.9; + var yMin = 0.1; + var yMax = 0.9; + + var rows = Math.sqrt(count); + var step = (xMax - xMin) / rows; + + var verticesCoordinates = new Array(); + for(var i = 0; i < rows; i ++){ + for ( var j = 0; j < rows; j++) { + x = i * step + xMin; + y = j * step + yMin; + verticesCoordinates.push({'x':x,'y':y}); + } + } + + var aux = 0; + for ( var vertex in this.dataset.getVertices()) { + if (this.vertices[vertex] == null){ + this.vertices[vertex] = new NodeLayout(vertex, 0, 0); + } + this.vertices[vertex].setCoordinates(verticesCoordinates[aux].x, verticesCoordinates[aux].y);//{"x":, "y":}; + aux++; + this.vertices[vertex].changed.attach(function (sender, item){ + _this.changed.notify(item); + }); + } + } + +}; + +LayoutDataset.prototype.getNodeById = function(id){ + return this.vertices[id]; +}; + +LayoutDataset.prototype.getVerticesByArea = function(x1, y1, x2, y2){ + var vertices = new Array(); + for ( var vertex in this.dataset.getVertices()) { + if ((this.vertices[vertex].x >= x1)&&(this.vertices[vertex].x <= x2)){ + if ((this.vertices[vertex].y >= y1)&&(this.vertices[vertex].y <= y2)){ + vertices.push(this.vertices[vertex]); + } + } + } + return vertices; +}; + + + + +LayoutDataset.prototype.getLayout = function(type){ + + if (type == "CIRCLE"){ + this.args.type = "CIRCLE"; + this._calculateLayout(); + return; + } + + if (type == "SQUARE"){ + this.args.type = "SQUARE"; + this._calculateLayout(); + return; + } + + if (type == "RANDOM"){ + this.args.type = "RANDOM"; + this._calculateLayout(); + return; + } + + + var dotText = this.dataset.toDOTID(); + var url = "http://bioinfo.cipf.es/utils/ws/rest/network/layout/"+type+".coords"; + var _this = this; + + $.ajax({ + async: true, + type: "POST", + url: url, + dataType: "text", + data: { + dot :dotText + }, + cache: false, + success: function(data){ + var response = JSON.parse(data); + for ( var vertexId in response) { + _this.vertices[vertexId].setCoordinates(response[vertexId].x, response[vertexId].y); + } + } + }); + +// $.ajax({ +// async: true, +// type: "POST", +// url: url, +// dataType: "script", +// data: { +// dot :dotText +// }, +// cache: false, +// success: function(data){ +// var response = JSON.parse(data); +// for ( var vertexId in response) { +// _this.vertices[vertexId].setCoordinates(response[vertexId].x, response[vertexId].y); +// } +// } +// }); + +}; + +function NodeLayout(id, x, y, args){ + this.id = id; + this.x = x; + this.y = y; + this.changed = new Event(this); +}; + +NodeLayout.prototype.getId = function(id){ + return this.id; +}; + +NodeLayout.prototype.setCoordinates = function(x, y){ + this.x = x; + this.y = y; + this.changed.notify(this); +}; + -GraphCanvas.prototype.getEdgeStroke = function(edgeId) { - return this.getFormatter().getEdgeById(edgeId).getDefault().getStroke(); -}; + +function NetworkDataSetFormatter(vertexFormatProperties, defaultEdgeProperties, args){ + DataSet.prototype.constructor.call(this); + + this.args = new Object(); + + this.vertices = new Object(); + this.edges = new Object(); + this.dataset = null; + this.maxClass = 0; + this.minClass = 0; + + /** Coordenates with default Setting */ + this.args.width = 1100; + this.args.height = 500; + + /** Optional parameters */ + this.args.backgroundColor = "#FFFFFF"; + this.args.backgroundImage = null; + this.args.backgroundImageHeight = null; + this.args.backgroundImageWidth = null; + this.args.backgroundImageX = null; + this.args.backgroundImageY = null; + + + this.args.balanceNodes = false; + this.args.nodesMaxSize = 3; + this.args.nodesMinSize = 0.5; + + + /** Zoom **/ + this.args.zoomScale = 1; + this.args.zoomScaleStepFactor = 0.4; + + if (args != null){ + if (args.backgroundImage != null){ + this.args.backgroundImage = args.backgroundImage; + } + + if (args.width != null){ + this.args.width = args.width; + } + + if (args.height != null){ + this.args.height = args.height; + this.args.svgHeight = args.height; + } + + if (args.svgHeight != null){ + this.args.svgHeight = args.svgHeight; + } + + if (args.backgroundColor != null){ + this.args.backgroundColor = args.backgroundColor; + } + + if(args.balanceNodes != null){ + this.args.balanceNodes = args.balanceNodes; + } + + if(args.nodesMaxSize != null){ + this.args.nodesMaxSize = args.nodesMaxSize; + } + if(args.nodesMinSize != null){ + this.args.nodesMinSize = args.nodesMinSize; + } + } + + this.args.defaultEdgeProperties = {"fill":"#000000", "radius":"1", "stroke":"#000000", "size":1, "title":{"fontSize":10, "fontColor":"#000000"}}; + this.args.vertexFormatProperties = { "fill":"#000000", "stroke":"#000000", "stroke-opacity":"#000000"}; + + if (vertexFormatProperties!= null){ + this.args.vertexFormatProperties = vertexFormatProperties; + } + if (defaultEdgeProperties!= null){ + this.args.defaultEdgeProperties = defaultEdgeProperties; + } + + /** Events **/ + this.changed = new Event(this); + this.edgeChanged = new Event(this); + this.resized = new Event(this); + this.backgroundImageChanged= new Event(this); + this.backgroundColorChanged= new Event(this); +}; + +NetworkDataSetFormatter.prototype.loadFromJSON = function(dataset, json){ + this.args = new Object(); + this.vertices = new Object(); + this.args = json; + this._setDataset(dataset); + var _this = this; + + for ( var vertex in json.vertices) { + this.addVertex(vertex, json.vertices[vertex]); + } + + for ( var edgeId in json.edges) { + this.addEdge(edgeId, json.edges[edgeId]); + } +}; + + +NetworkDataSetFormatter.prototype.toJSON = function(){ + + +// this.args.vertices = new Object(); +// this.args.edges = new Object(); +// +// for ( var vertex in this.vertices) { +// this.args.vertices[vertex] = this.getVertexById(vertex).toJSON(); +// } +// for ( var edge in this.edges) { +// this.args.edges[edge] = this.getEdgeById(edge).toJSON(); +// } +// +// return (this.args); + + + var serialize = new Object(); + serialize = JSON.parse(JSON.stringify(this.args)); + serialize.vertices = new Object(); + serialize.edges = new Object(); + + for ( var vertex in this.vertices) { + serialize.vertices[vertex] = this.getVertexById(vertex).toJSON(); + } + for ( var edge in this.edges) { + serialize.edges[edge] = this.getEdgeById(edge).toJSON(); + } + + return (serialize); +}; + + + +NetworkDataSetFormatter.prototype._getNodeSize = function(nodeId){ + if (this.isVerticesBalanced()){ + var total = this.maxClass - this.minClass; + if (total == 0){ total = 1;} + var nodeConnection = this.dataset.getVertexById(nodeId).getEdges().length; + return ((nodeConnection*this.args.nodesMaxSize)/total) + this.args.nodesMinSize; + } + return this.getVertexById(nodeId).getDefault().getSize(); +}; + +NetworkDataSetFormatter.prototype._recalculateSize = function(){ + if (this.isVerticesBalanced()){ + this.maxClass = this.dataset.getMaxClass(); + this.minClass = this.dataset.getMinClass(); + for ( var vertexIdAll in this.vertices) { + var size = this._getNodeSize(vertexIdAll); + this.vertices[vertexIdAll].getDefault().setSize(size); + this.vertices[vertexIdAll].getSelected().setSize(size); + this.vertices[vertexIdAll].getOver().setSize(size); + } + } +}; + + +NetworkDataSetFormatter.prototype.addVertex = function(vertexId, json){ + + + if (json == null){ + this.vertices[vertexId] = new CircleVertexGraphFormatter(vertexId, this.args.vertexFormatProperties.defaultFormat, this.args.vertexFormatProperties.selected, this.args.vertexFormatProperties.over, this.args.vertexFormatProperties.dragging); + } + else{ + + /** Cargo los attributos desde el json **/ + if (json.type == null){ + this.vertices[vertexId] = new CircleVertexGraphFormatter(vertexId, this.args.vertexFormatProperties.defaultFormat, this.args.vertexFormatProperties.selected, this.args.vertexFormatProperties.over, this.args.vertexFormatProperties.dragging); + } + + if ((json.type == "SquareVertexGraphFormatter")||(json.type == "SquareVertexNetworkFormatter")){ + this.vertices[vertexId] = new SquareVertexGraphFormatter(vertexId); + this.vertices[vertexId].loadFromJSON(json); + } + + if ((json.type == "CircleVertexGraphFormatter")||(json.type == "CircleVertexNetworkFormatter")){ + this.vertices[vertexId] = new CircleVertexGraphFormatter(vertexId); + this.vertices[vertexId].loadFromJSON(json); + } + + if ((json.type == "EllipseVertexGraphFormatter")||(json.type == "EllipseVertexNetworkFormatter")){ + this.vertices[vertexId] = new EllipseVertexGraphFormatter(vertexId); + this.vertices[vertexId].loadFromJSON(json); + } + + if ((json.type == "RectangleVertexGraphFormatter")||(json.type == "RectangleVertexNetworkFormatter")){ + this.vertices[vertexId] = new RectangleVertexGraphFormatter(vertexId); + this.vertices[vertexId].loadFromJSON(json); + } + + if ((json.type == "RoundedVertexGraphFormatter")||(json.type == "RoundedVertexNetworkFormatter")){ + this.vertices[vertexId] = new RoundedVertexGraphFormatter(vertexId); + this.vertices[vertexId].loadFromJSON(json); + } + + } + + + var _this = this; + this.vertices[vertexId].stateChanged.attach(function (sender, item){ + _this.changed.notify(item); + }); + + var size = this._getNodeSize(vertexId); + this.vertices[vertexId].defaultFormat.args.size = size; + this.vertices[vertexId].selected.args.size = size; + this.vertices[vertexId].over.args.size = size; + +}; + +NetworkDataSetFormatter.prototype.addEdge = function(edgeId, json){ + + /** Es un edge nuevo que le doy los atributos por defecto **/ + if (json == null){ + if (this.dataset.getEdgeById(edgeId).getNodeSource().getId() == this.dataset.getEdgeById(edgeId).getNodeTarget().getId()){ + this.edges[edgeId] = new BezierEdgeGraphFormatter(edgeId, this.args.defaultEdgeProperties.defaultFormat, this.args.defaultEdgeProperties.selected, this.args.defaultEdgeProperties.over, this.args.defaultEdgeProperties.dragging); + }else{ + this.edges[edgeId] = new DirectedLineEdgeGraphFormatter(edgeId, this.args.defaultEdgeProperties.defaultFormat, this.args.defaultEdgeProperties.selected, this.args.defaultEdgeProperties.over, this.args.defaultEdgeProperties.dragging); + } + } + else{ + /** Cargo los attributos desde el json **/ + if (json.type == null){ + this.edges[edgeId] = new LineEdgeGraphFormatter(edgeId, this.args.defaultEdgeProperties.defaultFormat, this.args.defaultEdgeProperties.selected, this.args.defaultEdgeProperties.over, this.args.defaultEdgeProperties.dragging); + } + + if ((json.type == "LineEdgeGraphFormatter")||(json.type == "LineEdgeNetworkFormatter")){ + this.edges[edgeId] = new LineEdgeGraphFormatter(edgeId); + this.edges[edgeId].loadFromJSON(json); + } + if ((json.type == "DirectedLineEdgeGraphFormatter")||(json.type == "DirectedLineEdgeNetworkFormatter")){ + this.edges[edgeId] = new DirectedLineEdgeGraphFormatter(edgeId); + this.edges[edgeId].loadFromJSON(json); + } + + if ((json.type == "BezierEdgeGraphFormatter")||(json.type == "BezierEdgeNetworkFormatter")){ + this.edges[edgeId] = new BezierEdgeGraphFormatter(edgeId); + this.edges[edgeId].loadFromJSON(json); + } + + + if ((json.type == "CutDirectedLineEdgeGraphFormatter")||(json.type == "CutDirectedLineEdgeNetworkFormatter")){ + this.edges[edgeId] = new CutDirectedLineEdgeGraphFormatter(edgeId); + this.edges[edgeId].loadFromJSON(json); + } + + if ((json.type == "DotDirectedLineEdgeGraphFormatter")||(json.type == "DotDirectedLineEdgeNetworkFormatter")){ + this.edges[edgeId] = new DotDirectedLineEdgeGraphFormatter(edgeId); + this.edges[edgeId].loadFromJSON(json); + } + if ((json.type == "OdotDirectedLineEdgeGraphFormatter")||(json.type == "OdotDirectedLineEdgeNetworkFormatter")){ + this.edges[edgeId] = new OdotDirectedLineEdgeGraphFormatter(edgeId); + this.edges[edgeId].loadFromJSON(json); + } + + if ((json.type == "OdirectedLineEdgeGraphFormatter")||(json.type == "OdirectedLineEdgeNetworkFormatter")){ + this.edges[edgeId] = new OdirectedLineEdgeGraphFormatter(edgeId); + this.edges[edgeId].loadFromJSON(json); + } + } + + var _this = this; + this.edges[edgeId].stateChanged.attach(function (sender, item){ + _this.edgeChanged.notify(item); + }); + + this._recalculateSize(); +}; + +NetworkDataSetFormatter.prototype._setDataset = function(dataset){ + this.dataset = dataset; + this.maxClass = dataset.getMaxClass(); + this.minClass = dataset.getMinClass(); + this._attachDatasetEvents(); +}; + +NetworkDataSetFormatter.prototype.changeEdgeType = function(edgeId, type){ + if ((type == "LineEdgeGraphFormatter")||(type == "LineEdgeNetworkFormatter")){ + this.edges[edgeId] = new LineEdgeGraphFormatter(edgeId, this.args.defaultEdgeProperties.defaultFormat, this.args.defaultEdgeProperties.selected, this.args.defaultEdgeProperties.over, this.args.defaultEdgeProperties.dragging); + + } + if ((type == "DirectedLineEdgeGraphFormatter")||(type == "DirectedLineEdgeNetworkFormatter")){ + this.edges[edgeId] = new DirectedLineEdgeGraphFormatter(edgeId, this.args.defaultEdgeProperties.defaultFormat, this.args.defaultEdgeProperties.selected, this.args.defaultEdgeProperties.over, this.args.defaultEdgeProperties.dragging); + } + + if ((type == "CutDirectedLineEdgeGraphFormatter")||(type == "CutDirectedLineEdgeNetworkFormatter")){ + this.edges[edgeId] = new CutDirectedLineEdgeGraphFormatter(edgeId, this.args.defaultEdgeProperties.defaultFormat, this.args.defaultEdgeProperties.selected, this.args.defaultEdgeProperties.over, this.args.defaultEdgeProperties.dragging); + } + + if ((type == "DotDirectedLineEdgeGraphFormatter")||(type == "DotDirectedLineEdgeNetworkFormatter")){ + this.edges[edgeId] = new DotDirectedLineEdgeGraphFormatter(edgeId, this.args.defaultEdgeProperties.defaultFormat, this.args.defaultEdgeProperties.selected, this.args.defaultEdgeProperties.over, this.args.defaultEdgeProperties.dragging); + } + + if ((type == "OdotDirectedLineEdgeGraphFormatter")||(type == "OdotDirectedLineEdgeNetworkFormatter")){ + this.edges[edgeId] = new OdotDirectedLineEdgeGraphFormatter(edgeId, this.args.defaultEdgeProperties.defaultFormat, this.args.defaultEdgeProperties.selected, this.args.defaultEdgeProperties.over, this.args.defaultEdgeProperties.dragging); + } + + if ((type == "OdirectedLineEdgeGraphFormatter")||(type == "OdirectedLineEdgeNetworkFormatter")){ + this.edges[edgeId] = new OdirectedLineEdgeGraphFormatter(edgeId, this.args.defaultEdgeProperties.defaultFormat, this.args.defaultEdgeProperties.selected, this.args.defaultEdgeProperties.over, this.args.defaultEdgeProperties.dragging); + } + + + + var _this = this; + this.edges[edgeId].stateChanged.attach(function (sender, item){ + _this.edgeChanged.notify(item); + }); + _this.edgeChanged.notify(this.edges[edgeId]); +}; +/* +classe = "SquareNetworkNodeFormatter"; +} +if (value == "circle"){ + classe = "CircleNetworkNodeFormatter"; + **/ +NetworkDataSetFormatter.prototype.changeNodeType = function(vertexId, type){ + var defaultFormat = JSON.parse(JSON.stringify(this.vertices[vertexId].getDefault())); + var selectedFormat = JSON.parse(JSON.stringify(this.vertices[vertexId].getSelected())); + var defaultFormat = JSON.parse(JSON.stringify(this.vertices[vertexId].getDefault())); + var defaultFormat = JSON.parse(JSON.stringify(this.vertices[vertexId].getDefault())); + + if ((type == "SquareVertexGraphFormatter")||(type == "SquareVertexNetworkFormatter")){ + this.vertices[vertexId] = new SquareVertexGraphFormatter(vertexId,defaultFormat, this.args.vertexFormatProperties.selected, this.args.vertexFormatProperties.over, this.args.vertexFormatProperties.dragging); + } + + if ((type == "CircleVertexGraphFormatter")||(type == "CircleVertexNetworkFormatter")){ + this.vertices[vertexId] = new CircleVertexGraphFormatter(vertexId, defaultFormat, this.args.vertexFormatProperties.selected, this.args.vertexFormatProperties.over, this.args.vertexFormatProperties.dragging); + } + + if ((type == "EllipseVertexGraphFormatter")||(type == "EllipseVertexNetworkFormatter")){ + this.vertices[vertexId] = new EllipseVertexGraphFormatter(vertexId, defaultFormat, this.args.vertexFormatProperties.selected, this.args.vertexFormatProperties.over, this.args.vertexFormatProperties.dragging); + } + + if ((type == "RectangleVertexGraphFormatter")||(type == "RectangleVertexNetworkFormatter")){ + this.vertices[vertexId] = new RectangleVertexGraphFormatter(vertexId, defaultFormat, this.args.vertexFormatProperties.selected, this.args.vertexFormatProperties.over, this.args.vertexFormatProperties.dragging); + } + + if ((type == "RoundedVertexGraphFormatter")||(type == "RoundedVertexNetworkFormatter")){ + this.vertices[vertexId] = new RoundedVertexGraphFormatter(vertexId, defaultFormat, this.args.vertexFormatProperties.selected, this.args.vertexFormatProperties.over, this.args.vertexFormatProperties.dragging); + } + + if ((type == "OctagonVertexGraphFormatter")||(type == "OctagonVertexNetworkhFormatter")){ + this.vertices[vertexId] = new OctagonVertexGraphFormatter(vertexId, defaultFormat, this.args.vertexFormatProperties.selected, this.args.vertexFormatProperties.over, this.args.vertexFormatProperties.dragging); + } + + + var _this = this; + this.vertices[vertexId].stateChanged.attach(function (sender, item){ + _this.changed.notify(item); + }); + _this.changed.notify(this.vertices[vertexId]); +}; + + + +NetworkDataSetFormatter.prototype.dataBind = function(networkDataSet){ + var _this = this; + this._setDataset(networkDataSet); + + for ( var vertex in this.dataset.getVertices()) { + this.addVertex(vertex); + } + + for ( var edge in this.dataset.getEdges()) { + this.addEdge(edge); + } +}; + +NetworkDataSetFormatter.prototype._removeEdge = function(edgeId){ + delete this.edges[edgeId]; +}; + +NetworkDataSetFormatter.prototype._removeVertex = function(vertexId){ + delete this.vertices[vertexId]; +}; + +NetworkDataSetFormatter.prototype._attachDatasetEvents = function(id){ + var _this = this; + this.dataset.vertexDeleted.attach(function (sender, item){ + _this._removeVertex(item.getId()); + }); + + this.dataset.edgeDeleted.attach(function (sender, item){ + _this._removeEdge(item.getId()); + }); + + this.dataset.newVertex.attach(function (sender, item){ + _this.addVertex(item.getId()); + }); + + this.dataset.newEdge.attach(function (sender, item){ + _this.addEdge(item.getId()); + }); +}; + + +NetworkDataSetFormatter.prototype.getVertexById = function(id){ + return this.vertices[id]; +}; + +NetworkDataSetFormatter.prototype.getEdgeById = function(id){ + return this.edges[id]; +}; + +NetworkDataSetFormatter.prototype.makeLabelsBigger = function(){ +for ( var vertexId in this.vertices) { + var fontSize = this.vertices[vertexId].getDefault().getFontSize() + 2; + this.vertices[vertexId].getDefault().setFontSize(fontSize); + } +}; + +NetworkDataSetFormatter.prototype.makeLabelsSmaller = function(){ + for ( var vertexId in this.vertices) { + var fontSize = this.vertices[vertexId].getDefault().getFontSize() - 2; + this.vertices[vertexId].getDefault().setFontSize(fontSize); + } +}; + + +NetworkDataSetFormatter.prototype.resize = function(width, height){ + this.args.width = width; + this.args.height = height; + this.resized.notify(); +}; + +/** ZOOM GETTERS **/ +NetworkDataSetFormatter.prototype.getZoomScaleStepFactor = function(){return this.args.zoomScaleStepFactor;}; +NetworkDataSetFormatter.prototype.setZoomScaleStepFactor = function(scaleFactor){this.args.zoomScaleStepFactor = scaleFactor;}; +NetworkDataSetFormatter.prototype.getZoomScale = function(){return this.args.zoomScale;}; +NetworkDataSetFormatter.prototype.setZoomScale = function(scale){this.args.zoomScale = scale;}; + +NetworkDataSetFormatter.prototype.getNodesMaxSize = function(){return this.args.nodesMaxSize;}; +NetworkDataSetFormatter.prototype.getNodesMinSize = function(){return this.args.nodesMinSize;}; + +/** SIZE SETTERS AND GETTERS **/ +NetworkDataSetFormatter.prototype.isVerticesBalanced = function(){return this.args.balanceNodes;}; +NetworkDataSetFormatter.prototype.getWidth = function(){return this.args.width;}; +NetworkDataSetFormatter.prototype.getHeight = function(){return this.args.height;}; + +/** OPTIONAL PARAMETERS GETTERS AND SETTERS **/ +NetworkDataSetFormatter.prototype.getBackgroundImage = function(){return this.args.backgroundImage;}; +NetworkDataSetFormatter.prototype.setBackgroundImage = function(value){this.args.backgroundImage = value; this.backgroundImageChanged.notify(this);}; + + +NetworkDataSetFormatter.prototype.getBackgroundImageWidth = function(){return this.args.backgroundImageWidth;}; +NetworkDataSetFormatter.prototype.setBackgroundImageWidth = function(value){this.args.backgroundImageWidth = value; this.backgroundImageChanged.notify(this);}; + +NetworkDataSetFormatter.prototype.getBackgroundImageHeight = function(){return this.args.backgroundImageHeight;}; +NetworkDataSetFormatter.prototype.setBackgroundImageHeight = function(value){this.args.backgroundImageHeight = value; this.backgroundImageChanged.notify(this);}; + +NetworkDataSetFormatter.prototype.getBackgroundImageX = function(){return this.args.backgroundImageX;}; +NetworkDataSetFormatter.prototype.setBackgroundImageX = function(value){this.args.backgroundImageX = value; this.backgroundImageChanged.notify(this);}; + +NetworkDataSetFormatter.prototype.getBackgroundImageY = function(){return this.args.backgroundImageX;}; +NetworkDataSetFormatter.prototype.setBackgroundImageY = function(value){this.args.backgroundImageY = value; this.backgroundImageChanged.notify(this);}; + + + +NetworkDataSetFormatter.prototype.getBackgroundColor = function(){return this.args.backgroundColor;}; +NetworkDataSetFormatter.prototype.setBackgroundColor = function(value){this.args.backgroundColor = value; this.backgroundColorChanged.notify(this);}; + +NetworkDataSetFormatter.prototype.getWidth = function(){return this.args.width;}; +NetworkDataSetFormatter.prototype.setWidth = function(value){this.args.width = value;}; + +NetworkDataSetFormatter.prototype.getHeight = function(){return this.args.height;}; +NetworkDataSetFormatter.prototype.setHeight = function(value){this.args.height = value;}; + + -GraphCanvas.prototype.setEdgeStrokeOpacity = function(edgeId, value) { - this.getFormatter().getEdgeById(edgeId).getDefault().setStrokeOpacity(value); -}; + +Vertex.prototype.getName = GraphItem.prototype.getName; +Vertex.prototype.setName = GraphItem.prototype.setName; +Vertex.prototype.getId = GraphItem.prototype.getId; + +function Vertex(id, name, args){ + GraphItem.prototype.constructor.call(this, id, name, args); + this.edgesIn= new Array(); + this.edgesOut= new Array(); +}; + +Vertex.prototype.getEdges = function(){ + return this.edgesIn.concat(this.edgesOut); +}; + +Vertex.prototype.getEdgesCount = function(){ + return this.getEdges().length; +}; + +Vertex.prototype.getEdgesIn = function(){ + return this.edgesIn; +}; + +Vertex.prototype.getEdgesOut = function(){ + return this.edgesOut; +}; + +Vertex.prototype.addEdge = function(edge){ + if (edge.getNodeSource().getId() == this.id){ + this.edgesIn.push(edge); + } + else{ + this.edgesOut.push(edge); + } +}; + +Vertex.prototype.removeEdge = function(edge){ + for ( var i = 0; i < this.getEdgesIn().length; i++) { + var edgeIn = this.edgesIn[i]; + if (edgeIn.getId() == edge.getId()){ + this.edgesIn.splice(i, 1); + } + } + for ( var i = 0; i < this.getEdgesOut().length; i++) { + var edgeIn = this.edgesOut[i]; + if (edgeIn.getId() == edge.getId()){ + this.edgesOut.splice(i, 1); + } + } +}; + +Vertex.prototype.remove = function(){ + var edges = this.getEdges(); + for ( var i = 0; i < edges.length; i++) { + var edge = edges[i]; + edge.remove(); + } + this.deleted.notify(this); +}; + + + + -GraphCanvas.prototype.getEdgeStrokeOpacity = function(edgeId) { - return this.getFormatter().getEdgeById(edgeId).getDefault().getStrokeOpacity(); -}; +VertexGraphFormatter.prototype.getDefault = ItemGraphFormatter.prototype.getDefault; +VertexGraphFormatter.prototype.getSelected = ItemGraphFormatter.prototype.getSelected; +VertexGraphFormatter.prototype.getOver = ItemGraphFormatter.prototype.getOver; +VertexGraphFormatter.prototype.getDragging = ItemGraphFormatter.prototype.getDragging; +VertexGraphFormatter.prototype.getId = ItemGraphFormatter.prototype.getId; +VertexGraphFormatter.prototype.toJSON = ItemGraphFormatter.prototype.toJSON; +VertexGraphFormatter.prototype.loadFromJSON = ItemGraphFormatter.prototype.loadFromJSON; +VertexGraphFormatter.prototype._setEvents = ItemGraphFormatter.prototype._setEvents; + + +function VertexGraphFormatter(defaultFormat, selectedFormat, overFormat, draggingFormat){ + ItemGraphFormatter.prototype.constructor.call(this, defaultFormat, selectedFormat, overFormat, draggingFormat); +}; + + +CircleVertexGraphFormatter.prototype.getDefault = VertexGraphFormatter.prototype.getDefault; +CircleVertexGraphFormatter.prototype.getSelected = VertexGraphFormatter.prototype.getSelected; +CircleVertexGraphFormatter.prototype.getOver = VertexGraphFormatter.prototype.getOver; +CircleVertexGraphFormatter.prototype.getDragging = VertexGraphFormatter.prototype.getDragging; +CircleVertexGraphFormatter.prototype.getId = VertexGraphFormatter.prototype.getId; +CircleVertexGraphFormatter.prototype.toJSON = VertexGraphFormatter.prototype.toJSON; +CircleVertexGraphFormatter.prototype.loadFromJSON = VertexGraphFormatter.prototype.loadFromJSON; +CircleVertexGraphFormatter.prototype._setEvents = VertexGraphFormatter.prototype._setEvents; + +function CircleVertexGraphFormatter(id, defaultFormat, selectedFormat, overFormat, draggingFormat){ + VertexGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, draggingFormat); + this.args.type = "CircleVertexGraphFormatter"; + if (defaultFormat != null){ + if (defaultFormat.radius != null){ + this.defaultFormat.args.radius = defaultFormat.radius; + } + } + + if (selectedFormat != null){ + if (selectedFormat.radius != null){ + this.selected.args.radius = selectedFormat.radius; + } + } + + if (overFormat != null){ + if (overFormat.radius != null){ + this.over.args.radius = overFormat.radius; + } + } + + if (draggingFormat != null){ + if (draggingFormat.radius != null){ + this.dragging.args.draggingFormat = draggingFormat.radius; + } + } +}; + + + +SquareVertexGraphFormatter.prototype.getDefault = VertexGraphFormatter.prototype.getDefault; +SquareVertexGraphFormatter.prototype.getSelected = VertexGraphFormatter.prototype.getSelected; +SquareVertexGraphFormatter.prototype.getOver = VertexGraphFormatter.prototype.getOver; +SquareVertexGraphFormatter.prototype.getDragging = VertexGraphFormatter.prototype.getDragging; +SquareVertexGraphFormatter.prototype.getId = VertexGraphFormatter.prototype.getId; +SquareVertexGraphFormatter.prototype.toJSON = VertexGraphFormatter.prototype.toJSON; +SquareVertexGraphFormatter.prototype.loadFromJSON = VertexGraphFormatter.prototype.loadFromJSON; +SquareVertexGraphFormatter.prototype._setEvents = VertexGraphFormatter.prototype._setEvents; + +function SquareVertexGraphFormatter(id, defaultFormat, selectedFormat, overFormat, draggingFormat){ + VertexGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, draggingFormat); + this.args.type = "SquareVertexGraphFormatter"; + if (defaultFormat != null){ + if (defaultFormat.radius != null){ + this.defaultFormat.args.radius = defaultFormat.radius; + } + } + + if (selectedFormat != null){ + if (selectedFormat.radius != null){ + this.selected.args.radius = selectedFormat.radius; + } + } + + if (overFormat != null){ + if (overFormat.radius != null){ + this.over.args.radius = overFormat.radius; + } + } + + if (draggingFormat != null){ + if (draggingFormat.radius != null){ + this.dragging.args.draggingFormat = draggingFormat.radius; + } + } +}; + + +EllipseVertexGraphFormatter.prototype.getDefault = VertexGraphFormatter.prototype.getDefault; +EllipseVertexGraphFormatter.prototype.getSelected = VertexGraphFormatter.prototype.getSelected; +EllipseVertexGraphFormatter.prototype.getOver = VertexGraphFormatter.prototype.getOver; +EllipseVertexGraphFormatter.prototype.getDragging = VertexGraphFormatter.prototype.getDragging; +EllipseVertexGraphFormatter.prototype.getId = VertexGraphFormatter.prototype.getId; +EllipseVertexGraphFormatter.prototype.toJSON = VertexGraphFormatter.prototype.toJSON; +EllipseVertexGraphFormatter.prototype.loadFromJSON = VertexGraphFormatter.prototype.loadFromJSON; +EllipseVertexGraphFormatter.prototype._setEvents = VertexGraphFormatter.prototype._setEvents; + +function EllipseVertexGraphFormatter(id, defaultFormat, selectedFormat, overFormat, draggingFormat){ + VertexGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, draggingFormat); + this.args.type = "EllipseVertexGraphFormatter"; + if (defaultFormat != null){ + if (defaultFormat.radius != null){ + this.defaultFormat.args.radius = defaultFormat.radius; + } + } + + if (selectedFormat != null){ + if (selectedFormat.radius != null){ + this.selected.args.radius = selectedFormat.radius; + } + } + + if (overFormat != null){ + if (overFormat.radius != null){ + this.over.args.radius = overFormat.radius; + } + } + + if (draggingFormat != null){ + if (draggingFormat.radius != null){ + this.dragging.args.draggingFormat = draggingFormat.radius; + } + } +}; + + + +RectangleVertexGraphFormatter.prototype.getDefault = ItemGraphFormatter.prototype.getDefault; +RectangleVertexGraphFormatter.prototype.getSelected = ItemGraphFormatter.prototype.getSelected; +RectangleVertexGraphFormatter.prototype.getOver = ItemGraphFormatter.prototype.getOver; +RectangleVertexGraphFormatter.prototype.getDragging = ItemGraphFormatter.prototype.getDragging; +RectangleVertexGraphFormatter.prototype.getId = ItemGraphFormatter.prototype.getId; +RectangleVertexGraphFormatter.prototype.toJSON = ItemGraphFormatter.prototype.toJSON; +RectangleVertexGraphFormatter.prototype.loadFromJSON = ItemGraphFormatter.prototype.loadFromJSON; +RectangleVertexGraphFormatter.prototype._setEvents = ItemGraphFormatter.prototype._setEvents; + +function RectangleVertexGraphFormatter(id, defaultFormat, selectedFormat, overFormat, draggingFormat){ + VertexGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, draggingFormat); + this.args.type = "RectangleVertexGraphFormatter"; + if (defaultFormat != null){ + if (defaultFormat.radius != null){ + this.defaultFormat.args.radius = defaultFormat.radius; + } + } + + if (selectedFormat != null){ + if (selectedFormat.radius != null){ + this.selected.args.radius = selectedFormat.radius; + } + } + + if (overFormat != null){ + if (overFormat.radius != null){ + this.over.args.radius = overFormat.radius; + } + } + + if (draggingFormat != null){ + if (draggingFormat.radius != null){ + this.dragging.args.draggingFormat = draggingFormat.radius; + } + } +}; + + + +RoundedVertexGraphFormatter.prototype.getDefault = ItemGraphFormatter.prototype.getDefault; +RoundedVertexGraphFormatter.prototype.getSelected = ItemGraphFormatter.prototype.getSelected; +RoundedVertexGraphFormatter.prototype.getOver = ItemGraphFormatter.prototype.getOver; +RoundedVertexGraphFormatter.prototype.getDragging = ItemGraphFormatter.prototype.getDragging; +RoundedVertexGraphFormatter.prototype.getId = ItemGraphFormatter.prototype.getId; +RoundedVertexGraphFormatter.prototype.toJSON = ItemGraphFormatter.prototype.toJSON; +RoundedVertexGraphFormatter.prototype.loadFromJSON = ItemGraphFormatter.prototype.loadFromJSON; +RoundedVertexGraphFormatter.prototype._setEvents = ItemGraphFormatter.prototype._setEvents; + +function RoundedVertexGraphFormatter(id, defaultFormat, selectedFormat, overFormat, draggingFormat){ + VertexGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, draggingFormat); + this.args.type = "RoundedVertexGraphFormatter"; + if (defaultFormat != null){ + if (defaultFormat.radius != null){ + this.defaultFormat.args.radius = defaultFormat.radius; + } + } + + if (selectedFormat != null){ + if (selectedFormat.radius != null){ + this.selected.args.radius = selectedFormat.radius; + } + } + + if (overFormat != null){ + if (overFormat.radius != null){ + this.over.args.radius = overFormat.radius; + } + } + + if (draggingFormat != null){ + if (draggingFormat.radius != null){ + this.dragging.args.draggingFormat = draggingFormat.radius; + } + } +}; + + + +OctagonVertexGraphFormatter.prototype.getDefault = ItemGraphFormatter.prototype.getDefault; +OctagonVertexGraphFormatter.prototype.getSelected = ItemGraphFormatter.prototype.getSelected; +OctagonVertexGraphFormatter.prototype.getOver = ItemGraphFormatter.prototype.getOver; +OctagonVertexGraphFormatter.prototype.getDragging = ItemGraphFormatter.prototype.getDragging; +OctagonVertexGraphFormatter.prototype.getId = ItemGraphFormatter.prototype.getId; +OctagonVertexGraphFormatter.prototype.toJSON = ItemGraphFormatter.prototype.toJSON; +OctagonVertexGraphFormatter.prototype.loadFromJSON = ItemGraphFormatter.prototype.loadFromJSON; +OctagonVertexGraphFormatter.prototype._setEvents = ItemGraphFormatter.prototype._setEvents; + +function OctagonVertexGraphFormatter(id, defaultFormat, selectedFormat, overFormat, draggingFormat){ + VertexGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, draggingFormat); + this.args.type = "OctagonVertexGraphFormatter"; + if (defaultFormat != null){ + if (defaultFormat.radius != null){ + this.defaultFormat.args.radius = defaultFormat.radius; + } + } + + if (selectedFormat != null){ + if (selectedFormat.radius != null){ + this.selected.args.radius = selectedFormat.radius; + } + } + + if (overFormat != null){ + if (overFormat.radius != null){ + this.over.args.radius = overFormat.radius; + } + } + + if (draggingFormat != null){ + if (draggingFormat.radius != null){ + this.dragging.args.draggingFormat = draggingFormat.radius; + } + } +}; + -GraphCanvas.prototype.setEdgeFill = function(edgeId, color) { - this.getFormatter().getEdgeById(edgeId).getDefault().setFill(color); + +var Colors = new function() +{ + this.hashColor = []; + this.getColorByScoreArrayValue = function (arrayScore) + { + var array = new Array(); + + for (var i = 0; i< arrayScore.length; i++) + { + + var color = this.getColorByScoreValue(arrayScore[i]) + array.push( color); + + } + return array; + }; + + this.getHexStringByScoreArrayValue = function (arrayScore) + { + var arrayColor = this.getColorByScoreArrayValue(arrayScore); + var arrayHex = new Array(); + for (var i = 0; i< arrayColor.length; i++) + { + arrayHex.push( arrayColor[i].HexString()); + } + return arrayHex; + }; + + this.getColorByScoreValue = function (score) + { + + var truncate = score.toString().substr(0,4); + if (this.hashColor[truncate]!=null) + { + return this.hashColor[truncate]; + } + + + if(isNaN(score)) { + return Colors.ColorFromRGB(0,0,0); + } + var value; + + var from, to; + if(score < 0.5) { + from = Colors.ColorFromRGB(0,0,255); + to = Colors.ColorFromRGB(255,255,255); + value = (score * 2); + } else { + from = Colors.ColorFromRGB(255,255,255); + to = Colors.ColorFromRGB(255,0,0); + value = (score * 2) - 1; + } + + var x = value; + var y = 1.0 - value; + var color = Colors.ColorFromRGB(y * from.Red() + x * to.Red(), y * from.Green() + x * to.Green(), y * from.Blue() + x * to.Blue()); + + this.hashColor[truncate] = color; + + return color; + }; + + this.ColorFromHSV = function(hue, sat, val) + { + var color = new Color(); + color.SetHSV(hue,sat,val); + return color; + }; + + this.ColorFromRGB = function(r, g, b) + { + var color = new Color(); + color.SetRGB(r,g,b); + return color; + }; + + this.ColorFromHex = function(hexStr) + { + var color = new Color(); + color.SetHexString(hexStr); + return color; + }; + + function Color() { + //Stored as values between 0 and 1 + var red = 0; + var green = 0; + var blue = 0; + + //Stored as values between 0 and 360 + var hue = 0; + + //Strored as values between 0 and 1 + var saturation = 0; + var value = 0; + + this.SetRGB = function(r, g, b) + { + red = r/255.0; + green = g/255.0; + blue = b/255.0; + calculateHSV(); + }; + + this.Red = function() { return Math.round(red*255); }; + + this.Green = function() { return Math.round(green*255); }; + + this.Blue = function() { return Math.round(blue*255); }; + + this.SetHSV = function(h, s, v) + { + hue = h; + saturation = s; + value = v; + calculateRGB(); + }; + + this.Hue = function() + { return hue; }; + + this.Saturation = function() + { return saturation; }; + + this.Value = function() + { return value; }; + + this.SetHexString = function(hexString) + { + if(hexString == null || typeof(hexString) != "string") + { + this.SetRGB(0,0,0); + return; + } + + if (hexString.substr(0, 1) == '#') + hexString = hexString.substr(1); + + if(hexString.length != 6) + { + this.SetRGB(0,0,0); + return; + } + + var r = parseInt(hexString.substr(0, 2), 16); + var g = parseInt(hexString.substr(2, 2), 16); + var b = parseInt(hexString.substr(4, 2), 16); + if (isNaN(r) || isNaN(g) || isNaN(b)) + { + this.SetRGB(0,0,0); + return; + } + + this.SetRGB(r,g,b); + }; + + this.HexString = function() + { + + var rStr = this.Red().toString(16); + if (rStr.length == 1) + rStr = '0' + rStr; + var gStr = this.Green().toString(16); + if (gStr.length == 1) + gStr = '0' + gStr; + var bStr = this.Blue().toString(16); + if (bStr.length == 1) + bStr = '0' + bStr; + return ('#' + rStr + gStr + bStr).toUpperCase(); + }; + + this.Complement = function() + { + var newHue = (hue >= 180) ? hue - 180 : hue + 180; + var newVal = (value * (saturation - 1) + 1); + var newSat = (value*saturation) / newVal; + var newColor = new Color(); + newColor.SetHSV(newHue, newSat, newVal); + return newColor; + } ; + + function calculateHSV() + { + var max = Math.max(Math.max(red, green), blue); + var min = Math.min(Math.min(red, green), blue); + + value = max; + + saturation = 0; + if(max != 0) + saturation = 1 - min/max; + + hue = 0; + if(min == max) + return; + + var delta = (max - min); + if (red == max) + hue = (green - blue) / delta; + else if (green == max) + hue = 2 + ((blue - red) / delta); + else + hue = 4 + ((red - green) / delta); + hue = hue * 60; + if(hue < 0) + hue += 360; + } + + function calculateRGB() + { + red = value; + green = value; + blue = value; + + if(value == 0 || saturation == 0) + return; + + var tHue = (hue / 60); + var i = Math.floor(tHue); + var f = tHue - i; + var p = value * (1 - saturation); + var q = value * (1 - saturation * f); + var t = value * (1 - saturation * (1 - f)); + switch(i) + { + case 0: + red = value; green = t; blue = p; + break; + case 1: + red = q; green = value; blue = p; + break; + case 2: + red = p; green = value; blue = t; + break; + case 3: + red = p; green = q; blue = value; + break; + case 4: + red = t; green = p; blue = value; + break; + default: + red = value; green = p; blue = q; + break; + } + } + } +} +(); +/* + * Clase gestiona la insercción, eliminación de los elementos en el DOM + * + * Vital hacerla SIEMPRE compatible hacia atrás + * + * Last update: 28-10-2010 + * + */ + + +var DOM = {}; + +DOM.createNewElement = function(type, nodeParent, attributes) +{ + + var node = document.createElement(type); + for (var i=0; i0) + { + parent.removeChild(parent.childNodes[0]); + + } +}; + +DOM.select = function(targetID) +{ + return document.getElementById(targetID); +// return $("#"+targetID); }; +var Geometry = +{ + + /** From tow points obtains the angles formed with the cartesian side **/ + getAngleBetweenTwoPoints : function(x1, y1, x2, y2){ + //var m = (y1 - y2)/ (x1 - x2); + return Math.atan2(y2-y1, x2-x1); + }, + + getAdjacentSideOfRectangleRight : function(angle, hypotenuse){ + return Math.abs(Math.cos(angle)*hypotenuse); + }, + + getOppositeSideOfRectangleRight : function(angle, hypotenuse){ + return Math.abs(Math.sin(angle)*hypotenuse); + }, + + toDegree: function(radian){ + return radian*180/Math.PI; + + } + + +}; -GraphCanvas.prototype.getEdgeFill = function(edgeId) { - return this.getFormatter().getEdgeById(edgeId).getDefault().getFill(); -}; +var SVG = +{ + svgns : 'http://www.w3.org/2000/svg', + xlinkns : "http://www.w3.org/1999/xlink", + + createSVGCanvas: function(parentNode, attributes) + { + + attributes.push( ['xmlns', SVG.svgns], ['xmlns:xlink', 'http://www.w3.org/1999/xlink']); + var svg = document.createElementNS("http://www.w3.org/2000/svg", "svg"); + this._setProperties(svg, attributes); + parentNode.appendChild( svg); + return svg; + + }, + + createRectangle : function (x, y, width, height, attributes){ + var rect = document.createElementNS(this.svgns, "rect"); + rect.setAttribute("x",x); + rect.setAttribute("y",y); + rect.setAttribute("width",width); + rect.setAttribute("height",height); + SVG._setProperties(rect, attributes); + return rect; + }, + + drawImage64 : function (x, y, width, height, base64, svgNode, attributes) { + var node = SVG.createImage64(x, y, width, height, base64, attributes); + svgNode.appendChild(node); + return node; + }, + + createImage64 : function (x, y, width, height, base64, attributes) { + var img = document.createElementNS(this.svgns, "image"); + img.setAttribute("x",x); + img.setAttribute("y",y); + img.setAttribute("width",width); + img.setAttribute("height",height); + img.setAttribute("xlink:href",base64); + SVG._setProperties(img, attributes); + return img; + }, + + createLine: function (x1, y1, x2, y2, attributes){ + var line = document.createElementNS(this.svgns,"line"); + line.setAttribute("x1",x1); + line.setAttribute("y1",y1); + line.setAttribute("x2", x2); + line.setAttribute("y2", y2); + SVG._setProperties(line, attributes); + return line; + }, + + createClip: function (id, nodeToClip, attributes){ + var clip = document.createElementNS(this.svgns,"clipPath"); + clip.setAttribute("id",id); + clip.appendChild(nodeToClip); + return clip; + }, + + drawClip : function (id, nodeToClip, svgNode) { + var node = SVG.createClip(id, nodeToClip); + svgNode.appendChild(node); + return node; + }, + + drawRectangle : function (cx, cy, width, height, svgNode, attributes) { + try{ + var node = SVG.createRectangle(cx, cy, width, height, attributes); + svgNode.appendChild(node); + } + catch(e){ + + console.log("-------------------- "); + console.log("Error on drawRectangle " + e); + console.log(attributes); + console.log("-------------------- "); + } + return node; + }, + + createEllipse : function (x, y, rx, ry, attributes){ + var rect = document.createElementNS(this.svgns, "ellipse"); + rect.setAttribute("cx",x); + rect.setAttribute("cy",y); + rect.setAttribute("rx",rx); + rect.setAttribute("ry",ry); + SVG._setProperties(rect, attributes); + return rect; + }, + + drawEllipse : function (cx, cy, rx, ry, svgNode, attributes) { + var node = SVG.createEllipse(cx, cy, rx, ry, attributes); + svgNode.appendChild(node); + return node; + }, + + drawImage : function (x, y, canvasSVG, attributes) { + var image = document.createElementNS(this.svgns, "image"); + image.setAttribute("x",x); + image.setAttribute("y",y); + canvasSVG.appendChild(image); + SVG._setProperties(image, attributes); + }, + + drawLine : function (x1, y1, x2, y2, nodeSVG, attributes) { + try{ + var line = SVG.createLine(x1, y1, x2, y2, attributes); + nodeSVG.appendChild(line); + }catch(e){ + } + return line; + }, + + + drawPath: function (d, nodeSVG, attributes) { + var path = SVG.createPath(d, attributes); + nodeSVG.appendChild(path); + return path; + }, + + createPoligon : function (points, attributes){ + var poligon = document.createElementNS(this.svgns, "polygon"); + poligon.setAttribute("points",points); + SVG._setProperties(poligon, attributes); + return poligon; + }, + + drawPoligon : function (points, canvasSVG, attributes){ + var poligon = SVG.createPoligon(points, attributes); + canvasSVG.appendChild(poligon); + return poligon; + }, + // + + createPath : function (d, attributes){ + var path = document.createElementNS(this.svgns, "path"); + path.setAttribute("d",d); + SVG._setProperties(path, attributes); + return path; + }, + + drawCircle : function (x, y, radio, canvasSVG, attributes) { + + var newText = document.createElementNS(this.svgns,"circle"); + newText.setAttribute("cx",x); + newText.setAttribute("cy",y); + newText.setAttribute("r",radio); + + canvasSVG.appendChild(newText); + attributes["cursor"] = "pointer"; + this._setProperties(newText, attributes); + return newText; + }, + + + _setProperties: function(node, attributes) + { + if (attributes instanceof Array){ + for (var i=0; i< attributes.length; i++) + { + node.setAttribute(attributes[i][0], attributes[i][1]); + } + } + else{ + for ( var key in attributes){ + node.setAttribute(key, attributes[key]); + } + } + }, + +/* drawPath: function(pointsArray, canvasSVG, attributes){ + var path = document.createElementNS(this.svgns,"polyline"); + path.setAttribute ('id', id); + + var d= pointsArray[0].x+ " "+ pointsArray[0].y; + for (var i=1; i< pointsArray.length; i++) + { + d=d+" "+pointsArray[i].x+" "+pointsArray[i].y; + } + path.setAttribute ('points', d); + canvasSVG.appendChild(path); + },*/ + + createText : function (x, y, text, attributes) { + var node = document.createElementNS(this.svgns,"text"); + node.setAttributeNS(null , "x",x); + node.setAttributeNS(null, "y",y); + + var textNode = document.createTextNode(text); + node.appendChild(textNode); + + this._setProperties(node, attributes); + return node; + }, + + drawText : function (x, y, text, canvasSVG, attributes) { + var text = SVG.createText(x, y, text, attributes); + canvasSVG.appendChild(text); + return text; + }, + + + + drawGroup: function(svgNode, attributes) + { + var group = SVG.createGroup(attributes); + svgNode.appendChild(group); + return group; + }, + + createGroup: function(attributes){ + var group = document.createElementNS(this.svgns,"g"); + this._setProperties(group, attributes); + return group; + } + +}; + + + +var CanvasToSVG = { + + convert: function(sourceCanvas, targetSVG, x, y, id, attributes) { + + var img = this._convert(sourceCanvas, targetSVG, x, y, id); + + for (var i=0; i< attributes.length; i++) + { + img.setAttribute(attributes[i][0], attributes[i][1]); + } + }, + + _convert: function(sourceCanvas, targetSVG, x, y, id) { + var svgNS = "http://www.w3.org/2000/svg"; + var xlinkNS = "http://www.w3.org/1999/xlink"; + // get base64 encoded png from Canvas + var image = sourceCanvas.toDataURL(); + + // must be careful with the namespaces + var svgimg = document.createElementNS(svgNS, "image"); + + svgimg.setAttribute('id', id); + + //svgimg.setAttribute('class', class); + //svgimg.setAttribute('xlink:href', image); + svgimg.setAttributeNS(xlinkNS, 'xlink:href', image); + + + + + svgimg.setAttribute('x', x ? x : 0); + svgimg.setAttribute('y', y ? y : 0); + svgimg.setAttribute('width', sourceCanvas.width); + svgimg.setAttribute('height', sourceCanvas.height); + //svgimg.setAttribute('cursor', 'pointer'); + svgimg.imageData = image; + + targetSVG.appendChild(svgimg); + return svgimg; + }, + + importSVG: function(sourceSVG, targetCanvas) { + svg_xml = sourceSVG;//(new XMLSerializer()).serializeToString(sourceSVG); + var ctx = targetCanvas.getContext('2d'); + + var img = new Image(); + img.src = "data:image/svg+xml;base64," + btoa(svg_xml); +// img.onload = function() { + ctx.drawImage(img, 0, 0); +// }; + } + +}; +/* +Graph.prototype.importSVG = function(sourceSVG, targetCanvas) { + sourceSVG = this._svg; + targetCanvas = document.createElementNS('canvas'); + // https://developer.mozilla.org/en/XMLSerializer + svg_xml = (new XMLSerializer()).serializeToString(sourceSVG); + var ctx = targetCanvas.getContext('2d'); + // this is just a JavaScript (HTML) image + var img = new Image(); + // http://en.wikipedia.org/wiki/SVG#Native_support + // https://developer.mozilla.org/en/DOM/window.btoa + img.src = "data:image/svg+xml;base64," + btoa(svg_xml); + img.onload = function() { + // after this, Canvas’ origin-clean is DIRTY + ctx.drawImage(img, 0, 0); + } +}; +*/ -/** API LAYOUT **/ -GraphCanvas.prototype.setCoordinates = function(vertexId, x, y) { - return this.getLayout().getEdgeById(vertexId).setCoordinates(x, y); -}; +/* + +Normalizacion de datos para dibujar colores +Issues: + No sé como debería llamarse esta libreria + No sé si ya existe una funciçon en javascript que lo haga + + +*/ + + +var Normalizer = new function() +{ + this.normalizeArray = function (arrayData) + { + + return this.standardizeArray(this.normal(arrayData)); + +// var result = this._getMaxAndMin(arrayData); +// var min =result[0]; +// var max = result[1]; +// +// +// //los hacemos todos positivos +// for (var i = 0; i< arrayData.length; i++) +// { +// arrayData[i]= Math.abs(min) + parseFloat(arrayData[i]); +// } +// +// var result = this._getMaxAndMin(arrayData); +// var min =result[0]; +// var max = result[1]; +// +// +// var resultArray = new Array(); +// for (var i = 0; i< arrayData.length; i++) +// { +// resultArray.push(arrayData[i]*1/max); +// } +// return resultArray; + }; + + this.normal = function(arrayData){ + var mean = this._getMean(arrayData); + var deviation = this._getStdDeviation(arrayData); + var result = this._getMaxAndMin(arrayData); + var min = result[0]; + var max = result[1]; + + var resultArray = new Array(); + for (var i = 0; i< arrayData.length; i++) { + if (deviation!=0){ + resultArray.push((arrayData[i]-mean)/deviation); + }else{ + resultArray.push(arrayData[i]); + } + } + return resultArray; + }; + + this.standardizeArray = function(arrayData) + { + var result = this._getMaxAndMin(arrayData); + var min = result[0]; + var max = result[1]; + + var offset = ( min <= 0 ) ? Math.abs(min) : (-1 * min); + var resultArray = new Array(); + for (var i = 0; i< arrayData.length; i++) { + if(max + offset!=0){ + resultArray.push((arrayData[i] + offset) / (max + offset)); + }else{ + resultArray.push(arrayData[i]+offset); + } + } + return resultArray; + }; + + + this._getMean = function(arrayData) { + var sum = 0; + for (var i = 0; i< arrayData.length; i++) { + sum = sum + parseFloat(arrayData[i]); + } + return sum/arrayData.length; + }; + + this._getStdDeviation = function(arrayData) { + var mean = this._getMean(arrayData); + var acum = 0.0; + for(var i=0; i max) max = parseFloat(arrayData[i]); + } + + return [min, max]; + }; +}; +function GraphCanvas(componentID, targetNode, args) { + this.args = {}; + /** target */ + this.targetID = targetNode.id; + + /** id manage */ + this.id = componentID; + this.args.idGraph = this.id + "main"; + this.args.idBackgroundNode = this.id + "background"; + + this.args.idEdgesGraph = this.id + "edges"; + this.args.idNodesGraph = this.id + "vertices"; + this.args.idLabelGraph = this.id + "label"; + this.args.idBackground = this.id + "background"; + + /** Objects Graph **/ + this.dataset = null; + this.formatter = null; + this.layout = null; + + /** Drawing **/ + this.circleDefaultRadius = 2; + this.squareDefaultSide = this.circleDefaultRadius * 1.5; + + /** Directed Arrow **/ + this.arrowDefaultSize = this.circleDefaultRadius; + + /** Groups **/ + this.GraphGroup = null; + this.GraphNodeGroup = null; + this.GraphLabelGroup = null; + this.GraphBackground = null; + + /** SETTINGS FLAGS **/ + this.args.draggingCanvasEnabled = false; //Flag to set if the canvas can be dragged + this.args.multipleSelectionEnabled = false; + this.args.interactive = false; + this.args.labeled = false; + this.args.linkEnabled = false; + + /** If numberEdge > maxNumberEdgesMoving then only it will move edges when mouse up **/ + this.args.maxNumberEdgesMoving = 3; + this.args.maxNumberEdgesFiringEvents = 50; + + /** Linking edges **/ + this.args.linking = false; + this.linkStartX = 0; + this.linkStartY = 0; + this.linkSVGNode = null; + this.linkNodeSource = null; + this.linkNodeTarget = null; + + /** Dragging Control **/ + this.draggingElement = null; + this.dragging = false; + this.nMouseOffsetX = 0; + this.nMouseOffsetY = 0; + this.dragStartX = 0; + this.dragStartY = 0; + this.desplazamientoX = 0; + this.desplazamientoY = 0; + + /** Selection Control **/ + this.selecting = false; + this.selectorX = null; + this.selectorY = null; + this.selectorSVGNode = null; + + /** Node status **/ + this.args.isVertexSelected = {}; + this.args.selectedVertices = []; + + /** Edges status **/ + this.args.isEdgeSelected = {}; + //this.args.selectedEdges = []; + + if (args != null) { + if (args.multipleSelectionEnabled != null) { + this.args.multipleSelectionEnabled = args.multipleSelectionEnabled; + this.args.draggingCanvasEnabled = !(this.args.multipleSelectionEnabled); + } + if (args.draggingCanvasEnabled != null) { + this.args.draggingCanvasEnabled = args.draggingCanvasEnabled; + this.args.multipleSelectionEnabled = !(this.args.draggingCanvasEnabled); + } + if (args.interactive != null) { + this.args.interactive = args.interactive; + } + if (args.labeled != null) { + this.args.labeled = args.labeled; + } + + } + + /** Hashmap with the svg node labels **/ + this.svgLabels = {}; + + /** EVENTS **/ + this.onVertexOut = new Event(this); + this.onVertexOver = new Event(this); + this.onVertexSelect = new Event(this); + this.onEdgeSelect = new Event(this); + this.onCanvasClicked = new Event(this); + this.onVertexUp = new Event(this); +} + +GraphCanvas.prototype.showLabels = function(value) { + this.args.labeled = value; + this.removeLabels(); + if (value) { + this.renderLabels(); + } +}; + +GraphCanvas.prototype.getSelectedVertices = function() { + return this.args.selectedVertices; +}; + +GraphCanvas.prototype.getSelectedEdges = function() { + var selected = []; + for ( var selectedEdge in this.args.isEdgeSelected) { + selected.push(selectedEdge); + } + return selected; +}; + +GraphCanvas.prototype.createSVGDom = function(targetID, id, width, height, backgroundColor) { + var container = document.getElementById(targetID); + this._svg = SVG.createSVGCanvas(container, [ + [ "style", "background-color:" + backgroundColor + ";" ], [ "id", id ], [ "dragx", 0 ], [ "dragy", 0 ], + [ "height", this.getFormatter().getHeight() ], [ "width", this.getFormatter().getWidth() ] ]); + return this._svg; +}; + +/** MULTIPLE SELECTION **/ +GraphCanvas.prototype.isMultipleSelectionEnabled = function() { + return this.args.multipleSelectionEnabled; +}; + +GraphCanvas.prototype.setMultipleSelection = function(value) { + this.args.multipleSelectionEnabled = value; + this.args.draggingCanvasEnabled = (!value); +}; + +GraphCanvas.prototype.setSelecting = function(value) { + this.selecting = value; +}; + +/** linking **/ +GraphCanvas.prototype.setLinking = function(value) { + this.args.linkEnabled = value; + this.selecting = !value; + this.dragging = !value; +}; + +/** CANVAS MOVING **/ +GraphCanvas.prototype.setDraggingCanvas = function(value) { + this.args.draggingCanvasEnabled = value; + this.args.multipleSelectionEnabled = !value; +}; + +GraphCanvas.prototype.isDraggingCanvasEnabled = function() { + return this.args.draggingCanvasEnabled; +}; +/** ZOOM **/ +GraphCanvas.prototype.getScale = function() { + return this.getFormatter().getZoomScale(); +}; + +GraphCanvas.prototype.setScale = function(scale) { + var graphNode = document.getElementById(this.args.idGraph); + graphNode.setAttribute("transform", graphNode.getAttribute("transform").replace("scale(" + this.getScale() + ")", "scale(" + scale + ")")); + this.getFormatter().setZoomScale(scale); +}; + +GraphCanvas.prototype.zoomIn = function() { + this.setScale(this.getScale() + this.getFormatter().getZoomScaleStepFactor()); +}; + +GraphCanvas.prototype.zoomOut = function() { + this.setScale(this.getScale() - this.getFormatter().getZoomScaleStepFactor()); + +}; + +/** SVG COORDENATES **/ +GraphCanvas.prototype.getSVGCoordenates = function(evt) { + var p = this._svg.createSVGPoint(); + p.x = evt.clientX; + p.y = evt.clientY; + + var m = this._svg.getScreenCTM(document.documentElement); + p = p.matrixTransform(m.inverse()); + return p; +}; + +/** SVG EVENTS **/ +GraphCanvas.prototype.mouseClick = function(event) { + if (event.button == 0) { + if (!this.args.interactive) { + return; + } + + if (this.isVertex(event.target)) { + this.clickNode(this.getVertexIdFromSVGId(event.target.id)); + } + /** Como el evento mouseClick viene despues del mouse up es aqui donde manejo el tema de deseccionar los elementos que estoy dragging **/ + if (this.dragging) { + this.dragging = false; + } + } +}; + +GraphCanvas.prototype.mouseMove = function(evt) { + if (this.selecting) { + this.clearLabels(); + + var width = (this.getSVGCoordenates(evt).x - this.selectorX); + var height = (this.getSVGCoordenates(evt).y - this.selectorY); + if ((width > 0) && (height > 0)) { + this.displaySelection(this.selectorX, this.selectorY, width, height); + } + if ((width > 0) && (height < 0)) { + this.displaySelection(this.selectorX, this.getSVGCoordenates(evt).y, width, Math.abs(height)); + } + if ((width < 0) && (height < 0)) { + this.displaySelection(this.getSVGCoordenates(evt).x, this.getSVGCoordenates(evt).y, Math.abs(width), Math.abs(height)); + } + if ((width < 0) && (height > 0)) { + this.displaySelection(this.selectorX + width, this.selectorY, Math.abs(width), Math.abs(height)); + } + + var x1 = (parseFloat(this.selectorSVGNode.getAttribute("x")) - DOM.select(this.id).getAttribute("dragx")) / this.getFormatter().getWidth(); + var y1 = (parseFloat(this.selectorSVGNode.getAttribute("y")) - DOM.select(this.id).getAttribute("dragy")) / this.getFormatter().getHeight(); + var x2 = (x1 + parseFloat(this.selectorSVGNode.getAttribute("width") / this.getFormatter().getWidth())); + var y2 = (y1 + parseFloat(this.selectorSVGNode.getAttribute("height") / this.getFormatter().getHeight())); + + this.deselectNodes(this.getLayout()); + var verticesSelected = this.getLayout().getVerticesByArea(x1 / this.getFormatter().getZoomScale(), y1 / this.getFormatter().getZoomScale(), + x2 / this.getFormatter().getZoomScale(), y2 / this.getFormatter().getZoomScale()); + for ( var i = 0; i < verticesSelected.length; i++) { + this.selectNode(verticesSelected[i].getId()); + this.renderLabel(verticesSelected[i].getId()); + } + + } + var p = null; + if (this.args.linking) { + p = this.getSVGCoordenates(evt); + if (this.linkSVGNode != null) { + this.linkSVGNode.setAttribute("x2", p.x - 2); + this.linkSVGNode.setAttribute("y2", p.y - 2); + } + } + + if (this.dragging) { + p = this.getSVGCoordenates(evt); + p.x -= this.nMouseOffsetX; + p.y -= this.nMouseOffsetY; + this.desplazamientoX = (this.getSVGCoordenates(evt).x - this.dragStartX);// + parseFloat(DOM.select(this.id).getAttribute("dragx")); + this.desplazamientoY = (this.getSVGCoordenates(evt).y - this.dragStartY);// + parseFloat(DOM.select(this.id).getAttribute("dragy")); + + if (this.draggingElement != null) { + /** Click sobre el recct del banground que provoca que mueva todo el canvas **/ + if (this.isNodeCanvas(this.draggingElement)) { + + p = this.getSVGCoordenates(evt); + p.x = this.desplazamientoX; + p.y = this.desplazamientoY; + + this.draggingElement.setAttribute("dragx", p.x); + this.draggingElement.setAttribute("dragy", p.y); + this.draggingElement = document.getElementById(this.args.idGraph); + this.draggingElement.setAttribute("transform", "translate(" + p.x + "," + p.y + "), scale(" + this.getScale() + ")"); + + DOM.select(this.id).setAttribute("dragx", p.x); + DOM.select(this.id).setAttribute("dragy", p.y); + + if (this.NodeSVGbackgroundImage != null) { + this.NodeSVGbackgroundImage.setAttribute("dragx", p.x); + this.NodeSVGbackgroundImage.setAttribute("dragy", p.y); + } + } else { + if (this.isVertex(this.draggingElement)) { + this.selectNode(this.getVertexIdFromSVGId(this.draggingElement.id)); + this.desplazamientoX = this.desplazamientoX / this.getFormatter().getZoomScale(); + this.desplazamientoY = this.desplazamientoY / this.getFormatter().getZoomScale(); + this.moveSelectedNodes(this.desplazamientoX, this.desplazamientoY); + + this.dragStartX = this.getSVGCoordenates(evt).x; + this.dragStartY = this.getSVGCoordenates(evt).y; + } else { + if (this.isNodeBackground(this.draggingElement)) { + + this.draggingElement.setAttribute("dragx", p.x); + this.draggingElement.setAttribute("dragy", p.y); + this.draggingElement = document.getElementById(this.args.idGraph); + this.draggingElement.setAttribute("transform", "translate(" + p.x + "," + p.y + "), scale(" + this.getScale() + ")"); + } else { + this.draggingElement.setAttribute("transform", "translate(" + p.x + "," + p.y + ")"); + } + } + } + } + } +}; + +GraphCanvas.prototype.moveSelectedNodes = function(offsetX, offsetY) { + for ( var i = 0; i < this.getSelectedVertices().length; i++) { + + var nodeId = this.getSelectedVertices()[i]; + var svgNodeId = this.getSVGNodeId(nodeId); + + var x = parseFloat(DOM.select(svgNodeId).getAttribute("dragx")) + parseFloat(offsetX);// - parseFloat(DOM.select(this.id).getAttribute("dragx")); + var y = parseFloat(DOM.select(svgNodeId).getAttribute("dragy")) + parseFloat(offsetY);// + parseFloat(DOM.select(this.id).getAttribute("dragy")); + + this._movingNode(DOM.select(svgNodeId), x, y); + } +}; + +GraphCanvas.prototype.mouseDown = function(evt) { + if (event.button == 0) { + + /** if !no interactive mouse events do anything **/ + if (!this.args.interactive) { + return; + } + + var p = this.getSVGCoordenates(evt); + + /** When click on canvas or background deselect all **/ + if (this.isNodeCanvas(evt.target) || this.isNodeBackground(evt.target)) { + this.deselectNodes(); + this.deselectEdges(); + this.onCanvasClicked.notify(); + } + + /** if I am linking vertices **/ + if (this.args.linkEnabled) { + + if (!this.args.linking) { + this.args.linking = true; + if (this.isVertex(evt.target)) { + this.linkStartX = p.x; + this.linkStartY = p.y; + this.linkSVGNode = SVG.drawLine(p.x, p.y, p.x, p.y, this._svg, { + "stroke" : "#FF0000" + }); + this.linkNodeSource = this.getVertexIdFromSVGId(evt.target.id); + } + } else { + this.linkNodeTarget = this.getVertexIdFromSVGId(evt.target.id); + this.args.linking = false; + this.args.linkEnabled = false; + if (this.isVertex(evt.target)) { + this.getDataset().addEdge(this.linkNodeSource + "_" + this.linkNodeTarget, this.linkNodeSource, this.linkNodeTarget, {}); + } + this.linkSVGNode.parentNode.removeChild(this.linkSVGNode); + } + return; + } + + /** Id is a vertex or the canvas **/ + if (this.isVertex(evt.target) || this.isNodeCanvas(evt.target) || this.isNodeBackground(evt.target)) { + this._startDragging(evt); + } + /** if i is edge **/ + if (this.isEdge(evt.target)) { + this.selectEdge(this.getEdgeIdFromSVGId(evt.target.getAttribute("id"))); + } + + if (this.args.multipleSelectionEnabled) { + if (!this.dragging) { + this.setSelecting(true); + this.selectorX = p.x; + this.selectorY = p.y; + this.displaySelection(p.x, p.y, 1, 1); + } + } + + } + if (event.button == 1) { + this.setLinking(false); + this.setMultipleSelection(false); + this.selecting = false; + + /** Id is a vertex or the canvas **/ + if (this.isVertex(evt.target) || this.isNodeCanvas(evt.target) || this.isNodeBackground(evt.target)) { + this._startDragging(evt); + } + } +}; + +GraphCanvas.prototype.mouseUp = function(event) { + if (!this.args.interactive) { + return; + } + + if (this.dragging) { + this._stopDragging(event); + if (this.isVertex(event.target)) { + var vertexId = this.getVertexIdFromSVGId(event.target.id); + if (this.getDataset().getVertexById(vertexId).getEdges().length >= this.args.maxNumberEdgesMoving) { + this.moveEdge(vertexId); + } + } + } + + if (this.selecting) { + this.setSelecting(false); + + var x1 = (parseFloat(this.selectorSVGNode.getAttribute("x")) - DOM.select(this.id).getAttribute("dragx")) / this.getFormatter().getWidth(); + var y1 = (parseFloat(this.selectorSVGNode.getAttribute("y")) - DOM.select(this.id).getAttribute("dragy")) / this.getFormatter().getHeight(); + var x2 = (x1 + parseFloat(this.selectorSVGNode.getAttribute("width") / this.formatter.getWidth())); + var y2 = (y1 + parseFloat(this.selectorSVGNode.getAttribute("height") / this.formatter.getHeight())); + + var verticesSelected = this.getLayout().getVerticesByArea(x1 / this.getFormatter().getZoomScale, y1 / this.getFormatter().getZoomScale, + x2 / this.getFormatter().getZoomScale, y2 / this.getFormatter().getZoomScale); + + for ( var i = 0; i < verticesSelected.length; i++) { + this.selectNode(verticesSelected[i].getId()); + } + + if (this.selectorSVGNode != null) { + this._svg.removeChild(this.selectorSVGNode); + } + + if (this.args.labeled) { + this.clearLabels(); + this.renderLabels(); + } + + this.selectorSVGNode = null; + // this.renderLabels(); + } +}; + +/** SELECTION **/ +GraphCanvas.prototype.displaySelection = function(x, y, width, height) { + if (this.selectorSVGNode != null) { + this.selectorSVGNode.setAttribute("x", x); + this.selectorSVGNode.setAttribute("y", y); + this.selectorSVGNode.setAttribute("width", width); + this.selectorSVGNode.setAttribute("height", height); + } else { + this.selectorSVGNode = SVG.drawRectangle(x, y, width, height, this._svg, { + "fill" : "red", + "stroke" : "black", + "opacity" : "0.2", + "stroke-opacity" : "1" + }); + } +}; + +/** DRAGGING **/ +GraphCanvas.prototype._startDragging = function(evt) { + if (!this.isDraggingCanvasEnabled()) { + if (this.isNodeCanvas(evt.target)) { + this.draggingElement = null; + } + } + + if (this.isVertex(evt.target) || (this.isNodeBackground(evt.target) && (this.isDraggingCanvasEnabled()))|| (this.isNodeCanvas(evt.target) && (this.isDraggingCanvasEnabled()))) { + this.clearLabels(); + this.draggingElement = evt.target; + this.dragging = true; + var p = this.getSVGCoordenates(evt); + + this.nMouseOffsetX = p.x - parseInt(evt.target.getAttribute("dragx")); + this.nMouseOffsetY = p.y - parseInt(evt.target.getAttribute("dragy")); + + if (this.isVertex(evt.target)) { + this.dragStartX = parseInt(this.draggingElement.getAttribute("dragx")) * this.getFormatter().getZoomScale() + + parseFloat(DOM.select(this.id).getAttribute("dragx")); + this.dragStartY = parseInt(this.draggingElement.getAttribute("dragy")) * this.getFormatter().getZoomScale() + + parseFloat(DOM.select(this.id).getAttribute("dragy")); + } else { + this.dragStartX = p.x - parseInt(this.draggingElement.getAttribute("dragx"));// + parseFloat(DOM.select(this.id).getAttribute("dragx")); + this.dragStartY = p.y - parseInt(this.draggingElement.getAttribute("dragy"));// + parseFloat(DOM.select(this.id).getAttribute("dragy")); + } + } +}; + +GraphCanvas.prototype._stopDragging = function(event) { + this.nMouseOffsetX = 0; + this.nMouseOffsetX = 0; + /** despues del evento up viene el evento click entonces acabo el dragging en el mouseclick **/ + this.dragging = false; + this.draggingElement = null; + this.renderLabels(); + + this.setLinking(false); + this.setMultipleSelection(true); + this.selecting = false; + +}; + +/** Move the edges of the vertex with the vertexId indicado **/ +GraphCanvas.prototype.moveEdge = function(vertexId) { + var x = this.getLayout().getNodeById(vertexId).x * this.getFormatter().getWidth(); + var y = this.getLayout().getNodeById(vertexId).y * this.getFormatter().getHeight(); + + /** Moving edges out **/ + for ( var i = 0; i < this.getDataset().getVertexById(vertexId).getEdgesOut().length; i++) { + var edgeId = this.getDataset().getVertexById(vertexId).getEdgesOut()[i].getId(); + var svgEdgeId = this.getSVGEdgeId(edgeId); + var edgeFormatter = this.getFormatter().getEdgeById(edgeId); + if (edgeFormatter instanceof LineEdgeGraphFormatter) { + DOM.select(svgEdgeId + "_shadow").setAttribute("x2", x); + DOM.select(svgEdgeId + "_shadow").setAttribute("y2", y); + DOM.select(svgEdgeId).setAttribute("x2", x); + DOM.select(svgEdgeId).setAttribute("y2", y); + } + + if ((edgeFormatter instanceof DirectedLineEdgeGraphFormatter) || (edgeFormatter instanceof OdirectedLineEdgeGraphFormatter) + || (edgeFormatter instanceof OdotDirectedLineEdgeGraphFormatter) || (edgeFormatter instanceof DotDirectedLineEdgeGraphFormatter) + || (edgeFormatter instanceof CutDirectedLineEdgeGraphFormatter)) { + this.removeEdge(edgeId); + this.renderEdge(edgeId); + } + } + + /** Moving edges in **/ + for ( var i = 0; i < this.getDataset().getVertexById(vertexId).getEdgesIn().length; i++) { + var edgeId = this.getDataset().getVertexById(vertexId).getEdgesIn()[i].getId(); + var svgEdgeId = this.getSVGEdgeId(edgeId); + var edgeFormatter = this.getFormatter().getEdgeById(edgeId); + if (edgeFormatter instanceof LineEdgeGraphFormatter) { + DOM.select(svgEdgeId).setAttribute("x1", x); + DOM.select(svgEdgeId).setAttribute("y1", y); + DOM.select(svgEdgeId + "_shadow").setAttribute("x1", x); + DOM.select(svgEdgeId + "_shadow").setAttribute("y1", y); + } + + if ((edgeFormatter instanceof DirectedLineEdgeGraphFormatter) || (edgeFormatter instanceof OdirectedLineEdgeGraphFormatter) + || (edgeFormatter instanceof OdotDirectedLineEdgeGraphFormatter) || (edgeFormatter instanceof DotDirectedLineEdgeGraphFormatter) + || (edgeFormatter instanceof CutDirectedLineEdgeGraphFormatter)) { + this.removeEdge(edgeId); + this.renderEdge(edgeId); + } + + if (edgeFormatter instanceof BezierEdgeGraphFormatter) { + var radius = this.getFormatter().getVertexById(vertexId).getDefault().getSize() * this.getFormatter().getNodesMaxSize(); + var d = this.calculateCoordenatesBezier(radius, x, y); + DOM.select(svgEdgeId).setAttribute("d", d); + } + } +}; + +GraphCanvas.prototype.moveNode = function(vertexId) { + var x = this.getLayout().getNodeById(vertexId).x * this.getFormatter().getWidth(); + var y = this.getLayout().getNodeById(vertexId).y * this.getFormatter().getHeight(); + var svgNodeElement = DOM.select(this.getSVGNodeId(vertexId)); + + svgNodeElement.setAttribute("dragx", x); + svgNodeElement.setAttribute("dragy", y); + svgNodeElement.setAttribute("transform", "translate(" + x + "," + y + ")"); + + if (this.getDataset().getVertexById(vertexId).getEdges().length < this.args.maxNumberEdgesMoving) { + this.moveEdge(vertexId); + } +}; + +GraphCanvas.prototype._movingNode = function(svgNodeElement, x, y) { + var vertexId = this.getVertexIdFromSVGId(svgNodeElement.getAttribute("id")); + this.getLayout().getNodeById(vertexId).setCoordinates(x / this.getFormatter().getWidth(), y / this.getFormatter().getHeight()); + this.desplazamientoX = 0; + this.desplazamientoY = 0; + this.removeLabel(vertexId); + this.renderLabel(vertexId); +}; + +/** INIT **/ +GraphCanvas.prototype.init = function() { + + this._svg = this.createSVGDom(this.targetID, this.id, this.getFormatter().getWidth(), this.getFormatter().getHeight(), this.getFormatter() + .getBackgroundColor()); + this.GraphGroup = SVG.drawGroup(this._svg, [ [ "id", this.args.idGraph ], [ "transform", "translate(0,0), scale(1)" ] ]); + this.GraphBackground = SVG.drawGroup(this.GraphGroup, [ [ "id", this.args.idBackground ] ]); + this.GraphEdgeGroup = SVG.drawGroup(this.GraphGroup, [ [ "id", this.args.idEdgesGraph ] ]); + this.GraphNodeGroup = SVG.drawGroup(this.GraphGroup, [ [ "id", this.args.idNodesGraph ] ]); + this.GraphLabelGroup = SVG.drawGroup(this.GraphGroup, [ [ "id", this.args.idLabelGraph ] ]); + + if ((this.getFormatter().getBackgroundImage() != null) && (this.getFormatter().getBackgroundImage() != "")) { + this.setBackgroundImage(this.getFormatter().getBackgroundImage()); + } + /** SVG Events listener */ + var _this = this; + this._svg.addEventListener("click", function(event) { + _this.mouseClick(event); + }, false); + this._svg.addEventListener("mousemove", function(event) { + _this.mouseMove(event, _this); + }, false); + this._svg.addEventListener("mousedown", function(event) { + _this.mouseDown(event, _this); + }, false); + this._svg.addEventListener("mouseup", function(event) { + _this.mouseUp(event, _this); + }, false); +}; + +/* + GraphCanvas.prototype.backgroungToSVG = function(){ + var _this = this; + var canvas = document.createElement('canvas'); + canvas.setAttribute("id", "canvas"); + canvas.width = this.formatter.getWidth(); + canvas.height = this.formatter.getHeight(); + + this._svg.parentNode.parentNode.appendChild(canvas); + var ctx = document.getElementById('canvas').getContext('2d'); + var img = new Image(); + + img.src = this.formatter.getBackgroundImage(); + ctx.drawImage(img,0,0 ,_this.formatter.getWidth(), _this.formatter.getHeight()); + + + img.onload = function() { + canvas.parentNode.removeChild(canvas); + } + + this.NodeSVGbackgroundImage.setAttribute("xlink:href", document.getElementById("canvas").toDataURL()); + this.NodeSVGbackgroundImage.removeAttribute("href"); + + // + + };*/ + +GraphCanvas.prototype.setBackgroundImage = function() { + if (this.NodeSVGbackgroundImage != null) { + this.NodeSVGbackgroundImage.parentNode.removeChild(this.NodeSVGbackgroundImage); + } + $('#' + this.targetID).svg(); + $('#' + this.targetID).svg("get"); + + $('#' + this.targetID).svg("get")._svg = document.getElementById(this.id); + + var svg = $('#' + this.targetID).svg("get"); + this.NodeSVGbackgroundImage = svg.image(0, 0, this.getFormatter().getWidth(), this.getFormatter().getHeight(), this.getFormatter() + .getBackgroundImage()); + this.NodeSVGbackgroundImage.setAttribute("id", this.args.idBackgroundNode); + + this.NodeSVGbackgroundImage.setAttribute("x", 0); + this.NodeSVGbackgroundImage.setAttribute("y", 0); + + this.NodeSVGbackgroundImage.setAttribute("dragx", 0); + this.NodeSVGbackgroundImage.setAttribute("dragy", 0); + + if (this.getFormatter().args.backgroundImageHeight != null) { + this.NodeSVGbackgroundImage.setAttribute("height", this.getFormatter().args.backgroundImageHeight); + } + if (this.getFormatter().args.backgroundImageWidth != null) { + this.NodeSVGbackgroundImage.setAttribute("width", this.getFormatter().args.backgroundImageWidth); + } + + if (this.getFormatter().args.backgroundImageX != null) { + this.NodeSVGbackgroundImage.setAttribute("x", this.getFormatter().args.backgroundImageX); + } + if (this.getFormatter().args.backgroundImageY != null) { + this.NodeSVGbackgroundImage.setAttribute("y", this.getFormatter().args.backgroundImageY); + } + + this.GraphBackground.appendChild(this.NodeSVGbackgroundImage); + this.NodeSVGbackgroundImage.removeAttribute("href"); + this.NodeSVGbackgroundImage.setAttribute("xlink:href", this.getFormatter().getBackgroundImage()); +}; + +GraphCanvas.prototype.removeBackgroundImage = function() { + if (this.NodeSVGbackgroundImage != null) { + this.NodeSVGbackgroundImage.parentNode.removeChild(this.NodeSVGbackgroundImage); + } +}; + +GraphCanvas.prototype._setBackgroundColor = function(color) { + var attributes = [ [ "fill", color ] ]; + SVG.drawRectangle(0, 0, this.getFormatter().getWidth(), this.getFormatter().getHeight(), this.GraphBackground, attributes); +}; + +/** Serialize **/ +GraphCanvas.prototype.toJSON = function() { + var json = {}; + json.dataset = {}; + json.formatter = {}; + json.layout = {}; + json.dataset = this.getDataset().toJSON(); + json.formatter = this.getFormatter().toJSON(); + json.layout = this.getLayout().toJSON(); + return json; +}; + +GraphCanvas.prototype.toHTML = function() { + //this.backgroungToSVG(); + var html = this._svg.parentElement.innerHTML; + + var start = html.indexOf("") + 6; + + return html.substr(start, end); +}; + +/** DRAW **/ +GraphCanvas.prototype.draw = function(graphdataset, graphformatter, graphlayout) { + this.setDataset(graphdataset); + this.setFormatter(graphformatter); + this.setLayout(graphlayout); + + var _this = this; + this.getFormatter().changed.attach(function(sender, item) { + _this.removeNode(item.getId()); + _this.renderNode(item.getId()); + if (_this.args.labeled) { + _this.removeLabel(item.getId()); + _this.renderLabel(item.getId()); + } + + }); + //TODO + this.getFormatter().edgeChanged.attach(function(sender, item) { + _this.removeEdge(item.getId()); + _this.renderEdge(item.getId()); + }); + + this.getFormatter().resized.attach(function(sender, item) { + _this.resize(_this.getFormatter().getWidth(), _this.getFormatter().getHeight()); + }); + + this.getFormatter().backgroundImageChanged.attach(function(sender, item) { + _this.setBackgroundImage(_this.getFormatter().getBackgroundImage()); + }); + + this.getFormatter().backgroundColorChanged.attach(function(sender, item) { + _this._setBackgroundColor(_this.getFormatter().getBackgroundColor()); + }); + + this.getLayout().changed.attach(function(sender, item) { + _this.moveNode(item.getId()); + _this.moveEdge(item.getId()); + if (_this.args.labeled) { + _this.removeLabel(item.getId()); + _this.renderLabel(item.getId()); + } + }); + + this.getDataset().newVertex.attach(function(sender, item) { + + _this.renderNode(item.getId()); + if (_this.args.labeled) { + _this.renderLabel(item.getId()); + } + }); + + this.getDataset().newEdge.attach(function(sender, item) { + _this.renderEdge(item.getId()); + }); + + this.getDataset().vertexDeleted.attach(function(sender, item) { + _this.removeNode(item.getId()); + if (_this.args.labeled) { + _this.removeLabel(item.getId()); + } + }); + + this.getDataset().edgeDeleted.attach(function(sender, item) { + _this.removeEdge(item.getId()); + }); + + this.getDataset().vertexNameChanged.attach(function(sender, args) { + if (_this.args.labeled) { + _this.removeLabel(args.item.getId()); + _this.removeLabel(args.item.getId()); + _this.renderLabel(args.item.getId()); + } + }); + this.init(); + this.render(); +}; + +GraphCanvas.prototype.render = function() { + for ( var id in this.getDataset().getVertices()) { + this.renderNode(id); + } + this.renderLabels(); + this.renderEdges(); +}; + +GraphCanvas.prototype.renderLabels = function() { + if (this.args.labeled) { + for ( var id in this.getDataset().getVertices()) { + this.renderLabel(id); + } + } +}; + +GraphCanvas.prototype.removeLabels = function() { + for ( var id in this.getDataset().getVertices()) { + this.removeLabel(id); + } +}; + +/** Utilities method for nodes **/ +GraphCanvas.prototype.isNodeCanvas = function(node) { + return ((node.id == this.args.idGraph) || (node.id == this.id)); +}; + +GraphCanvas.prototype.isNodeBackground = function(node) { + return ((node.id == this.args.idBackgroundNode)); +}; + +GraphCanvas.prototype.isVertex = function(node) { + if (node.getAttribute("id") != null) { + if (node.getAttribute("id").indexOf("_v_") != -1) { + return true; + } + } + return false; +}; + +GraphCanvas.prototype.isLabel = function(node) { + if (node.getAttribute("id") != null) { + if (node.getAttribute("id").indexOf("_l_") != -1) { + return true; + } + } + return false; +}; + +GraphCanvas.prototype.isEdge = function(node) { + if (node.getAttribute("id") != null) { + if (node.getAttribute("id").indexOf("_e_") != -1) { + return true; + } + } + return false; +}; + +/** Resize **/ +GraphCanvas.prototype.resize = function(width, height) { + // this._svg.setAttribute("width", width); + // this._svg.setAttribute("height", height); + if (this.NodeSVGbackgroundImage != null) { + this.NodeSVGbackgroundImage.setAttribute("width", width); + this.NodeSVGbackgroundImage.setAttribute("height", height); + } + + this._svg.setAttribute("width", width); + this._svg.setAttribute("height", height); + + this.clearCanvas(); + this.render(); +}; + +GraphCanvas.prototype.clearCanvas = function() { + DOM.removeChilds(this.GraphEdgeGroup.getAttribute("id")); + DOM.removeChilds(this.GraphNodeGroup.getAttribute("id")); + this.clearLabels(); +}; + +GraphCanvas.prototype.clearLabels = function() { + DOM.removeChilds(this.GraphLabelGroup.getAttribute("id")); +}; + +/** ID'S converter **/ +GraphCanvas.prototype.getSVGNodeId = function(nodeId) { + return this.id + "_v_" + nodeId; +}; + +GraphCanvas.prototype.getSVGEdgeId = function(edgeId) { + return this.id + "_e_" + edgeId; +}; + +GraphCanvas.prototype.getSVGArrowEdgeId = function(edgeId) { + return this.id + "_arrow_" + edgeId; +}; + +GraphCanvas.prototype.getSVGLabelId = function(edgeId) { + return this.id + "_l_" + edgeId; +}; + +GraphCanvas.prototype.blinkVertexById = function(vertexId) { + $("#" + this.getSVGNodeId(vertexId)).fadeIn().fadeOut().fadeIn().fadeOut().fadeIn().fadeOut(); +}; + +GraphCanvas.prototype.getVertexIdFromSVGId = function(svgVertexId) { + return svgVertexId.replace(this.id, "").replace("_v_", ""); +}; + +GraphCanvas.prototype.getEdgeIdFromSVGId = function(svgEdgeId) { + return svgEdgeId.replace(this.id, "").replace("_e_", ""); +}; + +/** VERTEX **/ +GraphCanvas.prototype.getVertexById = function(id) { + return document.getElementById(this.getSVGNodeId(id)); +}; + +GraphCanvas.prototype.renderNodes = function() { + for ( var id in this.getDataset().getVertices()) { + this.renderNode(id); + } +}; + +GraphCanvas.prototype.overNode = function(nodeId) { + if (!this.args.interactive) { + return; + } + /** If selected we don't change the format **/ + if (this.args.isVertexSelected[nodeId] == null) { + var args = this.getFormatter().getVertexById(nodeId).getOver(); + args.args["cursor"] = 'pointer'; + this.changeVertexFormat(nodeId, args); + } +}; + +GraphCanvas.prototype.outNode = function(nodeId) { + if (!this.args.interactive) { + return; + } + + /** If selected we don't change the format **/ + if (this.args.isVertexSelected[nodeId] == null) { + this.changeVertexFormat(nodeId, this.getFormatter().getVertexById(nodeId).getDefault()); + } +}; + +GraphCanvas.prototype.overLabel = function(nodeId) { + this.overNode(nodeId); + // this.svgLabels[nodeId].setAttribute("cursor", "pointer"); +}; + +GraphCanvas.prototype.outLabel = function(nodeId) { + this.outNode(nodeId); + // this.svgLabels[nodeId].setAttribute("cursor", ""); +}; + +GraphCanvas.prototype.clickNode = function(nodeId) { + if (!this.args.interactive) { + return; + } + + /** si el evento se dispara oprque estaba dragging entonces no activo nada **/ + if (this.args.isVertexSelected[nodeId] == null) { + this.selectNode(nodeId); + } else { + this.deselectNode(nodeId); + } +}; + +GraphCanvas.prototype.selectNode = function(nodeId) { + for ( var i = 0; i < this.args.selectedVertices.length; i++) { + var format = this.getFormatter().getVertexById(nodeId).getSelected(); + format.opacity = 0.2; + this.changeVertexFormat(nodeId, this.getFormatter().getVertexById(nodeId).getSelected()); + } + + if (this.args.isVertexSelected[nodeId] == null) { + var format = this.getFormatter().getVertexById(nodeId).getSelected(); + format.opacity = 1; + this.changeVertexFormat(nodeId, this.getFormatter().getVertexById(nodeId).getSelected()); + this.args.selectedVertices.push(nodeId); + this.args.isVertexSelected[nodeId] = this.args.selectedVertices.length - 1; + this.onVertexSelect.notify(nodeId); + } +}; + +GraphCanvas.prototype.selectAllEdges = function() { + this.deselectNodes(); + this.deselectEdges(); + + for ( var edgesId in this.getDataset().edges) { + this.selectEdge(edgesId); + } +}; + +GraphCanvas.prototype.selectAllNodes = function() { + this.deselectNodes(); + this.deselectEdges(); + + for ( var vertexId in this.getDataset().vertices) { + this.selectNode(vertexId); + } +}; + +GraphCanvas.prototype.selectAll = function() { + this.deselectNodes(); + this.deselectEdges(); + + for ( var vertexId in this.getDataset().vertices) { + this.selectNode(vertexId); + } + + for ( var edgesId in this.getDataset().edges) { + this.selectEdge(edgesId); + } +}; + +GraphCanvas.prototype.selectEdge = function(edgeId) { + if (this.args.isEdgeSelected[edgeId] == null) { + this.changeEdgeFormat(edgeId, this.getFormatter().getEdgeById(edgeId).getSelected()); + //this.args.selectedEdges.push(edgeId); + this.args.isEdgeSelected[edgeId] = true; //this.args.selectedEdges.length - 1; + this.onEdgeSelect.notify(edgeId); + } +}; + +GraphCanvas.prototype.selectEdges = function(edges) { + + for ( var i = 0; i < edges.length; i++) { + this.selectEdge(edges[i]); + } +}; + +GraphCanvas.prototype.deselectNode = function(nodeId) { + if (this.args.isVertexSelected[nodeId] != null) { + this.changeVertexFormat(nodeId, this.getFormatter().getVertexById(nodeId).getDefault()); + this.args.selectedVertices.splice(this.args.isVertexSelected[nodeId], 1); + var index = this.args.isVertexSelected[nodeId]; + delete this.args.isVertexSelected[nodeId]; + + for ( var vertex in this.args.isVertexSelected) { + if (this.args.isVertexSelected[vertex] > index) { + this.args.isVertexSelected[vertex] = this.args.isVertexSelected[vertex] - 1; + } + } + } +}; + +GraphCanvas.prototype.deselectNodes = function() { + var selected = JSON.parse(JSON.stringify(this.getSelectedVertices())); + for ( var i = 0; i < selected.length; i++) { + this.deselectNode(selected[i]); + } +}; +GraphCanvas.prototype.selectNodes = function(idNodes) { + + for ( var i = 0; i < idNodes.length; i++) { + this.selectNode(idNodes[i]); + } + + // for ( var vertex in this.args.isVertexSelected) { + // if (this.args.isVertexSelected[vertex] > index){ + // this.args.isVertexSelected[vertex] = this.args.isVertexSelected[vertex] - 1; + // } + // } + +}; + +GraphCanvas.prototype.changeVertexFormat = function(nodeId, format) { + var svgNode = DOM.select(this.getSVGNodeId(nodeId)); + if (svgNode != null) { + var properties = format.toJSON(); + for ( var item in properties) { + svgNode.setAttribute(item, properties[item]); + } + + if (this.getFormatter().getVertexById(nodeId) instanceof CircleVertexGraphFormatter) { + var transform = "translate(" + svgNode.getAttribute("dragx") + "," + svgNode.getAttribute("dragy") + "), scale(" + format.getSize() + ")"; + svgNode.setAttribute("transform", transform); + } + } +}; + +GraphCanvas.prototype.renderLabel = function(nodeId) { + var x = Math.ceil(this.getLayout().getNodeById(nodeId).x * this.getFormatter().getWidth()); + var y = Math.ceil(this.getLayout().getNodeById(nodeId).y * this.getFormatter().getHeight()); + + var svgAttributesNode = JSON.parse(JSON.stringify(this.getFormatter().getVertexById(nodeId).getDefault().toJSON().title)); + svgAttributesNode.id = this.getSVGLabelId(this.getDataset().getVertexById(nodeId).getId()); + svgAttributesNode.dx = (-1) * (this.getDataset().getVertexById(nodeId).getName().length * svgAttributesNode["font-size"]) / 4 - 4; + + svgAttributesNode.dy = parseFloat((this.getFormatter().getVertexById(nodeId).getDefault().getSize())) + + parseFloat(svgAttributesNode["font-size"]) + parseFloat(this.getFormatter().getVertexById(nodeId).getDefault().getStrokeWidth()) - 4; + svgAttributesNode.dragx = Math.ceil(this.getLayout().getNodeById(nodeId).x * this.getFormatter().getWidth()); + + var gragy = parseFloat(this.getFormatter().getVertexById(nodeId).getDefault().getSize()) + + Math.ceil(this.getLayout().getNodeById(nodeId).y * this.getFormatter().getHeight()); + svgAttributesNode.dragy = gragy; + svgAttributesNode.transform = "translate(" + svgAttributesNode.dragx + "," + svgAttributesNode.dragy + ")";//, scale("+this.formatter.getVertexById(nodeId).getDefault().getSize()+")"; + + var nodeSVG = SVG.drawText(0, 0, this.getDataset().getVertexById(nodeId).getName(), this.GraphLabelGroup, svgAttributesNode); + + this.svgLabels[nodeId] = nodeSVG; + + /** Events for the SVG node **/ + var _this = this; + if (nodeSVG != null) { + nodeSVG.addEventListener("mouseover", function() { + _this.overLabel(nodeId); + }, false); + nodeSVG.addEventListener("mouseout", function() { + _this.outLabel(nodeId); + }, false); + } + +}; + +GraphCanvas.prototype.removeLabel = function(labelId) { + if (DOM.select(this.getSVGLabelId(labelId)) != null) { + DOM.select(this.getSVGLabelId(labelId)).parentNode.removeChild(DOM.select(this.getSVGLabelId(labelId))); + } +}; + +GraphCanvas.prototype.renderNode = function(nodeId) { + var svgAttributesNode = JSON.parse(JSON.stringify(this.getFormatter().getVertexById(nodeId).getDefault().toJSON())); + svgAttributesNode.dragx = Math.ceil(this.getLayout().getNodeById(nodeId).x * this.getFormatter().getWidth()); + svgAttributesNode.dragy = Math.ceil(this.getLayout().getNodeById(nodeId).y * this.getFormatter().getHeight()); + svgAttributesNode.transform = "translate(" + svgAttributesNode.dragx + "," + svgAttributesNode.dragy + ")"; + svgAttributesNode.id = this.getSVGNodeId(nodeId); + /*svgAttributesNode["stroke-width"] = 3 ; + svgAttributesNode["stroke-opacity"] = 1 ; + svgAttributesNode["fill-opacity"] = svgAttributesNode["opacity"] ; + svgAttributesNode["opacity"] = 1 ;*/ + this.circleDefaultRadius = this.getFormatter().getVertexById(nodeId).getDefault().getSize(); + var nodeSVG; + + if (this.getFormatter().getVertexById(nodeId) instanceof CircleVertexGraphFormatter) { + nodeSVG = SVG.drawCircle(0, 0, this.circleDefaultRadius, this.GraphNodeGroup, svgAttributesNode); + } + + if (this.getFormatter().getVertexById(nodeId) instanceof SquareVertexGraphFormatter) { + //nodeSVG = SVG.drawRectangle(0 - (this.circleDefaultRadius) ,0 - (this.formatter.getVertexById(nodeId).getDefault().getSize()) , (this.getFormatter().getVertexById(nodeId).getDefault().getSize()*2), (this.getFormatter().getVertexById(nodeId).getDefault().getSize()*2), this.GraphNodeGroup, svgAttributesNode); + nodeSVG = SVG.drawRectangle(0 - (this.circleDefaultRadius), 0 - (this.circleDefaultRadius), (this.circleDefaultRadius * 2), + (this.circleDefaultRadius * 2), this.GraphNodeGroup, svgAttributesNode); + } + + if (this.getFormatter().getVertexById(nodeId) instanceof EllipseVertexGraphFormatter) { + nodeSVG = SVG.drawEllipse(0, 0, this.circleDefaultRadius * 1.5, this.circleDefaultRadius, this.GraphNodeGroup, svgAttributesNode); + } + + if (this.getFormatter().getVertexById(nodeId) instanceof RectangleVertexGraphFormatter) { + //nodeSVG = SVG.drawRectangle(0 - (this.circleDefaultRadius) ,0 - ((this.circleDefaultRadius*2)/2) , (this.circleDefaultRadius*2), (this.circleDefaultRadius), this.GraphNodeGroup, svgAttributesNode); + nodeSVG = SVG.drawRectangle(0 - (this.circleDefaultRadius * 1.5), 0 - (this.circleDefaultRadius), (this.circleDefaultRadius * 2 * 1.5), + (this.circleDefaultRadius * 2), this.GraphNodeGroup, svgAttributesNode); + + } + + if (this.getFormatter().getVertexById(nodeId) instanceof RoundedVertexGraphFormatter) { + svgAttributesNode.ry = 2;// this.formatter.getVertexById(nodeId).getDefault().getSize()/4; + svgAttributesNode.rx = 2;// this.formatter.getVertexById(nodeId).getDefault().getSize()/4; + nodeSVG = SVG.drawRectangle(0 - (this.circleDefaultRadius * 1.5), 0 - (this.circleDefaultRadius), (this.circleDefaultRadius * 2 * 1.5), + (this.circleDefaultRadius * 2), this.GraphNodeGroup, svgAttributesNode); + } + + // + + if (this.getFormatter().getVertexById(nodeId) instanceof OctagonVertexGraphFormatter) { + svgAttributesNode.ry = 2; + svgAttributesNode.rx = 2; + nodeSVG = SVG.drawRectangle(0 - (this.circleDefaultRadius * 1.5), 0 - (this.circleDefaultRadius), (this.circleDefaultRadius * 2 * 1.5), + (this.circleDefaultRadius * 2), this.GraphNodeGroup, svgAttributesNode); + } + + nodeSVG.internalId = nodeId; + // + var _this = this; + + /** Events for the SVG node **/ + if (nodeSVG != null) { + nodeSVG.addEventListener("mouseover", function() { + _this.onVertexOver.notify(nodeId); + _this.overNode(nodeId); + }, false); + nodeSVG.addEventListener("mouseout", function() { + _this.onVertexOut.notify(nodeId); + _this.outNode(nodeId); + }, false); + //nodeSVG.addEventListener("click", function(){_this.clickNode(nodeId);}, false); + // + nodeSVG.addEventListener("mouseup", function() { + _this.onVertexUp.notify(nodeId); + }, false); + } +}; + +GraphCanvas.prototype.removeNode = function(nodeId) { + DOM.select(this.getSVGNodeId(nodeId)).parentNode.removeChild(DOM.select(this.getSVGNodeId(nodeId))); + if (this.args.labeled) { + this.removeLabel(nodeId); + } +}; + +/** REMOVING **/ +GraphCanvas.prototype.removeSelected = function() { + /** El orden importa **/ + this.removeSelectedEdges(); + this.removeSelectedNode(); + +}; + +GraphCanvas.prototype.removeSelectedNode = function() { + var selected = JSON.parse(JSON.stringify(this.getSelectedVertices())); + this.deselectNodes(); + var sorted = selected.sort(function(a, b) { + return a - b + }); + for ( var i = 0; i < sorted.length; i++) { + if (this.getDataset().getVertexById(sorted[i]) != null) { + this.getDataset().getVertexById(sorted[i]).remove(); + } + } +}; + +/** EDGES **/ +GraphCanvas.prototype.removeEdge = function(edgeId) { + if (DOM.select(this.getSVGEdgeId(edgeId)) != null) { + DOM.select(this.getSVGEdgeId(edgeId)).parentNode.removeChild(DOM.select(this.getSVGEdgeId(edgeId))); + } + + if (DOM.select(this.getSVGEdgeId(edgeId) + "_shadow") != null) { + DOM.select(this.getSVGEdgeId(edgeId) + "_shadow").parentNode.removeChild(DOM.select(this.getSVGEdgeId(edgeId) + "_shadow")); + } + + if (DOM.select(this.getSVGArrowEdgeId(edgeId)) != null) { + DOM.select(this.getSVGArrowEdgeId(edgeId)).parentNode.removeChild(DOM.select(this.getSVGArrowEdgeId(edgeId))); + } +}; + +GraphCanvas.prototype.overEdge = function(edgeId) { + if ((!this.args.interactive) || this.dragging || this.selecting) { + return; + } + + /** If selected we don't change the format **/ + if (this.args.isEdgeSelected[edgeId] == null) { + var format = this.getFormatter().getEdgeById(edgeId).getOver(); + format.args["cursor"] = "pointer"; + this.changeEdgeFormat(edgeId, format); + } +}; + +GraphCanvas.prototype.outEdge = function(edgeId) { + if (!this.args.interactive) { + return; + } + + /** If selected we don't change the format **/ + if (this.args.isEdgeSelected[edgeId] == null) { + this.changeEdgeFormat(edgeId, this.getFormatter().getEdgeById(edgeId).getDefault()); + } +}; + +GraphCanvas.prototype.changeEdgeFormat = function(edgeId, format) { + var svgEdge = DOM.select(this.getSVGEdgeId(edgeId) + "_shadow"); + if (svgEdge != null) { + var properties = format.toJSON(); + for ( var item in properties) { + svgEdge.setAttribute(item, properties[item]); + } + } +}; + +GraphCanvas.prototype.deselectEdge = function(edgeID) { + if (this.args.isEdgeSelected[edgeID] != null) { + this.changeEdgeFormat(edgeID, this.getFormatter().getEdgeById(edgeID).getDefault()); + var index = this.args.isEdgeSelected[edgeID]; + delete this.args.isEdgeSelected[edgeID]; + } +}; + +GraphCanvas.prototype.deselectEdges = function() { + var selected = JSON.parse(JSON.stringify(this.getSelectedEdges())); + for ( var i = 0; i < selected.length; i++) { + this.deselectEdge(selected[i]); + } +}; + +GraphCanvas.prototype.removeSelectedEdges = function() { + var selected = JSON.parse(JSON.stringify(this.getSelectedEdges())); + this.deselectEdges(); + for ( var i = 0; i < selected.length; i++) { + if (this.getDataset().getEdgeById(selected[i]) != null) { + this.getDataset().getEdgeById(selected[i]).remove(); + } + } +}; + +GraphCanvas.prototype.renderEdge = function(edgeId) { + var svgAttributesEdge = this.getFormatter().getEdgeById(edgeId).getDefault().toJSON(); + var edge = this.getDataset().getEdgeById(edgeId); + + var svgNodeTarget = this.getVertexById(edge.getNodeTarget().getId()); + var svgNodeSource = this.getVertexById(edge.getNodeSource().getId()); + svgAttributesEdge.id = this.getSVGEdgeId(edge.getId()) + "_shadow"; + + var svgEdge = null; + + if (this.getFormatter().getEdgeById(edgeId) instanceof LineEdgeGraphFormatter) { + var coordenateSourceX = svgNodeSource.getAttribute("dragx"); + var coordenateSourceY = svgNodeSource.getAttribute("dragy"); + var coordenateTargetX = svgNodeTarget.getAttribute("dragx"); + var coordenateTargetY = svgNodeTarget.getAttribute("dragy"); + + SVG.drawLine(coordenateSourceX, coordenateSourceY, coordenateTargetX, coordenateTargetY, this.GraphEdgeGroup, svgAttributesEdge); + var attributesShadow = {}; + attributesShadow.id = this.getSVGEdgeId(edge.getId()); + attributesShadow["stroke-opacity"] = 0; + attributesShadow["stroke-width"] = 4; + attributesShadow["stroke"] = "black"; + svgEdge = SVG.drawLine(svgNodeSource.getAttribute("dragx"), svgNodeSource.getAttribute("dragy"), svgNodeTarget.getAttribute("dragx"), + svgNodeTarget.getAttribute("dragy"), this.GraphEdgeGroup, attributesShadow); + } + + if (this.getFormatter().getEdgeById(edgeId) instanceof BezierEdgeGraphFormatter) { + var nodeId = edge.getNodeTarget().getId(); + var nodeSize = this.formatter.getVertexById(nodeId).getDefault().getSize() * this.getFormatter().getNodesMaxSize(); + svgAttributesEdge.fill = "none"; + svgAttributesEdge.id = this.getSVGEdgeId(edgeId); + var d = this.calculateCoordenatesBezier(nodeSize, svgNodeSource.getAttribute("dragx"), svgNodeSource.getAttribute("dragy")); + svgEdge = SVG.drawPath(d, this.GraphEdgeGroup, svgAttributesEdge); + } + ; + + if ((this.getFormatter().getEdgeById(edgeId) instanceof DirectedLineEdgeGraphFormatter) + || (this.getFormatter().getEdgeById(edgeId) instanceof CutDirectedLineEdgeGraphFormatter) + || (this.getFormatter().getEdgeById(edgeId) instanceof DotDirectedLineEdgeGraphFormatter) + || (this.getFormatter().getEdgeById(edgeId) instanceof OdotDirectedLineEdgeGraphFormatter) + || (this.getFormatter().getEdgeById(edgeId) instanceof OdirectedLineEdgeGraphFormatter)) { + var coordenateSourceX = svgNodeSource.getAttribute("dragx"); + var coordenateSourceY = svgNodeSource.getAttribute("dragy"); + var coordenateTargetX = svgNodeTarget.getAttribute("dragx"); + var coordenateTargetY = svgNodeTarget.getAttribute("dragy"); + + var offset = parseFloat(this.getFormatter().getVertexById(this.getDataset().getEdgeById(edgeId).getNodeTarget().getId()).getDefault() + .getSize() + * this.circleDefaultRadius); + var point = this._calculateEdgePointerPosition(coordenateSourceX, coordenateSourceY, coordenateTargetX, coordenateTargetY, offset); + coordenateTargetX = point.x; + coordenateTargetY = point.y; + SVG.drawLine(coordenateSourceX, coordenateSourceY, coordenateTargetX, coordenateTargetY, this.GraphEdgeGroup, svgAttributesEdge); + + var attributesShadow = {}; + attributesShadow.id = this.getSVGEdgeId(edge.getId()); + attributesShadow["stroke-opacity"] = 0; + attributesShadow["stroke-width"] = 4; + attributesShadow["stroke"] = "black"; + svgEdge = SVG.drawLine(coordenateSourceX, coordenateSourceY, coordenateTargetX, coordenateTargetY, this.GraphEdgeGroup, attributesShadow); + } + + if (this.getFormatter().getEdgeById(edgeId) instanceof DirectedLineEdgeGraphFormatter + || (this.getFormatter().getEdgeById(edgeId) instanceof OdirectedLineEdgeGraphFormatter)) { + var coordenateSourceX = svgNodeSource.getAttribute("dragx"); + var coordenateSourceY = svgNodeSource.getAttribute("dragy"); + var coordenateTargetX = svgNodeTarget.getAttribute("dragx"); + var coordenateTargetY = svgNodeTarget.getAttribute("dragy"); + + var point = this._calculateEdgePointerPosition(coordenateSourceX, coordenateSourceY, coordenateTargetX, coordenateTargetY, offset); + coordenateTargetX = point.x; + coordenateTargetY = point.y; + + var angle = Geometry.toDegree(point.angle) + 90; + this.arrowDefaultSize = this.getFormatter().getEdgeById(edgeId).getArrowSize(); //getDefault().getArrowSize(); + var d = "-" + this.arrowDefaultSize + ",0 0,-" + parseFloat(this.arrowDefaultSize) * 2 + " " + this.arrowDefaultSize + ",0"; + + var attributes; + + if (this.getFormatter().getEdgeById(edgeId) instanceof DirectedLineEdgeGraphFormatter) { + attributes = [ + [ "fill", this.getFormatter().getEdgeById(edgeId).getDefault().getStroke() ], + [ "stroke", this.getFormatter().getEdgeById(edgeId).getDefault().getStroke() ], [ "id", this.getSVGArrowEdgeId(edgeId) ] ]; + } else { + attributes = [ + [ "fill", "#FFFFFF" ], [ "stroke", this.getFormatter().getEdgeById(edgeId).getDefault().getStroke() ], + [ "id", this.getSVGArrowEdgeId(edgeId) ] ]; + } + + var flechaSVGNode = SVG.drawPoligon(d, this.GraphEdgeGroup, attributes);//, ["transform", "rotate("+angle+"), translate(0,0)"]]); + flechaSVGNode.setAttribute("transform", " translate(" + coordenateTargetX + ", " + coordenateTargetY + "), rotate(" + angle + ")"); + } + ; + + if (this.getFormatter().getEdgeById(edgeId) instanceof CutDirectedLineEdgeGraphFormatter) { + var coordenateSourceX = svgNodeSource.getAttribute("dragx"); + var coordenateSourceY = svgNodeSource.getAttribute("dragy"); + var coordenateTargetX = svgNodeTarget.getAttribute("dragx"); + var coordenateTargetY = svgNodeTarget.getAttribute("dragy"); + + var point = this._calculateEdgePointerPosition(coordenateSourceX, coordenateSourceY, coordenateTargetX, coordenateTargetY, offset); + coordenateTargetX = point.x; + coordenateTargetY = point.y; + + var angle = Geometry.toDegree(point.angle) + 90; + + //this.arrowDefaultSize = 2; //getDefault().getArrowSize(); + var d = "-4,0 4,0 4,-2 -4,-2"; + + var flechaSVGNode = SVG.drawPoligon(d, this.GraphEdgeGroup, [ + [ "fill", this.getFormatter().getEdgeById(edgeId).getDefault().getStroke() ], + [ "stroke", this.getFormatter().getEdgeById(edgeId).getDefault().getStroke() ], [ "id", this.getSVGArrowEdgeId(edgeId) ] ]);//, ["transform", "rotate("+angle+"), translate(0,0)"]]); + flechaSVGNode.setAttribute("transform", " translate(" + coordenateTargetX + ", " + coordenateTargetY + "), rotate(" + angle + ")"); + } + ; + + if ((this.getFormatter().getEdgeById(edgeId) instanceof DotDirectedLineEdgeGraphFormatter) + || (this.getFormatter().getEdgeById(edgeId) instanceof OdotDirectedLineEdgeGraphFormatter)) { + var coordenateSourceX = svgNodeSource.getAttribute("dragx"); + var coordenateSourceY = svgNodeSource.getAttribute("dragy"); + var coordenateTargetX = svgNodeTarget.getAttribute("dragx"); + var coordenateTargetY = svgNodeTarget.getAttribute("dragy"); + var point = this._calculateEdgePointerPosition(coordenateSourceX, coordenateSourceY, coordenateTargetX, coordenateTargetY, offset); + coordenateTargetX = point.x; + coordenateTargetY = point.y; + var angle = Geometry.toDegree(point.angle) + 90; + // this.arrowDefaultSize = this.formatter.getEdgeById(edgeId).getArrowSize(); //getDefault().getArrowSize(); + var attributes = []; + if (this.getFormatter().getEdgeById(edgeId) instanceof OdotDirectedLineEdgeGraphFormatter) { + attributes = [ + [ "fill", "#FFFFFF" ], [ "stroke", this.getFormatter().getEdgeById(edgeId).getDefault().getStroke() ], + [ "id", this.getSVGArrowEdgeId(edgeId) ] ]; + } else { + attributes = [ + [ "fill", this.getFormatter().getEdgeById(edgeId).getDefault().getStroke() ], + [ "stroke", this.getFormatter().getEdgeById(edgeId).getDefault().getStroke() ], [ "id", this.getSVGArrowEdgeId(edgeId) ] ]; + } + var flechaSVGNode = SVG.drawCircle(0, 0, 4, this.GraphEdgeGroup, attributes); + flechaSVGNode.setAttribute("transform", " translate(" + coordenateTargetX + ", " + coordenateTargetY + "), rotate(" + angle + ")"); + } + ; + + var _this = this; + /** Events for the SVG edge **/ + if (svgEdge != null) { + if (this.getDataset().getEdgesCount() < this.args.maxNumberEdgesFiringEvents) { + svgEdge.addEventListener("mouseover", function() { + _this.overEdge(edgeId); + }, false); + svgEdge.addEventListener("mouseout", function() { + _this.outEdge(edgeId); + }, false); + } + } +}; + +GraphCanvas.prototype._calculateEdgePointerPosition = function(sourceX, sourceY, targetX, targetY, radius) { + var angle = Geometry.getAngleBetweenTwoPoints(sourceX, sourceY, targetX, targetY); + + /** Suponiendo el node source que este a la derecha **/ + if ((targetX - sourceX) < 0) { + var b = Geometry.getAdjacentSideOfRectangleRight(angle, radius); + targetX = parseFloat(targetX) + parseFloat(b); + arrowX = parseFloat(targetX) + parseFloat(b) + this.arrowDefaultSize / 2; + } else { + var b = Geometry.getAdjacentSideOfRectangleRight(angle, radius); + targetX = parseFloat(targetX) - parseFloat(b); + arrowX = parseFloat(targetX) - parseFloat(b) - this.arrowDefaultSize / 2; + } + + /** Suponiendo el node source que este a la arriba **/ + if ((targetY - sourceY) > 0) { + var a = Geometry.getOppositeSideOfRectangleRight(angle, radius); + targetY = parseFloat(targetY) - parseFloat(a); + arrowY = parseFloat(targetY) - parseFloat(a) - this.arrowDefaultSize / 2; + } else { + var a = Geometry.getOppositeSideOfRectangleRight(angle, radius); + targetY = parseFloat(targetY) + parseFloat(a); + arrowY = parseFloat(targetY) + parseFloat(a) - this.arrowDefaultSize / 2; + + } + + return { + "x" : arrowX, + "y" : arrowY, + "angle" : angle + }; +}; + +GraphCanvas.prototype.calculateCoordenatesBezier = function(nodeSize, x1, y1) { + var x11 = x1 - (nodeSize / 2); + var y11 = y1 - (nodeSize / 2); + + var x12 = parseFloat(x1) + parseFloat(nodeSize / 2); + var y12 = y1 - (nodeSize / 2); + + var curvePointX = (x12 - x11) / 2 + x11; + var curvePointY = y1 - (nodeSize * 2); + var d = "M" + x11 + "," + y11 + " T" + curvePointX + "," + curvePointY + " " + x12 + "," + y12; + return d; + +}; + +GraphCanvas.prototype.renderEdges = function() { + for ( var edge in this.getDataset().getEdges()) { + this.renderEdge(this.getDataset().getEdgeById(edge).getId()); + + } +}; + +GraphCanvas.prototype.getLastSelectedNode = function() { + var node = null; + if (this.getSelectedVertices().length > 0) { + var nodeId = this.getSelectedVertices()[this.getSelectedVertices().length - 1]; + node = this.getDataset().getVertexById(nodeId); + } + return node; +}; +/* + GraphCanvas.prototype.getNodeByNameAndIndex = function(node, index){ + var nodeId = this.getDataset().verticesIndex[node][index]; + var nodeItem = this.getDataset().getVertexById(nodeId); + return nodeItem; + }; + */ + +GraphCanvas.prototype.setDataset = function(dataset) { + this.dataset = dataset; +}; + +GraphCanvas.prototype.setFormatter = function(formatter) { + this.formatter = formatter; +}; + +GraphCanvas.prototype.setLayout = function(layout) { + this.layout = layout; +}; + +/** API **/ +GraphCanvas.prototype.getDataset = function() { + return this.dataset; +}; + +GraphCanvas.prototype.getFormatter = function() { + return this.formatter; +}; + +GraphCanvas.prototype.getLayout = function() { + return this.layout; +}; + +/** API DATASET **/ +GraphCanvas.prototype.addVertex = function(name, args) { + this.getDataset().addNode(name, args); +}; + +GraphCanvas.prototype.removeVertex = function(vertexId) { + this.getDataset().getVertexById(vertexId).remove(); +}; + +GraphCanvas.prototype.addEdge = function(edgeName, nodeSourceId, nodeTargetId, args) { + this.getDataset().addEdge(edgeName, nodeSourceId, nodeTargetId, args); +}; +/* + GraphCanvas.prototype.removeEdge = function(edgeId){ + this.getDataset().getEdgeById(edgeId).remove(); + }; + */ + +/** API FORMATTER **/ +GraphCanvas.prototype.getWidth = function() { + return this.getFormatter().getWidth(); +}; + +GraphCanvas.prototype.getHeight = function() { + return this.getFormatter().getHeight(); +}; + +GraphCanvas.prototype.getBackgroundImage = function() { + return this.getFormatter().getBackgroundImage(); +}; + +//GraphCanvas.prototype.setBackgroundImage = function(value){ +// this.getFormatter().setBackgroundImage(value); +//}; + +GraphCanvas.prototype.getBackgroundColor = function() { + return this.getFormatter().getBackgroundColor(); +}; + +GraphCanvas.prototype.setBackgroundColor = function() { + this.getFormatter().setBackgroundColor(value); +}; + +//GraphCanvas.prototype.setEdgeFill = function(edgeId, value){ +// this.getFormatter().getEdgeById(edgeId).getDefault().setFill(value); +//}; +// +//GraphCanvas.prototype.getEdgeFill = function(edgeId){ +// return this.getFormatter().getEdgeById(edgeId).getDefault().getFill(); +//}; + +/** VERTICES FORMATTER **/ +GraphCanvas.prototype.setVertexSize = function(vertexId, value) { + this.getFormatter().getVertexById(vertexId).getDefault().setSize(value); +}; + +GraphCanvas.prototype.getVertexSize = function(vertexId) { + return this.getFormatter().getVertexById(vertexId).getDefault().getSize(); +}; + +GraphCanvas.prototype.setVertexStroke = function(vertexId, value) { + this.getFormatter().getVertexById(vertexId).getDefault().setStroke(value); +}; + +GraphCanvas.prototype.getVertexStroke = function(vertexId) { + return this.getFormatter().getVertexById(vertexId).getDefault().getStroke(); +}; + +GraphCanvas.prototype.setVertexStrokeOpacity = function(vertexId, value) { + this.getFormatter().getVertexById(vertexId).getDefault().setStrokeOpacity(value); +}; + +GraphCanvas.prototype.getVertexStrokeOpacity = function(vertexId) { + return this.getFormatter().getVertexById(vertexId).getDefault().getStrokeOpacity(); +}; + +GraphCanvas.prototype.setVertexOpacity = function(vertexId, value) { + this.getFormatter().getVertexById(vertexId).getDefault().setOpacity(value); +}; + +GraphCanvas.prototype.getVertexOpacity = function(vertexId) { + return this.getFormatter().getVertexById(vertexId).getDefault().getOpacity(); +}; + +GraphCanvas.prototype.setVertexFill = function(vertexId, color) { + this.getFormatter().getVertexById(vertexId).getDefault().setFill(color); +}; + +GraphCanvas.prototype.getVertexFill = function(vertexId) { + return this.getFormatter().getVertexById(vertexId).getDefault().getFill(); +}; + +/** EDGES FORMATTER **/ +GraphCanvas.prototype.setEdgeSize = function(edgeId, value) { + this.getFormatter().getEdgeById(edgeId).getDefault().setSize(value); +}; + +GraphCanvas.prototype.getEdgeSize = function(edgeId) { + return this.getFormatter().getEdgeById(edgeId).getDefault().getSize(); +}; + +GraphCanvas.prototype.setEdgeStroke = function(edgeId, value) { + this.getFormatter().getEdgeById(edgeId).getDefault().setStroke(value); +}; + +GraphCanvas.prototype.getEdgeStroke = function(edgeId) { + return this.getFormatter().getEdgeById(edgeId).getDefault().getStroke(); +}; + +GraphCanvas.prototype.setEdgeStrokeOpacity = function(edgeId, value) { + this.getFormatter().getEdgeById(edgeId).getDefault().setStrokeOpacity(value); +}; + +GraphCanvas.prototype.getEdgeStrokeOpacity = function(edgeId) { + return this.getFormatter().getEdgeById(edgeId).getDefault().getStrokeOpacity(); +}; + +GraphCanvas.prototype.setEdgeFill = function(edgeId, color) { + this.getFormatter().getEdgeById(edgeId).getDefault().setFill(color); +}; + +GraphCanvas.prototype.getEdgeFill = function(edgeId) { + return this.getFormatter().getEdgeById(edgeId).getDefault().getFill(); +}; + +/** API LAYOUT **/ +GraphCanvas.prototype.setCoordinates = function(vertexId, x, y) { + return this.getLayout().getEdgeById(vertexId).setCoordinates(x, y); +}; @@ -14339,283 +14339,283 @@ MergesHPLCGraph.prototype.getMenu = function () { } }); - return actions; + return actions; +}; + + +MergesHPLCGraph.prototype.input = function () { + return DATADOC.getScatteringHPLCFrameData(); +}; + +MergesHPLCGraph.prototype.test = function (targetId) { + var mainPlotPanel = new MergesHPLCGraph({ + title : 'Scattering', + width : this.plotWidth, + height : 500, + showRangeSelector : false, + xParam : 0, + xlabel : "scattering_I", + plots : { + "scattering_I" : true, + "subtracted_I" : true, + "buffer_I" : true + } + }); + mainPlotPanel.getPanel().render(targetId); + mainPlotPanel.loadData(mainPlotPanel.input()); +}; + + +function NetworkWidget(args) { + this.id = "NetworkViewer_" + Math.random().toString().replace(".", "_"); + + this.label = true; + if (args != null) { + if (args.targetId != null) { + this.targetId = args.targetId; + } + if (args.label != null) { + this.label = args.label; + } + } + + this.onVertexOver = new Event(this); + this.onVertexOut = new Event(this); +} + +NetworkWidget.prototype.draw = function(dataset, formatter, layout) { + + this.graphCanvas = new GraphCanvas(this.id, document.getElementById(this.targetId), { + "labeled" : this.label, + "multipleSelectionEnabled" : false, + "draggingCanvasEnabled" : false + }); + this.graphCanvas.draw(dataset, formatter, layout); + + var _this = this; + this.graphCanvas.onVertexOver.attach(function(sender, nodeId) { + _this.onVertexOver.notify(nodeId); + }); + + this.graphCanvas.onVertexOut.attach(function(sender, nodeId) { + _this.onVertexOut.notify(nodeId); + }); +}; + +/** SELECT VERTICES BY NAME * */ +NetworkWidget.prototype.selectVertexByName = function(vertexName) { + var vertices = this.getDataset().getVertexByName(vertexName); + if (vertices != null) { + for ( var nodeId in vertices) { + if (vertices.hasOwnProperty(nodeId)) { + var vertexId = vertices[nodeId].getId(); + this.selectVertexById(vertexId); + } + } + } +}; + +NetworkWidget.prototype.selectVerticesByName = function(verticesName) { + for ( var i = 0; i < verticesName.length; i++) { + this.selectVertexByName(verticesName[i]); + } +}; + +/** SELECT VERTICES BY ID * */ +NetworkWidget.prototype.selectVertexById = function(vertexId) { + this.graphCanvas.selectNode(vertexId); + this.blinkVertexById(vertexId); +}; + +NetworkWidget.prototype.selectVerticesById = function(verticesId) { + for ( var i = 0; i < verticesId.length; i++) { + this.selectVertexById(verticesId[i]); + } +}; + +/** VECINDARIO * */ +NetworkWidget.prototype.selectNeighbourhood = function() { + this.selectEdgesFromVertices(); + this.selectAdjacent(); +}; + +/** DESELECT * */ +NetworkWidget.prototype.deselectNodes = function() { + this.graphCanvas.deselectNodes(); +}; + +/** SELECT ALL NODES * */ +NetworkWidget.prototype.selectAllNodes = function() { + this.getGraphCanvas().selectAllNodes(); +}; + +/** SELECT EVERYTHING * */ +NetworkWidget.prototype.selectAll = function() { + this.getGraphCanvas().selectAll(); +}; + +/** SELECT ALL EDGES * */ +NetworkWidget.prototype.selectAllEdges = function() { + this.getGraphCanvas().selectAllEdges(); +}; + +/** ZOOM * */ +NetworkWidget.prototype.setScale = function(value) { + this.graphCanvas.setScale(value); +}; + +NetworkWidget.prototype.getScale = function() { + return this.graphCanvas.getScale(value); +}; + +/** SELECT ADJACENT VERTICES FROM SELECTED VERTICES * */ +NetworkWidget.prototype.selectAdjacent = function() { + var selectedVertices = this.getGraphCanvas().getSelectedVertices(); + var edges = []; + for ( var i = 0; i < selectedVertices.length; i++) { + edges = edges.concat(this.getGraphCanvas().getDataset().getVertexById(selectedVertices[i]).getEdges()); + } + var vertices = []; + for ( i = 0; i < edges.length; i++) { + vertices.push(edges[i].getNodeSource().getId()); + vertices.push(edges[i].getNodeTarget().getId()); + } + + this.selectVerticesById(vertices); +}; + +/** SELECT EDGES FROM SELECTED VERTICES * */ +NetworkWidget.prototype.selectEdgesFromVertices = function() { + var selectedVertices = this.getGraphCanvas().getSelectedVertices(); + var edges = []; + for ( var i = 0; i < selectedVertices.length; i++) { + edges = edges.concat(this.getGraphCanvas().getDataset().getVertexById(selectedVertices[i]).getEdges()); + } + var edgesId = []; + for ( i = 0; i < edges.length; i++) { + edgesId.push(edges[i].getId()); + } + this.getGraphCanvas().selectEdges(edgesId); +}; + +/** BLINKING * */ +NetworkWidget.prototype.blinkVertexById = function(vertexId) { + this.graphCanvas.blinkVertexById(vertexId); +}; + +/** COMPONENTE CONEXA DE LOS NODOS SELECCIONADOS * */ +NetworkWidget.prototype.selectConnectedComponent = function() { + var elements = this.getConnectedComponent(); + this.selectVerticesById(elements.nodes); + this.graphCanvas.selectEdges(elements.edges); +}; + +NetworkWidget.prototype.getConnectedComponent = function() { + var nodosVisitados = {}; + var aristasVisitadas = {}; + var arrNodos = []; + var arrAristas = []; + var selectedGraphNodesId = this.getGraphCanvas().getSelectedVertices(); + for ( var i = 0; i < selectedGraphNodesId.length; i++) { + this.visitNode(selectedGraphNodesId[i], nodosVisitados, aristasVisitadas, arrNodos, arrAristas); + } + return { + nodes : arrNodos, + edges : arrAristas + }; +}; + +NetworkWidget.prototype.visitNode = function(nodeId, nodosVisitados, aristasVisitadas, arrNodos, arrAristas) { + nodosVisitados[nodeId] = true; + arrNodos.push(nodeId); + var nodeEdges = this.getDataset().getVertexById(nodeId).getEdges(); + for ( var j = 0; j < nodeEdges.length; j++) { + var edge = nodeEdges[j]; + var edgeId = edge.getId(); + if (aristasVisitadas[edgeId] == null) { + aristasVisitadas[edgeId] = true; + arrAristas.push(edgeId); + var nodeTargetId = edge.getNodeTarget().getId(); + if (nodosVisitados[nodeTargetId] == null) { + this.visitNode(nodeTargetId, nodosVisitados, aristasVisitadas, arrNodos, arrAristas); + } + var nodeSourceId = edge.getNodeSource().getId(); + if (nodosVisitados[nodeSourceId] == null) { + this.visitNode(nodeSourceId, nodosVisitados, aristasVisitadas, arrNodos, arrAristas); + } + } + } +}; + +/** COLLAPSE SELECTED VERTICES * */ +NetworkWidget.prototype.collapse = function() { + var selectedVertices = this.getGraphCanvas().getSelectedVertices(); + var xMin = -Infinity; + var xMax = Infinity; + var yMin = -Infinity; + var yMax = Infinity; + + for ( var i = 0; i < selectedVertices.length; i++) { + var vertex = this.getGraphCanvas().getLayout().getNodeById(selectedVertices[i]); + if (xMin < vertex.x) { + xMin = vertex.x; + } + if (xMax > vertex.x) { + xMax = vertex.x; + } + if (yMin < vertex.y) { + yMin = vertex.y; + } + if (yMax > vertex.y) { + yMax = vertex.y; + } + } + + var centerX = xMin - xMax; + var centerY = yMin - yMax; + var radius = (xMax - xMin) / 4; + + var count = selectedVertices.length; + var verticesCoordinates = []; + + for ( i = 0; i < selectedVertices.length; i++) { + x = centerX + radius * Math.sin(i * 2 * Math.PI / count); + y = centerY + radius * Math.cos(i * 2 * Math.PI / count); + verticesCoordinates.push({ + 'x' : x, + 'y' : y + }); + } + + for ( i = 0; i < selectedVertices.length; i++) { + this.getGraphCanvas().getLayout().getNodeById(selectedVertices[i]).setCoordinates(verticesCoordinates[i].x, verticesCoordinates[i].y); + } }; +/** SETTER FONT SIZE * */ +NetworkWidget.prototype.setVerticesFontSize = function(value) { + for ( var nodeId in this.getDataset().getVertices()) { + if (this.getDataset().getVertices().hasOwnProperty(nodeId)) { + this.getFormatter().getVertexById(nodeId).getDefault().setFontSize(value); + } + } +}; -MergesHPLCGraph.prototype.input = function () { - return DATADOC.getScatteringHPLCFrameData(); +/** GETTERS * */ +NetworkWidget.prototype.getFormatter = function() { + return this.getGraphCanvas().getFormatter(); +}; +NetworkWidget.prototype.getLayout = function() { + return this.getGraphCanvas().getLayout(); }; -MergesHPLCGraph.prototype.test = function (targetId) { - var mainPlotPanel = new MergesHPLCGraph({ - title : 'Scattering', - width : this.plotWidth, - height : 500, - showRangeSelector : false, - xParam : 0, - xlabel : "scattering_I", - plots : { - "scattering_I" : true, - "subtracted_I" : true, - "buffer_I" : true - } - }); - mainPlotPanel.getPanel().render(targetId); - mainPlotPanel.loadData(mainPlotPanel.input()); +NetworkWidget.prototype.getDataset = function() { + return this.getGraphCanvas().getDataset(); }; - -function NetworkWidget(args) { - this.id = "NetworkViewer_" + Math.random().toString().replace(".", "_"); - - this.label = true; - if (args != null) { - if (args.targetId != null) { - this.targetId = args.targetId; - } - if (args.label != null) { - this.label = args.label; - } - } - - this.onVertexOver = new Event(this); - this.onVertexOut = new Event(this); -} - -NetworkWidget.prototype.draw = function(dataset, formatter, layout) { - - this.graphCanvas = new GraphCanvas(this.id, document.getElementById(this.targetId), { - "labeled" : this.label, - "multipleSelectionEnabled" : false, - "draggingCanvasEnabled" : false - }); - this.graphCanvas.draw(dataset, formatter, layout); - - var _this = this; - this.graphCanvas.onVertexOver.attach(function(sender, nodeId) { - _this.onVertexOver.notify(nodeId); - }); - - this.graphCanvas.onVertexOut.attach(function(sender, nodeId) { - _this.onVertexOut.notify(nodeId); - }); -}; - -/** SELECT VERTICES BY NAME * */ -NetworkWidget.prototype.selectVertexByName = function(vertexName) { - var vertices = this.getDataset().getVertexByName(vertexName); - if (vertices != null) { - for ( var nodeId in vertices) { - if (vertices.hasOwnProperty(nodeId)) { - var vertexId = vertices[nodeId].getId(); - this.selectVertexById(vertexId); - } - } - } -}; - -NetworkWidget.prototype.selectVerticesByName = function(verticesName) { - for ( var i = 0; i < verticesName.length; i++) { - this.selectVertexByName(verticesName[i]); - } -}; - -/** SELECT VERTICES BY ID * */ -NetworkWidget.prototype.selectVertexById = function(vertexId) { - this.graphCanvas.selectNode(vertexId); - this.blinkVertexById(vertexId); -}; - -NetworkWidget.prototype.selectVerticesById = function(verticesId) { - for ( var i = 0; i < verticesId.length; i++) { - this.selectVertexById(verticesId[i]); - } -}; - -/** VECINDARIO * */ -NetworkWidget.prototype.selectNeighbourhood = function() { - this.selectEdgesFromVertices(); - this.selectAdjacent(); -}; - -/** DESELECT * */ -NetworkWidget.prototype.deselectNodes = function() { - this.graphCanvas.deselectNodes(); -}; - -/** SELECT ALL NODES * */ -NetworkWidget.prototype.selectAllNodes = function() { - this.getGraphCanvas().selectAllNodes(); -}; - -/** SELECT EVERYTHING * */ -NetworkWidget.prototype.selectAll = function() { - this.getGraphCanvas().selectAll(); -}; - -/** SELECT ALL EDGES * */ -NetworkWidget.prototype.selectAllEdges = function() { - this.getGraphCanvas().selectAllEdges(); -}; - -/** ZOOM * */ -NetworkWidget.prototype.setScale = function(value) { - this.graphCanvas.setScale(value); -}; - -NetworkWidget.prototype.getScale = function() { - return this.graphCanvas.getScale(value); -}; - -/** SELECT ADJACENT VERTICES FROM SELECTED VERTICES * */ -NetworkWidget.prototype.selectAdjacent = function() { - var selectedVertices = this.getGraphCanvas().getSelectedVertices(); - var edges = []; - for ( var i = 0; i < selectedVertices.length; i++) { - edges = edges.concat(this.getGraphCanvas().getDataset().getVertexById(selectedVertices[i]).getEdges()); - } - var vertices = []; - for ( i = 0; i < edges.length; i++) { - vertices.push(edges[i].getNodeSource().getId()); - vertices.push(edges[i].getNodeTarget().getId()); - } - - this.selectVerticesById(vertices); -}; - -/** SELECT EDGES FROM SELECTED VERTICES * */ -NetworkWidget.prototype.selectEdgesFromVertices = function() { - var selectedVertices = this.getGraphCanvas().getSelectedVertices(); - var edges = []; - for ( var i = 0; i < selectedVertices.length; i++) { - edges = edges.concat(this.getGraphCanvas().getDataset().getVertexById(selectedVertices[i]).getEdges()); - } - var edgesId = []; - for ( i = 0; i < edges.length; i++) { - edgesId.push(edges[i].getId()); - } - this.getGraphCanvas().selectEdges(edgesId); -}; - -/** BLINKING * */ -NetworkWidget.prototype.blinkVertexById = function(vertexId) { - this.graphCanvas.blinkVertexById(vertexId); -}; - -/** COMPONENTE CONEXA DE LOS NODOS SELECCIONADOS * */ -NetworkWidget.prototype.selectConnectedComponent = function() { - var elements = this.getConnectedComponent(); - this.selectVerticesById(elements.nodes); - this.graphCanvas.selectEdges(elements.edges); -}; - -NetworkWidget.prototype.getConnectedComponent = function() { - var nodosVisitados = {}; - var aristasVisitadas = {}; - var arrNodos = []; - var arrAristas = []; - var selectedGraphNodesId = this.getGraphCanvas().getSelectedVertices(); - for ( var i = 0; i < selectedGraphNodesId.length; i++) { - this.visitNode(selectedGraphNodesId[i], nodosVisitados, aristasVisitadas, arrNodos, arrAristas); - } - return { - nodes : arrNodos, - edges : arrAristas - }; -}; - -NetworkWidget.prototype.visitNode = function(nodeId, nodosVisitados, aristasVisitadas, arrNodos, arrAristas) { - nodosVisitados[nodeId] = true; - arrNodos.push(nodeId); - var nodeEdges = this.getDataset().getVertexById(nodeId).getEdges(); - for ( var j = 0; j < nodeEdges.length; j++) { - var edge = nodeEdges[j]; - var edgeId = edge.getId(); - if (aristasVisitadas[edgeId] == null) { - aristasVisitadas[edgeId] = true; - arrAristas.push(edgeId); - var nodeTargetId = edge.getNodeTarget().getId(); - if (nodosVisitados[nodeTargetId] == null) { - this.visitNode(nodeTargetId, nodosVisitados, aristasVisitadas, arrNodos, arrAristas); - } - var nodeSourceId = edge.getNodeSource().getId(); - if (nodosVisitados[nodeSourceId] == null) { - this.visitNode(nodeSourceId, nodosVisitados, aristasVisitadas, arrNodos, arrAristas); - } - } - } -}; - -/** COLLAPSE SELECTED VERTICES * */ -NetworkWidget.prototype.collapse = function() { - var selectedVertices = this.getGraphCanvas().getSelectedVertices(); - var xMin = -Infinity; - var xMax = Infinity; - var yMin = -Infinity; - var yMax = Infinity; - - for ( var i = 0; i < selectedVertices.length; i++) { - var vertex = this.getGraphCanvas().getLayout().getNodeById(selectedVertices[i]); - if (xMin < vertex.x) { - xMin = vertex.x; - } - if (xMax > vertex.x) { - xMax = vertex.x; - } - if (yMin < vertex.y) { - yMin = vertex.y; - } - if (yMax > vertex.y) { - yMax = vertex.y; - } - } - - var centerX = xMin - xMax; - var centerY = yMin - yMax; - var radius = (xMax - xMin) / 4; - - var count = selectedVertices.length; - var verticesCoordinates = []; - - for ( i = 0; i < selectedVertices.length; i++) { - x = centerX + radius * Math.sin(i * 2 * Math.PI / count); - y = centerY + radius * Math.cos(i * 2 * Math.PI / count); - verticesCoordinates.push({ - 'x' : x, - 'y' : y - }); - } - - for ( i = 0; i < selectedVertices.length; i++) { - this.getGraphCanvas().getLayout().getNodeById(selectedVertices[i]).setCoordinates(verticesCoordinates[i].x, verticesCoordinates[i].y); - } -}; - -/** SETTER FONT SIZE * */ -NetworkWidget.prototype.setVerticesFontSize = function(value) { - for ( var nodeId in this.getDataset().getVertices()) { - if (this.getDataset().getVertices().hasOwnProperty(nodeId)) { - this.getFormatter().getVertexById(nodeId).getDefault().setFontSize(value); - } - } -}; - -/** GETTERS * */ -NetworkWidget.prototype.getFormatter = function() { - return this.getGraphCanvas().getFormatter(); -}; -NetworkWidget.prototype.getLayout = function() { - return this.getGraphCanvas().getLayout(); -}; - -NetworkWidget.prototype.getDataset = function() { - return this.getGraphCanvas().getDataset(); -}; - -NetworkWidget.prototype.getGraphCanvas = function() { - return this.graphCanvas; -}; +NetworkWidget.prototype.getGraphCanvas = function() { + return this.graphCanvas; +}; RangeWhiskerGraph.prototype.cleanArray = GenericGraph.prototype.cleanArray; RangeWhiskerGraph.prototype.plotAxes = GenericGraph.prototype.plotAxes; @@ -14883,280 +14883,280 @@ RangeWhiskerGraph.prototype.plotWhisters = function(data, properties) { } } } - } - -}; - -RangeWhiskerGraph.prototype.draw = function(targetId, data) { - //this.calculate(data); - this.targetId = targetId; - var properties = (this.getDimensions(data)); - this.svg = SVG.createSVGCanvas(document.getElementById(this.targetId), [ [ "width", this.width ], [ "height", this.height ] ]); - this.plotAxes(properties); - this.plotWhisters(data, properties); -}; - -RangeWhiskerGraph.prototype.input = function() { - return DATADOC.getBoxWhikerData(); -}; - -RangeWhiskerGraph.prototype.test = function(targetId) { - var plot = new RangeWhiskerGraph({ - targetId : targetId, - height : 350, - width : 450, - maxBoxWidth : 25 - }); - plot.refresh(this.input()); -}; - -StdDevDyGraph.prototype.dblclick = DygraphWidget.prototype.dblclick; -StdDevDyGraph.prototype._createHTLMWrapper = DygraphWidget.prototype._createHTLMWrapper; -StdDevDyGraph.prototype.draw = DygraphWidget.prototype.draw; - -function StdDevDyGraph(targetId, args) { - this.scaled = false; - if (args == null) { - args = {}; - } - args.customBars = true; - DygraphWidget.prototype.constructor.call(this, targetId, args); -} - -StdDevDyGraph.prototype.input = function () { - return { - data : [ [ 1, [ 2, 3, 3.5 ], [ 4, 4.2, 5 ] ], [ 2, [ 5, 5.5, 5.7 ], [ 4, 4.2, 5 ] ] ], - colors : [ "blue", "red" ], - labels : [ "", 'data1', 'data2' ] - }; -}; - -StdDevDyGraph.prototype.test = function (targetId) { - var dygraphObject = new StdDevDyGraph(targetId, { - width : 500, - height : 400, - xlabel : "xLabel", - showRangeSelector : false + } + +}; + +RangeWhiskerGraph.prototype.draw = function(targetId, data) { + //this.calculate(data); + this.targetId = targetId; + var properties = (this.getDimensions(data)); + this.svg = SVG.createSVGCanvas(document.getElementById(this.targetId), [ [ "width", this.width ], [ "height", this.height ] ]); + this.plotAxes(properties); + this.plotWhisters(data, properties); +}; + +RangeWhiskerGraph.prototype.input = function() { + return DATADOC.getBoxWhikerData(); +}; + +RangeWhiskerGraph.prototype.test = function(targetId) { + var plot = new RangeWhiskerGraph({ + targetId : targetId, + height : 350, + width : 450, + maxBoxWidth : 25 + }); + plot.refresh(this.input()); +}; + +StdDevDyGraph.prototype.dblclick = DygraphWidget.prototype.dblclick; +StdDevDyGraph.prototype._createHTLMWrapper = DygraphWidget.prototype._createHTLMWrapper; +StdDevDyGraph.prototype.draw = DygraphWidget.prototype.draw; + +function StdDevDyGraph(targetId, args) { + this.scaled = false; + if (args == null) { + args = {}; + } + args.customBars = true; + DygraphWidget.prototype.constructor.call(this, targetId, args); +} + +StdDevDyGraph.prototype.input = function () { + return { + data : [ [ 1, [ 2, 3, 3.5 ], [ 4, 4.2, 5 ] ], [ 2, [ 5, 5.5, 5.7 ], [ 4, 4.2, 5 ] ] ], + colors : [ "blue", "red" ], + labels : [ "", 'data1', 'data2' ] + }; +}; + +StdDevDyGraph.prototype.test = function (targetId) { + var dygraphObject = new StdDevDyGraph(targetId, { + width : 500, + height : 400, + xlabel : "xLabel", + showRangeSelector : false + }); + + dygraphObject.draw(dygraphObject.input().data, dygraphObject.input().colors, dygraphObject.input().labels); +}; + + + +function AbinitioGrid(args) { + this.height = null; + this.width = null; + this.id = BUI.id(); + + if (args != null) { + if (args.height != null) { + this.height = args.height; + + if (args.width != null) { + this.width = args.width; + } + } + } + + this.onSelected = new Event(this); +}; + + +AbinitioGrid.prototype.refresh = function(subtractions){ + this.store.loadData(this._prepareData(subtractions)); +}; + +AbinitioGrid.prototype._prepareData = function(subtractions){ + /** Parsing data * */ + var models = []; + for (var l = 0; l < subtractions.length; l++) { + var subtraction = subtractions[l]; + for (var k = 0; k < subtraction.substractionToAbInitioModel3VOs.length; k++) { + var data = subtraction.substractionToAbInitioModel3VOs[k].abinitiomodel3VO; + if (data.averagedModel != null) { + models.push(data.averagedModel); + models[models.length - 1].type = "Reference"; + } + + if (data.shapeDeterminationModel != null) { + models.push(data.shapeDeterminationModel); + models[models.length - 1].type = "Refined"; + } + + if (data.modelList3VO != null) { + if (data.modelList3VO.modeltolist3VOs != null) { + for (var i = 0; i < data.modelList3VO.modeltolist3VOs.length; i++) { + models.push(data.modelList3VO.modeltolist3VOs[i].model3VO); + models[models.length - 1].type = "Model"; + } + } + } + } + } + return models; +}; + +AbinitioGrid.prototype.getPanel = function(){ + var _this = this; + + + var modelFields = [ "modelId", "type", "chiSqrt", "dmax", "firFile", "logFile", "fitFile", "pdbFile", "rfactor", "rg", "volume" ]; + Ext.define('AbinitioModel', { + extend : 'Ext.data.Model', + fields : modelFields + + }); + + /** + * Store in Memory + */ + this.store = Ext.create('Ext.data.Store', { + model : 'AbinitioModel', + autoload : true, + groupField : 'type' + }); + + + var groupingFeature = Ext.create('Ext.grid.feature.Grouping',{ + groupHeaderTpl: '{name} ({rows.length} model{[values.rows.length > 1 ? "s" : ""]})', + startCollapsed: true, + collapsible : true + }); + + this.grid = Ext.create('Ext.grid.Panel', { + collapsible : false, + resizable : true, + features: [groupingFeature], + autoscroll : true, + multiSelect : true, + store : this.store, + height : this.height, + width : this.width, + margin : 10, + columns : [ { + text : "Type", + dataindex : "type", + hidden : true, + renderer : function(a, b, record) { + return record.data.type; + }, + flex : 1 + }, + { + text : "ModelId", + dataindex : "modelId", + hidden : true, + renderer : function(a, b, record) { + return record.data.modelId; + + }, + flex : 1 + }, + + { + text : "chiSqrt", + dataindex : "chiSqrt", + renderer : function(a, b, record) { + if (record.data.dmax != null) { + return BUI.formatValuesUnits(record.data.chiSqrt, "", 12, this.decimals); + } + + }, + flex : 1 + }, + { + text : "Dmax", + dataindex : "dmax", + renderer : function(a, b, record) { + if (record.data.dmax != null) { + return BUI.formatValuesUnits(record.data.dmax, "nm", 12, this.decimals); + } + + }, + flex : 1 + }, { + text : "rFactor", + dataindex : "rfactor", + hidden : true, + renderer : function(a, b, record) { + if (record.data.rfactor != null) { + return record.data.rfactor; + } + }, + flex : 1 + }, { + text : "Rg", + dataindex : "rg", + renderer : function(a, b, record) { + if (record.data.rg != null) { + return BUI.formatValuesUnits(record.data.rg, "nm", 12, this.decimals); + } + + }, + flex : 1 + }, + { + text : "Volume", + dataindex : "volume", + renderer : function(a, b, record) { + if (record.raw.volume != null){ + return BUI.formatValuesUnits(record.raw.volume, '') + " nm3"; + } + }, + flex : 1 + }, + { + text : "PDB", + dataindex : "pdbFile", + renderer : function(a, b, record) { + if (record.data.pdbFile != null){ + return record.data.pdbFile.split("/")[record.data.pdbFile.split("/").length - 1]; + } + }, + flex : 1 + }, { + text : "Fir", + dataindex : "firFile", + renderer : function(a, b, record) { + if (record.data.firFile != null){ + return record.data.firFile.split("/")[record.data.firFile.split("/").length - 1]; + } + }, + flex : 1 + }, { + text : "LOG", + dataindex : "logFile", + hidden : true, + renderer : function(a, b, record) { + if (record.data.logFile != null){ + return record.data.logFile.split("/")[record.data.logFile.split("/").length - 1]; + } + }, + flex : 1 + } + ], + viewConfig : { + enableTextSelection : true, + preserveScrollOnRefresh : true, + stripeRows : true, + listeners : { + 'celldblclick' : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + }, + 'cellclick' : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + var models = []; + for (var i = 0; i < grid.getSelectionModel().selected.items.length; i++) { + models.push(grid.getSelectionModel().selected.items[i].raw); + } + _this.onSelected.notify(models); + } + } + } }); - - dygraphObject.draw(dygraphObject.input().data, dygraphObject.input().colors, dygraphObject.input().labels); + return this.grid; + }; - - -function AbinitioGrid(args) { - this.height = null; - this.width = null; - this.id = BUI.id(); - - if (args != null) { - if (args.height != null) { - this.height = args.height; - - if (args.width != null) { - this.width = args.width; - } - } - } - - this.onSelected = new Event(this); -}; - - -AbinitioGrid.prototype.refresh = function(subtractions){ - this.store.loadData(this._prepareData(subtractions)); -}; - -AbinitioGrid.prototype._prepareData = function(subtractions){ - /** Parsing data * */ - var models = []; - for (var l = 0; l < subtractions.length; l++) { - var subtraction = subtractions[l]; - for (var k = 0; k < subtraction.substractionToAbInitioModel3VOs.length; k++) { - var data = subtraction.substractionToAbInitioModel3VOs[k].abinitiomodel3VO; - if (data.averagedModel != null) { - models.push(data.averagedModel); - models[models.length - 1].type = "Reference"; - } - - if (data.shapeDeterminationModel != null) { - models.push(data.shapeDeterminationModel); - models[models.length - 1].type = "Refined"; - } - - if (data.modelList3VO != null) { - if (data.modelList3VO.modeltolist3VOs != null) { - for (var i = 0; i < data.modelList3VO.modeltolist3VOs.length; i++) { - models.push(data.modelList3VO.modeltolist3VOs[i].model3VO); - models[models.length - 1].type = "Model"; - } - } - } - } - } - return models; -}; - -AbinitioGrid.prototype.getPanel = function(){ - var _this = this; - - - var modelFields = [ "modelId", "type", "chiSqrt", "dmax", "firFile", "logFile", "fitFile", "pdbFile", "rfactor", "rg", "volume" ]; - Ext.define('AbinitioModel', { - extend : 'Ext.data.Model', - fields : modelFields - - }); - - /** - * Store in Memory - */ - this.store = Ext.create('Ext.data.Store', { - model : 'AbinitioModel', - autoload : true, - groupField : 'type' - }); - - - var groupingFeature = Ext.create('Ext.grid.feature.Grouping',{ - groupHeaderTpl: '{name} ({rows.length} model{[values.rows.length > 1 ? "s" : ""]})', - startCollapsed: true, - collapsible : true - }); - - this.grid = Ext.create('Ext.grid.Panel', { - collapsible : false, - resizable : true, - features: [groupingFeature], - autoscroll : true, - multiSelect : true, - store : this.store, - height : this.height, - width : this.width, - margin : 10, - columns : [ { - text : "Type", - dataindex : "type", - hidden : true, - renderer : function(a, b, record) { - return record.data.type; - }, - flex : 1 - }, - { - text : "ModelId", - dataindex : "modelId", - hidden : true, - renderer : function(a, b, record) { - return record.data.modelId; - - }, - flex : 1 - }, - - { - text : "chiSqrt", - dataindex : "chiSqrt", - renderer : function(a, b, record) { - if (record.data.dmax != null) { - return BUI.formatValuesUnits(record.data.chiSqrt, "", 12, this.decimals); - } - - }, - flex : 1 - }, - { - text : "Dmax", - dataindex : "dmax", - renderer : function(a, b, record) { - if (record.data.dmax != null) { - return BUI.formatValuesUnits(record.data.dmax, "nm", 12, this.decimals); - } - - }, - flex : 1 - }, { - text : "rFactor", - dataindex : "rfactor", - hidden : true, - renderer : function(a, b, record) { - if (record.data.rfactor != null) { - return record.data.rfactor; - } - }, - flex : 1 - }, { - text : "Rg", - dataindex : "rg", - renderer : function(a, b, record) { - if (record.data.rg != null) { - return BUI.formatValuesUnits(record.data.rg, "nm", 12, this.decimals); - } - - }, - flex : 1 - }, - { - text : "Volume", - dataindex : "volume", - renderer : function(a, b, record) { - if (record.raw.volume != null){ - return BUI.formatValuesUnits(record.raw.volume, '') + " nm3"; - } - }, - flex : 1 - }, - { - text : "PDB", - dataindex : "pdbFile", - renderer : function(a, b, record) { - if (record.data.pdbFile != null){ - return record.data.pdbFile.split("/")[record.data.pdbFile.split("/").length - 1]; - } - }, - flex : 1 - }, { - text : "Fir", - dataindex : "firFile", - renderer : function(a, b, record) { - if (record.data.firFile != null){ - return record.data.firFile.split("/")[record.data.firFile.split("/").length - 1]; - } - }, - flex : 1 - }, { - text : "LOG", - dataindex : "logFile", - hidden : true, - renderer : function(a, b, record) { - if (record.data.logFile != null){ - return record.data.logFile.split("/")[record.data.logFile.split("/").length - 1]; - } - }, - flex : 1 - } - ], - viewConfig : { - enableTextSelection : true, - preserveScrollOnRefresh : true, - stripeRows : true, - listeners : { - 'celldblclick' : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { - }, - 'cellclick' : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { - var models = []; - for (var i = 0; i < grid.getSelectionModel().selected.items.length; i++) { - models.push(grid.getSelectionModel().selected.items[i].raw); - } - _this.onSelected.notify(models); - } - } - } - }); - return this.grid; - -}; - function AdditiveGrid(args) { this.onRemoveButtonClicked = new Event(this); @@ -16236,255 +16236,255 @@ HPLCAnalysisGrid.prototype._getFramesColumn = function() { }; - - -/** - * Rigid body grid to show PDB, symmetry and multiplicity - * - * - * #onUploadFile click on upload file - */ -function AprioriRigidBodyGrid(args) { - - this.height = 250; - this.btnEditVisible = true; - this.btnRemoveVisible = true; - - if (args != null) { - if (args.height != null) { - this.height = args.height; - } - if (args.btnEditVisible != null) { - this.btnEditVisible = args.btnEditVisible; - } - if (args.btnRemoveVisible != null) { - this.btnRemoveVisible = args.btnRemoveVisible; - } - } - - /** Events **/ - this.onUploadFile = new Event(this); - this.onRemove = new Event(this); -} - -AprioriRigidBodyGrid.prototype._getColumns = function() { -}; - -AprioriRigidBodyGrid.prototype._getTopButtons = function() { - var _this = this; - /** Actions buttons **/ - var actions = []; - - /** ADD BUTTON **/ - actions.push(Ext.create('Ext.Action', { - icon : '../images/add.png', - text : 'Add', - disabled : false, - handler : function(widget, event) { - _this.onAddButtonClicked.notify(); - } - })); - - return actions; -}; - -AprioriRigidBodyGrid.prototype.refresh = function(macromolecule) { - this.macromolecule = macromolecule; - if (macromolecule != null){ - this.pdbStore.loadData(macromolecule.structure3VOs); - } -}; - -AprioriRigidBodyGrid.prototype._prepareData = function() { - var data = []; - for ( var i = 0; i < this.features.length; i++) { - data.push(this.features[i]); - } - return data; -}; - -AprioriRigidBodyGrid.prototype._getPlugins = function() { - var _this = this; - var plugins = []; - plugins.push(Ext.create('Ext.grid.plugin.RowEditing', { - clicksToEdit : 1, - listeners : { - validateedit : function(grid, e) { - /** Comments are always updatable* */ - e.record.raw.symmetry = e.newValues.symmetry; - e.record.raw.multiplicity = e.newValues.multiplicity; - - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, proposal) { - BIOSAXS.proposal.setItems(proposal); - _this.refresh(BIOSAXS.proposal.getMacromoleculeById(_this.macromolecule.macromoleculeId)); - _this.panel.setLoading(false); - }); - adapter.onError.attach(function() { - alert("Error"); - }); - - _this.panel.setLoading(); - adapter.saveStructure(e.record.raw); - } - } - })); - return plugins; -}; - -AprioriRigidBodyGrid.prototype.getPanel = function() { - var _this = this; - - this.pdbStore = Ext.create('Ext.data.Store', { - fields : [ 'filePath', 'structureId', 'structureType', 'symmetry', 'structureId', 'name', 'multiplicity' ], - groupField : 'structureType', - sorters : { - property : 'structureId', - direction : 'DESC' - } - }); - - var groupingFeature = Ext.create('Ext.grid.feature.Grouping', { - groupHeaderTpl : Ext.create('Ext.XTemplate', - "
{name:this.formatName}
", { - formatName : function(name) { - return name; - } - }), - hideGroupedHeader : true, - startCollapsed : false - }); - - this.panel = Ext.create('Ext.grid.Panel', { - margin : "15 10 0 10", - height : this.height, - store : this.pdbStore, - plugins : _this._getPlugins(), - tbar : [ { - text : 'Add Modeling Option (PDB)', - icon : '../images/add.png', - handler : function() { - _this.onUploadFile.notify('PDB', 'Upload PDB File'); - } - } - - ], - columns : [ - { - text : "structureId", - flex : 0.2, - hidden : true, - dataIndex : 'structureId', - sortable : true - }, - { - text : "File", - flex : 0.5, - dataIndex : 'filePath', - sortable : true, - hidden : true - }, - { - text : "PDB", - flex : 0.4, - dataIndex : 'name', - sortable : true - }, - { - text : "Symmetry", - flex : 0.2, - dataIndex : 'symmetry', - sortable : true, - editor : { - xtype : 'combobox', - typeAhead : true, - triggerAction : 'all', - selectOnTab : true, - store : [ [ "P1", "P1" ], [ "P2", "P2" ], [ "P3", "P3" ], [ "P4", "P4" ], [ "P5", "P5" ], [ "P6", "P6" ], [ "P32", "P32" ], [ "P42", "P42" ], - [ "P52", "P52" ], [ "P62", "P62" ], [ "P222", "P222" ] ], - } - }, { - text : "Multiplicity", - flex : 0.2, - dataIndex : 'multiplicity', - sortable : true, - editor : { - xtype : 'textfield' - } - - }, { - text : "Subunit", - flex : 0.2, - dataIndex : 'isSubunit', - sortable : true, - hidden : true - }, { - text : "Type", - flex : 0.2, - dataIndex : 'structureType', - sortable : true, - hidden : true - }, - - { - id : this.id + 'REMOVE', - flex : 0.2, - sortable : false, - renderer : function(value, metaData, record, rowIndex, colIndex, store) { - return BUI.getRedButton('REMOVE'); - } - }, ], - - listeners : { - itemdblclick : function(dataview, record, item, e) { - _this._editExperiment(record.raw.experimentId); - }, - cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { - - if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'REMOVE') { - var dataAdapter = new BiosaxsDataAdapter(); - dataAdapter.onSuccess.attach(function() { - _this.panel.setLoading(false); - _this.onRemove.notify(); - }); - _this.panel.setLoading("Removing PDB file"); - dataAdapter.removeStructure(record.data.structureId); - } - - } - }, - viewConfig : { - getRowClass : function(record, rowIdx, params, store) { - if (record.raw.isSubunit != null) { - return "blue-row"; - } - } - } - }); - - return this.panel; -}; - - - - -AprioriRigidBodyGrid.prototype.input = function() { - return { - proposal : DATADOC.getProposal_10(), - dewars : DATADOC.getDewars_10() - - }; -}; - -AprioriRigidBodyGrid.prototype.test = function(targetId) { - var AprioriRigidBodyGrid = new AprioriRigidBodyGrid({ - height : 150 - }); - BIOSAXS.proposal = new Proposal(AprioriRigidBodyGrid.input().proposal); - var panel = AprioriRigidBodyGrid.getPanel(AprioriRigidBodyGrid.input().dewars); - panel.render(targetId); - -}; + + +/** + * Rigid body grid to show PDB, symmetry and multiplicity + * + * + * #onUploadFile click on upload file + */ +function AprioriRigidBodyGrid(args) { + + this.height = 250; + this.btnEditVisible = true; + this.btnRemoveVisible = true; + + if (args != null) { + if (args.height != null) { + this.height = args.height; + } + if (args.btnEditVisible != null) { + this.btnEditVisible = args.btnEditVisible; + } + if (args.btnRemoveVisible != null) { + this.btnRemoveVisible = args.btnRemoveVisible; + } + } + + /** Events **/ + this.onUploadFile = new Event(this); + this.onRemove = new Event(this); +} + +AprioriRigidBodyGrid.prototype._getColumns = function() { +}; + +AprioriRigidBodyGrid.prototype._getTopButtons = function() { + var _this = this; + /** Actions buttons **/ + var actions = []; + + /** ADD BUTTON **/ + actions.push(Ext.create('Ext.Action', { + icon : '../images/add.png', + text : 'Add', + disabled : false, + handler : function(widget, event) { + _this.onAddButtonClicked.notify(); + } + })); + + return actions; +}; + +AprioriRigidBodyGrid.prototype.refresh = function(macromolecule) { + this.macromolecule = macromolecule; + if (macromolecule != null){ + this.pdbStore.loadData(macromolecule.structure3VOs); + } +}; + +AprioriRigidBodyGrid.prototype._prepareData = function() { + var data = []; + for ( var i = 0; i < this.features.length; i++) { + data.push(this.features[i]); + } + return data; +}; + +AprioriRigidBodyGrid.prototype._getPlugins = function() { + var _this = this; + var plugins = []; + plugins.push(Ext.create('Ext.grid.plugin.RowEditing', { + clicksToEdit : 1, + listeners : { + validateedit : function(grid, e) { + /** Comments are always updatable* */ + e.record.raw.symmetry = e.newValues.symmetry; + e.record.raw.multiplicity = e.newValues.multiplicity; + + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, proposal) { + BIOSAXS.proposal.setItems(proposal); + _this.refresh(BIOSAXS.proposal.getMacromoleculeById(_this.macromolecule.macromoleculeId)); + _this.panel.setLoading(false); + }); + adapter.onError.attach(function() { + alert("Error"); + }); + + _this.panel.setLoading(); + adapter.saveStructure(e.record.raw); + } + } + })); + return plugins; +}; + +AprioriRigidBodyGrid.prototype.getPanel = function() { + var _this = this; + + this.pdbStore = Ext.create('Ext.data.Store', { + fields : [ 'filePath', 'structureId', 'structureType', 'symmetry', 'structureId', 'name', 'multiplicity' ], + groupField : 'structureType', + sorters : { + property : 'structureId', + direction : 'DESC' + } + }); + + var groupingFeature = Ext.create('Ext.grid.feature.Grouping', { + groupHeaderTpl : Ext.create('Ext.XTemplate', + "
{name:this.formatName}
", { + formatName : function(name) { + return name; + } + }), + hideGroupedHeader : true, + startCollapsed : false + }); + + this.panel = Ext.create('Ext.grid.Panel', { + margin : "15 10 0 10", + height : this.height, + store : this.pdbStore, + plugins : _this._getPlugins(), + tbar : [ { + text : 'Add Modeling Option (PDB)', + icon : '../images/add.png', + handler : function() { + _this.onUploadFile.notify('PDB', 'Upload PDB File'); + } + } + + ], + columns : [ + { + text : "structureId", + flex : 0.2, + hidden : true, + dataIndex : 'structureId', + sortable : true + }, + { + text : "File", + flex : 0.5, + dataIndex : 'filePath', + sortable : true, + hidden : true + }, + { + text : "PDB", + flex : 0.4, + dataIndex : 'name', + sortable : true + }, + { + text : "Symmetry", + flex : 0.2, + dataIndex : 'symmetry', + sortable : true, + editor : { + xtype : 'combobox', + typeAhead : true, + triggerAction : 'all', + selectOnTab : true, + store : [ [ "P1", "P1" ], [ "P2", "P2" ], [ "P3", "P3" ], [ "P4", "P4" ], [ "P5", "P5" ], [ "P6", "P6" ], [ "P32", "P32" ], [ "P42", "P42" ], + [ "P52", "P52" ], [ "P62", "P62" ], [ "P222", "P222" ] ], + } + }, { + text : "Multiplicity", + flex : 0.2, + dataIndex : 'multiplicity', + sortable : true, + editor : { + xtype : 'textfield' + } + + }, { + text : "Subunit", + flex : 0.2, + dataIndex : 'isSubunit', + sortable : true, + hidden : true + }, { + text : "Type", + flex : 0.2, + dataIndex : 'structureType', + sortable : true, + hidden : true + }, + + { + id : this.id + 'REMOVE', + flex : 0.2, + sortable : false, + renderer : function(value, metaData, record, rowIndex, colIndex, store) { + return BUI.getRedButton('REMOVE'); + } + }, ], + + listeners : { + itemdblclick : function(dataview, record, item, e) { + _this._editExperiment(record.raw.experimentId); + }, + cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + + if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'REMOVE') { + var dataAdapter = new BiosaxsDataAdapter(); + dataAdapter.onSuccess.attach(function() { + _this.panel.setLoading(false); + _this.onRemove.notify(); + }); + _this.panel.setLoading("Removing PDB file"); + dataAdapter.removeStructure(record.data.structureId); + } + + } + }, + viewConfig : { + getRowClass : function(record, rowIdx, params, store) { + if (record.raw.isSubunit != null) { + return "blue-row"; + } + } + } + }); + + return this.panel; +}; + + + + +AprioriRigidBodyGrid.prototype.input = function() { + return { + proposal : DATADOC.getProposal_10(), + dewars : DATADOC.getDewars_10() + + }; +}; + +AprioriRigidBodyGrid.prototype.test = function(targetId) { + var AprioriRigidBodyGrid = new AprioriRigidBodyGrid({ + height : 150 + }); + BIOSAXS.proposal = new Proposal(AprioriRigidBodyGrid.input().proposal); + var panel = AprioriRigidBodyGrid.getPanel(AprioriRigidBodyGrid.input().dewars); + panel.render(targetId); + +}; /** * It shows buffer grid with a top bar with "Add" button @@ -16513,159 +16513,552 @@ function BufferGrid(args) { this.tbar = args.tbar; } - if (args.collapsed != null) { - this.collapsed = args.collapsed; - } + if (args.collapsed != null) { + this.collapsed = args.collapsed; + } + + if (args.width != null) { + this.width = args.width; + } + } +} + +BufferGrid.prototype._edit = function(bufferId) { + var _this = this; + var window = new BufferWindow(); + window.onSuccess.attach(function(sender, proposal) { + _this.store.loadData(BIOSAXS.proposal.getBuffers()); + }); + window.draw(BIOSAXS.proposal.getBufferById(bufferId)); +}; + +BufferGrid.prototype.refresh = function(buffers) { + this.store.loadData(this._prepareData(buffers), false); +}; + +BufferGrid.prototype._prepareData = function(buffers) { + return buffers; +}; + +BufferGrid.prototype._getTbar = function() { + var _this = this; + var actions = []; + + actions.push(Ext.create('Ext.Action', { + icon : '../images/add.png', + text : 'Add buffer', + disabled : false, + handler : function(widget, event) { + var window = new BufferWindow(); + window.onSuccess.attach(function(sender) { + _this.store.loadData(BIOSAXS.proposal.getBuffers()); + }); + window.draw({}); + } + })); + return actions; +}; + +BufferGrid.prototype.getPanel = function(buffers) { + var _this = this; + + this.store = Ext.create('Ext.data.Store', { + fields : [ 'bufferId', 'acronym', 'name', 'composition' ], + data : buffers + }); + + this.store.sort('acronym'); + + var type = 'Ext.grid.Panel'; + if (this.searchBar == true) { + type = 'Ext.ux.LiveSearchGridPanel'; + } + + this.grid = Ext.create(type, { + title : 'Buffers', + collapsible : true, + collapsed : this.collapsed, + store : this.store, + height : this.height, + width : this.width, + columns : [ + /*{ + text : '', + dataIndex : 'bufferId', + width : 20, + renderer : function(val, y, sample) { + return BUI.getRectangleColorDIV(BIOSAXS.proposal.bufferColors[val], 10, 10); + } + },*/ + { + text : 'Acronym', + dataIndex : 'acronym', + flex : 1 + }, { + text : 'Name', + dataIndex : 'name', + flex : 1, + hidden : true + }, { + text : 'Composition', + dataIndex : 'composition', + flex : 1, + hidden : true + }, { + id : _this.id + 'buttonEditBuffer', + width : 80, + sortable : false, + renderer : function(value, metaData, record, rowIndex, colIndex, store) { + return BUI.getGreenButton('EDIT'); + } + }, { + id : _this.id + 'buttonRemoveBuffer', + width : 85, + hidden : true, + sortable : false, + renderer : function(value, metaData, record, rowIndex, colIndex, store) { + return BUI.getRedButton('REMOVE'); + } + } ], + flex : 1, + viewConfig : { + stripeRows : true, + listeners : { + 'celldblclick' : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + _this._edit(record.data.bufferId); + }, + 'cellclick' : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonEditBuffer') { + _this._edit(record.data.bufferId); + } + + if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonRemoveBuffer') { + BUI.showBetaWarning(); + } + } + + } + } + }); + + /** Adding the tbar **/ + if (this.tbar) { + this.grid.addDocked({ + xtype : 'toolbar', + items : this._getTbar() + }); + } + return this.grid; +}; + +BufferGrid.prototype.input = function() { + return new MacromoleculeGrid().input(); +}; + +BufferGrid.prototype.test = function(targetId) { + var bufferGrid = new BufferGrid({ + width : 800, + height : 350, + collapsed : false, + tbar : true + }); + + BIOSAXS.proposal = new Proposal(bufferGrid.input().proposal); + var panel = bufferGrid.getPanel(BIOSAXS.proposal.macromolecules); + panel.render(targetId); +}; + +/** + * A shipment may contains one or more cases where stock solutions and sample plates are stored + * + * @height + * @btnEditVisible + * @btnRemoveVisible + * + * #onEditButtonClicked + * #onAddButtonClicked + * #onRemoveButtonClicked + * #onDuplicateButtonClicked + */ +function CaseGrid(args) { + + this.height = 100; + this.btnEditVisible = true; + this.btnRemoveVisible = true; + + if (args != null) { + if (args.height != null) { + this.height = args.height; + } + if (args.btnEditVisible != null) { + this.btnEditVisible = args.btnEditVisible; + } + if (args.btnRemoveVisible != null) { + this.btnRemoveVisible = args.btnRemoveVisible; + } + } + + /** Events **/ + this.onEditButtonClicked = new Event(this); + this.onAddButtonClicked = new Event(this); + this.onRemoveButtonClicked = new Event(this); + this.onDuplicateButtonClicked = new Event(this); +} + +CaseGrid.prototype._getColumns = function() { + var _this = this; + + function actionItemRenderer(value, meta, record, rowIx, ColIx, store) { + /*if (record.raw.specimen3VOs.length > 0){ + return 'x-hide-display'; + }*/ + } + + var columns = [ + { + header : 'Code', + dataIndex : 'code', + name : 'code', + type : 'string', + flex : 1 + }, + { + header : 'Bar Code', + dataIndex : 'barCode', + name : 'barCode', + type : 'string', + flex : 1, + hidden : true + }, + { + header : 'BeamLine', + dataIndex : 'barCode', + type : 'string', + flex : 1, + renderer : function(comp, val, record) { + if (record.raw.sessionVO != null) { + return "" + record.raw.sessionVO.beamlineName + ""; + } + } + }, + { + header : 'Session', + dataIndex : 'barCode', + type : 'string', + flex : 1, + renderer : function(comp, val, record) { + if (record.raw.sessionVO != null) { + return "" + moment(record.raw.sessionVO.startDate).format("MMM Do YY") + ""; + } + } + }, + { + header : 'Status', + dataIndex : 'dewarStatus', + name : 'dewarStatus', + type : 'string', + flex : 1, + renderer : function(comp, val, record) { + if (new String(record.raw.dewarStatus).toUpperCase() == 'OPENED') { + return "" + new String(record.raw.dewarStatus).toUpperCase() + ""; + } + return "" + new String(record.raw.dewarStatus).toUpperCase() + ""; + } + }, + { + header : 'Type', + dataIndex : 'type', + name : 'type', + type : 'string', + flex : 1, + hidden : true + }, + { + header : 'Sample Plates', + dataIndex : 'plates', + name : 'plates', + type : 'string', + flex : 1, + hidden : true + }, + { + header : 'Stock Solutions', + dataIndex : 'plates', + name : 'plates', + type : 'string', + width : 100, + renderer : function(comp, val, record) { + var html = "
"; + var stockSolutions = BIOSAXS.proposal.getStockSolutionsByDewarId(record.raw.dewarId); + html = html + "" + stockSolutions.length + " x"; + return html + "
"; + } + }, { + header : 'isStorageDewar', + dataIndex : 'isStorageDewar', + name : 'isStorageDewar', + type : 'string', + flex : 1, + hidden : true + }, { + header : 'Tracking Number From Synchrotron', + dataIndex : 'trackingNumberFromSynchrotron', + name : 'trackingNumberFromSynchrotron', + type : 'string', + flex : 1, + hidden : true + }, { + header : 'Tracking Number To Synchrotron', + dataIndex : 'trackingNumberToSynchrotron', + name : 'trackingNumberToSynchrotron', + type : 'string', + flex : 1, + hidden : true + }, { + header : 'Storage Location', + dataIndex : 'storageLocation', + name : 'storageLocation', + type : 'string', + flex : 1, + hidden : false + }, { + header : 'Comments', + dataIndex : 'comments', + name : 'comments', + type : 'string', + flex : 1, + hidden : false + } + ]; + + if (this.btnEditVisible) { + columns.push({ + id : _this.id + 'buttonEdit', + text : '', + hidden : !_this.btnEditVisible, + width : 85, + sortable : false, + renderer : function(value, metaData, record, rowIndex, colIndex, store) { + return BUI.getGreenButton('EDIT'); + } + }); + } - if (args.width != null) { - this.width = args.width; - } + if (this.btnRemoveVisible) { + columns.push({ + id : _this.id + 'buttonRemove', + width : 85, + sortable : false, + renderer : function(value, metaData, record, rowIndex, colIndex, store) { + if (_this.btnRemoveVisible) { + return BUI.getRedButton('REMOVE'); + } + } + }); } -} -BufferGrid.prototype._edit = function(bufferId) { - var _this = this; - var window = new BufferWindow(); - window.onSuccess.attach(function(sender, proposal) { - _this.store.loadData(BIOSAXS.proposal.getBuffers()); + columns.push({ + dataIndex : 'comments', + type : 'string', + width : 85, + hidden : false, + renderer : function(comp, val, record) { + return BUI.getBlueButton("LABELS", { + href : BUI.getPrintcomponentURL(record.raw.dewarId) + }); + } }); - window.draw(BIOSAXS.proposal.getBufferById(bufferId)); -}; - -BufferGrid.prototype.refresh = function(buffers) { - this.store.loadData(this._prepareData(buffers), false); -}; -BufferGrid.prototype._prepareData = function(buffers) { - return buffers; + return columns; }; -BufferGrid.prototype._getTbar = function() { +CaseGrid.prototype._getTopButtons = function() { var _this = this; + /** Actions buttons **/ var actions = []; + /** ADD BUTTON **/ actions.push(Ext.create('Ext.Action', { icon : '../images/add.png', - text : 'Add buffer', + text : 'Add', disabled : false, handler : function(widget, event) { - var window = new BufferWindow(); - window.onSuccess.attach(function(sender) { - _this.store.loadData(BIOSAXS.proposal.getBuffers()); - }); - window.draw({}); + _this.onAddButtonClicked.notify(); } })); + return actions; }; -BufferGrid.prototype.getPanel = function(buffers) { - var _this = this; +CaseGrid.prototype.refresh = function(dewars) { + this.features = dewars; + this.store.loadData(this._prepareData(dewars), false); +}; - this.store = Ext.create('Ext.data.Store', { - fields : [ 'bufferId', 'acronym', 'name', 'composition' ], - data : buffers - }); +CaseGrid.prototype._sort = function(store) { + store.sort('dewarId', 'DESC'); +}; - this.store.sort('acronym'); +CaseGrid.prototype._prepareData = function() { + var data = []; + for ( var i = 0; i < this.features.length; i++) { + data.push(this.features[i]); + } + return data; +}; - var type = 'Ext.grid.Panel'; - if (this.searchBar == true) { - type = 'Ext.ux.LiveSearchGridPanel'; +CaseGrid.prototype.getPanel = function(dewars, plates) { + this.features = dewars; + this.plates = plates; + return this._renderGrid(); +}; + +CaseGrid.prototype._edit = function(dewar) { + var _this = this; + var caseWindow = new CaseWindow(); + /**SAVED **/ + caseWindow.onSaved.attach(function(sender, dewar) { + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, shipment) { + _this.refresh(shipment.dewarVOs); + }); + adapter.saveCase(BIOSAXS.proposal.getShipmentByDewarId(dewar.dewarId).shippingId, dewar); + }); + caseWindow.draw(dewar); +}; + +CaseGrid.prototype._getStoreFields = function(data) { + return [ { + name : 'dewarId', + type : 'string' + }, { + name : 'barCode', + type : 'string' + }, { + name : 'code', + type : 'string' + }, { + name : 'comments', + type : 'string' + }, { + name : 'dewarStatus', + type : 'string' + }, { + name : 'isStorageDewar', + type : 'string' + }, { + name : 'plates', + type : 'string' + }, { + name : 'transportValue', + type : 'string' + }, { + name : 'trackingNumberFromSynchrotron', + type : 'string' + }, { + name : 'trackingNumberToSynchrotron', + type : 'string' + }, { + name : 'timeStamp', + type : 'string' + }, { + name : 'storageLocation', + type : 'string' + }, { + name : 'type', + type : 'string' } - this.grid = Ext.create(type, { - title : 'Buffers', - collapsible : true, - collapsed : this.collapsed, - store : this.store, + ]; +}; + +CaseGrid.prototype._renderGrid = function() { + var _this = this; + + /** Store **/ + var data = this._prepareData(); + this.store = Ext.create('Ext.data.Store', { + fields : this._getStoreFields(data), + autoload : true, + data : data + }); + this._sort(this.store); + + this.store.loadData(data, false); + + this.grid = Ext.create('Ext.grid.Panel', { + style : { + padding : 5 + }, height : this.height, - width : this.width, - columns : [ - /*{ - text : '', - dataIndex : 'bufferId', - width : 20, - renderer : function(val, y, sample) { - return BUI.getRectangleColorDIV(BIOSAXS.proposal.bufferColors[val], 10, 10); - } - },*/ - { - text : 'Acronym', - dataIndex : 'acronym', - flex : 1 - }, { - text : 'Name', - dataIndex : 'name', - flex : 1, - hidden : true - }, { - text : 'Composition', - dataIndex : 'composition', - flex : 1, - hidden : true - }, { - id : _this.id + 'buttonEditBuffer', - width : 80, - sortable : false, - renderer : function(value, metaData, record, rowIndex, colIndex, store) { - return BUI.getGreenButton('EDIT'); - } - }, { - id : _this.id + 'buttonRemoveBuffer', - width : 85, - hidden : true, - sortable : false, - renderer : function(value, metaData, record, rowIndex, colIndex, store) { - return BUI.getRedButton('REMOVE'); - } - } ], - flex : 1, + store : this.store, + columns : this._getColumns(), viewConfig : { stripeRows : true, listeners : { - 'celldblclick' : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { - _this._edit(record.data.bufferId); + itemdblclick : function(dataview, record, item, e) { + _this._edit(record.raw); }, - 'cellclick' : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { - if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonEditBuffer') { - _this._edit(record.data.bufferId); + cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonEdit') { + _this._edit(record.raw); } - if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonRemoveBuffer') { - BUI.showBetaWarning(); + if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonRemove') { + _this.onRemoveButtonClicked.notify(_this.store.getAt(rowIndex).raw.dewarId); } } - } + }, + selModel : { + mode : 'SINGLE' } }); - /** Adding the tbar **/ - if (this.tbar) { - this.grid.addDocked({ - xtype : 'toolbar', - items : this._getTbar() - }); - } + var actions = _this._getTopButtons(); + this.grid.addDocked({ + xtype : 'toolbar', + items : actions + }); + + this.grid.getSelectionModel().on({ + selectionchange : function(sm, selections) { + if (selections.length) { + for ( var i = 0; i < actions.length; i++) { + if (actions[i].enable) { + actions[i].enable(); + } + } + } else { + for ( var i = 0; i < actions.length; i++) { + if (actions[i].alwaysEnabled == false) { + if (actions[i].disable) { + actions[i].disable(); + } + } + } + } + } + }); return this.grid; }; -BufferGrid.prototype.input = function() { - return new MacromoleculeGrid().input(); +CaseGrid.prototype.input = function() { + return { + proposal : DATADOC.getProposal_10(), + dewars : DATADOC.getDewars_10() + + }; }; -BufferGrid.prototype.test = function(targetId) { - var bufferGrid = new BufferGrid({ - width : 800, - height : 350, - collapsed : false, - tbar : true +CaseGrid.prototype.test = function(targetId) { + var CaseGrid = new CaseGrid({ + height : 150 }); - - BIOSAXS.proposal = new Proposal(bufferGrid.input().proposal); - var panel = bufferGrid.getPanel(BIOSAXS.proposal.macromolecules); + BIOSAXS.proposal = new Proposal(CaseGrid.input().proposal); + var panel = CaseGrid.getPanel(CaseGrid.input().dewars); panel.render(targetId); + }; /** @@ -16680,7 +17073,7 @@ BufferGrid.prototype.test = function(targetId) { * #onRemoveButtonClicked * #onDuplicateButtonClicked */ -function CaseGrid(args) { +function ExampleGrid(args) { this.height = 100; this.btnEditVisible = true; @@ -16705,7 +17098,7 @@ function CaseGrid(args) { this.onDuplicateButtonClicked = new Event(this); } -CaseGrid.prototype._getColumns = function() { +ExampleGrid.prototype._getColumns = function() { var _this = this; function actionItemRenderer(value, meta, record, rowIx, ColIx, store) { @@ -16872,7 +17265,7 @@ CaseGrid.prototype._getColumns = function() { return columns; }; -CaseGrid.prototype._getTopButtons = function() { +ExampleGrid.prototype._getTopButtons = function() { var _this = this; /** Actions buttons **/ var actions = []; @@ -16890,16 +17283,16 @@ CaseGrid.prototype._getTopButtons = function() { return actions; }; -CaseGrid.prototype.refresh = function(dewars) { +ExampleGrid.prototype.refresh = function(dewars) { this.features = dewars; this.store.loadData(this._prepareData(dewars), false); }; -CaseGrid.prototype._sort = function(store) { +ExampleGrid.prototype._sort = function(store) { store.sort('dewarId', 'DESC'); }; -CaseGrid.prototype._prepareData = function() { +ExampleGrid.prototype._prepareData = function() { var data = []; for ( var i = 0; i < this.features.length; i++) { data.push(this.features[i]); @@ -16907,27 +17300,66 @@ CaseGrid.prototype._prepareData = function() { return data; }; -CaseGrid.prototype.getPanel = function(dewars, plates) { - this.features = dewars; - this.plates = plates; - return this._renderGrid(); -}; +ExampleGrid.prototype.getPanel = function() { + this.grid = Ext.create('Ext.grid.Panel', { + style : { + padding : 5 + }, + height : this.height, + store : this.store, + columns : this._getColumns(), + viewConfig : { + stripeRows : true, + listeners : { + itemdblclick : function(dataview, record, item, e) { + _this._edit(record.raw); + }, + cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonEdit') { + _this._edit(record.raw); + } -CaseGrid.prototype._edit = function(dewar) { - var _this = this; - var caseWindow = new CaseWindow(); - /**SAVED **/ - caseWindow.onSaved.attach(function(sender, dewar) { - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, shipment) { - _this.refresh(shipment.dewarVOs); - }); - adapter.saveCase(BIOSAXS.proposal.getShipmentByDewarId(dewar.dewarId).shippingId, dewar); + if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonRemove') { + _this.onRemoveButtonClicked.notify(_this.store.getAt(rowIndex).raw.dewarId); + } + } + } + }, + selModel : { + mode : 'SINGLE' + } }); - caseWindow.draw(dewar); + + var actions = _this._getTopButtons(); + this.grid.addDocked({ + xtype : 'toolbar', + items : actions + }); + + this.grid.getSelectionModel().on({ + selectionchange : function(sm, selections) { + if (selections.length) { + for ( var i = 0; i < actions.length; i++) { + if (actions[i].enable) { + actions[i].enable(); + } + } + } else { + for ( var i = 0; i < actions.length; i++) { + if (actions[i].alwaysEnabled == false) { + if (actions[i].disable) { + actions[i].disable(); + } + } + } + } + } + }); + return this.grid; }; -CaseGrid.prototype._getStoreFields = function(data) { + +ExampleGrid.prototype._getStoreFields = function(data) { return [ { name : 'dewarId', type : 'string' @@ -16972,7 +17404,7 @@ CaseGrid.prototype._getStoreFields = function(data) { ]; }; -CaseGrid.prototype._renderGrid = function() { +ExampleGrid.prototype._renderGrid = function() { var _this = this; /** Store **/ @@ -17043,7 +17475,7 @@ CaseGrid.prototype._renderGrid = function() { return this.grid; }; -CaseGrid.prototype.input = function() { +ExampleGrid.prototype.input = function() { return { proposal : DATADOC.getProposal_10(), dewars : DATADOC.getDewars_10() @@ -17051,447 +17483,15 @@ CaseGrid.prototype.input = function() { }; }; -CaseGrid.prototype.test = function(targetId) { - var CaseGrid = new CaseGrid({ +ExampleGrid.prototype.test = function(targetId) { + var ExampleGrid = new ExampleGrid({ height : 150 }); - BIOSAXS.proposal = new Proposal(CaseGrid.input().proposal); - var panel = CaseGrid.getPanel(CaseGrid.input().dewars); - panel.render(targetId); - -}; - -/** - * A shipment may contains one or more cases where stock solutions and sample plates are stored - * - * @height - * @btnEditVisible - * @btnRemoveVisible - * - * #onEditButtonClicked - * #onAddButtonClicked - * #onRemoveButtonClicked - * #onDuplicateButtonClicked - */ -function ExampleGrid(args) { - - this.height = 100; - this.btnEditVisible = true; - this.btnRemoveVisible = true; - - if (args != null) { - if (args.height != null) { - this.height = args.height; - } - if (args.btnEditVisible != null) { - this.btnEditVisible = args.btnEditVisible; - } - if (args.btnRemoveVisible != null) { - this.btnRemoveVisible = args.btnRemoveVisible; - } - } - - /** Events **/ - this.onEditButtonClicked = new Event(this); - this.onAddButtonClicked = new Event(this); - this.onRemoveButtonClicked = new Event(this); - this.onDuplicateButtonClicked = new Event(this); -} - -ExampleGrid.prototype._getColumns = function() { - var _this = this; - - function actionItemRenderer(value, meta, record, rowIx, ColIx, store) { - /*if (record.raw.specimen3VOs.length > 0){ - return 'x-hide-display'; - }*/ - } - - var columns = [ - { - header : 'Code', - dataIndex : 'code', - name : 'code', - type : 'string', - flex : 1 - }, - { - header : 'Bar Code', - dataIndex : 'barCode', - name : 'barCode', - type : 'string', - flex : 1, - hidden : true - }, - { - header : 'BeamLine', - dataIndex : 'barCode', - type : 'string', - flex : 1, - renderer : function(comp, val, record) { - if (record.raw.sessionVO != null) { - return "" + record.raw.sessionVO.beamlineName + ""; - } - } - }, - { - header : 'Session', - dataIndex : 'barCode', - type : 'string', - flex : 1, - renderer : function(comp, val, record) { - if (record.raw.sessionVO != null) { - return "" + moment(record.raw.sessionVO.startDate).format("MMM Do YY") + ""; - } - } - }, - { - header : 'Status', - dataIndex : 'dewarStatus', - name : 'dewarStatus', - type : 'string', - flex : 1, - renderer : function(comp, val, record) { - if (new String(record.raw.dewarStatus).toUpperCase() == 'OPENED') { - return "" + new String(record.raw.dewarStatus).toUpperCase() + ""; - } - return "" + new String(record.raw.dewarStatus).toUpperCase() + ""; - } - }, - { - header : 'Type', - dataIndex : 'type', - name : 'type', - type : 'string', - flex : 1, - hidden : true - }, - { - header : 'Sample Plates', - dataIndex : 'plates', - name : 'plates', - type : 'string', - flex : 1, - hidden : true - }, - { - header : 'Stock Solutions', - dataIndex : 'plates', - name : 'plates', - type : 'string', - width : 100, - renderer : function(comp, val, record) { - var html = "
"; - var stockSolutions = BIOSAXS.proposal.getStockSolutionsByDewarId(record.raw.dewarId); - html = html + "" + stockSolutions.length + " x"; - return html + "
"; - } - }, { - header : 'isStorageDewar', - dataIndex : 'isStorageDewar', - name : 'isStorageDewar', - type : 'string', - flex : 1, - hidden : true - }, { - header : 'Tracking Number From Synchrotron', - dataIndex : 'trackingNumberFromSynchrotron', - name : 'trackingNumberFromSynchrotron', - type : 'string', - flex : 1, - hidden : true - }, { - header : 'Tracking Number To Synchrotron', - dataIndex : 'trackingNumberToSynchrotron', - name : 'trackingNumberToSynchrotron', - type : 'string', - flex : 1, - hidden : true - }, { - header : 'Storage Location', - dataIndex : 'storageLocation', - name : 'storageLocation', - type : 'string', - flex : 1, - hidden : false - }, { - header : 'Comments', - dataIndex : 'comments', - name : 'comments', - type : 'string', - flex : 1, - hidden : false - } - ]; - - if (this.btnEditVisible) { - columns.push({ - id : _this.id + 'buttonEdit', - text : '', - hidden : !_this.btnEditVisible, - width : 85, - sortable : false, - renderer : function(value, metaData, record, rowIndex, colIndex, store) { - return BUI.getGreenButton('EDIT'); - } - }); - } - - if (this.btnRemoveVisible) { - columns.push({ - id : _this.id + 'buttonRemove', - width : 85, - sortable : false, - renderer : function(value, metaData, record, rowIndex, colIndex, store) { - if (_this.btnRemoveVisible) { - return BUI.getRedButton('REMOVE'); - } - } - }); - } - - columns.push({ - dataIndex : 'comments', - type : 'string', - width : 85, - hidden : false, - renderer : function(comp, val, record) { - return BUI.getBlueButton("LABELS", { - href : BUI.getPrintcomponentURL(record.raw.dewarId) - }); - } - }); - - return columns; -}; - -ExampleGrid.prototype._getTopButtons = function() { - var _this = this; - /** Actions buttons **/ - var actions = []; - - /** ADD BUTTON **/ - actions.push(Ext.create('Ext.Action', { - icon : '../images/add.png', - text : 'Add', - disabled : false, - handler : function(widget, event) { - _this.onAddButtonClicked.notify(); - } - })); - - return actions; -}; - -ExampleGrid.prototype.refresh = function(dewars) { - this.features = dewars; - this.store.loadData(this._prepareData(dewars), false); -}; - -ExampleGrid.prototype._sort = function(store) { - store.sort('dewarId', 'DESC'); -}; - -ExampleGrid.prototype._prepareData = function() { - var data = []; - for ( var i = 0; i < this.features.length; i++) { - data.push(this.features[i]); - } - return data; -}; - -ExampleGrid.prototype.getPanel = function() { - this.grid = Ext.create('Ext.grid.Panel', { - style : { - padding : 5 - }, - height : this.height, - store : this.store, - columns : this._getColumns(), - viewConfig : { - stripeRows : true, - listeners : { - itemdblclick : function(dataview, record, item, e) { - _this._edit(record.raw); - }, - cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { - if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonEdit') { - _this._edit(record.raw); - } - - if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonRemove') { - _this.onRemoveButtonClicked.notify(_this.store.getAt(rowIndex).raw.dewarId); - } - } - } - }, - selModel : { - mode : 'SINGLE' - } - }); - - var actions = _this._getTopButtons(); - this.grid.addDocked({ - xtype : 'toolbar', - items : actions - }); - - this.grid.getSelectionModel().on({ - selectionchange : function(sm, selections) { - if (selections.length) { - for ( var i = 0; i < actions.length; i++) { - if (actions[i].enable) { - actions[i].enable(); - } - } - } else { - for ( var i = 0; i < actions.length; i++) { - if (actions[i].alwaysEnabled == false) { - if (actions[i].disable) { - actions[i].disable(); - } - } - } - } - } - }); - return this.grid; -}; - - -ExampleGrid.prototype._getStoreFields = function(data) { - return [ { - name : 'dewarId', - type : 'string' - }, { - name : 'barCode', - type : 'string' - }, { - name : 'code', - type : 'string' - }, { - name : 'comments', - type : 'string' - }, { - name : 'dewarStatus', - type : 'string' - }, { - name : 'isStorageDewar', - type : 'string' - }, { - name : 'plates', - type : 'string' - }, { - name : 'transportValue', - type : 'string' - }, { - name : 'trackingNumberFromSynchrotron', - type : 'string' - }, { - name : 'trackingNumberToSynchrotron', - type : 'string' - }, { - name : 'timeStamp', - type : 'string' - }, { - name : 'storageLocation', - type : 'string' - }, { - name : 'type', - type : 'string' - } - - ]; -}; - -ExampleGrid.prototype._renderGrid = function() { - var _this = this; - - /** Store **/ - var data = this._prepareData(); - this.store = Ext.create('Ext.data.Store', { - fields : this._getStoreFields(data), - autoload : true, - data : data - }); - this._sort(this.store); - - this.store.loadData(data, false); - - this.grid = Ext.create('Ext.grid.Panel', { - style : { - padding : 5 - }, - height : this.height, - store : this.store, - columns : this._getColumns(), - viewConfig : { - stripeRows : true, - listeners : { - itemdblclick : function(dataview, record, item, e) { - _this._edit(record.raw); - }, - cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { - if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonEdit') { - _this._edit(record.raw); - } - - if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonRemove') { - _this.onRemoveButtonClicked.notify(_this.store.getAt(rowIndex).raw.dewarId); - } - } - } - }, - selModel : { - mode : 'SINGLE' - } - }); - - var actions = _this._getTopButtons(); - this.grid.addDocked({ - xtype : 'toolbar', - items : actions - }); - - this.grid.getSelectionModel().on({ - selectionchange : function(sm, selections) { - if (selections.length) { - for ( var i = 0; i < actions.length; i++) { - if (actions[i].enable) { - actions[i].enable(); - } - } - } else { - for ( var i = 0; i < actions.length; i++) { - if (actions[i].alwaysEnabled == false) { - if (actions[i].disable) { - actions[i].disable(); - } - } - } - } - } - }); - return this.grid; -}; - -ExampleGrid.prototype.input = function() { - return { - proposal : DATADOC.getProposal_10(), - dewars : DATADOC.getDewars_10() - - }; -}; - -ExampleGrid.prototype.test = function(targetId) { - var ExampleGrid = new ExampleGrid({ - height : 150 - }); - BIOSAXS.proposal = new Proposal(ExampleGrid.input().proposal); - var panel = ExampleGrid.getPanel(ExampleGrid.input().dewars); - panel.render(targetId); - -}; + BIOSAXS.proposal = new Proposal(ExampleGrid.input().proposal); + var panel = ExampleGrid.getPanel(ExampleGrid.input().dewars); + panel.render(targetId); + +}; /** @@ -18106,188 +18106,188 @@ ExperimentGrid.prototype.test = function(targetId) { panel.render(targetId); }; - -function FitStructureToExperimentDataGrid(args) { - this.height = null; - this.width = null; - this.id = BUI.id(); - - if (args != null) { - if (args.height != null) { - this.height = args.height; - - if (args.width != null) { - this.width = args.width; - } - } - } - - this.onSelected = new Event(this); -} - -FitStructureToExperimentDataGrid.prototype.getStructuresByMacromolecule = function(macromolecule) { - var structures = []; - if (macromolecule.structure3VOs != null) { - if (macromolecule.structure3VOs.length > 0) { - for (var i = 0; i < macromolecule.structure3VOs.length; i++) { - structures.push(macromolecule.structure3VOs[i]); - } - } - } - return structures; -}; - -FitStructureToExperimentDataGrid.prototype._prepareData = function(subtractions) { - var data = []; - for (var i = 0; i < subtractions.length; i++) { - - for (var j = 0; j < subtractions[i].fitStructureToExperimentalData3VOs.length; j++) { - var fit = subtractions[i].fitStructureToExperimentalData3VOs[j]; - data.push({ - fit : fit.fitFilePath, - fitStructureToExperimentalDataId : fit.fitStructureToExperimentalDataId, - mixtureToStructure3VOs : fit.mixtureToStructure3VOs, - subtractedFile : subtractions[i].substractedFilePath - }); - } - } - return data; - -}; - -FitStructureToExperimentDataGrid.prototype.refresh = function(subtractions) { - this.store.loadData(this._prepareData(subtractions), false); -}; - -FitStructureToExperimentDataGrid.prototype.getPanel = function() { - var _this = this; - this.store = Ext.create('Ext.data.Store', { - fields : [ 'fit', 'subtractedFile', 'structureId', 'fitFilePath', 'mixtureToStructure3VOs' ], - data : [] - }); - - this.store.sort([ { - property : 'name', - direction : 'ASC' - } ]); - - this.selModel = Ext.create('Ext.selection.RowModel', { - allowDeselect : true, - mode : 'MULTI', - listeners : { - selectionchange : function(sm, selections) { - var selected = []; - for (var i = 0; i < selections.length; i++) { - selected.push(selections[i].raw); - } - _this.onSelected.notify(selected); - } - } - }); - - this.panel = Ext.create('Ext.grid.Panel', { - store : this.store, - deferredRender : false, - width : this.width, - height : this.height, - margin : 10, - selModel : this.selModel, - columns : [ { - text : 'Name', - dataIndex : 'fit', - flex : 1, - renderer : function(val, b, record) { - return BUI.getFileName(val); - } - }, { - text : 'PDB', - dataIndex : 'mixtureToStructure3VOs', - flex : 1, - renderer : function(values, b, record) { - var html = ""; - for (var i = 0; i < values.length; i++) { - var structure = BIOSAXS.proposal.getStructuresById(values[i].structureId); - html = html + ""; - if (structure != null){ - html = html + ""; - } - html = html + ""; - } - return html + "
" + structure.name + "
"; - } - }, { - text : 'Volume Fraction', - dataIndex : 'mixtureToStructure3VOs', - flex : 1, - renderer : function(values, b, record) { - var html = ""; - for (var i = 0; i < values.length; i++) { - html = html + ""; - html = html + ""; - html = html + ""; - } - - return html + "
" + values[i].volumeFraction + "
"; - } - },{ - text : 'Subtraction', - dataIndex : 'subtractedFile', - flex : 1, - renderer : function(values, b, record) { - return values.split("/")[values.split("/").length - 1]; - } - },], - - viewConfig : { - enableTextSelection : true, - preserveScrollOnRefresh : true - }, - listeners : { - cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { - }, - afterrender : function() { - } - } - }); - return this.panel; -}; - -/** Static method **/ -FitStructureToExperimentDataGrid.prototype.RUNFitScattering = function(subtractionId, structureId) { - var _this = this; - /** Add to Workflow **/ - var adapter = new BiosaxsDataAdapter(); - var workflow = { - 'workflowTitle' : 'FitExperimentalDatatoStructure', - 'comments' : 'FitExperimentalDatatoStructure run from ISPyB for subtractionId: ' + subtractionId + ' and structureId ' + structureId - } - - var inputs = [ { - name : 'subtractionId', - value : subtractionId - }, { - name : 'structureId', - value : structureId - } ]; - - adapter.onSuccess.attach(function(sender, data) { - /** Add to Fit **/ - var adapter2 = new BiosaxsDataAdapter(); - var fit = { - 'workflowId' : data.workflowId, - 'subtractionId' : subtractionId, - 'structureId' : structureId, - 'comments' : 'Sent to workflow engine' - } - adapter2.onSuccess.attach(function(sender, fit) { - _this.refresh(_this.subtractionId, _this.macromolecule); - }); - adapter2.addFitStructureData(fit); - - }); - adapter.addWorkflow(workflow, inputs); - -}; + +function FitStructureToExperimentDataGrid(args) { + this.height = null; + this.width = null; + this.id = BUI.id(); + + if (args != null) { + if (args.height != null) { + this.height = args.height; + + if (args.width != null) { + this.width = args.width; + } + } + } + + this.onSelected = new Event(this); +} + +FitStructureToExperimentDataGrid.prototype.getStructuresByMacromolecule = function(macromolecule) { + var structures = []; + if (macromolecule.structure3VOs != null) { + if (macromolecule.structure3VOs.length > 0) { + for (var i = 0; i < macromolecule.structure3VOs.length; i++) { + structures.push(macromolecule.structure3VOs[i]); + } + } + } + return structures; +}; + +FitStructureToExperimentDataGrid.prototype._prepareData = function(subtractions) { + var data = []; + for (var i = 0; i < subtractions.length; i++) { + + for (var j = 0; j < subtractions[i].fitStructureToExperimentalData3VOs.length; j++) { + var fit = subtractions[i].fitStructureToExperimentalData3VOs[j]; + data.push({ + fit : fit.fitFilePath, + fitStructureToExperimentalDataId : fit.fitStructureToExperimentalDataId, + mixtureToStructure3VOs : fit.mixtureToStructure3VOs, + subtractedFile : subtractions[i].substractedFilePath + }); + } + } + return data; + +}; + +FitStructureToExperimentDataGrid.prototype.refresh = function(subtractions) { + this.store.loadData(this._prepareData(subtractions), false); +}; + +FitStructureToExperimentDataGrid.prototype.getPanel = function() { + var _this = this; + this.store = Ext.create('Ext.data.Store', { + fields : [ 'fit', 'subtractedFile', 'structureId', 'fitFilePath', 'mixtureToStructure3VOs' ], + data : [] + }); + + this.store.sort([ { + property : 'name', + direction : 'ASC' + } ]); + + this.selModel = Ext.create('Ext.selection.RowModel', { + allowDeselect : true, + mode : 'MULTI', + listeners : { + selectionchange : function(sm, selections) { + var selected = []; + for (var i = 0; i < selections.length; i++) { + selected.push(selections[i].raw); + } + _this.onSelected.notify(selected); + } + } + }); + + this.panel = Ext.create('Ext.grid.Panel', { + store : this.store, + deferredRender : false, + width : this.width, + height : this.height, + margin : 10, + selModel : this.selModel, + columns : [ { + text : 'Name', + dataIndex : 'fit', + flex : 1, + renderer : function(val, b, record) { + return BUI.getFileName(val); + } + }, { + text : 'PDB', + dataIndex : 'mixtureToStructure3VOs', + flex : 1, + renderer : function(values, b, record) { + var html = ""; + for (var i = 0; i < values.length; i++) { + var structure = BIOSAXS.proposal.getStructuresById(values[i].structureId); + html = html + ""; + if (structure != null){ + html = html + ""; + } + html = html + ""; + } + return html + "
" + structure.name + "
"; + } + }, { + text : 'Volume Fraction', + dataIndex : 'mixtureToStructure3VOs', + flex : 1, + renderer : function(values, b, record) { + var html = ""; + for (var i = 0; i < values.length; i++) { + html = html + ""; + html = html + ""; + html = html + ""; + } + + return html + "
" + values[i].volumeFraction + "
"; + } + },{ + text : 'Subtraction', + dataIndex : 'subtractedFile', + flex : 1, + renderer : function(values, b, record) { + return values.split("/")[values.split("/").length - 1]; + } + },], + + viewConfig : { + enableTextSelection : true, + preserveScrollOnRefresh : true + }, + listeners : { + cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + }, + afterrender : function() { + } + } + }); + return this.panel; +}; + +/** Static method **/ +FitStructureToExperimentDataGrid.prototype.RUNFitScattering = function(subtractionId, structureId) { + var _this = this; + /** Add to Workflow **/ + var adapter = new BiosaxsDataAdapter(); + var workflow = { + 'workflowTitle' : 'FitExperimentalDatatoStructure', + 'comments' : 'FitExperimentalDatatoStructure run from ISPyB for subtractionId: ' + subtractionId + ' and structureId ' + structureId + } + + var inputs = [ { + name : 'subtractionId', + value : subtractionId + }, { + name : 'structureId', + value : structureId + } ]; + + adapter.onSuccess.attach(function(sender, data) { + /** Add to Fit **/ + var adapter2 = new BiosaxsDataAdapter(); + var fit = { + 'workflowId' : data.workflowId, + 'subtractionId' : subtractionId, + 'structureId' : structureId, + 'comments' : 'Sent to workflow engine' + } + adapter2.onSuccess.attach(function(sender, fit) { + _this.refresh(_this.subtractionId, _this.macromolecule); + }); + adapter2.addFitStructureData(fit); + + }); + adapter.addWorkflow(workflow, inputs); + +}; /** * It shows buffer grid with a top bar with "Add" button @@ -19893,244 +19893,244 @@ MeasurementGrid.prototype.getPanel = function(measurements, experiments) { }, 1000); } - } catch (e) { - } + } catch (e) { + } + } + } + } + }); + + return this.grid; +}; + +/** Method for testing * */ +MeasurementGrid.prototype.input = function() { + var experiment = DATADOC.getExperiment_10(); + var measurements = DATADOC.getMeasurements_10(); + var proposal = DATADOC.getProposal_10(); + return { + experiment : experiment, + measurements : measurements, + proposal : proposal + }; +}; + +MeasurementGrid.prototype.test = function(targetId) { + var measurementGrid = new MeasurementGrid({ + tbar : true + + }); + BIOSAXS.proposal = new Proposal(measurementGrid.input().proposal); + var panel = measurementGrid.getPanel(measurementGrid.input().measurements, new ExperimentList([ new Experiment(measurementGrid.input().experiment) ])); + panel.render(targetId); +}; + +/** + * A shipment may contains one or more cases where stock solutions and sample + * plates are stored + * + * @height + * @btnEditVisible + * @btnRemoveVisible + * + * #onEditButtonClicked + */ +function MolarityGrid(args) { + this.height = 100; + this.width = 100; + + if (args != null) { + if (args.height != null) { + this.height = args.height; + } + if (args.width != null) { + this.width = args.width; + } + } + + var _this = this; + + this.molarityForm = new MolarityForm({height : 180, width : 455}); + + this.molarityForm.onSave.attach(function(sender){ + _this.molarityWindow.destroy(); + _this.updateProposal(); + + }); + + this.molarityForm.onClose.attach(function(sender){ + _this.molarityWindow.destroy(); + + }); + + /** Events * */ + this.onEditButtonClicked = new Event(this); +} + +MolarityGrid.prototype._getColumns = function() { + return [ { + text : 'Subunit', + columns : [ { + text : "Acronym", + width : 100, + hidden : false, + dataIndex : 'acronym', + sortable : true + }, { + text : "Name", + width : 125, + hidden : false, + dataIndex : 'name', + sortable : true + }, { + text : "MM Est.", + width : 100, + dataIndex : 'molecularMass', + sortable : true, + renderer : function(grid, cls, record){ + return BUI.formatValuesUnits(record.data.molecularMass , "Da", 10, 2); + + } + } ] + }, { +// text : "Number
in assymmetric units", + text : "Ratio", + width : 100, + dataIndex : 'ratio', + tooltip : 'Number of times the subunit is present in the macromolecule', + sortable : true + }, { + id : this.id + 'MOLARITY_REMOVE', + flex : 0.1, + sortable : false, + renderer : function(value, metaData, record, rowIndex, colIndex, store) { + return BUI.getRedButton('REMOVE'); + } + } ]; +}; + +MolarityGrid.prototype._openMolarityWindow = function() { + this.molarityWindow = Ext.create('Ext.window.Window', { + title : 'Molarity', + height : 220, + width : 500, + modal : true, + items : [this.molarityForm.getPanel() ] + }).show(); +}; + +MolarityGrid.prototype._getButtons = function() { + var _this = this; + return [ { + text : 'Add subunit', + icon : '../images/add.png', + handler : function() { + _this._openMolarityWindow(); + } + }]; +}; + + +MolarityGrid.prototype.updateProposal = function() { + var _this = this; + this.panel.setLoading(); + BIOSAXS.proposal.onInitialized.attach(function() { + if (BIOSAXS.proposal != null) { + var macromolecules = BIOSAXS.proposal.macromolecules; + for (var i = 0; i < macromolecules.length; i++) { + + if (macromolecules[i].macromoleculeId == _this.macromolecule.macromoleculeId) { + _this.refresh(macromolecules[i]); + _this.panel.setLoading(false); + } + } + } + }); + BIOSAXS.proposal.init(); +}; + + +MolarityGrid.prototype.getPanel = function() { + var _this = this; + + this.molarityStore = Ext.create('Ext.data.Store', { + fields : [ 'acronym', 'ratio', 'comments', 'stoichiometryId', 'name', 'molecularMass' ], + sorters : { + property : 'ratio', + direction : 'DESC' + } + }); + + this.panel = Ext.create('Ext.grid.Panel', { + store : this.molarityStore, + height : this.height, + padding : 5, + columns : this._getColumns(), + listeners : { + cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + + /** Remove entry * */ + if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'MOLARITY_REMOVE') { + var dataAdapter = new BiosaxsDataAdapter(); + dataAdapter.onSuccess.attach(function(sender) { + _this.updateProposal(); + + }); + dataAdapter.removeStoichiometry(record.data.stoichiometryId); + _this.panel.setLoading("Removing Structure"); } } - } + }, + tbar : this._getButtons() }); + return this.panel; +}; - return this.grid; +MolarityGrid.prototype._prepareData = function(macromolecule) { + /** Return an array of [{ratio,acronym, stoichiometryId, name}] **/ + var data = []; + if (macromolecule.stoichiometry != null) { + for (var i = 0; i < macromolecule.stoichiometry.length; i++) { + var hostMacromolecule = BIOSAXS.proposal.getMacromoleculeById(macromolecule.stoichiometry[i].macromoleculeId); + data.push({ + ratio : macromolecule.stoichiometry[i].ratio, + acronym : hostMacromolecule.acronym, + comments : hostMacromolecule.comments, + molecularMass : hostMacromolecule.molecularMass, + stoichiometryId : macromolecule.stoichiometry[i].stoichiometryId, + name : hostMacromolecule.name + }); + } + } + return data; }; -/** Method for testing * */ -MeasurementGrid.prototype.input = function() { - var experiment = DATADOC.getExperiment_10(); - var measurements = DATADOC.getMeasurements_10(); - var proposal = DATADOC.getProposal_10(); +MolarityGrid.prototype.refresh = function(macromolecule) { + if (macromolecule != null){ + this.molarityStore.loadData(this._prepareData(macromolecule)); + this.molarityForm.refresh(macromolecule); + this.macromolecule = macromolecule; + } +}; + +MolarityGrid.prototype.input = function() { return { - experiment : experiment, - measurements : measurements, - proposal : proposal + proposal : DATADOC.getProposal_10(), + dewars : DATADOC.getDewars_10() + }; }; -MeasurementGrid.prototype.test = function(targetId) { - var measurementGrid = new MeasurementGrid({ - tbar : true - +MolarityGrid.prototype.test = function(targetId) { + var MolarityGrid = new MolarityGrid({ + height : 150 }); - BIOSAXS.proposal = new Proposal(measurementGrid.input().proposal); - var panel = measurementGrid.getPanel(measurementGrid.input().measurements, new ExperimentList([ new Experiment(measurementGrid.input().experiment) ])); + BIOSAXS.proposal = new Proposal(MolarityGrid.input().proposal); + var panel = MolarityGrid.getPanel(MolarityGrid.input().dewars); panel.render(targetId); + }; -/** - * A shipment may contains one or more cases where stock solutions and sample - * plates are stored - * - * @height - * @btnEditVisible - * @btnRemoveVisible - * - * #onEditButtonClicked - */ -function MolarityGrid(args) { - this.height = 100; - this.width = 100; - - if (args != null) { - if (args.height != null) { - this.height = args.height; - } - if (args.width != null) { - this.width = args.width; - } - } - - var _this = this; - - this.molarityForm = new MolarityForm({height : 180, width : 455}); - - this.molarityForm.onSave.attach(function(sender){ - _this.molarityWindow.destroy(); - _this.updateProposal(); - - }); - - this.molarityForm.onClose.attach(function(sender){ - _this.molarityWindow.destroy(); - - }); - - /** Events * */ - this.onEditButtonClicked = new Event(this); -} - -MolarityGrid.prototype._getColumns = function() { - return [ { - text : 'Subunit', - columns : [ { - text : "Acronym", - width : 100, - hidden : false, - dataIndex : 'acronym', - sortable : true - }, { - text : "Name", - width : 125, - hidden : false, - dataIndex : 'name', - sortable : true - }, { - text : "MM Est.", - width : 100, - dataIndex : 'molecularMass', - sortable : true, - renderer : function(grid, cls, record){ - return BUI.formatValuesUnits(record.data.molecularMass , "Da", 10, 2); - - } - } ] - }, { -// text : "Number
in assymmetric units", - text : "Ratio", - width : 100, - dataIndex : 'ratio', - tooltip : 'Number of times the subunit is present in the macromolecule', - sortable : true - }, { - id : this.id + 'MOLARITY_REMOVE', - flex : 0.1, - sortable : false, - renderer : function(value, metaData, record, rowIndex, colIndex, store) { - return BUI.getRedButton('REMOVE'); - } - } ]; -}; - -MolarityGrid.prototype._openMolarityWindow = function() { - this.molarityWindow = Ext.create('Ext.window.Window', { - title : 'Molarity', - height : 220, - width : 500, - modal : true, - items : [this.molarityForm.getPanel() ] - }).show(); -}; - -MolarityGrid.prototype._getButtons = function() { - var _this = this; - return [ { - text : 'Add subunit', - icon : '../images/add.png', - handler : function() { - _this._openMolarityWindow(); - } - }]; -}; - - -MolarityGrid.prototype.updateProposal = function() { - var _this = this; - this.panel.setLoading(); - BIOSAXS.proposal.onInitialized.attach(function() { - if (BIOSAXS.proposal != null) { - var macromolecules = BIOSAXS.proposal.macromolecules; - for (var i = 0; i < macromolecules.length; i++) { - - if (macromolecules[i].macromoleculeId == _this.macromolecule.macromoleculeId) { - _this.refresh(macromolecules[i]); - _this.panel.setLoading(false); - } - } - } - }); - BIOSAXS.proposal.init(); -}; - - -MolarityGrid.prototype.getPanel = function() { - var _this = this; - - this.molarityStore = Ext.create('Ext.data.Store', { - fields : [ 'acronym', 'ratio', 'comments', 'stoichiometryId', 'name', 'molecularMass' ], - sorters : { - property : 'ratio', - direction : 'DESC' - } - }); - - this.panel = Ext.create('Ext.grid.Panel', { - store : this.molarityStore, - height : this.height, - padding : 5, - columns : this._getColumns(), - listeners : { - cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { - - /** Remove entry * */ - if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'MOLARITY_REMOVE') { - var dataAdapter = new BiosaxsDataAdapter(); - dataAdapter.onSuccess.attach(function(sender) { - _this.updateProposal(); - - }); - dataAdapter.removeStoichiometry(record.data.stoichiometryId); - _this.panel.setLoading("Removing Structure"); - } - } - }, - tbar : this._getButtons() - }); - return this.panel; -}; - -MolarityGrid.prototype._prepareData = function(macromolecule) { - /** Return an array of [{ratio,acronym, stoichiometryId, name}] **/ - var data = []; - if (macromolecule.stoichiometry != null) { - for (var i = 0; i < macromolecule.stoichiometry.length; i++) { - var hostMacromolecule = BIOSAXS.proposal.getMacromoleculeById(macromolecule.stoichiometry[i].macromoleculeId); - data.push({ - ratio : macromolecule.stoichiometry[i].ratio, - acronym : hostMacromolecule.acronym, - comments : hostMacromolecule.comments, - molecularMass : hostMacromolecule.molecularMass, - stoichiometryId : macromolecule.stoichiometry[i].stoichiometryId, - name : hostMacromolecule.name - }); - } - } - return data; -}; - -MolarityGrid.prototype.refresh = function(macromolecule) { - if (macromolecule != null){ - this.molarityStore.loadData(this._prepareData(macromolecule)); - this.molarityForm.refresh(macromolecule); - this.macromolecule = macromolecule; - } -}; - -MolarityGrid.prototype.input = function() { - return { - proposal : DATADOC.getProposal_10(), - dewars : DATADOC.getDewars_10() - - }; -}; - -MolarityGrid.prototype.test = function(targetId) { - var MolarityGrid = new MolarityGrid({ - height : 150 - }); - BIOSAXS.proposal = new Proposal(MolarityGrid.input().proposal); - var panel = MolarityGrid.getPanel(MolarityGrid.input().dewars); - panel.render(targetId); - -}; - /** * Given data analysis parsed with ResultsAssemblyWidget makes a selector grid with buffer/protein @@ -20815,135 +20815,135 @@ ResultsAssemblyGrid.prototype.test = function(targetId) { }; - -function RigidModelGrid(args) { - this.height = null; - this.width = null; - this.id = BUI.id(); - - if (args != null) { - if (args.height != null) { - this.height = args.height; - - if (args.width != null) { - this.width = args.width; - } - } - } - - this.onSelected = new Event(this); -} - - -RigidModelGrid.prototype.refresh = function(subtractions) { - var data = []; - if (subtractions != null){ - if (subtractions.length > 0){ - for (j in subtractions){ - var subtraction = subtractions[j]; - if (subtraction.rigidBodyModeling3VOs != null){ - for (i in subtraction.rigidBodyModeling3VOs){ - data.push(subtraction.rigidBodyModeling3VOs[i]); - } - } - } - } - } - this.store.loadData(data); -}; - -RigidModelGrid.prototype.getPanel = function() { - var _this = this; - this.store = Ext.create('Ext.data.Store', { - fields : [ 'symmetry', 'subtractionId', 'subUnitConfigFilePath', 'rigidBodyModelingId', 'rigidBodyModelFilePath', 'logFilePath', 'fitFilePath', 'curveConfigFilePath', - 'crossCorrConfigFilePath', 'contactDescriptionFilePath' ], - data : [] - }); - - this.store.sort([ { - property : 'name', - direction : 'ASC' - } ]); - - this.panel = Ext.create('Ext.grid.Panel', { - store : this.store, - width : this.width, - height : this.height, - margin : 10, - deferredRender : false, - columns : [ { - text : 'RBM', - dataIndex : 'rigidBodyModelFilePath', - hidden : false, - flex : 1, - renderer : function(val){ - return BUI.getFileName(val); - } - }, { - text : 'Sub Unit Conf.', - dataIndex : 'subUnitConfigFilePath', - hidden : true, - flex : 1, - renderer : function(val){ - return BUI.getFileName(val); - } - }, { - text : 'Log', - dataIndex : 'logFilePath', - hidden : true, - flex : 1, - renderer : function(val){ - return BUI.getFileName(val); - } - }, { - text : 'Fit', - dataIndex : 'fitFilePath', - hidden : false, - flex : 1, - renderer : function(val){ - return BUI.getFileName(val); - } - }, { - text : 'Curve Conf.', - dataIndex : 'curveConfigFilePath', - hidden : false, - flex : 1, - renderer : function(val){ - return BUI.getFileName(val); - } - }, { - text : 'Cross Corr.', - dataIndex : 'crossCorrConfigFilePath', - hidden : false, - flex : 1, - renderer : function(val){ - return BUI.getFileName(val); - } - }, { - text : 'Contact Desc.', - dataIndex : 'contactDescriptionFilePath', - hidden : true, - flex : 1, - renderer : function(val){ - return BUI.getFileName(val); - } - } ], - - viewConfig : { - enableTextSelection : true, - preserveScrollOnRefresh : true - }, - listeners : { - cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { - - }, - afterrender : function() { - } - } - }); - return this.panel; -}; - + +function RigidModelGrid(args) { + this.height = null; + this.width = null; + this.id = BUI.id(); + + if (args != null) { + if (args.height != null) { + this.height = args.height; + + if (args.width != null) { + this.width = args.width; + } + } + } + + this.onSelected = new Event(this); +} + + +RigidModelGrid.prototype.refresh = function(subtractions) { + var data = []; + if (subtractions != null){ + if (subtractions.length > 0){ + for (j in subtractions){ + var subtraction = subtractions[j]; + if (subtraction.rigidBodyModeling3VOs != null){ + for (i in subtraction.rigidBodyModeling3VOs){ + data.push(subtraction.rigidBodyModeling3VOs[i]); + } + } + } + } + } + this.store.loadData(data); +}; + +RigidModelGrid.prototype.getPanel = function() { + var _this = this; + this.store = Ext.create('Ext.data.Store', { + fields : [ 'symmetry', 'subtractionId', 'subUnitConfigFilePath', 'rigidBodyModelingId', 'rigidBodyModelFilePath', 'logFilePath', 'fitFilePath', 'curveConfigFilePath', + 'crossCorrConfigFilePath', 'contactDescriptionFilePath' ], + data : [] + }); + + this.store.sort([ { + property : 'name', + direction : 'ASC' + } ]); + + this.panel = Ext.create('Ext.grid.Panel', { + store : this.store, + width : this.width, + height : this.height, + margin : 10, + deferredRender : false, + columns : [ { + text : 'RBM', + dataIndex : 'rigidBodyModelFilePath', + hidden : false, + flex : 1, + renderer : function(val){ + return BUI.getFileName(val); + } + }, { + text : 'Sub Unit Conf.', + dataIndex : 'subUnitConfigFilePath', + hidden : true, + flex : 1, + renderer : function(val){ + return BUI.getFileName(val); + } + }, { + text : 'Log', + dataIndex : 'logFilePath', + hidden : true, + flex : 1, + renderer : function(val){ + return BUI.getFileName(val); + } + }, { + text : 'Fit', + dataIndex : 'fitFilePath', + hidden : false, + flex : 1, + renderer : function(val){ + return BUI.getFileName(val); + } + }, { + text : 'Curve Conf.', + dataIndex : 'curveConfigFilePath', + hidden : false, + flex : 1, + renderer : function(val){ + return BUI.getFileName(val); + } + }, { + text : 'Cross Corr.', + dataIndex : 'crossCorrConfigFilePath', + hidden : false, + flex : 1, + renderer : function(val){ + return BUI.getFileName(val); + } + }, { + text : 'Contact Desc.', + dataIndex : 'contactDescriptionFilePath', + hidden : true, + flex : 1, + renderer : function(val){ + return BUI.getFileName(val); + } + } ], + + viewConfig : { + enableTextSelection : true, + preserveScrollOnRefresh : true + }, + listeners : { + cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + + }, + afterrender : function() { + } + } + }); + return this.panel; +}; + /** * shows shipments @@ -22309,119 +22309,119 @@ StockSolutionGrid.prototype.test = function(targetId) { panel.render(targetId); }; - -function SuperpositionGrid(args) { - this.height = null; - this.width = null; - this.id = BUI.id(); - - if (args != null) { - if (args.height != null) { - this.height = args.height; - - if (args.width != null) { - this.width = args.width; - } - } - } - - this.onSelected = new Event(this); -} - -SuperpositionGrid.prototype._prepareData = function(data) { - return data; -}; - -SuperpositionGrid.prototype.refresh = function(subtractions) { - var data = []; - if (subtractions != null){ - if (subtractions.length > 0){ - for (j in subtractions){ - var subtraction = subtractions[j]; - if (subtraction.superposition3VOs != null){ - for (i in subtraction.superposition3VOs){ - data.push(subtraction.superposition3VOs[i]); - } - } - } - } - } - this.store.loadData(data); -}; - -SuperpositionGrid.prototype.getPanel = function() { - var _this = this; - this.store = Ext.create('Ext.data.Store', { - fields : [ 'abinitioModelPdbFilePath', 'aprioriPdbFilePath', 'alignedPdbFilePath' ], - data : [] - }); - - this.selModel = Ext.create('Ext.selection.RowModel', { - allowDeselect : true, - mode : 'MULTI', - listeners : { - selectionchange : function(sm, selections) { - var selected = []; - for (var i = 0; i < selections.length; i++) { - selected.push(selections[i].raw); - } - _this.onSelected.notify(selected); - } - } - }); - - this.store.sort([ { - property : 'name', - direction : 'ASC' - } ]); - - this.panel = Ext.create('Ext.grid.Panel', { - store : this.store, - selModel : this.selModel, - width : this.width, - height : this.height, - margin : 10, - deferredRender : false, - columns : [ { - text : 'Abinitio', - dataIndex : 'abinitioModelPdbFilePath', - hidden : false, - flex : 1, - renderer : function(val){ - return BUI.getFileName(val); - } - }, { - text : 'Apriori', - dataIndex : 'aprioriPdbFilePath', - hidden : false, - flex : 1, - renderer : function(val){ - return BUI.getFileName(val); - } - }, { - text : 'Aligned', - dataIndex : 'alignedPdbFilePath', - hidden : false, - flex : 1, - renderer : function(val){ - return BUI.getFileName(val); - } - } ], - - viewConfig : { - enableTextSelection : true, - preserveScrollOnRefresh : true - }, - listeners : { - cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { - - }, - afterrender : function() { - } - } - }); - return this.panel; -}; + +function SuperpositionGrid(args) { + this.height = null; + this.width = null; + this.id = BUI.id(); + + if (args != null) { + if (args.height != null) { + this.height = args.height; + + if (args.width != null) { + this.width = args.width; + } + } + } + + this.onSelected = new Event(this); +} + +SuperpositionGrid.prototype._prepareData = function(data) { + return data; +}; + +SuperpositionGrid.prototype.refresh = function(subtractions) { + var data = []; + if (subtractions != null){ + if (subtractions.length > 0){ + for (j in subtractions){ + var subtraction = subtractions[j]; + if (subtraction.superposition3VOs != null){ + for (i in subtraction.superposition3VOs){ + data.push(subtraction.superposition3VOs[i]); + } + } + } + } + } + this.store.loadData(data); +}; + +SuperpositionGrid.prototype.getPanel = function() { + var _this = this; + this.store = Ext.create('Ext.data.Store', { + fields : [ 'abinitioModelPdbFilePath', 'aprioriPdbFilePath', 'alignedPdbFilePath' ], + data : [] + }); + + this.selModel = Ext.create('Ext.selection.RowModel', { + allowDeselect : true, + mode : 'MULTI', + listeners : { + selectionchange : function(sm, selections) { + var selected = []; + for (var i = 0; i < selections.length; i++) { + selected.push(selections[i].raw); + } + _this.onSelected.notify(selected); + } + } + }); + + this.store.sort([ { + property : 'name', + direction : 'ASC' + } ]); + + this.panel = Ext.create('Ext.grid.Panel', { + store : this.store, + selModel : this.selModel, + width : this.width, + height : this.height, + margin : 10, + deferredRender : false, + columns : [ { + text : 'Abinitio', + dataIndex : 'abinitioModelPdbFilePath', + hidden : false, + flex : 1, + renderer : function(val){ + return BUI.getFileName(val); + } + }, { + text : 'Apriori', + dataIndex : 'aprioriPdbFilePath', + hidden : false, + flex : 1, + renderer : function(val){ + return BUI.getFileName(val); + } + }, { + text : 'Aligned', + dataIndex : 'alignedPdbFilePath', + hidden : false, + flex : 1, + renderer : function(val){ + return BUI.getFileName(val); + } + } ], + + viewConfig : { + enableTextSelection : true, + preserveScrollOnRefresh : true + }, + listeners : { + cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + + }, + afterrender : function() { + } + } + }); + return this.panel; +}; /** * See ExperimentGrid @@ -26158,502 +26158,502 @@ ConcentrationHTMLTableWidget.prototype.input = function() { "values" : [] } } - } ] - }, - "concentrationLabel" : "7.17 mg/ml ,3.53 mg/ml ,1.75 mg/ml ,0.91 mg/ml " + } ] + }, + "concentrationLabel" : "7.17 mg/ml ,3.53 mg/ml ,1.75 mg/ml ,0.91 mg/ml " + } + }; +}; + +ConcentrationHTMLTableWidget.prototype.test = function(targetId) { + var concentrationHTMLTableWidget = new ConcentrationHTMLTableWidget(); + document.getElementById(targetId).innerHTML = concentrationHTMLTableWidget.getPanel(concentrationHTMLTableWidget.input().data); +}; + +function DataCollectionWidget() { + +}; + +DataCollectionWidget.prototype.refresh = function(subtractionId) { + +}; + +DataCollectionWidget.prototype.getMacromolecule = function(data) { + for (var i = 0; i < data.length; i++) { + if (data[i].macromoleculeId != null) { + return BIOSAXS.proposal.getMacromoleculeById(data[i].macromoleculeId); + } + } + return null; +}; + +DataCollectionWidget.prototype.getMacromoleculeContainer = function(data) { + var macromolecule = this.getMacromolecule(data); + + var disabled = false; + if (macromolecule == null) { + disabled = true; + } + + var acronym = (macromolecule == null ? "" : macromolecule.acronym); + var mm = (macromolecule == null ? "" : macromolecule.molecularMass); + var comments = (macromolecule == null ? "" : macromolecule.comments); + + return Ext.create('Ext.Panel', { + width : 500, + height : 200, + disabled : disabled, + title : "Macromolecule", + layout : 'form', + bodyPadding : 5, + defaultType : 'textfield', + tbar : { + defaultButtonUI : 'default', + items : [ { + xtype : 'button', + text : 'Edit', + cls : 'btn-with-border', + handler : function() { + var macromoleculeWindow = new MacromoleculeWindow(); + macromoleculeWindow.draw(macromolecule); + /** TODO: update when save **/ + } + } ] + }, + items : [ { + fieldLabel : 'Acronym', + name : 'first', + readOnly : true, + value : acronym + }, { + fieldLabel : 'Molecular Mass', + name : 'last', + readOnly : true, + value : mm + }, { + xtype : 'textareafield', + fieldLabel : 'Comments', + value : '', + name : 'last', + readOnly : true, + value : comments + } ] + }); +}; + +DataCollectionWidget.prototype.getBufferContainer = function(data) { + var _this = this; + var buffer = BIOSAXS.proposal.getBufferById(data[0].bufferId); + + return Ext.create('Ext.Panel', { + width : 500, + height : 200, + title : "Buffer", + layout : 'form', + // collapsed : true, + // collapsible : true, + bodyPadding : 5, + defaultType : 'textfield', + style : { + padding : '0px 0px 0px 2px' + }, + tbar : { + defaultButtonUI : 'default', + items : [ { + xtype : 'button', + text : 'Edit', + cls : 'btn-with-border', + handler : function() { + var bufferWindow = new BufferWindow(); + bufferWindow.draw(BIOSAXS.proposal.getBufferById(data[0].bufferId)); + /** TODO: update when save **/ + } + + } ] + }, + items : [ { + fieldLabel : 'Buffer', + name : 'acronym', + readOnly : true, + value : buffer.acronym + }, { + fieldLabel : 'Composition', + name : 'last', + readOnly : true, + value : buffer.composition + }, { + xtype : 'textareafield', + fieldLabel : 'Comments', + value : buffer.comments, + name : 'last', + readOnly : true + + } ] + }); +}; + +DataCollectionWidget.prototype.getSpecimenContainer = function(data) { + return Ext.create('Ext.container.Container', { + layout : { + type : 'hbox' + }, + style : { + margin : '10px 10px 10px 10px' + }, + border : 0, + style : { + borderColor : '#000000', + borderStyle : 'solid', + borderWidth : '1px' + }, + defaults : { + labelWidth : 80, + // implicitly create Container by specifying xtype + xtype : 'datefield', + flex : 1, + + }, + items : [ this.getMacromoleculeContainer(data), this.getBufferContainer(data) ] + }); +}; + +DataCollectionWidget.prototype.getSeparator = function() { + return { + html : "
", + border : 0 + } +}; + +DataCollectionWidget.prototype.getFitStructurePanel = function(data) { + var _this = this; + + return Ext.create('Ext.container.Container', { + layout : { + type : 'hbox' + }, + style : { + margin : '10px 10px 10px 10px' + }, + border : 0, + style : { + borderColor : '#000000', + borderStyle : 'solid', + borderWidth : '1px' + }, + defaults : { + labelWidth : 80, + xtype : 'datefield', + flex : 1 + }, + items : [ + + ] + }); +}; + +DataCollectionWidget.prototype.getFitStructurePanelWorkflow = function(data) { + var _this = this; + var macromolecule = this.getMacromolecule(data); + + var items = []; + + /** Adding all the pdb files linked to the macromolecule **/ + // if (macromolecule.structure3VOs != null) { + // if (macromolecule.structure3VOs.length > 0) { + // + // items.push(); + // } + // } else { + // items.push({ + // html : "No apriori information added to this macromolecule. Apriori information needed for further analysis", + // margin : "5 5 5 5" + // }); + // } + var fitStructureToExperimentDataGrid = new FitStructureToExperimentDataGrid(); + + fitStructureToExperimentDataGrid.refresh(_this.data[0].subtractionId, _this.getMacromolecule(data)); + return Ext.create('Ext.container.Container', { + style : { + margin : '10px 10px 10px 10px' + }, + border : 0, + + style : { + borderColor : '#000000', + borderStyle : 'solid', + borderWidth : '1px' + }, + defaults : { + labelWidth : 80, + xtype : 'datefield', + flex : 1 + }, + items : [ fitStructureToExperimentDataGrid.getPanel() ], + listeners : { + afterrender : function() { + } + } + }); +}; +/** Static method **/ +DataCollectionWidget.prototype.RUNFitScattering = function(subtractionId, structureId) { + var _this = this; + /** Add to Workflow **/ + var adapter = new BiosaxsDataAdapter(); + var workflow = { + 'workflowTitle' : 'FitExperimentalDatatoStructure', + 'comments' : 'FitExperimentalDatatoStructure run from ISPyB for subtractionId: ' + subtractionId + ' and structureId ' + structureId + } + + var inputs = [ { + name : 'subtractionId', + value : subtractionId + }, { + name : 'structureId', + value : structureId + } ]; + + adapter.onSuccess.attach(function(sender, data) { + /** Add to Fit **/ + var adapter2 = new BiosaxsDataAdapter(); + var fit = { + 'workflowId' : data.workflowId, + 'subtractionId' : subtractionId, + 'structureId' : structureId, + 'comments' : 'Sent to workflow engine' + } + adapter2.onSuccess.attach(function(sender, fit) { + + }); + adapter2.addFitStructureData(fit); + + }); + adapter.addWorkflow(workflow, inputs); + +}; + +DataCollectionWidget.prototype.getSuperpositionTab = function(data) { + var _this = this; + + this.superpositionGrid = new SuperpositionGrid(); + + return Ext.create('Ext.container.Container', { + style : { + margin : '10px 10px 10px 10px' + }, + border : 0, + style : { + borderColor : '#000000', + borderStyle : 'solid', + borderWidth : '1px' + }, + items : [ _this.superpositionGrid.getPanel() ], + listeners : { + cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + + }, + afterrender : function() { + /** Getting Rigid bodies **/ + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, result) { + _this.superpositionGrid.refresh(result); + }); + adapter.getSuperpositionBySubtractionId(data[1].subtractionId); + } + } + }); +}; + +DataCollectionWidget.prototype.getAdvancedTab = function(data) { + var _this = this; + return Ext.create('Ext.container.Container', { + style : { + margin : '10px 10px 10px 10px' + }, + border : 0, + style : { + borderColor : '#000000', + borderStyle : 'solid', + borderWidth : '1px' + }, + items : [ _this.getFitStructurePanel(data), _this.getFitStructurePanelWorkflow(data) ] + }); +}; + +DataCollectionWidget.prototype.getRigiBodyForm = function(data) { + var _this = this; + _this.rigiBodyGrid = new RigidModelGrid(); + return _this.rigiBodyGrid.getPanel(); + +}; + +DataCollectionWidget.prototype.getRigidBodyModelingTab = function(data) { + var _this = this; + return Ext.create('Ext.container.Container', { + style : { + margin : '10px 10px 10px 10px' + }, + border : 0, + style : { + borderColor : '#000000', + borderStyle : 'solid', + borderWidth : '1px' + }, + items : [ _this.getRigiBodyForm(data) ], + listeners : { + cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + + }, + afterrender : function() { + /** Getting Rigid bodies **/ + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, result) { + _this.rigiBodyGrid.refresh(result); + }); + adapter.getRigidBodyModelingBySubtractionId(data[1].subtractionId); + } + } + }); +}; + +DataCollectionWidget.prototype.getTabs = function(data) { + var _this = this; + + this.subtractionCurveVisualizer = new SubtractionCurveVisualizer(); + + this.tabs = Ext.createWidget('tabpanel', { + activeTab : 1, + plain : true, + defaults : { + autoScroll : true, + bodyPadding : 10 + }, + items : [ { + title : 'Solution', + items : [ _this.getSpecimenContainer(data) ] + }, { + title : 'Primary Data Reduction', + active : true, + tabConfig : { + xtype : 'tab', + margins : '0 0 0 20', + }, + items : [ _this.subtractionCurveVisualizer.getPanel() ] + }, { + title : 'Abinitio', + items : [ _this.getAbinitioModellingContainer(data), _this.getSeparator(), _this.getModelViz(data) ] + }, { + title : 'Mixtures', + items : [ _this.getAdvancedTab(data) ] + }, { + title : 'Superpositions', + items : [ _this.getSuperpositionTab(data) ] + }, { + title : 'Rigid Body Modeling', + items : [ _this.getRigidBodyModelingTab(data) ] + } ] + }); + return this.tabs; +}; + +DataCollectionWidget.prototype.getPanel = function(data) { + var _this = this; + _this.data = data; + this.panel = Ext.create('Ext.container.Container', { + width : 1000, + layout : { + type : 'vbox', + align : 'stretch', + padding : 5 + }, + items : [ _this.getTabs(data) + + ] + }); + + this.panel.on("afterrender", function() { + _this.subtractionCurveVisualizer.refresh([ _this.data[1].mergeId ], [ _this.data[1].subtractionId ]); + }); + return this.panel; +}; + +DataCollectionWidget.prototype.getModelViz = function(data) { + this.modelViz = new ModelVisualizerForm({ + height : 800, + width : 1000 + }); + return this.modelViz.getPanel(); +}; + +/** + * PRIMARY DATA PROCESSING + */ +DataCollectionWidget.prototype.getPrimaryDataProcessingContainer = function(data) { + var _this = this; + this.primaryDataProcessingGrid = new PrimaryDataProcessingGrid({ + height : 250, + width : 1000, + title : 'Primary Data Processing' + }); + + this.primaryDataProcessingGrid.onSelected.attach(function(sender, selected) { + /** Refresh tabs **/ + var averagesId = []; + var subtractionIds = []; + + var subtractionKeys = {}; + for (var i = 0; i < selected.length; i++) { + if (selected[i].mergeId != null) { + averagesId.push(selected[i].mergeId); + } + + if (selected[i].subtractionId != null) { + if (selected[i].macromoleculeId != null) { + if (subtractionKeys[selected[i].subtractionId] == null) { + subtractionIds.push(selected[i].subtractionId); + } + subtractionKeys[selected[i].subtractionId] = true; + } + } } - }; + + /** Refreshing Primary Data Reduction **/ + _this.subtractionCurveVisualizer.refresh(averagesId, subtractionIds); + + }); + return this.primaryDataProcessingGrid.getPanel(data); }; +/** + * getAbinitioModellingContainer + */ +DataCollectionWidget.prototype.getAbinitioModellingContainer = function(data) { + var _this = this; -ConcentrationHTMLTableWidget.prototype.test = function(targetId) { - var concentrationHTMLTableWidget = new ConcentrationHTMLTableWidget(); - document.getElementById(targetId).innerHTML = concentrationHTMLTableWidget.getPanel(concentrationHTMLTableWidget.input().data); + this.abinitioGrid = new AbinitioGrid(); + this.abinitioGrid.onSelected.attach(function(sender, models) { + _this.modelViz.refresh(models); + + }); + /** It may be abinitio models linked to the buffers **/ + var abinitioIdList = []; + for (var i = 0; i < data.length; i++) { + abinitioIdList.push(data[i].abInitioId); + } + + var uniqueIds = []; + $.each(abinitioIdList, function(i, el) { + if ($.inArray(el, uniqueIds) === -1) + uniqueIds.push(el); + }); + + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, data) { + _this.abinitioGrid.refresh(data); + }); + adapter.getAbinitioByIdsList(uniqueIds); + return this.abinitioGrid.getPanel([]); }; -function DataCollectionWidget() { - -}; - -DataCollectionWidget.prototype.refresh = function(subtractionId) { - -}; - -DataCollectionWidget.prototype.getMacromolecule = function(data) { - for (var i = 0; i < data.length; i++) { - if (data[i].macromoleculeId != null) { - return BIOSAXS.proposal.getMacromoleculeById(data[i].macromoleculeId); - } - } - return null; -}; - -DataCollectionWidget.prototype.getMacromoleculeContainer = function(data) { - var macromolecule = this.getMacromolecule(data); - - var disabled = false; - if (macromolecule == null) { - disabled = true; - } - - var acronym = (macromolecule == null ? "" : macromolecule.acronym); - var mm = (macromolecule == null ? "" : macromolecule.molecularMass); - var comments = (macromolecule == null ? "" : macromolecule.comments); - - return Ext.create('Ext.Panel', { - width : 500, - height : 200, - disabled : disabled, - title : "Macromolecule", - layout : 'form', - bodyPadding : 5, - defaultType : 'textfield', - tbar : { - defaultButtonUI : 'default', - items : [ { - xtype : 'button', - text : 'Edit', - cls : 'btn-with-border', - handler : function() { - var macromoleculeWindow = new MacromoleculeWindow(); - macromoleculeWindow.draw(macromolecule); - /** TODO: update when save **/ - } - } ] - }, - items : [ { - fieldLabel : 'Acronym', - name : 'first', - readOnly : true, - value : acronym - }, { - fieldLabel : 'Molecular Mass', - name : 'last', - readOnly : true, - value : mm - }, { - xtype : 'textareafield', - fieldLabel : 'Comments', - value : '', - name : 'last', - readOnly : true, - value : comments - } ] - }); -}; - -DataCollectionWidget.prototype.getBufferContainer = function(data) { - var _this = this; - var buffer = BIOSAXS.proposal.getBufferById(data[0].bufferId); - - return Ext.create('Ext.Panel', { - width : 500, - height : 200, - title : "Buffer", - layout : 'form', - // collapsed : true, - // collapsible : true, - bodyPadding : 5, - defaultType : 'textfield', - style : { - padding : '0px 0px 0px 2px' - }, - tbar : { - defaultButtonUI : 'default', - items : [ { - xtype : 'button', - text : 'Edit', - cls : 'btn-with-border', - handler : function() { - var bufferWindow = new BufferWindow(); - bufferWindow.draw(BIOSAXS.proposal.getBufferById(data[0].bufferId)); - /** TODO: update when save **/ - } - - } ] - }, - items : [ { - fieldLabel : 'Buffer', - name : 'acronym', - readOnly : true, - value : buffer.acronym - }, { - fieldLabel : 'Composition', - name : 'last', - readOnly : true, - value : buffer.composition - }, { - xtype : 'textareafield', - fieldLabel : 'Comments', - value : buffer.comments, - name : 'last', - readOnly : true - - } ] - }); -}; - -DataCollectionWidget.prototype.getSpecimenContainer = function(data) { - return Ext.create('Ext.container.Container', { - layout : { - type : 'hbox' - }, - style : { - margin : '10px 10px 10px 10px' - }, - border : 0, - style : { - borderColor : '#000000', - borderStyle : 'solid', - borderWidth : '1px' - }, - defaults : { - labelWidth : 80, - // implicitly create Container by specifying xtype - xtype : 'datefield', - flex : 1, - - }, - items : [ this.getMacromoleculeContainer(data), this.getBufferContainer(data) ] - }); -}; - -DataCollectionWidget.prototype.getSeparator = function() { - return { - html : "
", - border : 0 - } -}; - -DataCollectionWidget.prototype.getFitStructurePanel = function(data) { - var _this = this; - - return Ext.create('Ext.container.Container', { - layout : { - type : 'hbox' - }, - style : { - margin : '10px 10px 10px 10px' - }, - border : 0, - style : { - borderColor : '#000000', - borderStyle : 'solid', - borderWidth : '1px' - }, - defaults : { - labelWidth : 80, - xtype : 'datefield', - flex : 1 - }, - items : [ - - ] - }); -}; - -DataCollectionWidget.prototype.getFitStructurePanelWorkflow = function(data) { - var _this = this; - var macromolecule = this.getMacromolecule(data); - - var items = []; - - /** Adding all the pdb files linked to the macromolecule **/ - // if (macromolecule.structure3VOs != null) { - // if (macromolecule.structure3VOs.length > 0) { - // - // items.push(); - // } - // } else { - // items.push({ - // html : "No apriori information added to this macromolecule. Apriori information needed for further analysis", - // margin : "5 5 5 5" - // }); - // } - var fitStructureToExperimentDataGrid = new FitStructureToExperimentDataGrid(); - - fitStructureToExperimentDataGrid.refresh(_this.data[0].subtractionId, _this.getMacromolecule(data)); - return Ext.create('Ext.container.Container', { - style : { - margin : '10px 10px 10px 10px' - }, - border : 0, - - style : { - borderColor : '#000000', - borderStyle : 'solid', - borderWidth : '1px' - }, - defaults : { - labelWidth : 80, - xtype : 'datefield', - flex : 1 - }, - items : [ fitStructureToExperimentDataGrid.getPanel() ], - listeners : { - afterrender : function() { - } - } - }); -}; -/** Static method **/ -DataCollectionWidget.prototype.RUNFitScattering = function(subtractionId, structureId) { - var _this = this; - /** Add to Workflow **/ - var adapter = new BiosaxsDataAdapter(); - var workflow = { - 'workflowTitle' : 'FitExperimentalDatatoStructure', - 'comments' : 'FitExperimentalDatatoStructure run from ISPyB for subtractionId: ' + subtractionId + ' and structureId ' + structureId - } - - var inputs = [ { - name : 'subtractionId', - value : subtractionId - }, { - name : 'structureId', - value : structureId - } ]; - - adapter.onSuccess.attach(function(sender, data) { - /** Add to Fit **/ - var adapter2 = new BiosaxsDataAdapter(); - var fit = { - 'workflowId' : data.workflowId, - 'subtractionId' : subtractionId, - 'structureId' : structureId, - 'comments' : 'Sent to workflow engine' - } - adapter2.onSuccess.attach(function(sender, fit) { - - }); - adapter2.addFitStructureData(fit); - - }); - adapter.addWorkflow(workflow, inputs); - -}; - -DataCollectionWidget.prototype.getSuperpositionTab = function(data) { - var _this = this; - - this.superpositionGrid = new SuperpositionGrid(); - - return Ext.create('Ext.container.Container', { - style : { - margin : '10px 10px 10px 10px' - }, - border : 0, - style : { - borderColor : '#000000', - borderStyle : 'solid', - borderWidth : '1px' - }, - items : [ _this.superpositionGrid.getPanel() ], - listeners : { - cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { - - }, - afterrender : function() { - /** Getting Rigid bodies **/ - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, result) { - _this.superpositionGrid.refresh(result); - }); - adapter.getSuperpositionBySubtractionId(data[1].subtractionId); - } - } - }); -}; - -DataCollectionWidget.prototype.getAdvancedTab = function(data) { - var _this = this; - return Ext.create('Ext.container.Container', { - style : { - margin : '10px 10px 10px 10px' - }, - border : 0, - style : { - borderColor : '#000000', - borderStyle : 'solid', - borderWidth : '1px' - }, - items : [ _this.getFitStructurePanel(data), _this.getFitStructurePanelWorkflow(data) ] - }); -}; - -DataCollectionWidget.prototype.getRigiBodyForm = function(data) { - var _this = this; - _this.rigiBodyGrid = new RigidModelGrid(); - return _this.rigiBodyGrid.getPanel(); - -}; - -DataCollectionWidget.prototype.getRigidBodyModelingTab = function(data) { - var _this = this; - return Ext.create('Ext.container.Container', { - style : { - margin : '10px 10px 10px 10px' - }, - border : 0, - style : { - borderColor : '#000000', - borderStyle : 'solid', - borderWidth : '1px' - }, - items : [ _this.getRigiBodyForm(data) ], - listeners : { - cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { - - }, - afterrender : function() { - /** Getting Rigid bodies **/ - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, result) { - _this.rigiBodyGrid.refresh(result); - }); - adapter.getRigidBodyModelingBySubtractionId(data[1].subtractionId); - } - } - }); -}; - -DataCollectionWidget.prototype.getTabs = function(data) { - var _this = this; - - this.subtractionCurveVisualizer = new SubtractionCurveVisualizer(); - - this.tabs = Ext.createWidget('tabpanel', { - activeTab : 1, - plain : true, - defaults : { - autoScroll : true, - bodyPadding : 10 - }, - items : [ { - title : 'Solution', - items : [ _this.getSpecimenContainer(data) ] - }, { - title : 'Primary Data Reduction', - active : true, - tabConfig : { - xtype : 'tab', - margins : '0 0 0 20', - }, - items : [ _this.subtractionCurveVisualizer.getPanel() ] - }, { - title : 'Abinitio', - items : [ _this.getAbinitioModellingContainer(data), _this.getSeparator(), _this.getModelViz(data) ] - }, { - title : 'Mixtures', - items : [ _this.getAdvancedTab(data) ] - }, { - title : 'Superpositions', - items : [ _this.getSuperpositionTab(data) ] - }, { - title : 'Rigid Body Modeling', - items : [ _this.getRigidBodyModelingTab(data) ] - } ] - }); - return this.tabs; -}; - -DataCollectionWidget.prototype.getPanel = function(data) { - var _this = this; - _this.data = data; - this.panel = Ext.create('Ext.container.Container', { - width : 1000, - layout : { - type : 'vbox', - align : 'stretch', - padding : 5 - }, - items : [ _this.getTabs(data) - - ] - }); - - this.panel.on("afterrender", function() { - _this.subtractionCurveVisualizer.refresh([ _this.data[1].mergeId ], [ _this.data[1].subtractionId ]); - }); - return this.panel; -}; - -DataCollectionWidget.prototype.getModelViz = function(data) { - this.modelViz = new ModelVisualizerForm({ - height : 800, - width : 1000 - }); - return this.modelViz.getPanel(); -}; - -/** - * PRIMARY DATA PROCESSING - */ -DataCollectionWidget.prototype.getPrimaryDataProcessingContainer = function(data) { - var _this = this; - this.primaryDataProcessingGrid = new PrimaryDataProcessingGrid({ - height : 250, - width : 1000, - title : 'Primary Data Processing' - }); - - this.primaryDataProcessingGrid.onSelected.attach(function(sender, selected) { - /** Refresh tabs **/ - var averagesId = []; - var subtractionIds = []; - - var subtractionKeys = {}; - for (var i = 0; i < selected.length; i++) { - if (selected[i].mergeId != null) { - averagesId.push(selected[i].mergeId); - } - - if (selected[i].subtractionId != null) { - if (selected[i].macromoleculeId != null) { - if (subtractionKeys[selected[i].subtractionId] == null) { - subtractionIds.push(selected[i].subtractionId); - } - subtractionKeys[selected[i].subtractionId] = true; - } - } - } - - /** Refreshing Primary Data Reduction **/ - _this.subtractionCurveVisualizer.refresh(averagesId, subtractionIds); - - }); - return this.primaryDataProcessingGrid.getPanel(data); -}; -/** - * getAbinitioModellingContainer - */ -DataCollectionWidget.prototype.getAbinitioModellingContainer = function(data) { - var _this = this; - - this.abinitioGrid = new AbinitioGrid(); - this.abinitioGrid.onSelected.attach(function(sender, models) { - _this.modelViz.refresh(models); - - }); - /** It may be abinitio models linked to the buffers **/ - var abinitioIdList = []; - for (var i = 0; i < data.length; i++) { - abinitioIdList.push(data[i].abInitioId); - } - - var uniqueIds = []; - $.each(abinitioIdList, function(i, el) { - if ($.inArray(el, uniqueIds) === -1) - uniqueIds.push(el); - }); - - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, data) { - _this.abinitioGrid.refresh(data); - }); - adapter.getAbinitioByIdsList(uniqueIds); - return this.abinitioGrid.getPanel([]); -}; - /** * Edit the information of a buffer * @@ -29163,258 +29163,258 @@ ShippingWidget.prototype.refresh = function() { }; -/** - * Widget container of Specimen grid and samplePlate widget - * Depending of the sample changer layout it may be displayed vertically or horizontally - * - * @param args - * - * #onExperimentChanged It happens when specimen are modified - */ -function SpecimenWidget(args){ - - this.width = 1000; - this.height = 600; - - if (args != null){ - if (args.width != null){ - this.width = args.width; - } - if (args.height != null){ - this.height = args.height; - } - } - - var _this = this; - - /** Specimen Grid **/ - this.specimenGrid = new SpecimenGrid({ - minHeight : 425, - selectionMode : "SINGLE", - editEnabled : false, - updateRowEnabled : true, - width : 900, - showTitle : false - }); - - - this.specimenGrid.onSpecimenChanged.attach(function(sender, specimen) { - _this.experiment.setSpecimenById(specimen); - _this.refresh(_this.experiment); - }); - - this.specimenGrid.onSelected.attach(function(sender, specimens) { - if (specimens.length > 0) { - _this.specimenSelected = specimens[0]; - } else { - _this.specimenSelected = null; - } - _this.samplePlateGroupWidget.selectSpecimens(specimens); - }); - - - /** Sample plate Widget **/ - this.samplePlateGroupWidget = new SamplePlateGroupWidget({ - showTitle : false, - height : 250, - margin : 5, - bbar : true - }); - - - this.samplePlateGroupWidget.onExperimentChanged.attach(function(sender, json) { - _this.refresh(new Experiment(json)); - }); - - this.samplePlateGroupWidget.onClick.attach(function(sender, args) { - /** Clicking on a plate * */ - var row = args.row; - var column = args.column; - var samplePlateId = args.samplePlate.samplePlateId; - - /** is specimen selected on the grid? * */ - if (_this.specimenSelected != null) { - /** Is position target empty * */ - if (_this.experiment.getSampleByPosition(args.samplePlate.samplePlateId, args.row, args.column).length == 0) { - var specimen = _this.experiment.getSampleById(_this.specimenSelected.specimenId); - if (specimen.sampleplateposition3VO == null) { - specimen.sampleplateposition3VO = {}; - } - - specimen.sampleplateposition3VO = { - columnNumber : column, - rowNumber : row, - samplePlateId : samplePlateId - }; - - _this.samplePlateGroupWidget.panel.setLoading("ISPyB: Saving specimen"); - var adapter = new BiosaxsDataAdapter(); - /** If success * */ - adapter.onSuccess.attach(function(sender, experiment) { - _this.samplePlateGroupWidget.panel.setLoading(false); - }); - - adapter.onError.attach(function(sender, error) { - _this.samplePlateGroupWidget.panel.setLoading(false); - showError(error); - }); - - adapter.saveSpecimen(specimen, _this.experiment); - - _this.samplePlateGroupWidget.refresh(_this.experiment); - _this.specimenGrid.refresh(_this.experiment); - //_this.refresh(_this.experiment); - _this.specimenGrid.deselectAll(); - - } else { - /** - * Can we merge? We can merge when specimen are the - * same. So, same buffer, macromolecule, concentration * - */ - var target = _this.experiment.getSampleByPosition(args.samplePlate.samplePlateId, args.row, args.column)[0]; - var specimen = _this.experiment.getSampleById(_this.specimenSelected.specimenId); - - if ((specimen.bufferId == target.bufferId) && (specimen.concentration == target.concentration)) { - if (((specimen.macromolecule3VO != null) && (target.macromolecule3VO != null) && (specimen.macromolecule3VO.macromoleculeId == target.macromolecule3VO.macromoleculeId)) || - ((specimen.macromolecule3VO == null) && (target.macromolecule3VO == null))) { - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, data) { - _this.refresh(new Experiment(data)); - _this.samplePlateGroupWidget.panel.setLoading(false); - - _this.onExperimentChanged.notify(experiment); - }); - adapter.onError.attach(function(sender, error) { - _this.samplePlateGroupWidget.panel.setLoading(false); - showError(error); - }); - _this.samplePlateGroupWidget.panel.setLoading("ISPyB: Merging specimens"); - adapter.mergeSpecimens(specimen.specimenId, target.specimenId); - } - } else { - alert("Well is not empty. Select another well!"); - } - } - } else { - var specimen = _this.experiment.getSampleByPosition(args.samplePlate.samplePlateId, args.row, args.column)[0]; - if (specimen != null) { - _this.specimenGrid.selectById(specimen.specimenId); - } - } - }); - - /** Events **/ - this.onExperimentChanged = new Event(this); -}; - -/** - * Return vbox or hbox depending on the slot positions of the plates - */ -SpecimenWidget.prototype.getContainerLayoutConfiguration = function(experiment){ - var dimensions = this.samplePlateGroupWidget.getDimensions(experiment.getSamplePlates()); - if (dimensions.maxSlotPositionRow < dimensions.maxSlotPositionColumn){ - return { - layout : "vbox", - specimenGridWidth : this.width - 10, - specimenGridHeight : this.height - 260, - samplePlateGroupWidth : this.width - 10, - samplePlateGroupHeight : 250 - }; - } - return { - layout : "hbox", - samplePlateGroupWidth : this.width*1/3 -10, - samplePlateGroupHeight : this.height - 10, - specimenGridWidth : this.width*2/3, - specimenGridHeight : this.height - 10 - }; - -}; - - -SpecimenWidget.prototype.refresh = function(experiment){ - this.experiment = experiment; - - /** Removing all components **/ - this.panel.removeAll(); - - var layoutConfiguration = this.getContainerLayoutConfiguration(experiment); - - /** Setting new width and height for layout vbox and hbox **/ - this.specimenGrid.width = layoutConfiguration.specimenGridWidth; - this.specimenGrid.height = layoutConfiguration.specimenGridHeight; - - this.samplePlateGroupWidget.width = layoutConfiguration.samplePlateGroupWidth; - this.samplePlateGroupWidget.height = layoutConfiguration.samplePlateGroupHeight; - - if (layoutConfiguration.layout == "hbox"){ - this.specimenGrid.margin = "0 0 0 5"; - this.specimenGrid.width =this.specimenGrid.width - 5; - } - /** Insert container depending on layout [vertical|horizontal] */ - var container = Ext.create('Ext.container.Container', { - layout : layoutConfiguration.layout, - height : this.height, - width : this.width, - padding : '2px', - items : [ ] - }); - if (layoutConfiguration.layout == "vbox"){ - container.insert(this.specimenGrid.getPanel()); - container.insert(this.samplePlateGroupWidget.getPanel()); - } - else{ - container.insert(this.samplePlateGroupWidget.getPanel()); - container.insert(this.specimenGrid.getPanel()); - } - - /** Insert Widget **/ - this.panel.insert(container); - - /** Load data **/ - this.specimenGrid.refresh(experiment); - this.samplePlateGroupWidget.refresh(experiment); - - -}; - -/** It creates a dummy container to be inserted the plates once the method refresh has been called - * This is necessay because we can not know the sample changer layout before hand - * **/ -SpecimenWidget.prototype.getPanel = function(){ - this.panel = Ext.create('Ext.container.Container', { - layout : 'vbox', - height : this.height, - border : 0, - margin : 5, - width : this.width, - items : [] - }); - return this.panel; -}; - - -SpecimenWidget.prototype.input = function() { - return { - experiment : DATADOC.getExperiment_10(), - proposal : DATADOC.getProposal_10() - }; -}; - -SpecimenWidget.prototype.test = function(targetId) { - var specimenWidget = new SpecimenWidget({ - height : 500, - width : 1000 - }); - BIOSAXS.proposal = new Proposal(specimenWidget.input().proposal); - var experiment = new Experiment(specimenWidget.input().experiment); - var panel = specimenWidget.getPanel(); - panel.render(targetId); - specimenWidget.refresh(experiment); - -}; - - +/** + * Widget container of Specimen grid and samplePlate widget + * Depending of the sample changer layout it may be displayed vertically or horizontally + * + * @param args + * + * #onExperimentChanged It happens when specimen are modified + */ +function SpecimenWidget(args){ + + this.width = 1000; + this.height = 600; + + if (args != null){ + if (args.width != null){ + this.width = args.width; + } + if (args.height != null){ + this.height = args.height; + } + } + + var _this = this; + + /** Specimen Grid **/ + this.specimenGrid = new SpecimenGrid({ + minHeight : 425, + selectionMode : "SINGLE", + editEnabled : false, + updateRowEnabled : true, + width : 900, + showTitle : false + }); + + + this.specimenGrid.onSpecimenChanged.attach(function(sender, specimen) { + _this.experiment.setSpecimenById(specimen); + _this.refresh(_this.experiment); + }); + + this.specimenGrid.onSelected.attach(function(sender, specimens) { + if (specimens.length > 0) { + _this.specimenSelected = specimens[0]; + } else { + _this.specimenSelected = null; + } + _this.samplePlateGroupWidget.selectSpecimens(specimens); + }); + + + /** Sample plate Widget **/ + this.samplePlateGroupWidget = new SamplePlateGroupWidget({ + showTitle : false, + height : 250, + margin : 5, + bbar : true + }); + + + this.samplePlateGroupWidget.onExperimentChanged.attach(function(sender, json) { + _this.refresh(new Experiment(json)); + }); + + this.samplePlateGroupWidget.onClick.attach(function(sender, args) { + /** Clicking on a plate * */ + var row = args.row; + var column = args.column; + var samplePlateId = args.samplePlate.samplePlateId; + + /** is specimen selected on the grid? * */ + if (_this.specimenSelected != null) { + /** Is position target empty * */ + if (_this.experiment.getSampleByPosition(args.samplePlate.samplePlateId, args.row, args.column).length == 0) { + var specimen = _this.experiment.getSampleById(_this.specimenSelected.specimenId); + if (specimen.sampleplateposition3VO == null) { + specimen.sampleplateposition3VO = {}; + } + + specimen.sampleplateposition3VO = { + columnNumber : column, + rowNumber : row, + samplePlateId : samplePlateId + }; + + _this.samplePlateGroupWidget.panel.setLoading("ISPyB: Saving specimen"); + var adapter = new BiosaxsDataAdapter(); + /** If success * */ + adapter.onSuccess.attach(function(sender, experiment) { + _this.samplePlateGroupWidget.panel.setLoading(false); + }); + + adapter.onError.attach(function(sender, error) { + _this.samplePlateGroupWidget.panel.setLoading(false); + showError(error); + }); + + adapter.saveSpecimen(specimen, _this.experiment); + + _this.samplePlateGroupWidget.refresh(_this.experiment); + _this.specimenGrid.refresh(_this.experiment); + //_this.refresh(_this.experiment); + _this.specimenGrid.deselectAll(); + + } else { + /** + * Can we merge? We can merge when specimen are the + * same. So, same buffer, macromolecule, concentration * + */ + var target = _this.experiment.getSampleByPosition(args.samplePlate.samplePlateId, args.row, args.column)[0]; + var specimen = _this.experiment.getSampleById(_this.specimenSelected.specimenId); + + if ((specimen.bufferId == target.bufferId) && (specimen.concentration == target.concentration)) { + if (((specimen.macromolecule3VO != null) && (target.macromolecule3VO != null) && (specimen.macromolecule3VO.macromoleculeId == target.macromolecule3VO.macromoleculeId)) || + ((specimen.macromolecule3VO == null) && (target.macromolecule3VO == null))) { + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, data) { + _this.refresh(new Experiment(data)); + _this.samplePlateGroupWidget.panel.setLoading(false); + + _this.onExperimentChanged.notify(experiment); + }); + adapter.onError.attach(function(sender, error) { + _this.samplePlateGroupWidget.panel.setLoading(false); + showError(error); + }); + _this.samplePlateGroupWidget.panel.setLoading("ISPyB: Merging specimens"); + adapter.mergeSpecimens(specimen.specimenId, target.specimenId); + } + } else { + alert("Well is not empty. Select another well!"); + } + } + } else { + var specimen = _this.experiment.getSampleByPosition(args.samplePlate.samplePlateId, args.row, args.column)[0]; + if (specimen != null) { + _this.specimenGrid.selectById(specimen.specimenId); + } + } + }); + + /** Events **/ + this.onExperimentChanged = new Event(this); +}; + +/** + * Return vbox or hbox depending on the slot positions of the plates + */ +SpecimenWidget.prototype.getContainerLayoutConfiguration = function(experiment){ + var dimensions = this.samplePlateGroupWidget.getDimensions(experiment.getSamplePlates()); + if (dimensions.maxSlotPositionRow < dimensions.maxSlotPositionColumn){ + return { + layout : "vbox", + specimenGridWidth : this.width - 10, + specimenGridHeight : this.height - 260, + samplePlateGroupWidth : this.width - 10, + samplePlateGroupHeight : 250 + }; + } + return { + layout : "hbox", + samplePlateGroupWidth : this.width*1/3 -10, + samplePlateGroupHeight : this.height - 10, + specimenGridWidth : this.width*2/3, + specimenGridHeight : this.height - 10 + }; + +}; + + +SpecimenWidget.prototype.refresh = function(experiment){ + this.experiment = experiment; + + /** Removing all components **/ + this.panel.removeAll(); + + var layoutConfiguration = this.getContainerLayoutConfiguration(experiment); + + /** Setting new width and height for layout vbox and hbox **/ + this.specimenGrid.width = layoutConfiguration.specimenGridWidth; + this.specimenGrid.height = layoutConfiguration.specimenGridHeight; + + this.samplePlateGroupWidget.width = layoutConfiguration.samplePlateGroupWidth; + this.samplePlateGroupWidget.height = layoutConfiguration.samplePlateGroupHeight; + + if (layoutConfiguration.layout == "hbox"){ + this.specimenGrid.margin = "0 0 0 5"; + this.specimenGrid.width =this.specimenGrid.width - 5; + } + /** Insert container depending on layout [vertical|horizontal] */ + var container = Ext.create('Ext.container.Container', { + layout : layoutConfiguration.layout, + height : this.height, + width : this.width, + padding : '2px', + items : [ ] + }); + if (layoutConfiguration.layout == "vbox"){ + container.insert(this.specimenGrid.getPanel()); + container.insert(this.samplePlateGroupWidget.getPanel()); + } + else{ + container.insert(this.samplePlateGroupWidget.getPanel()); + container.insert(this.specimenGrid.getPanel()); + } + + /** Insert Widget **/ + this.panel.insert(container); + + /** Load data **/ + this.specimenGrid.refresh(experiment); + this.samplePlateGroupWidget.refresh(experiment); + + +}; + +/** It creates a dummy container to be inserted the plates once the method refresh has been called + * This is necessay because we can not know the sample changer layout before hand + * **/ +SpecimenWidget.prototype.getPanel = function(){ + this.panel = Ext.create('Ext.container.Container', { + layout : 'vbox', + height : this.height, + border : 0, + margin : 5, + width : this.width, + items : [] + }); + return this.panel; +}; + + +SpecimenWidget.prototype.input = function() { + return { + experiment : DATADOC.getExperiment_10(), + proposal : DATADOC.getProposal_10() + }; +}; + +SpecimenWidget.prototype.test = function(targetId) { + var specimenWidget = new SpecimenWidget({ + height : 500, + width : 1000 + }); + BIOSAXS.proposal = new Proposal(specimenWidget.input().proposal); + var experiment = new Experiment(specimenWidget.input().experiment); + var panel = specimenWidget.getPanel(); + panel.render(targetId); + specimenWidget.refresh(experiment); + +}; + + function WarningWidget(args) { this.actions = []; diff --git a/ispyb-ws/src/main/java/ispyb/ws/rest/mx/AutoprocintegrationRestWebService.java b/ispyb-ws/src/main/java/ispyb/ws/rest/mx/AutoprocintegrationRestWebService.java index cdfe38b5e..4de80758b 100644 --- a/ispyb-ws/src/main/java/ispyb/ws/rest/mx/AutoprocintegrationRestWebService.java +++ b/ispyb-ws/src/main/java/ispyb/ws/rest/mx/AutoprocintegrationRestWebService.java @@ -8,6 +8,8 @@ import ispyb.server.mx.vos.autoproc.AutoProcIntegration3VO; import ispyb.server.mx.vos.autoproc.AutoProcProgram3VO; import ispyb.server.mx.vos.autoproc.AutoProcProgramAttachment3VO; +import ispyb.server.mx.vos.collections.DataCollection3VO; +import ispyb.server.mx.vos.collections.Session3VO; import java.io.File; import java.util.ArrayList; @@ -32,6 +34,7 @@ public class AutoprocintegrationRestWebService extends MXRestWebService { private final static Logger logger = Logger.getLogger(AutoprocintegrationRestWebService.class); + private static final String NOT_ALLOWED = "You don't have access to this resource"; @RolesAllowed({ "User", "Manager", "Industrial", "Localcontact" }) @GET @@ -126,6 +129,13 @@ public Response getAttachments(@PathParam("token") String token, @PathParam("pro } + + + private boolean checkProposalByAutoProcProgramId(int proposalId, int autoProcProgramId) throws NamingException, Exception{ + return this.getSession3Service().findByAutoProcProgramId(autoProcProgramId).getProposalVOId().equals(proposalId); + } + + /** * AutoProcProgramAttachment has not AutoProcProgramId mapped in the EJB object * so it is necessary to keep separately the possible list of ids in order @@ -147,13 +157,19 @@ public Response downloadAttachments( String methodName = "downloadAttachments"; long start = this.logInit(methodName, logger, token, proposal); try { - List ids = this.parseToInteger(autoprocattachmentids); + List autoProcProgramIds = this.parseToInteger(autoprocattachmentids); List> list = new ArrayList>(); HashMap filePaths = new HashMap(); String filename = "download.zip"; - for (Integer id : ids) { - AutoProcProgram3VO autoProcProgram3VO = this.getAutoProcProgram3Service().findByPk(id, true); + for (Integer autoProcProgramId : autoProcProgramIds) { + /** Check that id correspond to the proposal **/ + if (!this.checkProposalByAutoProcProgramId(this.getProposalId(proposal), autoProcProgramId)){ + throw new Exception(NOT_ALLOWED); + } + + + AutoProcProgram3VO autoProcProgram3VO = this.getAutoProcProgram3Service().findByPk(autoProcProgramId, true); /** Prefix for the name of the file and the internal structure if many results are retrieved **/ String prefix = String.format("%s_%s", autoProcProgram3VO.getProcessingPrograms(), autoProcProgram3VO.getAutoProcProgramId()); @@ -164,7 +180,7 @@ public Response downloadAttachments( String filePath = auto.getFilePath() + "/" + auto.getFileName(); if (new File(filePath).exists()){ if (new File(filePath).isFile()){ - if (ids.size() > 1){ + if (autoProcProgramIds.size() > 1){ String zipNameFile = prefix + "/" + auto.getFileName(); filePaths.put(zipNameFile, filePath); } @@ -176,7 +192,7 @@ public Response downloadAttachments( } /** If it is a single result then filename is the name of the program and the ID **/ - if (ids.size() == 1){ + if (autoProcProgramIds.size() == 1){ filename = prefix + ".zip"; } @@ -333,6 +349,10 @@ public Response getXScaleWilson(@PathParam("token") String token, @PathParam("pr } } + private boolean checkProposalByAutoProcProgramAttachmentId(int proposalId, int autoProcProgramAttachmentId) throws NamingException, Exception{ + return this.getSession3Service().findByAutoProcProgramAttachmentId(autoProcProgramAttachmentId).getProposalVOId().equals(proposalId); + } + @RolesAllowed({ "User", "Manager", "Industrial", "Localcontact" }) @GET @Path("{token}/proposal/{proposal}/mx/autoprocintegration/autoprocattachmentid/{autoProcAttachmentId}/download") @@ -343,11 +363,17 @@ public Response downloadAutoProcAttachment(@PathParam("token") String token, @Pa String methodName = "downloadAutoProcAttachment"; long start = this.logInit(methodName, logger, token, proposal); try { - AutoProcProgramAttachment3VO attachment = this.getAutoProcProgramAttachment3Service().findByPk(autoProcAttachmentId); - this.logFinish(methodName, start, logger); - File file = new File(attachment.getFilePath() + "/" + attachment.getFileName()); - this.logFinish(methodName, start, logger); - return this.downloadFileAsAttachment(file.getAbsolutePath()); + /** Checking that attachment is linked to the proposal **/ + if (this.checkProposalByAutoProcProgramAttachmentId(this.getProposalId(proposal), autoProcAttachmentId)){ + AutoProcProgramAttachment3VO attachment = this.getAutoProcProgramAttachment3Service().findByPk(autoProcAttachmentId); + this.logFinish(methodName, start, logger); + File file = new File(attachment.getFilePath() + "/" + attachment.getFileName()); + this.logFinish(methodName, start, logger); + return this.downloadFileAsAttachment(file.getAbsolutePath()); + } + else{ + throw new Exception(NOT_ALLOWED); + } } catch (Exception e) { return this.logError(methodName, e, start, logger); } @@ -360,13 +386,19 @@ public Response downloadAutoProcAttachment(@PathParam("token") String token, @Pa public Response getAutoProcAttachment(@PathParam("token") String token, @PathParam("proposal") String proposal, @PathParam("autoProcAttachmentId") int autoProcAttachmentId) { - String methodName = "downloadAutoProcAttachment"; + String methodName = "getAutoProcAttachment"; long start = this.logInit(methodName, logger, token, proposal); try { - AutoProcProgramAttachment3VO attachment = this.getAutoProcProgramAttachment3Service().findByPk(autoProcAttachmentId); - File file = new File(attachment.getFilePath() + "/" + attachment.getFileName()); - this.logFinish(methodName, start, logger); - return this.downloadFile(file.getAbsolutePath()); + /** Checking that attachment is linked to the proposal **/ + if (this.checkProposalByAutoProcProgramAttachmentId(this.getProposalId(proposal), autoProcAttachmentId)){ + AutoProcProgramAttachment3VO attachment = this.getAutoProcProgramAttachment3Service().findByPk(autoProcAttachmentId); + File file = new File(attachment.getFilePath() + "/" + attachment.getFileName()); + this.logFinish(methodName, start, logger); + return this.downloadFile(file.getAbsolutePath()); + } + else{ + throw new Exception(NOT_ALLOWED); + } } catch (Exception e) { return this.logError(methodName, e, start, logger); } From f0359679e32ddd743ec2150890a4c18a94b0a842 Mon Sep 17 00:00:00 2001 From: delageniere Date: Tue, 31 Oct 2017 15:39:32 +0100 Subject: [PATCH 31/38] analysis report improvement --- .../common/util/export/ExiPdfRtfExporter.java | 82 +- .../src/main/webapp/help/release_notes.txt | 13 +- .../src/main/webapp/js/ispyb/min/ispyb-bx.js | 42726 ++++++++-------- 3 files changed, 21414 insertions(+), 21407 deletions(-) diff --git a/ispyb-ejb/src/main/java/ispyb/common/util/export/ExiPdfRtfExporter.java b/ispyb-ejb/src/main/java/ispyb/common/util/export/ExiPdfRtfExporter.java index 9b3ac7606..347bca985 100644 --- a/ispyb-ejb/src/main/java/ispyb/common/util/export/ExiPdfRtfExporter.java +++ b/ispyb-ejb/src/main/java/ispyb/common/util/export/ExiPdfRtfExporter.java @@ -171,8 +171,9 @@ public class ExiPdfRtfExporter { public final static float CRYSTAL_IMAGE_HEIGHT = 174; //public final static float IMAGE_HEIGHT = 120; - public final static float IMAGE_HEIGHT = 100; + public final static float IMAGE_HEIGHT = 80; public final static float IMAGE_HEIGHT_SMALL = 50; + public final static float IMAGE_HEIGHT_SNAPSHOT = 60; // public final static float CRYSTAL_IMAGE_WIDTH = 160; // public final static float CRYSTAL_IMAGE_HEIGHT = 99; @@ -491,13 +492,16 @@ private void setSessionTable(Document document) throws Exception { * @throws Exception */ private void setDataCollectionTable(Document document) throws Exception { + document.add(new Paragraph("Data Collections:", FONT_TITLE)); document.add(new Paragraph(" ")); + if (dataCollections.isEmpty()) { document.add(new Paragraph("There is no data collection in this report", FONT_DOC)); } else { - document.add(new Paragraph(" ")); + document.add(new Paragraph(" ")); + // need the list of DCgroups for crystal class summary Map mapDataCollectionGroupIdCClass = new HashMap(); @@ -583,15 +587,17 @@ private void setDataCollectionMapData(Document document, Map dat table.getDefaultCell().setHorizontalAlignment(Element.ALIGN_LEFT); table.getDefaultCell().setBorderWidth(0); table.setBorder(0); + table.setCellsFitPage(true); + table.setWidth(90); // 1st Cell - parag = "Workflow: \n" - + "Protein: \n" - + "Sample: \n" - + "Prefix: \n" - + "Run #: \n" - + "Images: \n" - + "Transmission: \n"; + parag = "Workflow:\n" + + "Protein:\n" + + "Sample:\n" + + "Prefix:\n" + + "Run #:\n" + + "Images:\n" + + "Transmission:\n"; LOG.info("parag=" + parag); p = new Paragraph(parag, FONT_DOC); table.addCell(p); @@ -603,7 +609,7 @@ private void setDataCollectionMapData(Document document, Map dat + getCellParam(dataCollectionMapItem, "DataCollection_imagePrefix", null) + "\n" + getCellParam(dataCollectionMapItem, "DataCollection_dataCollectionNumber", null) + "\n" + getCellParam(dataCollectionMapItem, "DataCollection_numberOfImages", null) + "\n" - + getCellParam(dataCollectionMapItem, "DataCollection_transmission", df2) + "\n"; + + getCellParam(dataCollectionMapItem, "transmission", df2) + "%\n"; LOG.info("parag=" + parag); p = new Paragraph(parag, FONT_DOC_BOLD); table.addCell(p); @@ -622,14 +628,14 @@ private void setDataCollectionMapData(Document document, Map dat // Cell 4 - parag = getCellParam(dataCollectionMapItem, "DataCollection_resolution", df2) - + "("+ getCellParam(dataCollectionMapItem, "DataCollection_resolutionAtCorner", df2) + ") \n" - + getCellParam(dataCollectionMapItem, "DataCollection_wavelength", df3) + "\n" - + getCellParam(dataCollectionMapItem, "DataCollection_axisRange", df2) + "\n" - + getCellParam(dataCollectionMapItem, "DataCollection_omegaStart", df2) + "\n" - + getCellParam(dataCollectionMapItem, "DataCollection_exposureTime", df2) + "\n" - + getCellParam(dataCollectionMapItem, "DataCollection_flux", null) + "\n" - + getCellParam(dataCollectionMapItem, "DataCollection_flux_end", null) + "\n" ; + parag = getCellParam(dataCollectionMapItem, "DataCollection_resolution", df2) + Constants.ANGSTROM + + " ("+ getCellParam(dataCollectionMapItem, "DataCollection_resolutionAtCorner", df2) + Constants.ANGSTROM + ") \n" + + getCellParam(dataCollectionMapItem, "DataCollection_wavelength", df3) + Constants.ANGSTROM + "\n" + + getCellParam(dataCollectionMapItem, "DataCollection_axisRange", df2) + Constants.DEGREE + "\n" + + getCellParam(dataCollectionMapItem, "DataCollection_omegaStart", df2) + Constants.DEGREE + "\n" + + getCellParam(dataCollectionMapItem, "DataCollection_exposureTime", df2) + "s \n" + + getCellParam(dataCollectionMapItem, "DataCollection_flux", null) + "ph/sec \n" + + getCellParam(dataCollectionMapItem, "DataCollection_flux_end", null) + "ph/sec \n" ; table.addCell(new Paragraph(parag, FONT_DOC_BOLD)); @@ -643,13 +649,9 @@ private void setDataCollectionMapData(Document document, Map dat } else { table.addCell(" "); } - - //cellThumbnail.setRowspan(nbRows); - - + // 6 Cell : snapshot - Cell cellSnapshot = getCellImage(dataCollectionMapItem,"DataCollection_xtalSnapshotFullPath1", IMAGE_HEIGHT); - //cellSnapshot.setRowspan(nbRows); + Cell cellSnapshot = getCellImage(dataCollectionMapItem,"DataCollection_xtalSnapshotFullPath1", IMAGE_HEIGHT_SNAPSHOT); cellSnapshot.setBorderWidth(0); table.addCell(cellSnapshot); @@ -688,7 +690,13 @@ private void setDataCollectionMapData(Document document, Map dat private void setDataAnalysisMapData(Document document, Map dataCollectionMapItem) throws Exception { //row 1 + String parag = getCellParam(dataCollectionMapItem, "DataCollectionGroup_experimentType", null) + + " " + getCellParam(dataCollectionMapItem, "DataCollection_startTime", null); + Paragraph p = new Paragraph(parag, FONT_DOC_BLUE); + //document.add(p); + Table table = new Table(NB_COL_DATA_ANALYSIS); + table.setWidth(100); table.setCellsFitPage(true); table.setBorder(0); @@ -696,12 +704,12 @@ private void setDataAnalysisMapData(Document document, Map dataC table.getDefaultCell().setBorderWidth(0); // 1st Cell - String parag = "Protein: \n\n" + parag = "Protein: \n\n" + "Prefix: \n\n" + "Images: \n\n" ; LOG.info("parag=" + parag); - Paragraph p = new Paragraph(parag, FONT_DOC_SMALL); + p = new Paragraph(parag, FONT_DOC_SMALL); table.addCell(p); // 2st Cell @@ -723,9 +731,9 @@ private void setDataAnalysisMapData(Document document, Map dataC // Cell 4 parag = getCellParam(dataCollectionMapItem, "Workflow_workflowType", null) + "\n" - + getCellParam(dataCollectionMapItem, "DataCollection_resolution", df2) - + "("+ getCellParam(dataCollectionMapItem, "DataCollection_resolutionAtCorner", df2) + ") \n" - + getCellParam(dataCollectionMapItem, "DataCollection_wavelength", df3) + "\n" ; + + getCellParam(dataCollectionMapItem, "DataCollection_resolution", df2) + Constants.ANGSTROM + + "("+ getCellParam(dataCollectionMapItem, "DataCollection_resolutionAtCorner", df2) + Constants.ANGSTROM + ") \n" + + getCellParam(dataCollectionMapItem, "DataCollection_wavelength", df3) + Constants.ANGSTROM + "\n" ; p = new Paragraph(parag, FONT_DOC_SMALL_BOLD); table.addCell(p); @@ -955,20 +963,8 @@ private Cell getCellValue(String value) { private Cell getCellImage(Map dataCollectionMapItem, String imageParam, float image_size) throws Exception { if (dataCollectionMapItem.get(imageParam) != null && !(dataCollectionMapItem.get(imageParam).toString()).equals("") ) { - String image = dataCollectionMapItem.get(imageParam).toString(); - image = PathUtils.getPath(image); - try { - Image jpg1 = Image.getInstance(image); - jpg1.scaleAbsolute(jpg1.getWidth() * image_size / jpg1.getHeight(), image_size); - Cell cell = new Cell(jpg1); - cell.setLeading(0); - cell.setBorderWidth(0); - cell.setHorizontalAlignment(Element.ALIGN_CENTER); - cell.setVerticalAlignment(Element.ALIGN_CENTER); - return cell; - } catch (IOException e) { - return new Cell(new Paragraph(image + " not found", FONT_DOC)); - } + String imagePath = dataCollectionMapItem.get(imageParam).toString(); + return this.getCellImage(imagePath, image_size); } return new Cell(new Paragraph("", FONT_DOC)); } diff --git a/ispyb-ui/src/main/webapp/help/release_notes.txt b/ispyb-ui/src/main/webapp/help/release_notes.txt index b107e4d6f..9e02aaccc 100644 --- a/ispyb-ui/src/main/webapp/help/release_notes.txt +++ b/ispyb-ui/src/main/webapp/help/release_notes.txt @@ -1,4 +1,15 @@ -New TAG : 5.2.1 +New TAG : 5.3.3 + + +************** +* 31.10.2017 * +************** + +- work in progress: #135 +- fixes issue #176 + + +New TAG : 5.3.2 ************** diff --git a/ispyb-ui/src/main/webapp/js/ispyb/min/ispyb-bx.js b/ispyb-ui/src/main/webapp/js/ispyb/min/ispyb-bx.js index a98a06520..23e07209a 100644 --- a/ispyb-ui/src/main/webapp/js/ispyb/min/ispyb-bx.js +++ b/ispyb-ui/src/main/webapp/js/ispyb/min/ispyb-bx.js @@ -38,321 +38,321 @@ Event.prototype = { } }; -function GenericGraph(args) { - this.width = 600; - this.height = 400; - - this.targetId = null; - /** Free spaces in the borders * */ - this.top = 10; - this.left = 10; - this.bottom = 50; - this.right = 40; - - /** Ruler * */ - this.rulerHeight = 50; - this.rulerWidth = 50; - this.rulerStroke = 2; - this.rulerVerticalMarksNumber = 5; - - this.rulerMaxValue = null; - this.rulerMinValue = null; - - /** plot options * */ - this.plotPoints = true; - this.pointRadius = 2; - this.fillOpacityPoint = 0.2; - this.strokeOpacityPoint = 0.2; - - /** Cluster titles * */ - this.clusterTitleHeight = 20; - this.interClassesSpace = 2; - this.interClustersSpace = 4; - this.fontSize = 7; - - /** - * If true classes and title will be rendender in the rule otherwise will be - * integer values - */ - this.plotHorizontalByCluster = true; - - if (args != null) { - if (args.targetId != null) { - this.targetId = args.targetId; - } - if (args.plotHorizontalByCluster != null) { - this.plotHorizontalByCluster = args.plotHorizontalByCluster; - } - if (args.height != null) { - this.height = args.height; - } - if (args.width != null) { - this.width = args.width; - } - if (args.rulerMinValue != null) { - this.rulerMinValue = args.rulerMinValue; - } - if (args.rulerMaxValue != null) { - this.rulerMaxValue = args.rulerMaxValue; - } - if (args.rulerHeight != null) { - this.rulerHeight = args.rulerHeight; - } - - } -} - -GenericGraph.prototype.calculate = function(data) { - var result = {}; - - var checked = this.cleanArray(data); - - /** sorting array * */ - checked.sort(function(a, b) { - return a - b; - }); - - var median = this.getMedian(checked); - - result.median = median; - result.Q1 = Number(this.getQ1(checked)); - result.Q2 = Number(this.getQ2(checked)); - result.Q3 = Number(this.getQ3(checked)); - result.population = checked; - - result.IQR = Number(result.Q3 - result.Q1); - result.outlier_below_limit = result.Q1 - (1.5 * result.IQR); - result.outlier_above_limit = result.Q3 + (1.5 * result.IQR); - result.below_outliers = this.getBelowOutliers(result.outlier_below_limit, checked); - result.above_outliers = this.getAboveOutliers(result.outlier_above_limit, checked); - - result.min_whisker = this.minValueWhisker(result.outlier_below_limit, result.Q1, checked); - result.max_whisker = this.maxValueWhisker(result.outlier_above_limit, result.Q3, checked); - - return result; -}; - -GenericGraph.prototype.drawSVGVerticalText = function(x, y, text, properties) { - var transform = [ "transform", "translate(" + x + ", " + y + "), rotate(-90) " ]; - properties.push(transform); - SVG.drawText(0, 0, text, this.svg, properties); -}; - -/** Plot the numbers on the axis * */ -GenericGraph.prototype.plotRuler = function(rulerProperties) { - - SVG.drawRectangle(rulerProperties.vertical.x, rulerProperties.vertical.y, rulerProperties.vertical.width, rulerProperties.vertical.height, - this.svg, [ [ "fill", "black" ] ]); - var distance = rulerProperties.vertical.height / (this.rulerVerticalMarksNumber + 1); - for ( var i = 0; i <= this.rulerVerticalMarksNumber + 1; i++) { - var deltaHeight = distance * i; - var aux = rulerProperties.vertical.height - deltaHeight; - - var text = Number((aux / rulerProperties.vertical.height) * (rulerProperties.maxPoint - rulerProperties.minPoint) + rulerProperties.minPoint) - .toFixed(3); - - SVG.drawLine(rulerProperties.horizontal.x, this.top + deltaHeight, rulerProperties.horizontal.x - rulerProperties.markWidth, - this.top + deltaHeight, this.svg, [ [ "stroke", "black" ] ]); - SVG.drawText(this.left, this.top + distance * i + 5, text, this.svg, [ [ "style", "font-size:xx-small" ] ]); - /** Drawing the mark up to the end * */ - SVG.drawLine(rulerProperties.horizontal.x, this.top + deltaHeight, this.width - this.right, this.top + deltaHeight, this.svg, [ - [ "stroke", rulerProperties.markColor ], [ "stroke-width", "0.3" ] ]); - - if (i == this.rulerVerticalMarksNumber + 1) { - SVG.drawLine(rulerProperties.horizontal.x, this.top + deltaHeight, this.width - this.right, this.top + deltaHeight, this.svg, [ [ - "stroke", rulerProperties.markColor ] ]); - } - } - - /** Drawing horizontal rulers * */ - if (!this.plotHorizontalByCluster) { - var width = rulerProperties.horizontal.width; - var ratio = width / (rulerProperties.horizontal.xValues.range); - for ( i = 0; i < rulerProperties.horizontal.xValues.values.length; i++) { - var coorX = rulerProperties.horizontal.xValues.values[i] - rulerProperties.horizontal.xValues.min; - SVG.drawText(rulerProperties.horizontal.x + coorX * ratio, this.height - (this.bottom + (this.clusterTitleHeight)), - rulerProperties.horizontal.xValues.values[i], this.svg, [ [ "style", "font-size:small" ] ]); - } - } -}; - -GenericGraph.prototype.plotAxes = function(properties) { - /** - * Drawing canvas plot-free spaces SVG.drawRectangle(0, this.top, this.left, - * this.height, this.svg, [["fill", "pink"]]); SVG.drawRectangle(this.width - - * this.right, this.top, plot.width, plot.height, this.svg, [["fill", - * "pink"]]); SVG.drawRectangle(0, 0, this.width, this.top, this.svg, - * [["fill", "red"]]); SVG.drawRectangle(0, this.height - this.bottom, - * this.width, this.bottom, this.svg, [["fill", "red"]]); - */ - - /** Drawing ruler Space * */ - this.plotRuler({ - minPoint : Number(properties.minPoint), - maxPoint : Number(properties.maxPoint), - markColor : "black", - markWidth : 20, - vertical : { - x : this.left + this.rulerWidth - this.rulerStroke, - y : this.top, - width : this.rulerStroke, - height : this.height - (this.top + this.bottom + this.clusterTitleHeight) - this.rulerHeight - }, - horizontal : { - x : this.left + this.rulerWidth - this.rulerStroke, - y : this.height - (this.bottom + this.clusterTitleHeight + this.rulerHeight), - width : properties.width, - height : this.rulerStroke, - xValues : properties.xValues - } - }); - -}; - -/** Remove nulls and NaN elements in the array * */ -GenericGraph.prototype.cleanArray = function(data) { - var checked = []; - - /** checking data are numbers * */ - for ( var i = 0; i < data.length; i++) { - if (data[i] != null) { - if (!isNaN(data[i])) { - checked.push(data[i]); - } - } - } - return checked; -}; - -GenericGraph.prototype.refresh = function(data) { - document.getElementById(this.targetId).innerHTML = ""; - this.draw(this.targetId, data); -}; - -GenericGraph.prototype.getClassColor = function(className) { - for ( var i = 0; i < this.data.clusters.length; i++) { - var cluster = this.data.clusters[i]; - for ( var j = 0; j < cluster.classes.length; j++) { - var classes = cluster.classes[j]; - if (classes.name == className) { - if (classes.color != null) { - return classes.color; - } - } - } - } - return "black"; -}; - -GenericGraph.prototype.getDimensions = function(data) { - var results = {}; - var points = []; - - this.data = data; - var classesNumber = 0; - var xValues = []; - - for ( var i = 0; i < data.clusters.length; i++) { - var cluster = data.clusters[i]; - if (!this.plotHorizontalByCluster) { - xValues.push(data.clusters[i].x); - } - for ( var j = 0; j < cluster.classes.length; j++) { - var classes = cluster.classes[j]; - points = points.concat(classes.values); - classesNumber = classesNumber + 1; - } - } - - var checked = this.cleanArray(points); - - checked.sort(function(a, b) { - return a - b; - }); - - results.minPoint = checked[0]; - if (this.rulerMinValue != null) { - results.minPoint = this.rulerMinValue; - } - results.maxPoint = checked[checked.length - 1]; - if (this.rulerMaxValue != null) { - results.maxPoint = this.rulerMaxValue; - } - - results.classesNumber = classesNumber; - results.clusterNumber = data.clusters.length; - - var totalInterClassesFreeSpace = (classesNumber - data.clusters.length) * this.interClassesSpace; - var totalInterClusterFreeSpace = (data.clusters.length) * this.interClustersSpace; - results.classWidth = (this.width - (this.rulerWidth + this.left + this.right + totalInterClassesFreeSpace + totalInterClusterFreeSpace))/ classesNumber; - results.width = this.width - (this.right + this.left) - this.rulerWidth + this.rulerStroke; - - results.xValues = {}; - - xValues.sort(function(a, b) { - return a - b; - }); - results.xValues.values = xValues; - if (xValues.length > 0) { - results.xValues.min = xValues[0]; - results.xValues.max = xValues[xValues.length - 1]; - results.xValues.range = results.xValues.max - results.xValues.min; - } - return results; - -}; - -GenericGraph.prototype.draw = function(targetId, data) { - this.targetId = targetId; - var properties = (this.getDimensions(data)); - this.svg = SVG.createSVGCanvas(document.getElementById(this.targetId), [ [ "width", this.width ], [ "height", this.height ] ]); - this.plotAxes(properties); -}; - -GenericGraph.prototype.getMedian = function(checked) { - /** Calculating median * */ - if (checked.length % 2 == 1) { - return checked[Math.floor(checked.length / 2)]; - } else { - return (Number(checked[(checked.length / 2) - 1]) + Number(checked[checked.length / 2])) / 2; - } -}; - -GenericGraph.prototype.pointToPixel = function(value, boxProperties) { - var ratio = (value - boxProperties.minPoint) / (boxProperties.maxPoint - boxProperties.minPoint); - var pixelLength = boxProperties.height - boxProperties.y + this.top; - return (-1 * ratio) * (pixelLength) + (boxProperties.y + boxProperties.height); -}; - -GenericGraph.prototype.maxValueWhisker = function(aboveLimit, q3, array) { - var points = []; - for ( var i = 0; i < array.length; i++) { - if ((array[i] > q3) && (array[i]) <= aboveLimit) { - points.push(array[i]); - } - } - if (points.length > 0) { - points.sort(function(a, b) { - return a - b; - }); - return points[points.length - 1]; - } - return null; -}; - -GenericGraph.prototype.input = function() { - return DATADOC.getBoxWhikerData(); -}; - -GenericGraph.prototype.test = function(targetId) { - var plot = new GenericGraph({ - targetId : targetId, - height : 350, - width : 450, - maxBoxWidth : 25 - }); - plot.refresh(this.input()); -}; +function GenericGraph(args) { + this.width = 600; + this.height = 400; + + this.targetId = null; + /** Free spaces in the borders * */ + this.top = 10; + this.left = 10; + this.bottom = 50; + this.right = 40; + + /** Ruler * */ + this.rulerHeight = 50; + this.rulerWidth = 50; + this.rulerStroke = 2; + this.rulerVerticalMarksNumber = 5; + + this.rulerMaxValue = null; + this.rulerMinValue = null; + + /** plot options * */ + this.plotPoints = true; + this.pointRadius = 2; + this.fillOpacityPoint = 0.2; + this.strokeOpacityPoint = 0.2; + + /** Cluster titles * */ + this.clusterTitleHeight = 20; + this.interClassesSpace = 2; + this.interClustersSpace = 4; + this.fontSize = 7; + + /** + * If true classes and title will be rendender in the rule otherwise will be + * integer values + */ + this.plotHorizontalByCluster = true; + + if (args != null) { + if (args.targetId != null) { + this.targetId = args.targetId; + } + if (args.plotHorizontalByCluster != null) { + this.plotHorizontalByCluster = args.plotHorizontalByCluster; + } + if (args.height != null) { + this.height = args.height; + } + if (args.width != null) { + this.width = args.width; + } + if (args.rulerMinValue != null) { + this.rulerMinValue = args.rulerMinValue; + } + if (args.rulerMaxValue != null) { + this.rulerMaxValue = args.rulerMaxValue; + } + if (args.rulerHeight != null) { + this.rulerHeight = args.rulerHeight; + } + + } +} + +GenericGraph.prototype.calculate = function(data) { + var result = {}; + + var checked = this.cleanArray(data); + + /** sorting array * */ + checked.sort(function(a, b) { + return a - b; + }); + + var median = this.getMedian(checked); + + result.median = median; + result.Q1 = Number(this.getQ1(checked)); + result.Q2 = Number(this.getQ2(checked)); + result.Q3 = Number(this.getQ3(checked)); + result.population = checked; + + result.IQR = Number(result.Q3 - result.Q1); + result.outlier_below_limit = result.Q1 - (1.5 * result.IQR); + result.outlier_above_limit = result.Q3 + (1.5 * result.IQR); + result.below_outliers = this.getBelowOutliers(result.outlier_below_limit, checked); + result.above_outliers = this.getAboveOutliers(result.outlier_above_limit, checked); + + result.min_whisker = this.minValueWhisker(result.outlier_below_limit, result.Q1, checked); + result.max_whisker = this.maxValueWhisker(result.outlier_above_limit, result.Q3, checked); + + return result; +}; + +GenericGraph.prototype.drawSVGVerticalText = function(x, y, text, properties) { + var transform = [ "transform", "translate(" + x + ", " + y + "), rotate(-90) " ]; + properties.push(transform); + SVG.drawText(0, 0, text, this.svg, properties); +}; + +/** Plot the numbers on the axis * */ +GenericGraph.prototype.plotRuler = function(rulerProperties) { + + SVG.drawRectangle(rulerProperties.vertical.x, rulerProperties.vertical.y, rulerProperties.vertical.width, rulerProperties.vertical.height, + this.svg, [ [ "fill", "black" ] ]); + var distance = rulerProperties.vertical.height / (this.rulerVerticalMarksNumber + 1); + for ( var i = 0; i <= this.rulerVerticalMarksNumber + 1; i++) { + var deltaHeight = distance * i; + var aux = rulerProperties.vertical.height - deltaHeight; + + var text = Number((aux / rulerProperties.vertical.height) * (rulerProperties.maxPoint - rulerProperties.minPoint) + rulerProperties.minPoint) + .toFixed(3); + + SVG.drawLine(rulerProperties.horizontal.x, this.top + deltaHeight, rulerProperties.horizontal.x - rulerProperties.markWidth, + this.top + deltaHeight, this.svg, [ [ "stroke", "black" ] ]); + SVG.drawText(this.left, this.top + distance * i + 5, text, this.svg, [ [ "style", "font-size:xx-small" ] ]); + /** Drawing the mark up to the end * */ + SVG.drawLine(rulerProperties.horizontal.x, this.top + deltaHeight, this.width - this.right, this.top + deltaHeight, this.svg, [ + [ "stroke", rulerProperties.markColor ], [ "stroke-width", "0.3" ] ]); + + if (i == this.rulerVerticalMarksNumber + 1) { + SVG.drawLine(rulerProperties.horizontal.x, this.top + deltaHeight, this.width - this.right, this.top + deltaHeight, this.svg, [ [ + "stroke", rulerProperties.markColor ] ]); + } + } + + /** Drawing horizontal rulers * */ + if (!this.plotHorizontalByCluster) { + var width = rulerProperties.horizontal.width; + var ratio = width / (rulerProperties.horizontal.xValues.range); + for ( i = 0; i < rulerProperties.horizontal.xValues.values.length; i++) { + var coorX = rulerProperties.horizontal.xValues.values[i] - rulerProperties.horizontal.xValues.min; + SVG.drawText(rulerProperties.horizontal.x + coorX * ratio, this.height - (this.bottom + (this.clusterTitleHeight)), + rulerProperties.horizontal.xValues.values[i], this.svg, [ [ "style", "font-size:small" ] ]); + } + } +}; + +GenericGraph.prototype.plotAxes = function(properties) { + /** + * Drawing canvas plot-free spaces SVG.drawRectangle(0, this.top, this.left, + * this.height, this.svg, [["fill", "pink"]]); SVG.drawRectangle(this.width - + * this.right, this.top, plot.width, plot.height, this.svg, [["fill", + * "pink"]]); SVG.drawRectangle(0, 0, this.width, this.top, this.svg, + * [["fill", "red"]]); SVG.drawRectangle(0, this.height - this.bottom, + * this.width, this.bottom, this.svg, [["fill", "red"]]); + */ + + /** Drawing ruler Space * */ + this.plotRuler({ + minPoint : Number(properties.minPoint), + maxPoint : Number(properties.maxPoint), + markColor : "black", + markWidth : 20, + vertical : { + x : this.left + this.rulerWidth - this.rulerStroke, + y : this.top, + width : this.rulerStroke, + height : this.height - (this.top + this.bottom + this.clusterTitleHeight) - this.rulerHeight + }, + horizontal : { + x : this.left + this.rulerWidth - this.rulerStroke, + y : this.height - (this.bottom + this.clusterTitleHeight + this.rulerHeight), + width : properties.width, + height : this.rulerStroke, + xValues : properties.xValues + } + }); + +}; + +/** Remove nulls and NaN elements in the array * */ +GenericGraph.prototype.cleanArray = function(data) { + var checked = []; + + /** checking data are numbers * */ + for ( var i = 0; i < data.length; i++) { + if (data[i] != null) { + if (!isNaN(data[i])) { + checked.push(data[i]); + } + } + } + return checked; +}; + +GenericGraph.prototype.refresh = function(data) { + document.getElementById(this.targetId).innerHTML = ""; + this.draw(this.targetId, data); +}; + +GenericGraph.prototype.getClassColor = function(className) { + for ( var i = 0; i < this.data.clusters.length; i++) { + var cluster = this.data.clusters[i]; + for ( var j = 0; j < cluster.classes.length; j++) { + var classes = cluster.classes[j]; + if (classes.name == className) { + if (classes.color != null) { + return classes.color; + } + } + } + } + return "black"; +}; + +GenericGraph.prototype.getDimensions = function(data) { + var results = {}; + var points = []; + + this.data = data; + var classesNumber = 0; + var xValues = []; + + for ( var i = 0; i < data.clusters.length; i++) { + var cluster = data.clusters[i]; + if (!this.plotHorizontalByCluster) { + xValues.push(data.clusters[i].x); + } + for ( var j = 0; j < cluster.classes.length; j++) { + var classes = cluster.classes[j]; + points = points.concat(classes.values); + classesNumber = classesNumber + 1; + } + } + + var checked = this.cleanArray(points); + + checked.sort(function(a, b) { + return a - b; + }); + + results.minPoint = checked[0]; + if (this.rulerMinValue != null) { + results.minPoint = this.rulerMinValue; + } + results.maxPoint = checked[checked.length - 1]; + if (this.rulerMaxValue != null) { + results.maxPoint = this.rulerMaxValue; + } + + results.classesNumber = classesNumber; + results.clusterNumber = data.clusters.length; + + var totalInterClassesFreeSpace = (classesNumber - data.clusters.length) * this.interClassesSpace; + var totalInterClusterFreeSpace = (data.clusters.length) * this.interClustersSpace; + results.classWidth = (this.width - (this.rulerWidth + this.left + this.right + totalInterClassesFreeSpace + totalInterClusterFreeSpace))/ classesNumber; + results.width = this.width - (this.right + this.left) - this.rulerWidth + this.rulerStroke; + + results.xValues = {}; + + xValues.sort(function(a, b) { + return a - b; + }); + results.xValues.values = xValues; + if (xValues.length > 0) { + results.xValues.min = xValues[0]; + results.xValues.max = xValues[xValues.length - 1]; + results.xValues.range = results.xValues.max - results.xValues.min; + } + return results; + +}; + +GenericGraph.prototype.draw = function(targetId, data) { + this.targetId = targetId; + var properties = (this.getDimensions(data)); + this.svg = SVG.createSVGCanvas(document.getElementById(this.targetId), [ [ "width", this.width ], [ "height", this.height ] ]); + this.plotAxes(properties); +}; + +GenericGraph.prototype.getMedian = function(checked) { + /** Calculating median * */ + if (checked.length % 2 == 1) { + return checked[Math.floor(checked.length / 2)]; + } else { + return (Number(checked[(checked.length / 2) - 1]) + Number(checked[checked.length / 2])) / 2; + } +}; + +GenericGraph.prototype.pointToPixel = function(value, boxProperties) { + var ratio = (value - boxProperties.minPoint) / (boxProperties.maxPoint - boxProperties.minPoint); + var pixelLength = boxProperties.height - boxProperties.y + this.top; + return (-1 * ratio) * (pixelLength) + (boxProperties.y + boxProperties.height); +}; + +GenericGraph.prototype.maxValueWhisker = function(aboveLimit, q3, array) { + var points = []; + for ( var i = 0; i < array.length; i++) { + if ((array[i] > q3) && (array[i]) <= aboveLimit) { + points.push(array[i]); + } + } + if (points.length > 0) { + points.sort(function(a, b) { + return a - b; + }); + return points[points.length - 1]; + } + return null; +}; + +GenericGraph.prototype.input = function() { + return DATADOC.getBoxWhikerData(); +}; + +GenericGraph.prototype.test = function(targetId) { + var plot = new GenericGraph({ + targetId : targetId, + height : 350, + width : 450, + maxBoxWidth : 25 + }); + plot.refresh(this.input()); +}; /** * Using dygraph it plots a chart. Params: targetId, labelsContainerId, args @@ -2087,338 +2087,338 @@ MacromoleculeConditionGrid.prototype.test = function(targetId) { panel.render(targetId); }; - -function ItemGraphFormatter(id, defaultFormat, selectedFormat, overFormat, draggingFormat){ - this.id = id; - this.args = new Object(); - - this.defaultFormat = new ItemFormat(defaultFormat); - - if(selectedFormat != null){ - this.selected = new ItemFormat(selectedFormat); - } - else{ - this.selected = new ItemFormat(defaultFormat); - } - - if(overFormat != null){ - this.over = new ItemFormat(overFormat); - } - else{ - this.over = new ItemFormat(defaultFormat); - } - - if(draggingFormat != null){ - this.dragging = new ItemFormat(draggingFormat); - } - else{ - this.dragging = new ItemFormat(defaultFormat); - } - - //Events - this.stateChanged = new Event(this); - - - //Attaching events - var _this = this; - this._setEvents(); -}; - -ItemGraphFormatter.prototype.getType = function(){ - return this.args.type; -}; - - -ItemGraphFormatter.prototype.toJSON = function(){ - var json = this.args; - json.defaultFormat = this.getDefault().toJSON(); - json.over = this.getOver().toJSON(); - json.selected = this.getSelected().toJSON(); - json.dragging = this.getDragging().toJSON(); - json.id = this.id; - return json; -}; - -ItemGraphFormatter.prototype.loadFromJSON = function(json){ - this.args = json; - this.defaultFormat = new ItemFormat(json.defaultFormat); - this.over = new ItemFormat(json.over); - this.selected = new ItemFormat(json.selected); - this.dragging = new ItemFormat(json.dragging); - this._setEvents(); -}; - -ItemGraphFormatter.prototype._setEvents = function(){ - //Attaching events - var _this = this; - - this.defaultFormat.changed.attach(function (sender, item){ - _this.over.setSize(_this.defaultFormat.getSize()); - _this.selected.setSize(_this.defaultFormat.getSize()); - _this.dragging.setSize(_this.defaultFormat.getSize()); - _this.stateChanged.notify(_this); - }); - - this.selected.changed.attach(function (sender, item){ - _this.stateChanged.notify(_this); - }); - - this.over.changed.attach(function (sender, item){ - _this.stateChanged.notify(_this); - }); - - this.dragging.changed.attach(function (sender, item){ - _this.stateChanged.notify(_this); - }); -}; - -/** Getters **/ -ItemGraphFormatter.prototype.getId = function(){return this.id;}; -ItemGraphFormatter.prototype.getDefault = function(){return this.defaultFormat;}; -ItemGraphFormatter.prototype.getSelected = function(){return this.selected;}; -ItemGraphFormatter.prototype.getOver = function(){return this.over;}; -ItemGraphFormatter.prototype.getDragging = function(){return this.dragging;}; - -function ItemFormat(args){ - this.defaultFormat = new Object(); - this.args = new Object(); - this.args.title = new Object(); - //Defult properties - this.args.visible = true; - this.args.hidden = false; - this.args.stroke = "#000000"; - this.args.strokeOpacity = 0.8; - this.args["stroke-width"] = 1; - this.args.fill = "#000000"; - this.args["fill-opacity"] = 1; - this.args.size = 1; - this.args.opacity = 1; - this.args.fontSize = "8"; - this.args.fontColor = "#000000"; - - /** For directed edge with arrow **/ - //this.args.arrowSize = 1; - - - if (args != null){ - if (args.visible != null){ - this.args.visible = args.visible; - } - if (args.opacity != null){ - this.args.opacity = args.opacity; - } - if (args.size != null){ - this.args.size = args.size; - } - if (args.hidden != null){ - this.args.hidden = args.hidden; - } - if (args.stroke != null){ - this.args.stroke = this._fixColor(args.stroke); - } - if (args.strokeOpacity != null){ - this.args.strokeOpacity = args.strokeOpacity; - } - if (args["stroke-width"]!=null){ - this.args["stroke-width"] = args["stroke-width"]; - } - if (args["fill-opacity"]!=null){ - this.args["fill-opacity"] = args["fill-opacity"]; - } - if (args.shape!=null){ - this.args.shape = args.shape; - } - if (args.fill!=null){ - this.args.fill = this._fixColor(args.fill); - } - - - if (args.title!=null){ - if (args.title.fontSize!=null){ - this.args.title.fontSize = args.title.fontSize; - } - if (args.title.fill!=null){ - this.args.title.fill = this._fixColor(args.title.fill); - } - } - - /** For directed edge with arrow **/ - /*if (args.arrowSize!=null){ - this.args.arrowSize = args.arrowSize; - }*/ - - } - - this.changed = new Event(); -}; - -ItemFormat.prototype._fixColor = function(color){ - var fixed = color; - if (color.indexOf("green") != -1){ - fixed = '#04B431'; - } - - if (color.indexOf("blue") != -1){ - fixed = '#045FB4'; - } - - if (color.indexOf("red") != -1){ - fixed = '#DF0101'; - } - - if (color.indexOf("black") != -1){ - fixed = '#000000'; - } - - if (color.indexOf("white") != -1){ - fixed = '#FFFFFF'; - } - - if (color.indexOf("#") == -1){ - fixed = "#" + color; - } - return fixed; -}; - -ItemFormat.prototype.toJSON = function(){ - if(this.args.strokeOpacity != null){ - this.args["stroke-opacity"] = this.args.strokeOpacity; - delete this.args.strokeOpacity; - } - -// if(this.args.strokeWidth != null){ -// this.args["stroke-width"] = this.args.strokeWidth; -// delete this.args["stroke-width"]; -// } - - if(this.args.title.fontColor != null){ - this.args.title["font-color"] = this.args.title.fontColor; - } - else{ - this.args.title["font-color"] = this.args.fontColor;//;this.args.title.fontColor; - } - - if(this.args.title.fontSize != null){ - this.args.title["font-size"] = this.args.title.fontSize;//;this.args.title.fontColor; - } - else{ - this.args.title["font-size"] = this.args.fontSize;//;this.args.title.fontColor; - } - //return this.args; - return this.args; -}; - -ItemFormat.prototype.getAttribute = function(name){return this.args[name];}; - -//Getters and Setters -ItemFormat.prototype.setVisible = function(visible){ - if (this.args.visible != visible){ - this.args.visible = visible; - this.changed.notify(this); - } -}; - -ItemFormat.prototype.getVisible = function(){return this.args.visible;}; - -ItemFormat.prototype.setHidden = function(hidden){ - if (this.args.hidden != hidden){ - this.args.hidden = hidden; - this.changed.notify(this); - } -}; - -ItemFormat.prototype.getHidden = function(){return this.args.hidden;}; - - -ItemFormat.prototype.setStroke = function(stroke){ - if (this.args.stroke != stroke){ - this.args.stroke = stroke; - this.changed.notify(this); - } -}; - -ItemFormat.prototype.getStroke = function(){return this.args.stroke;}; - -ItemFormat.prototype.setStrokeOpacity = function(strokeOpacity){ - if (this.args.strokeOpacity != strokeOpacity){ - this.args.strokeOpacity = strokeOpacity; - this.changed.notify(this); - } -}; - -ItemFormat.prototype.getStrokeOpacity = function(){return this.args["stroke-opacity"];}; - -ItemFormat.prototype.setStrokeWidth = function(strokeWidth){ - if (this.args["stroke-width"] != strokeWidth){ - this.args["stroke-width"] = strokeWidth; - this.changed.notify(this); - } -}; - - -ItemFormat.prototype.getFillOpacity = function(){return this.args["fill-opacity"];}; - -ItemFormat.prototype.setfillOpacity = function(strokeWidth){ - if (this.args["fill-opacity"] != strokeWidth){ - this.args["fill-opacity"] = strokeWidth; - this.changed.notify(this); - } -}; - - -ItemFormat.prototype.getStrokeWidth = function(){ - return this.args["stroke-width"]; -}; - -ItemFormat.prototype.setFill = function(fill){ - if (this.args.fill != fill){ - this.args.fill = fill; - this.changed.notify(this); - } -}; - -ItemFormat.prototype.getFill = function(){return this.args.fill;}; - -ItemFormat.prototype.setSize = function(size){ - if (this.args.size != size){ - this.args.size = size; - this.changed.notify(this); - } -}; - -ItemFormat.prototype.getSize = function(){return this.args.size;}; - -ItemFormat.prototype.setOpacity = function(opacity){ - if (this.args.opacity != opacity){ - this.args.opacity = opacity; - this.changed.notify(this); - } -}; - -ItemFormat.prototype.getOpacity = function(){return this.args.opacity;}; - -ItemFormat.prototype.getArrowSize = function(){return this.args.arrowSize;}; - -ItemFormat.prototype.setArrowSize = function(arrowSize){ - if (this.args.arrowSize != arrowSize){ - this.args.arrowSize = arrowSize; - this.changed.notify(this); - } -}; - -ItemFormat.prototype.getFontSize = function(){return this.args.title.fontSize;}; - -ItemFormat.prototype.setFontSize = function(fontSize){ - - if (this.args.title.fontSize != fontSize){ - this.args.title.fontSize = fontSize; - this.changed.notify(this); - } -}; - - - - + +function ItemGraphFormatter(id, defaultFormat, selectedFormat, overFormat, draggingFormat){ + this.id = id; + this.args = new Object(); + + this.defaultFormat = new ItemFormat(defaultFormat); + + if(selectedFormat != null){ + this.selected = new ItemFormat(selectedFormat); + } + else{ + this.selected = new ItemFormat(defaultFormat); + } + + if(overFormat != null){ + this.over = new ItemFormat(overFormat); + } + else{ + this.over = new ItemFormat(defaultFormat); + } + + if(draggingFormat != null){ + this.dragging = new ItemFormat(draggingFormat); + } + else{ + this.dragging = new ItemFormat(defaultFormat); + } + + //Events + this.stateChanged = new Event(this); + + + //Attaching events + var _this = this; + this._setEvents(); +}; + +ItemGraphFormatter.prototype.getType = function(){ + return this.args.type; +}; + + +ItemGraphFormatter.prototype.toJSON = function(){ + var json = this.args; + json.defaultFormat = this.getDefault().toJSON(); + json.over = this.getOver().toJSON(); + json.selected = this.getSelected().toJSON(); + json.dragging = this.getDragging().toJSON(); + json.id = this.id; + return json; +}; + +ItemGraphFormatter.prototype.loadFromJSON = function(json){ + this.args = json; + this.defaultFormat = new ItemFormat(json.defaultFormat); + this.over = new ItemFormat(json.over); + this.selected = new ItemFormat(json.selected); + this.dragging = new ItemFormat(json.dragging); + this._setEvents(); +}; + +ItemGraphFormatter.prototype._setEvents = function(){ + //Attaching events + var _this = this; + + this.defaultFormat.changed.attach(function (sender, item){ + _this.over.setSize(_this.defaultFormat.getSize()); + _this.selected.setSize(_this.defaultFormat.getSize()); + _this.dragging.setSize(_this.defaultFormat.getSize()); + _this.stateChanged.notify(_this); + }); + + this.selected.changed.attach(function (sender, item){ + _this.stateChanged.notify(_this); + }); + + this.over.changed.attach(function (sender, item){ + _this.stateChanged.notify(_this); + }); + + this.dragging.changed.attach(function (sender, item){ + _this.stateChanged.notify(_this); + }); +}; + +/** Getters **/ +ItemGraphFormatter.prototype.getId = function(){return this.id;}; +ItemGraphFormatter.prototype.getDefault = function(){return this.defaultFormat;}; +ItemGraphFormatter.prototype.getSelected = function(){return this.selected;}; +ItemGraphFormatter.prototype.getOver = function(){return this.over;}; +ItemGraphFormatter.prototype.getDragging = function(){return this.dragging;}; + +function ItemFormat(args){ + this.defaultFormat = new Object(); + this.args = new Object(); + this.args.title = new Object(); + //Defult properties + this.args.visible = true; + this.args.hidden = false; + this.args.stroke = "#000000"; + this.args.strokeOpacity = 0.8; + this.args["stroke-width"] = 1; + this.args.fill = "#000000"; + this.args["fill-opacity"] = 1; + this.args.size = 1; + this.args.opacity = 1; + this.args.fontSize = "8"; + this.args.fontColor = "#000000"; + + /** For directed edge with arrow **/ + //this.args.arrowSize = 1; + + + if (args != null){ + if (args.visible != null){ + this.args.visible = args.visible; + } + if (args.opacity != null){ + this.args.opacity = args.opacity; + } + if (args.size != null){ + this.args.size = args.size; + } + if (args.hidden != null){ + this.args.hidden = args.hidden; + } + if (args.stroke != null){ + this.args.stroke = this._fixColor(args.stroke); + } + if (args.strokeOpacity != null){ + this.args.strokeOpacity = args.strokeOpacity; + } + if (args["stroke-width"]!=null){ + this.args["stroke-width"] = args["stroke-width"]; + } + if (args["fill-opacity"]!=null){ + this.args["fill-opacity"] = args["fill-opacity"]; + } + if (args.shape!=null){ + this.args.shape = args.shape; + } + if (args.fill!=null){ + this.args.fill = this._fixColor(args.fill); + } + + + if (args.title!=null){ + if (args.title.fontSize!=null){ + this.args.title.fontSize = args.title.fontSize; + } + if (args.title.fill!=null){ + this.args.title.fill = this._fixColor(args.title.fill); + } + } + + /** For directed edge with arrow **/ + /*if (args.arrowSize!=null){ + this.args.arrowSize = args.arrowSize; + }*/ + + } + + this.changed = new Event(); +}; + +ItemFormat.prototype._fixColor = function(color){ + var fixed = color; + if (color.indexOf("green") != -1){ + fixed = '#04B431'; + } + + if (color.indexOf("blue") != -1){ + fixed = '#045FB4'; + } + + if (color.indexOf("red") != -1){ + fixed = '#DF0101'; + } + + if (color.indexOf("black") != -1){ + fixed = '#000000'; + } + + if (color.indexOf("white") != -1){ + fixed = '#FFFFFF'; + } + + if (color.indexOf("#") == -1){ + fixed = "#" + color; + } + return fixed; +}; + +ItemFormat.prototype.toJSON = function(){ + if(this.args.strokeOpacity != null){ + this.args["stroke-opacity"] = this.args.strokeOpacity; + delete this.args.strokeOpacity; + } + +// if(this.args.strokeWidth != null){ +// this.args["stroke-width"] = this.args.strokeWidth; +// delete this.args["stroke-width"]; +// } + + if(this.args.title.fontColor != null){ + this.args.title["font-color"] = this.args.title.fontColor; + } + else{ + this.args.title["font-color"] = this.args.fontColor;//;this.args.title.fontColor; + } + + if(this.args.title.fontSize != null){ + this.args.title["font-size"] = this.args.title.fontSize;//;this.args.title.fontColor; + } + else{ + this.args.title["font-size"] = this.args.fontSize;//;this.args.title.fontColor; + } + //return this.args; + return this.args; +}; + +ItemFormat.prototype.getAttribute = function(name){return this.args[name];}; + +//Getters and Setters +ItemFormat.prototype.setVisible = function(visible){ + if (this.args.visible != visible){ + this.args.visible = visible; + this.changed.notify(this); + } +}; + +ItemFormat.prototype.getVisible = function(){return this.args.visible;}; + +ItemFormat.prototype.setHidden = function(hidden){ + if (this.args.hidden != hidden){ + this.args.hidden = hidden; + this.changed.notify(this); + } +}; + +ItemFormat.prototype.getHidden = function(){return this.args.hidden;}; + + +ItemFormat.prototype.setStroke = function(stroke){ + if (this.args.stroke != stroke){ + this.args.stroke = stroke; + this.changed.notify(this); + } +}; + +ItemFormat.prototype.getStroke = function(){return this.args.stroke;}; + +ItemFormat.prototype.setStrokeOpacity = function(strokeOpacity){ + if (this.args.strokeOpacity != strokeOpacity){ + this.args.strokeOpacity = strokeOpacity; + this.changed.notify(this); + } +}; + +ItemFormat.prototype.getStrokeOpacity = function(){return this.args["stroke-opacity"];}; + +ItemFormat.prototype.setStrokeWidth = function(strokeWidth){ + if (this.args["stroke-width"] != strokeWidth){ + this.args["stroke-width"] = strokeWidth; + this.changed.notify(this); + } +}; + + +ItemFormat.prototype.getFillOpacity = function(){return this.args["fill-opacity"];}; + +ItemFormat.prototype.setfillOpacity = function(strokeWidth){ + if (this.args["fill-opacity"] != strokeWidth){ + this.args["fill-opacity"] = strokeWidth; + this.changed.notify(this); + } +}; + + +ItemFormat.prototype.getStrokeWidth = function(){ + return this.args["stroke-width"]; +}; + +ItemFormat.prototype.setFill = function(fill){ + if (this.args.fill != fill){ + this.args.fill = fill; + this.changed.notify(this); + } +}; + +ItemFormat.prototype.getFill = function(){return this.args.fill;}; + +ItemFormat.prototype.setSize = function(size){ + if (this.args.size != size){ + this.args.size = size; + this.changed.notify(this); + } +}; + +ItemFormat.prototype.getSize = function(){return this.args.size;}; + +ItemFormat.prototype.setOpacity = function(opacity){ + if (this.args.opacity != opacity){ + this.args.opacity = opacity; + this.changed.notify(this); + } +}; + +ItemFormat.prototype.getOpacity = function(){return this.args.opacity;}; + +ItemFormat.prototype.getArrowSize = function(){return this.args.arrowSize;}; + +ItemFormat.prototype.setArrowSize = function(arrowSize){ + if (this.args.arrowSize != arrowSize){ + this.args.arrowSize = arrowSize; + this.changed.notify(this); + } +}; + +ItemFormat.prototype.getFontSize = function(){return this.args.title.fontSize;}; + +ItemFormat.prototype.setFontSize = function(fontSize){ + + if (this.args.title.fontSize != fontSize){ + this.args.title.fontSize = fontSize; + this.changed.notify(this); + } +}; + + + + /** * This class executes the actions @@ -4952,1101 +4952,2095 @@ function openExperiment(experimentId) { BIOSAXS.openExperiment(experimentId); } -var SITE_CONF = { - SAMPLE_CHANGER_CONFIGURATION : { - "3":{ - "platetype3VO": {"plateTypeId":4,"name":"96 Well plate","rowCount":8,"columnCount":12,"shape":"REC"}, - "name":"96 Well plate", - "slotPositionRow":"1", - "slotPositionColumn":"3", - "storageTemperature":"0", - "sampleplateposition3VOs":[], - "experimentId":0 - }, - "2":{ - "platetype3VO":{"plateTypeId":2,"name":" 4 x ( 8 + 3 ) Block","rowCount":4,"columnCount":11,"shape":"REC"}, - "name":" 4 x ( 8 + 3 ) Block", - "slotPositionRow":"1", - "slotPositionColumn":"2", - "storageTemperature":"0", - "sampleplateposition3VOs":[],"experimentId":0 - }, - "1":{ - "platetype3VO":{"plateTypeId":1,"name":"Deep Well","rowCount":8,"columnCount":12,"shape":"REC"}, - "name":"Deep Well", - "slotPositionRow":"1", - "slotPositionColumn":"1", - "storageTemperature":"0", - "sampleplateposition3VOs":[], - "experimentId":0 - } - } +var SITE_CONF = { + SAMPLE_CHANGER_CONFIGURATION : { + "3":{ + "platetype3VO": {"plateTypeId":4,"name":"96 Well plate","rowCount":8,"columnCount":12,"shape":"REC"}, + "name":"96 Well plate", + "slotPositionRow":"1", + "slotPositionColumn":"3", + "storageTemperature":"0", + "sampleplateposition3VOs":[], + "experimentId":0 + }, + "2":{ + "platetype3VO":{"plateTypeId":2,"name":" 4 x ( 8 + 3 ) Block","rowCount":4,"columnCount":11,"shape":"REC"}, + "name":" 4 x ( 8 + 3 ) Block", + "slotPositionRow":"1", + "slotPositionColumn":"2", + "storageTemperature":"0", + "sampleplateposition3VOs":[],"experimentId":0 + }, + "1":{ + "platetype3VO":{"plateTypeId":1,"name":"Deep Well","rowCount":8,"columnCount":12,"shape":"REC"}, + "name":"Deep Well", + "slotPositionRow":"1", + "slotPositionColumn":"1", + "storageTemperature":"0", + "sampleplateposition3VOs":[], + "experimentId":0 + } + } }; - -var ISPYB_CONF = { - load : function(config) { - for (var key in config) { - ISPYB_CONF[key] = config[key]; - } - }, - SAMPLE_CHANGER_CONFIGURATION : { - "1": { - "platetype3VO": {"plateTypeId": 1, "name": "Deep Well", "rowCount": 8, "columnCount": 12, "shape": "REC"}, - "name": "Deep Well", - "slotPositionRow": "1", - "slotPositionColumn": "1", - "storageTemperature": "0", - "sampleplateposition3VOs": [], - "experimentId": 0 - } - } + +var ISPYB_CONF = { + load : function(config) { + for (var key in config) { + ISPYB_CONF[key] = config[key]; + } + }, + SAMPLE_CHANGER_CONFIGURATION : { + "1": { + "platetype3VO": {"plateTypeId": 1, "name": "Deep Well", "rowCount": 8, "columnCount": 12, "shape": "REC"}, + "name": "Deep Well", + "slotPositionRow": "1", + "slotPositionColumn": "1", + "storageTemperature": "0", + "sampleplateposition3VOs": [], + "experimentId": 0 + } + } }; -var SITE_CONF = { - -}; +var SITE_CONF = { + +}; -/** - * Example form - * - * @witdh - * @height - */ -function AbinitioForm(args) { - this.id = BUI.id(); - this.width = null; - this.height = null; - - if (args != null) { - if (args.width != null) { - this.width = args.width; - } - if (args.height != null) { - this.height = args.height; - } - } - - var _this = this; - /** Widgets **/ - this.abinitioGrid = new AbinitioGrid({ - width : null, - height : 200 - }); - - this.abinitioGrid.onSelected.attach(function(sender, models) { - var modelsIdList = []; - for ( var i in models) { - modelsIdList.push(models[i].modelId); - } - _this._renderPlot(_this.subtractions[0].subtractionId, modelsIdList); - _this._renderPDB(modelsIdList); - }); - - /** Dygraph Widget that plots fir files**/ - this.plotWidget = new PlotWidget({ - width : 745 / 2, - height : 300, - margin : "10 0 5 10" - }); - - /** PDB viewer **/ - this.viewer = new PDBViewer({ - width : 745 / 2, - height : 300 - }); - -} - -AbinitioForm.prototype._renderPlot = function(subtractionId, modelsIdList) { - /** Trying to plot tje subtraction and the models **/ - try { - var colors = [ "#669900", "#0000FF" ]; - this.plotWidget.refresh([], [ ], modelsIdList, [], [], colors); - } catch (e) { - console.log(e); - } -}; - -AbinitioForm.prototype._renderPDB = function(modelsIdList) { - /** Trying to plot the PDB file **/ - try { - var viz = []; - for (var i = 0; i < modelsIdList.length; i++) { - viz.push({ - modelId : modelsIdList[i], - color : "0xFF6600", - opacity : 0.8 - }); - } - this.viewer.refresh(viz); - } catch (e) { - console.log(e); - } -}; - -AbinitioForm.prototype._getItems = function() { - var _this = this; - return [ { - xtype : 'label', - forId : 'myFieldId', - text : 'INLINE HELP: To be updated', - margin : '15 0 20 10', - cls : "inline-help" - }, this.abinitioGrid.getPanel(), { - xtype : 'container', - layout : 'hbox', - items : [ this.plotWidget.getPanel(), this.viewer.getPanel() ] - } ] -}; - -AbinitioForm.prototype._getButtons = function() { - return []; -}; - -AbinitioForm.prototype.getPanel = function() { - var _this = this; - this.panel = Ext.create('Ext.form.Panel', { - width : this.width, - height : this.height, - margin : 10, - border : 1, - defaultType : 'textfield', - items : this._getItems(), - buttons : this._getButtons(), - listeners : { - afterrender : function() { - _this._populate(); - - } - } - }); - return this.panel; -}; - -/** Populates could be call when the DOM is not filled yet **/ -AbinitioForm.prototype._populate = function() { -}; - -/** It populates the form * */ -AbinitioForm.prototype.refresh = function(subtractions) { - this.subtractions = subtractions; - this.abinitioGrid.refresh(subtractions); -}; - -AbinitioForm.prototype.input = function() { - return {}; -}; - -/** It populates the form **/ -AbinitioForm.prototype.test = function(targetId) { - var macromoleculeForm = new AbinitioForm(); - var panel = macromoleculeForm.getPanel(); - panel.render(targetId); -}; +/** + * Example form + * + * @witdh + * @height + */ +function AbinitioForm(args) { + this.id = BUI.id(); + this.width = null; + this.height = null; -/** - * Example form - * - * @witdh - * @height - */ -function DataReductionForm(args) { - this.id = BUI.id(); - this.width = null; - this.height = null; - - if (args != null) { - if (args.width != null) { - this.width = args.width; - } - if (args.height != null) { - this.height = args.height; - } - } - + if (args != null) { + if (args.width != null) { + this.width = args.width; + } + if (args.height != null) { + this.height = args.height; + } + } + + var _this = this; + /** Widgets **/ + this.abinitioGrid = new AbinitioGrid({ + width : null, + height : 200 + }); + + this.abinitioGrid.onSelected.attach(function(sender, models) { + var modelsIdList = []; + for ( var i in models) { + modelsIdList.push(models[i].modelId); + } + _this._renderPlot(_this.subtractions[0].subtractionId, modelsIdList); + _this._renderPDB(modelsIdList); + }); + + /** Dygraph Widget that plots fir files**/ + this.plotWidget = new PlotWidget({ + width : 745 / 2, + height : 300, + margin : "10 0 5 10" + }); + + /** PDB viewer **/ + this.viewer = new PDBViewer({ + width : 745 / 2, + height : 300 + }); + +} + +AbinitioForm.prototype._renderPlot = function(subtractionId, modelsIdList) { + /** Trying to plot tje subtraction and the models **/ + try { + var colors = [ "#669900", "#0000FF" ]; + this.plotWidget.refresh([], [ ], modelsIdList, [], [], colors); + } catch (e) { + console.log(e); + } +}; + +AbinitioForm.prototype._renderPDB = function(modelsIdList) { + /** Trying to plot the PDB file **/ + try { + var viz = []; + for (var i = 0; i < modelsIdList.length; i++) { + viz.push({ + modelId : modelsIdList[i], + color : "0xFF6600", + opacity : 0.8 + }); + } + this.viewer.refresh(viz); + } catch (e) { + console.log(e); + } +}; + +AbinitioForm.prototype._getItems = function() { + var _this = this; + return [ { + xtype : 'label', + forId : 'myFieldId', + text : 'INLINE HELP: To be updated', + margin : '15 0 20 10', + cls : "inline-help" + }, this.abinitioGrid.getPanel(), { + xtype : 'container', + layout : 'hbox', + items : [ this.plotWidget.getPanel(), this.viewer.getPanel() ] + } ] +}; + +AbinitioForm.prototype._getButtons = function() { + return []; +}; + +AbinitioForm.prototype.getPanel = function() { + var _this = this; + this.panel = Ext.create('Ext.form.Panel', { + width : this.width, + height : this.height, + margin : 10, + border : 1, + defaultType : 'textfield', + items : this._getItems(), + buttons : this._getButtons(), + listeners : { + afterrender : function() { + _this._populate(); + + } + } + }); + return this.panel; +}; + +/** Populates could be call when the DOM is not filled yet **/ +AbinitioForm.prototype._populate = function() { +}; + +/** It populates the form * */ +AbinitioForm.prototype.refresh = function(subtractions) { + this.subtractions = subtractions; + this.abinitioGrid.refresh(subtractions); +}; + +AbinitioForm.prototype.input = function() { + return {}; +}; + +/** It populates the form **/ +AbinitioForm.prototype.test = function(targetId) { + var macromoleculeForm = new AbinitioForm(); + var panel = macromoleculeForm.getPanel(); + panel.render(targetId); +}; + +/** + * Example form + * + * @witdh + * @height + */ +function DataReductionForm(args) { + this.id = BUI.id(); + this.width = null; + this.height = null; + + if (args != null) { + if (args.width != null) { + this.width = args.width; + } + if (args.height != null) { + this.height = args.height; + } + } + + var _this = this; + + /** Widgets **/ + this.plotWidget = new PlotWidget({ + width : 650, + height : 490 + }); + + /** Selected frames to be displayed **/ + this.selectedItems = { + frames : [], + averages : [], + subtractions : [] + }; + +} + +DataReductionForm.prototype._parseSelectedItemsToIds = function(selectedArray) { + var ids = []; + if (selectedArray != null) { + for (var i = 0; i < selectedArray.length; i++) { + ids.push(selectedArray[i].id); + } + } + return ids; +}; + +DataReductionForm.prototype.onSelectionChanged = function(columnName, selected) { + if (selected != null) { + if (columnName == "Frames") { + this.selectedItems.frames = selected; + this.selectedItems.subtractions = []; + } + if (columnName == "Averages") { + this.selectedItems.averages = selected; + } + if (columnName == "Subtractions") { + this.selectedItems.frames = []; + this.selectedItems.subtractions = selected; + } + } + this.plotWidget.refresh(this._parseSelectedItemsToIds(this.selectedItems.frames), this._parseSelectedItemsToIds(this.selectedItems.subtractions)); +}; + +DataReductionForm.prototype._getFramesExtPanel = function(store, columnName, height) { + var _this = this; + + var selModel = Ext.create('Ext.selection.RowModel', { + allowDeselect : true, + mode : 'MULTI', + listeners : { + selectionchange : function(sm, selections) { + var selected = []; + for (var i = 0; i < selections.length; i++) { + selected.push(selections[i].raw); + } + _this.onSelectionChanged(columnName, selected); + } + } + }); + + return Ext.create('Ext.grid.Panel', { + store : store, + margin : 10, + height : height, + width : 200, + selModel : selModel, + columns : [ { + text : columnName, + dataIndex : 'fileName', + flex : 1 + } ], + viewConfig : { + } + }); +}; + +DataReductionForm.prototype._getFramesPanel = function() { + var fields = [ 'fileName', 'type', 'id' ]; + + this.framesStore = Ext.create('Ext.data.Store', { + fields : fields, + sorters : 'fileName' + }); + + this.averagesStore = Ext.create('Ext.data.Store', { + fields : fields + }); + + this.subtractionStore = Ext.create('Ext.data.Store', { + fields : fields + }); + + var gridFrames = this._getFramesExtPanel(this.framesStore, "Frames", 375); + var gridAvgs = this._getFramesExtPanel(this.averagesStore, "Averages", 125); + var subtractionAvgs = this._getFramesExtPanel(this.subtractionStore, "Subtractions", 75); + + return { + xtype : 'container', + layout : 'vbox', + items : [ gridFrames, subtractionAvgs ] + }; +}; + +DataReductionForm.prototype._getImageContainer = function(name, help) { + var html = "
" + name + "
" + return { + xtype : 'container', + layout : 'vbox', + items : [ { + html : html, + margin : "5 0 0 0", + height : 95, + width : 100 + }, { + xtype : 'label', + forId : 'myFieldId', + text : help, + margin : '5 0 0 0', + cls : "inline-help" + } ] + } +}; + +DataReductionForm.prototype._getItems = function() { + var _this = this; + return [ { + xtype : 'container', + layout : 'hbox', + items : [ + this._getFramesPanel(), + this.plotWidget.getPanel(), + { + xtype : 'panel', + width : 110, + frame : true, + margin : "10 5 5 5", + border : 0, + layout : 'vbox', + items : [ this._getImageContainer("scattering", "Scattering"), this._getImageContainer("guinier", "Guinier Region"), + this._getImageContainer("kratky", "Kratky Plot"), this._getImageContainer("gnom", "P(r) distribution") ] + } ] + } ] +}; + +DataReductionForm.prototype._getButtons = function() { + return []; +}; + +DataReductionForm.prototype.getPanel = function() { + var _this = this; + this.panel = Ext.create('Ext.form.Panel', { + width : this.width, + height : this.height, + border : 0, + items : this._getItems(), + buttons : this._getButtons(), + listeners : { + afterrender : function() { + _this._populate(); + } + } + }); + return this.panel; +}; + +/** Populates could be call when the DOM is not filled yet **/ +DataReductionForm.prototype._populate = function() { +}; + +/** It populates the form * */ +DataReductionForm.prototype.refresh = function(subtractions) { + if (subtractions != null) { + for (var i = 0; i < subtractions.length; i++) { + /** Loading frame grids **/ + var subtraction = subtractions[i]; + var averages = [ { + fileName : BUI.getFileName(subtraction.bufferAverageFilePath), + type : 'bufferAvg', + id : subtraction.subtractionId + }, { + fileName : BUI.getFileName(subtraction.sampleAverageFilePath), + type : 'sampleAvg', + id : subtraction.subtractionId + } + + ]; + this.averagesStore.loadData(averages, true); + this.subtractionStore.loadData([ { + fileName : BUI.getFileName(subtraction.substractedFilePath), + type : 'SUBTRACTION', + id : subtraction.subtractionId + } ], true); + + var frames = []; + /** Buffers **/ + if (subtraction.bufferOneDimensionalFiles != null) { + if (subtraction.bufferOneDimensionalFiles.frametolist3VOs != null) { + for (var j = 0; j < subtraction.bufferOneDimensionalFiles.frametolist3VOs.length; j++) { + var frametolist3VO = subtraction.bufferOneDimensionalFiles.frametolist3VOs[j]; + if (frametolist3VO.frame3VO != null) { + frames.push({ + fileName : BUI.getFileName(frametolist3VO.frame3VO.filePath), + type : 'BUFFER', + id : frametolist3VO.frame3VO.frameId + }); + } + } + } + } + /** Samples **/ + if (subtraction.sampleOneDimensionalFiles != null) { + if (subtraction.sampleOneDimensionalFiles.frametolist3VOs != null) { + for (var j = 0; j < subtraction.sampleOneDimensionalFiles.frametolist3VOs.length; j++) { + var frametolist3VO = subtraction.sampleOneDimensionalFiles.frametolist3VOs[j]; + if (frametolist3VO.frame3VO != null) { + frames.push({ + fileName : BUI.getFileName(frametolist3VO.frame3VO.filePath), + type : 'SAMPLE', + id : frametolist3VO.frame3VO.frameId + }); + } + } + } + } + + this.framesStore.loadData(frames, true); + + /** Loading images **/ + this._displayImage("scattering", subtraction.subtractionId); + this._displayImage("kratky", subtraction.subtractionId); + this._displayImage("guinier", subtraction.subtractionId); + this._displayImage("gnom", subtraction.subtractionId); + } + } +}; + +DataReductionForm.prototype._displayImage = function(name, subtractionId) { + var url = BUI.getURL() + '&type=' + name + '&subtractionId=' + subtractionId; + var event = "OnClick= window.open('" + url + "')"; + document.getElementById(this.id + "_" + name).innerHTML = ''; +}; + +DataReductionForm.prototype.input = function() { + return {}; +}; + +/** It populates the form **/ +DataReductionForm.prototype.test = function(targetId) { + var macromoleculeForm = new DataReductionForm(); + var panel = macromoleculeForm.getPanel(); + panel.render(targetId); +}; + +function PlotWidget(args) { + this.width = 600; + this.height = 600; + this.id = BUI.id(); + + this.linear = false; + + this.margin = "10 0 0 0"; + /** for each stat the max and minimum value when it is scaled in order to show correctly in the legend **/ + this.ranges = {}; + if (args != null) { + if (args.width != null) { + this.width = args.width; + } + if (args.height != null) { + this.height = args.height; + } + if (args.margin != null) { + this.margin = args.margin; + } + } + +} + +PlotWidget.prototype.getMenu = function() { + var _this = this; + /** Actions buttons **/ + var actions = []; + actions.push("->"); + actions.push({ + text : "Export as Image", + scope : this, + icon : '../images/save.gif', + handler : function(item, pressed) { + var largeImage = document.createElement("img"); + largeImage.style.display = 'block'; + largeImage.style.width = 200 + "px"; + largeImage.style.height = 200 + "px"; + largeImage.setAttribute('src', Dygraph.Export.asCanvas(this.dygraphObject.dygraph).toDataURL()); + window.open(Dygraph.Export.asCanvas(this.dygraphObject.dygraph).toDataURL(), 'Image', ''); + } + }); + + return actions; +}; + +/** Looks for the maximum value and then divide everything but that value **/ +PlotWidget.prototype.scaledData = function(data) { + for (var i = 0; i < data.length; i++) { + var values = this.getMaxAndMinValue(data[i]); + data[i] = this.divideValuesByMax(data[i], values.max); + this.ranges[data[i].label] = values; + } + return data; +}; + +/** Given a stat float[] and a max number it will divide each value by max **/ +PlotWidget.prototype.divideValuesByMax = function(stat, max) { + for (var j = 0; j < stat.data.length; j++) { + if (max != 0) { + stat.data[j] = Number(stat.data[j]) / max; + stat.std[j] = Number(stat.std[j]) / max; + } + } + return stat; +}; + +/** returns max value of a stat **/ +PlotWidget.prototype.getMaxAndMinValue = function(stat) { + var max = 0; + var min = stat.data[0]; + for (var j = 0; j < stat.data.length; j++) { + if (Number(stat.data[j]) > max) { + max = Number(stat.data[j]); + } + if (Number(stat.std[j]) > max) { + max = Number(stat.std[j]); + } + if (Number(stat.data[j]) < min) { + min = Number(stat.data[j]); + } + } + return { + max : Number(max), + min : Number(min) + }; +}; + +PlotWidget.prototype._renderDygraph = function(parsed, colors, labels) { + this.dygraphObject = new StdDevDyGraph(this.id, { + width : this.width - 20, + height : this.height - 40, + xlabel : "", + }); + + this.dygraphObject.draw(parsed, colors, labels); + +}; + +PlotWidget.prototype.getPanel = function() { + this.panel = Ext.create('Ext.panel.Panel', { + width : this.width, + height : this.height, + margin : this.margin, + tbar : this.getMenu(), + items : [ { + html : "", + id : this.id, + width : this.width, + height : this.height, + padding : 10, + margin : "0 0 0 -30", + border : 0 + } ] + }); + + return this.panel; +}; + +PlotWidget.prototype.getPoint = function(y, error) { + var minus = y - error; + var max = y + error; + + if (this.linear) { + return [ Math.abs(minus), y, Math.abs(max) ]; + } + if ((minus != 0) && (max != 0)) { + return [ Math.log(Math.abs(minus)), Math.log(y), Math.log(Math.abs(max)) ]; + } else { + return [ Math.log(y), Math.log(y), Math.log(y) ]; + } + +}; + +PlotWidget.prototype.getDataPlot = function(frames, subtractions, models, fits, rbms) { + var files = []; + var labels = [ "Intensity" ]; + if (frames != null) { + for (var i = 0; i < frames.length; i++) { + files.push(frames[i].data); + labels.push(frames[i].fileName); + } + } + function splitData(data, column, errorColumn, name){ + var result = [] + for (var j = 0; j < data.length; j++) { + console.log(data[j][column]); + result.push([j,data[j][column],data[j][errorColumn]]);//[0, data[i][column],0]]); + } + files.push(result); + labels.push(name); + } + + if (subtractions != null) { + for (var i = 0; i < subtractions.length; i++) { + /** For subtraction **/ + files.push(subtractions[i].subtraction.data); + labels.push(subtractions[i].subtraction.fileName); + /** For sample average **/ +// files.push(subtractions[i].sampleAvg.data); +// labels.push(subtractions[i].sampleAvg.fileName); + /** For buffer average **/ +// files.push(subtractions[i].bufferAvg.data); +// labels.push(subtractions[i].bufferAvg.fileName); + } + } + + if (models != null) { + for (var i = 0; i < models.length; i++) { + for ( var key in models[i]) { + splitData(models[i][key].fir.data, 1, 2, "Intensity"); + splitData(models[i][key].fir.data, 3, 3, "Fit"); + } + } + } + + if (fits != null) { + for (var i = 0; i < fits.length; i++) { + for ( var key in fits[i]) { + + /** adding fit file to be plotted **/ + if (fits[i][key].fit.data[0].length == 3){ + splitData(fits[i][key].fit.data, 1, 1, "Intensity"); + splitData(fits[i][key].fit.data, 2, 2, "Fit"); + } + + /** s, Iexp(s), err, Ifit(s). **/ + if (fits[i][key].fit.data[0].length == 4){ + splitData(fits[i][key].fit.data, 1, 2, "Intensity"); + splitData(fits[i][key].fit.data, 3, 3, "Fit"); + } + + if (fits[i][key].fit.data[0].length == 5){ + /** X Intensity Fit Error Residues **/ + + splitData(fits[i][key].fit.data, 1, 3, "Intensity"); + splitData(fits[i][key].fit.data, 2, 2, "Fit"); + } + } + } + } + + var dataPoints = []; + if (files.length > 0) { + for (var i = 0; i < files[0].length; i++) { + dataPoints.push([ files[0][i][0], this.getPoint(files[0][i][1], files[0][i][2]) ]); + } + if (files.length > 1) { + for (var i = 1; i < files.length; i++) { + for (var j = 0; j < dataPoints.length; j++) { + if (files[i][j] != null){ + dataPoints[j].push(this.getPoint(files[i][j][1], files[i][j][2])); + } + else{ + dataPoints[j].push([0,0,0]); + } + } + } + } + } + + return { + dataPoints : dataPoints, + labels : labels + } +}; + +PlotWidget.prototype.refresh = function(frames, subtractions, models, fits, rbms, colors) { + + var _this = this; + this.panel.setLoading("Reading Files"); + + var dataAdapter = new BiosaxsDataAdapter(); + dataAdapter.onSuccess.attach(function(sender, data) { + _this.panel.setLoading("Rendering"); + var parsed = _this.getDataPlot(data.frames, data.subtractions, data.models, data.fits, rbms); + _this._renderDygraph(parsed.dataPoints, colors, parsed.labels); + _this.panel.setLoading(false); + }); + dataAdapter.onError.attach(function(sender, data) { + _this.panel.setLoading(false); + }); + dataAdapter.getDataPlot(frames, subtractions, models, fits, rbms); +}; + +PlotWidget.prototype.input = function() { + return DATADOC.getHPLCData(); +}; + + +/** + * Fit form + * + * @witdh + * @height + */ +function FitForm(args) { + this.id = BUI.id(); + this.width = null; + this.height = null; + + if (args != null) { + if (args.width != null) { + this.width = args.width; + } + if (args.height != null) { + this.height = args.height; + } + } + + var _this = this; + + /** Widgets **/ + this.fitGrid = new FitStructureToExperimentDataGrid({ + width : null, + height : 200 + }); + + this.fitGrid.onSelected.attach(function(sender, fits) { + var modelsIdList = []; + for ( var i in fits) { + modelsIdList.push(fits[i].fitStructureToExperimentalDataId); + } + _this._renderPlot(_this.subtractions[0].subtractionId, modelsIdList); +// var adapter = new BiosaxsDataAdapter(); +// adapter.onSuccess.attach(function(sender, data) { +// +// }); +// adapter.getScatteringCurveByFrameIdsList([], [], [], ids); + }); + + + /** Dygraph Widget that plots fir files**/ + this.plotWidget = new PlotWidget({ + width : 745, + height : 300, + margin : "10 0 10 10" + }); +} + +FitForm.prototype._renderPlot = function(subtractionId, modelsIdList) { + /** Trying to plot tje subtraction and the models **/ + try { + var colors = [ "#669900", "#0000FF" ]; + + this.plotWidget.refresh([], [ ], [], modelsIdList, [], colors); + } catch (e) { + console.log(e); + } +}; + +FitForm.prototype._getItems = function() { + var _this = this; + return [ { + xtype : 'label', + forId : 'myFieldId', + text : 'INLINE HELP: To be updated', + margin : '15 0 20 10', + cls : "inline-help" + }, this.fitGrid.getPanel(), this.plotWidget.getPanel() ] + +}; + +FitForm.prototype._getButtons = function() { + return []; +}; + +FitForm.prototype.getPanel = function() { + var _this = this; + this.panel = Ext.create('Ext.form.Panel', { + width : this.width, + height : this.height, + margin : 10, + border : 1, + defaultType : 'textfield', + items : this._getItems(), + buttons : this._getButtons(), + listeners : { + afterrender : function() { + _this._populate(); + + } + } + }); + return this.panel; +}; + +/** Populates could be call when the DOM is not filled yet **/ +FitForm.prototype._populate = function() { +}; + +/** It populates the form * */ +FitForm.prototype.refresh = function(subtractions) { + this.subtractions = subtractions; + this.fitGrid.refresh(subtractions); +}; + +FitForm.prototype.input = function() { + return {}; +}; + +/** It populates the form **/ +FitForm.prototype.test = function(targetId) { + var macromoleculeForm = new FitForm(); + var panel = macromoleculeForm.getPanel(); + panel.render(targetId); +}; + +/** + * Example form + * + * @witdh + * @height + */ +function RigidBodyModelingResultForm(args) { + this.id = BUI.id(); + this.width = null; + this.height = null; + + if (args != null) { + if (args.width != null) { + this.width = args.width; + } + if (args.height != null) { + this.height = args.height; + } + } + + var _this = this; + /** Widgets **/ + this.rigidModelGrid = new RigidModelGrid({ + width : null, + height : 200 + }); + + this.rigidModelGrid.onSelected.attach(function(sender, fits){ + var ids = []; + for ( var i in fits) { + ids.push(fits[i].fitStructureToExperimentalDataId); + } + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, data){ +// debugger + + }); + + adapter.getScatteringCurveByFrameIdsList([], [], [], ids); + }); + +} + +RigidBodyModelingResultForm.prototype._getItems = function() { + var _this = this; + return [ { + xtype : 'label', + forId : 'myFieldId', + text : 'INLINE HELP: To be updated', + margin : '15 0 20 10', + cls : "inline-help" + }, this.rigidModelGrid.getPanel() ] + +}; + +RigidBodyModelingResultForm.prototype._getButtons = function() { + return []; +}; + +RigidBodyModelingResultForm.prototype.getPanel = function() { + var _this = this; + this.panel = Ext.create('Ext.form.Panel', { + width : this.width, + height : this.height, + margin : 10, + border : 1, + defaultType : 'textfield', + items : this._getItems(), + buttons : this._getButtons(), + listeners : { + afterrender : function() { + _this._populate(); + + } + } + }); + return this.panel; +}; + +/** Populates could be call when the DOM is not filled yet **/ +RigidBodyModelingResultForm.prototype._populate = function() { +}; + +/** It populates the form * */ +RigidBodyModelingResultForm.prototype.refresh = function(subtractions) { + this.rigidModelGrid.refresh(subtractions); +}; + +RigidBodyModelingResultForm.prototype.input = function() { + return {}; +}; + +/** It populates the form **/ +RigidBodyModelingResultForm.prototype.test = function(targetId) { + var macromoleculeForm = new RigidBodyModelingResultForm(); + var panel = macromoleculeForm.getPanel(); + panel.render(targetId); +}; + +/** + * Example form + * + * @witdh + * @height + */ +function SuperpositionForm(args) { + this.id = BUI.id(); + this.width = null; + this.height = null; + + if (args != null) { + if (args.width != null) { + this.width = args.width; + } + if (args.height != null) { + this.height = args.height; + } + } + + var _this = this; + + /** Widgets **/ + this.superpositionGrid = new SuperpositionGrid({ + width : null, + height : 200 + }); + + this.superpositionGrid.onSelected.attach(function(sender, superpositions) { + var ids = []; + for ( var i in superpositions) { + ids.push(superpositions[i].superpositionId); + } +// _this._renderAbinitio(ids); +// _this.aprioriPDBViewer.refresh(); + _this._renderAligned(ids); + // getAlignedPDBContentBySuperpositionList + }); + + /** PDB viewer **/ +// this.abinitioPDBViewer = new PDBViewer({ +// width : 860 / 2, +// height : 300 / 2, +// margin : 0 +// }); +// +// /** PDB viewer **/ +// this.aprioriPDBViewer = new StructurePDBViewer({ +// width : 860 / 2, +// height : 300 / 2, +// margin : 0 +// }); + + this.alignedPDBViewer = new AlignedSuperpositionPDBViewer({ + width : 860 , + height : 300 + }); + +} + +SuperpositionForm.prototype._renderAligned = function(modelsIdList) { + /** Trying to plot the PDB file **/ + try { + this.alignedPDBViewer.refresh(modelsIdList); + } catch (e) { + console.log(e); + } +}; + +SuperpositionForm.prototype._renderAbinitio = function(modelsIdList) { + /** Trying to plot the PDB file **/ + try { + var viz = []; + for (var i = 0; i < modelsIdList.length; i++) { + viz.push({ + modelId : modelsIdList[i], + color : "0xFF6600", + opacity : 0.8 + }); + } + this.abinitioPDBViewer.refresh(viz); + } catch (e) { + console.log(e); + } +}; + +SuperpositionForm.prototype._getItems = function() { + var _this = this; + return [ { + xtype : 'label', + forId : 'myFieldId', + text : 'INLINE HELP: To be updated', + margin : '15 0 20 10', + cls : "inline-help" + }, this.superpositionGrid.getPanel(), { + xtype : 'container', + layout : 'hbox', + items : [ +// { +// xtype : 'container', +// layout : 'vbox', +// items : [ this.abinitioPDBViewer.getPanel(), this.aprioriPDBViewer.getPanel() +// +// ] +// }, + + this.alignedPDBViewer.getPanel() ] + } ] + +}; + +SuperpositionForm.prototype._getButtons = function() { + return []; +}; + +SuperpositionForm.prototype.getPanel = function() { + var _this = this; + this.panel = Ext.create('Ext.form.Panel', { + width : this.width, + height : this.height, + margin : 10, + border : 1, + defaultType : 'textfield', + items : this._getItems(), + buttons : this._getButtons(), + listeners : { + afterrender : function() { + _this._populate(); + + } + } + }); + return this.panel; +}; + +/** Populates could be call when the DOM is not filled yet **/ +SuperpositionForm.prototype._populate = function() { +}; + +/** It populates the form * */ +SuperpositionForm.prototype.refresh = function(subtractions) { + this.subtractions = subtractions; + this.superpositionGrid.refresh(subtractions); +}; + +SuperpositionForm.prototype.input = function() { + return {}; +}; + +/** It populates the form **/ +SuperpositionForm.prototype.test = function(targetId) { + var macromoleculeForm = new SuperpositionForm(); + var panel = macromoleculeForm.getPanel(); + panel.render(targetId); +}; + +/** + * Example form + * + * @witdh + * @height + */ +function AssemblyForm(args) { + this.id = BUI.id(); + this.width = 700; + this.height = 500; + + if (args != null) { + if (args.width != null) { + this.width = args.width; + } + if (args.height != null) { + this.height = args.height; + } + } + + this.molarityGrid = new MolarityGrid({height : this.height - 50}); +} + +AssemblyForm.prototype._getItems = function() { + return [ { + xtype : 'label', + forId : 'myFieldId', + text : 'List of previously defined macromolecules present in the assembly. This information will be used for additional cross-checks where possible', + margin : '15 0 20 10', + cls : "inline-help" + }, this.molarityGrid.getPanel() ]; +}; + +AssemblyForm.prototype._getButtons = function() { + return []; +}; + +AssemblyForm.prototype.getPanel = function() { + this.panel = Ext.create('Ext.form.Panel', { + width : this.width, + height : this.height, + margin : 10, + border : 0, + defaultType : 'textfield', + items : this._getItems(), + buttons : this._getButtons() + }); + return this.panel; +}; + + +/** It populates the form **/ +AssemblyForm.prototype.refresh = function(macromolecule) { + this.macromolecule = macromolecule; + this.molarityGrid.refresh(macromolecule); +}; + +AssemblyForm.prototype.input = function() { + return {}; +}; + + +AssemblyForm.prototype.test = function(targetId) { + var assemblyForm = new AssemblyForm(); + + var panel = assemblyForm.getPanel(); + panel.render(targetId); +}; +/** + * Edit the information of a buffer + * + * #onSaved + * #onRemoveAdditive + */ +function BufferForm() { var _this = this; - /** Widgets **/ - this.plotWidget = new PlotWidget({ - width : 650, - height : 490 + this.additiveGrid = new AdditiveGrid(); + this.additiveGrid.onRemoveButtonClicked.attach(function(sender, args) { + _this.onRemoveAdditive.notify(args); }); - /** Selected frames to be displayed **/ - this.selectedItems = { - frames : [], - averages : [], - subtractions : [] - }; - + this.onSaved = new Event(this); + this.onRemoveAdditive = new Event(this); } -DataReductionForm.prototype._parseSelectedItemsToIds = function(selectedArray) { - var ids = []; - if (selectedArray != null) { - for (var i = 0; i < selectedArray.length; i++) { - ids.push(selectedArray[i].id); - } - } - return ids; -}; - -DataReductionForm.prototype.onSelectionChanged = function(columnName, selected) { - if (selected != null) { - if (columnName == "Frames") { - this.selectedItems.frames = selected; - this.selectedItems.subtractions = []; - } - if (columnName == "Averages") { - this.selectedItems.averages = selected; - } - if (columnName == "Subtractions") { - this.selectedItems.frames = []; - this.selectedItems.subtractions = selected; - } - } - this.plotWidget.refresh(this._parseSelectedItemsToIds(this.selectedItems.frames), this._parseSelectedItemsToIds(this.selectedItems.subtractions)); -}; - -DataReductionForm.prototype._getFramesExtPanel = function(store, columnName, height) { - var _this = this; - - var selModel = Ext.create('Ext.selection.RowModel', { - allowDeselect : true, - mode : 'MULTI', - listeners : { - selectionchange : function(sm, selections) { - var selected = []; - for (var i = 0; i < selections.length; i++) { - selected.push(selections[i].raw); - } - _this.onSelectionChanged(columnName, selected); - } - } - }); - - return Ext.create('Ext.grid.Panel', { - store : store, - margin : 10, - height : height, - width : 200, - selModel : selModel, - columns : [ { - text : columnName, - dataIndex : 'fileName', - flex : 1 - } ], - viewConfig : { - } - }); +BufferForm.prototype.getBuffer = function() { + this.buffer.name = Ext.getCmp("buffer_name").getValue(); + this.buffer.acronym = Ext.getCmp("buffer_acronym").getValue(); + this.buffer.comments = Ext.getCmp("buffer_comments").getValue(); + this.buffer.ph = Ext.getCmp("buffer_ph").getValue(); + this.buffer.composition = Ext.getCmp("buffer_composition").getValue(); + this.buffer.bufferhasadditive3VOs = this.additiveGrid.getAdditives(); + return this.buffer; }; -DataReductionForm.prototype._getFramesPanel = function() { - var fields = [ 'fileName', 'type', 'id' ]; - - this.framesStore = Ext.create('Ext.data.Store', { - fields : fields, - sorters : 'fileName' - }); - - this.averagesStore = Ext.create('Ext.data.Store', { - fields : fields - }); - - this.subtractionStore = Ext.create('Ext.data.Store', { - fields : fields - }); - - var gridFrames = this._getFramesExtPanel(this.framesStore, "Frames", 375); - var gridAvgs = this._getFramesExtPanel(this.averagesStore, "Averages", 125); - var subtractionAvgs = this._getFramesExtPanel(this.subtractionStore, "Subtractions", 75); - - return { - xtype : 'container', - layout : 'vbox', - items : [ gridFrames, subtractionAvgs ] - }; -}; - -DataReductionForm.prototype._getImageContainer = function(name, help) { - var html = "
" + name + "
" +BufferForm.prototype._getTopPanel = function() { return { xtype : 'container', - layout : 'vbox', + layout : 'hbox', + border : 0, + frame : true, items : [ { - html : html, - margin : "5 0 0 0", - height : 95, - width : 100 + xtype : 'container', + layout : 'hbox', + items : [ { + xtype : 'container', + flex : 1, + border : false, + layout : 'anchor', + defaultType : 'textfield', + items : [ { + xtype : 'requiredtext', + id : 'buffer_name', + fieldLabel : 'Name', + name : 'name', + width : '200px', + value : this.buffer.name + }, { + xtype : 'requiredtext', + id : 'buffer_acronym', + fieldLabel : 'Acronym', + name : 'acronym', + width : '200px', + value : this.buffer.acronym + } ] + } ] }, { - xtype : 'label', - forId : 'myFieldId', - text : help, - margin : '5 0 0 0', - cls : "inline-help" + xtype : 'container', + flex : 1, + layout : 'anchor', + defaultType : 'textfield', + margin : '0 0 0 10', + items : [ { + id : 'buffer_ph', + fieldLabel : 'pH', + name : 'ph', + value : this.buffer.ph, + xtype : 'numberfield', + width : 200, + minValue : 0, + maxValue : 15 + }, { + xtype : 'requiredtext', + id : 'buffer_composition', + fieldLabel : 'Composition', + name : 'composition', + width : 200, + value : this.buffer.composition + } ] } ] - } + }; }; -DataReductionForm.prototype._getItems = function() { - var _this = this; - return [ { +BufferForm.prototype.getPanel = function(buffer) { + this.buffer = buffer; + this.panel = Ext.createWidget({ xtype : 'container', - layout : 'hbox', - items : [ - this._getFramesPanel(), - this.plotWidget.getPanel(), - { - xtype : 'panel', - width : 110, - frame : true, - margin : "10 5 5 5", - border : 0, - layout : 'vbox', - items : [ this._getImageContainer("scattering", "Scattering"), this._getImageContainer("guinier", "Guinier Region"), - this._getImageContainer("kratky", "Kratky Plot"), this._getImageContainer("gnom", "P(r) distribution") ] - } ] - } ] -}; - -DataReductionForm.prototype._getButtons = function() { - return []; -}; + layout : 'vbox', + style : { + padding : '10px' + }, + fieldDefaults : { + labelAlign : 'left', + labelWidth : 50 -DataReductionForm.prototype.getPanel = function() { - var _this = this; - this.panel = Ext.create('Ext.form.Panel', { - width : this.width, - height : this.height, - border : 0, - items : this._getItems(), - buttons : this._getButtons(), - listeners : { - afterrender : function() { - _this._populate(); - } - } + }, + items : [ this._getTopPanel(), { + id : 'buffer_comments', + xtype : 'textareafield', + name : 'comments', + fieldLabel : 'Comments', + width : '100%', + value : buffer.comments + }, this.additiveGrid.getPanel(buffer) ] }); return this.panel; }; -/** Populates could be call when the DOM is not filled yet **/ -DataReductionForm.prototype._populate = function() { -}; - -/** It populates the form * */ -DataReductionForm.prototype.refresh = function(subtractions) { - if (subtractions != null) { - for (var i = 0; i < subtractions.length; i++) { - /** Loading frame grids **/ - var subtraction = subtractions[i]; - var averages = [ { - fileName : BUI.getFileName(subtraction.bufferAverageFilePath), - type : 'bufferAvg', - id : subtraction.subtractionId - }, { - fileName : BUI.getFileName(subtraction.sampleAverageFilePath), - type : 'sampleAvg', - id : subtraction.subtractionId - } - - ]; - this.averagesStore.loadData(averages, true); - this.subtractionStore.loadData([ { - fileName : BUI.getFileName(subtraction.substractedFilePath), - type : 'SUBTRACTION', - id : subtraction.subtractionId - } ], true); - - var frames = []; - /** Buffers **/ - if (subtraction.bufferOneDimensionalFiles != null) { - if (subtraction.bufferOneDimensionalFiles.frametolist3VOs != null) { - for (var j = 0; j < subtraction.bufferOneDimensionalFiles.frametolist3VOs.length; j++) { - var frametolist3VO = subtraction.bufferOneDimensionalFiles.frametolist3VOs[j]; - if (frametolist3VO.frame3VO != null) { - frames.push({ - fileName : BUI.getFileName(frametolist3VO.frame3VO.filePath), - type : 'BUFFER', - id : frametolist3VO.frame3VO.frameId - }); - } - } - } - } - /** Samples **/ - if (subtraction.sampleOneDimensionalFiles != null) { - if (subtraction.sampleOneDimensionalFiles.frametolist3VOs != null) { - for (var j = 0; j < subtraction.sampleOneDimensionalFiles.frametolist3VOs.length; j++) { - var frametolist3VO = subtraction.sampleOneDimensionalFiles.frametolist3VOs[j]; - if (frametolist3VO.frame3VO != null) { - frames.push({ - fileName : BUI.getFileName(frametolist3VO.frame3VO.filePath), - type : 'SAMPLE', - id : frametolist3VO.frame3VO.frameId - }); - } - } - } - } - - this.framesStore.loadData(frames, true); - - /** Loading images **/ - this._displayImage("scattering", subtraction.subtractionId); - this._displayImage("kratky", subtraction.subtractionId); - this._displayImage("guinier", subtraction.subtractionId); - this._displayImage("gnom", subtraction.subtractionId); +BufferForm.prototype.input = function() { + return { + buffer : { + "bufferId" : 422, + "proposalId" : 10, + "safetyLevelId" : null, + "name" : "B1", + "acronym" : "B1", + "ph" : null, + "composition" : null, + "bufferhasadditive3VOs" : [], + "comments" : null } - } -}; - -DataReductionForm.prototype._displayImage = function(name, subtractionId) { - var url = BUI.getURL() + '&type=' + name + '&subtractionId=' + subtractionId; - var event = "OnClick= window.open('" + url + "')"; - document.getElementById(this.id + "_" + name).innerHTML = ''; -}; - -DataReductionForm.prototype.input = function() { - return {}; + }; }; -/** It populates the form **/ -DataReductionForm.prototype.test = function(targetId) { - var macromoleculeForm = new DataReductionForm(); - var panel = macromoleculeForm.getPanel(); +BufferForm.prototype.test = function(targetId) { + var bufferForm = new BufferForm(); + var panel = bufferForm.getPanel(bufferForm.input().buffer); panel.render(targetId); }; - -function PlotWidget(args) { - this.width = 600; - this.height = 600; - this.id = BUI.id(); - - this.linear = false; - - this.margin = "10 0 0 0"; - /** for each stat the max and minimum value when it is scaled in order to show correctly in the legend **/ - this.ranges = {}; + +/** + * @showTitle + * + * #onSaved + * #onAddPlates + * #onRemovePlates + **/ +function CaseForm(args) { + this.width = 700; + this.showTitle = true; if (args != null) { - if (args.width != null) { - this.width = args.width; - } - if (args.height != null) { - this.height = args.height; - } - if (args.margin != null) { - this.margin = args.margin; + if (args.showTitle != null) { + this.showTitle = args.showTitle; } } -} - -PlotWidget.prototype.getMenu = function() { var _this = this; - /** Actions buttons **/ - var actions = []; - actions.push("->"); - actions.push({ - text : "Export as Image", - scope : this, - icon : '../images/save.gif', - handler : function(item, pressed) { - var largeImage = document.createElement("img"); - largeImage.style.display = 'block'; - largeImage.style.width = 200 + "px"; - largeImage.style.height = 200 + "px"; - largeImage.setAttribute('src', Dygraph.Export.asCanvas(this.dygraphObject.dygraph).toDataURL()); - window.open(Dygraph.Export.asCanvas(this.dygraphObject.dygraph).toDataURL(), 'Image', ''); + this.stockSolutionGrid = new StockSolutionGrid({ + width : this.width - 10, + minHeight : 300, + height : 300, + tbar : true, + showTitle : true, + isPackedVisible : false, + btnAddExisting : true, + btnRemoveVisible : false, + btnUnpackVisible : true + }); + + /** When selecting existing stock solutions **/ + this.stockSolutionGrid.onStockSolutionSelected.attach(function(sender, stockSolutions) { + if (stockSolutions != null) { + for ( var i = 0; i < stockSolutions.length; i++) { + _this.saveStockSolution(stockSolutions[i]); + } } }); - return actions; -}; + /** it can be because it has been added a new one or removed **/ + this.stockSolutionGrid.onProposalChanged.attach(function(sender, stockSolution) { + if (stockSolution != null) { + _this.saveStockSolution(stockSolution); + } else { + BIOSAXS.proposal.onInitialized.attach(function() { + _this.stockSolutionGrid.refresh(BIOSAXS.proposal.getStockSolutionsByDewarId(_this.dewar.dewarId)); -/** Looks for the maximum value and then divide everything but that value **/ -PlotWidget.prototype.scaledData = function(data) { - for (var i = 0; i < data.length; i++) { - var values = this.getMaxAndMinValue(data[i]); - data[i] = this.divideValuesByMax(data[i], values.max); - this.ranges[data[i].label] = values; - } - return data; -}; + _this.stockSolutionGrid.grid.setLoading(false); + }); + BIOSAXS.proposal.init(); -/** Given a stat float[] and a max number it will divide each value by max **/ -PlotWidget.prototype.divideValuesByMax = function(stat, max) { - for (var j = 0; j < stat.data.length; j++) { - if (max != 0) { - stat.data[j] = Number(stat.data[j]) / max; - stat.std[j] = Number(stat.std[j]) / max; } - } - return stat; -}; -/** returns max value of a stat **/ -PlotWidget.prototype.getMaxAndMinValue = function(stat) { - var max = 0; - var min = stat.data[0]; - for (var j = 0; j < stat.data.length; j++) { - if (Number(stat.data[j]) > max) { - max = Number(stat.data[j]); - } - if (Number(stat.std[j]) > max) { - max = Number(stat.std[j]); - } - if (Number(stat.data[j]) < min) { - min = Number(stat.data[j]); - } - } - return { - max : Number(max), - min : Number(min) - }; -}; - -PlotWidget.prototype._renderDygraph = function(parsed, colors, labels) { - this.dygraphObject = new StdDevDyGraph(this.id, { - width : this.width - 20, - height : this.height - 40, - xlabel : "", }); - this.dygraphObject.draw(parsed, colors, labels); + this.onSaved = new Event(this); + this.onAddPlates = new Event(this); + this.onRemovePlates = new Event(this); +} +CaseForm.prototype.saveStockSolution = function(stockSolution) { + var _this = this; + var adapter = new BiosaxsDataAdapter(); + this.stockSolutionGrid.grid.setLoading("ISPyB: setting case to Stock solution"); + adapter.onSuccess.attach(function(sender, stockSolution) { + BIOSAXS.proposal.onInitialized.attach(function() { + _this.stockSolutionGrid.refresh(BIOSAXS.proposal.getStockSolutionsByDewarId(_this.dewar.dewarId)); + _this.stockSolutionGrid.grid.setLoading(false); + }); + BIOSAXS.proposal.init(); + }); + adapter.onError.attach(function(sender, data) { + _this.stockSolutionGrid.grid.setLoading(false); + BUI.showError(data); + }); + stockSolution.boxId = _this.dewar.dewarId; + adapter.saveStockSolution(stockSolution); }; -PlotWidget.prototype.getPanel = function() { - this.panel = Ext.create('Ext.panel.Panel', { - width : this.width, - height : this.height, - margin : this.margin, - tbar : this.getMenu(), - items : [ { - html : "", - id : this.id, - width : this.width, - height : this.height, - padding : 10, - margin : "0 0 0 -30", - border : 0 - } ] +CaseForm.prototype.fillStores = function() { + var _this = this; + this.panel.setLoading("Loading Labcontacts from database"); + + var proposal = BUI.getProposal(); + proposal.onDataRetrieved.attach(function(sender, data) { + _this.labContactForSendingStore.loadData(data, false); + _this.labContactForReturnStore.loadData(data, false); + _this.panel.setLoading(false); }); + proposal.getLabContactsByProposalId(); - return this.panel; }; -PlotWidget.prototype.getPoint = function(y, error) { - var minus = y - error; - var max = y + error; - - if (this.linear) { - return [ Math.abs(minus), y, Math.abs(max) ]; - } - if ((minus != 0) && (max != 0)) { - return [ Math.log(Math.abs(minus)), Math.log(y), Math.log(Math.abs(max)) ]; - } else { - return [ Math.log(y), Math.log(y), Math.log(y) ]; - } - +CaseForm.prototype.refresh = function(dewar) { + this.setDewar(dewar); + this.stockSolutionGrid.refresh(BIOSAXS.proposal.getStockSolutionsByDewarId(dewar.dewarId)); }; -PlotWidget.prototype.getDataPlot = function(frames, subtractions, models, fits, rbms) { - var files = []; - var labels = [ "Intensity" ]; - if (frames != null) { - for (var i = 0; i < frames.length; i++) { - files.push(frames[i].data); - labels.push(frames[i].fileName); - } - } - function splitData(data, column, errorColumn, name){ - var result = [] - for (var j = 0; j < data.length; j++) { - console.log(data[j][column]); - result.push([j,data[j][column],data[j][errorColumn]]);//[0, data[i][column],0]]); - } - files.push(result); - labels.push(name); - } - - if (subtractions != null) { - for (var i = 0; i < subtractions.length; i++) { - /** For subtraction **/ - files.push(subtractions[i].subtraction.data); - labels.push(subtractions[i].subtraction.fileName); - /** For sample average **/ -// files.push(subtractions[i].sampleAvg.data); -// labels.push(subtractions[i].sampleAvg.fileName); - /** For buffer average **/ -// files.push(subtractions[i].bufferAvg.data); -// labels.push(subtractions[i].bufferAvg.fileName); - } - } - - if (models != null) { - for (var i = 0; i < models.length; i++) { - for ( var key in models[i]) { - splitData(models[i][key].fir.data, 1, 2, "Intensity"); - splitData(models[i][key].fir.data, 3, 3, "Fit"); - } - } - } - - if (fits != null) { - for (var i = 0; i < fits.length; i++) { - for ( var key in fits[i]) { - - /** adding fit file to be plotted **/ - if (fits[i][key].fit.data[0].length == 3){ - splitData(fits[i][key].fit.data, 1, 1, "Intensity"); - splitData(fits[i][key].fit.data, 2, 2, "Fit"); - } - - /** s, Iexp(s), err, Ifit(s). **/ - if (fits[i][key].fit.data[0].length == 4){ - splitData(fits[i][key].fit.data, 1, 2, "Intensity"); - splitData(fits[i][key].fit.data, 3, 3, "Fit"); - } - - if (fits[i][key].fit.data[0].length == 5){ - /** X Intensity Fit Error Residues **/ - - splitData(fits[i][key].fit.data, 1, 3, "Intensity"); - splitData(fits[i][key].fit.data, 2, 2, "Fit"); - } - } - } - } - - var dataPoints = []; - if (files.length > 0) { - for (var i = 0; i < files[0].length; i++) { - dataPoints.push([ files[0][i][0], this.getPoint(files[0][i][1], files[0][i][2]) ]); - } - if (files.length > 1) { - for (var i = 1; i < files.length; i++) { - for (var j = 0; j < dataPoints.length; j++) { - if (files[i][j] != null){ - dataPoints[j].push(this.getPoint(files[i][j][1], files[i][j][2])); - } - else{ - dataPoints[j].push([0,0,0]); - } - } - } - } - } +CaseForm.prototype.getDewar = function() { + this.dewar.code = Ext.getCmp("dewar_code").getValue(); + this.dewar.comments = Ext.getCmp("dewar_comments").getValue(); + this.dewar.trackingNumberFromSynchrotron = Ext.getCmp("dewar_trackingNumberFromSynchrotron").getValue(); + this.dewar.trackingNumberToSynchrotron = Ext.getCmp("dewar_trackingNumberToSynchrotron").getValue(); + this.dewar.transportValue = Ext.getCmp("dewar_transportValue").getValue(); + this.dewar.storageLocation = Ext.getCmp("dewar_storageLocation").getValue(); + this.dewar.firstExperimentId = this.macromoleculeCombo.getValue(); + return this.dewar; +}; - return { - dataPoints : dataPoints, - labels : labels +CaseForm.prototype.setDewar = function(dewar) { + this.dewar = dewar; + Ext.getCmp("dewar_code").setValue(this.dewar.code); + Ext.getCmp("dewar_dewarStatus").setText(new String(this.dewar.dewarStatus).toUpperCase()); + Ext.getCmp("dewar_comments").setValue(this.dewar.comments); + Ext.getCmp("dewar_trackingNumberFromSynchrotron").setValue(this.dewar.trackingNumberFromSynchrotron); + Ext.getCmp("dewar_trackingNumberToSynchrotron").setValue(this.dewar.trackingNumberToSynchrotron); + Ext.getCmp("dewar_transportValue").setValue(this.dewar.transportValue); + Ext.getCmp("dewar_storageLocation").setValue(this.dewar.storageLocation); + if (dewar.sessionVO != null) { + this.macromoleculeCombo.setValue(dewar.sessionVO.sessionId); } }; -PlotWidget.prototype.refresh = function(frames, subtractions, models, fits, rbms, colors) { - - var _this = this; - this.panel.setLoading("Reading Files"); - - var dataAdapter = new BiosaxsDataAdapter(); - dataAdapter.onSuccess.attach(function(sender, data) { - _this.panel.setLoading("Rendering"); - var parsed = _this.getDataPlot(data.frames, data.subtractions, data.models, data.fits, rbms); - _this._renderDygraph(parsed.dataPoints, colors, parsed.labels); - _this.panel.setLoading(false); - }); - dataAdapter.onError.attach(function(sender, data) { - _this.panel.setLoading(false); +CaseForm.prototype.getSessionCombo = function() { + this.macromoleculeCombo = BIOSAXS_COMBOMANAGER.getComboSessions(BIOSAXS.proposal.getSessions(), { + labelWidth : 100, + margin : '5 0 00 0', + width : 250 }); - dataAdapter.getDataPlot(frames, subtractions, models, fits, rbms); -}; - -PlotWidget.prototype.input = function() { - return DATADOC.getHPLCData(); + return this.macromoleculeCombo; }; - -/** - * Fit form - * - * @witdh - * @height - */ -function FitForm(args) { - this.id = BUI.id(); - this.width = null; - this.height = null; - - if (args != null) { - if (args.width != null) { - this.width = args.width; - } - if (args.height != null) { - this.height = args.height; - } - } +CaseForm.prototype.getInformationPanel = function() { + if (this.panel == null) { + this.informationPanel = Ext.create('Ext.form.Panel', { + width : this.width - 10, + border : 0, + items : [ { + xtype : 'container', + margin : "2 2 2 2", + collapsible : false, + defaultType : 'textfield', + layout : 'anchor', + items : [ { + xtype : 'container', + layout : 'hbox', + items : [ { + xtype : 'requiredtext', + fieldLabel : 'Code', + allowBlank : false, + name : 'code', + id : 'dewar_code', + anchor : '50%' + }, { + xtype : 'label', + margin : '0 0 0 20', + readOnly : true, + id : 'dewar_dewarStatus', + anchor : '50%' + } ] + }, this.getSessionCombo(), { + margin : '5 0 0 0', + xtype : 'textareafield', + name : 'comments', + id : 'dewar_comments', + width : this.width - 50, + fieldLabel : 'Comments' + } ] + }, { + xtype : 'fieldset', + title : 'Courier Accounts Details for Return', + collapsible : false, + layout : 'anchor', + defaults : { + anchor : '100%' + }, + items : [ { + xtype : 'container', + layout : 'hbox', + items : [ { + xtype : 'textfield', + labelWidth : 200, + width : 280, + fieldLabel : 'Track Number From Synchrotron', + id : 'dewar_trackingNumberFromSynchrotron' + }, { + xtype : 'numberfield', + width : 190, + labelWidth : 110, + margin : '0 0 0 30', + fieldLabel : 'Transport Value', + id : 'dewar_transportValue' + } - var _this = this; - - /** Widgets **/ - this.fitGrid = new FitStructureToExperimentDataGrid({ - width : null, - height : 200 - }); + ] + }, { + xtype : 'container', + layout : 'hbox', + margin : '10 0 0 0', + items : [ - this.fitGrid.onSelected.attach(function(sender, fits) { - var modelsIdList = []; - for ( var i in fits) { - modelsIdList.push(fits[i].fitStructureToExperimentalDataId); - } - _this._renderPlot(_this.subtractions[0].subtractionId, modelsIdList); -// var adapter = new BiosaxsDataAdapter(); -// adapter.onSuccess.attach(function(sender, data) { -// -// }); -// adapter.getScatteringCurveByFrameIdsList([], [], [], ids); - }); - - - /** Dygraph Widget that plots fir files**/ - this.plotWidget = new PlotWidget({ - width : 745, - height : 300, - margin : "10 0 10 10" - }); -} + { + xtype : 'textfield', + labelWidth : 200, + width : 280, + fieldLabel : 'Track Number To Synchrotron', + id : 'dewar_trackingNumberToSynchrotron' + }, { + xtype : 'textfield', + margin : '0 0 0 30', + width : 190, + labelWidth : 110, + fieldLabel : 'Storage Location', + id : 'dewar_storageLocation' + } -FitForm.prototype._renderPlot = function(subtractionId, modelsIdList) { - /** Trying to plot tje subtraction and the models **/ - try { - var colors = [ "#669900", "#0000FF" ]; - - this.plotWidget.refresh([], [ ], [], modelsIdList, [], colors); - } catch (e) { - console.log(e); + ] + } ] + } ] + }); } -}; - -FitForm.prototype._getItems = function() { - var _this = this; - return [ { - xtype : 'label', - forId : 'myFieldId', - text : 'INLINE HELP: To be updated', - margin : '15 0 20 10', - cls : "inline-help" - }, this.fitGrid.getPanel(), this.plotWidget.getPanel() ] - -}; -FitForm.prototype._getButtons = function() { - return []; + return this.informationPanel; }; -FitForm.prototype.getPanel = function() { - var _this = this; - this.panel = Ext.create('Ext.form.Panel', { +CaseForm.prototype.getPanel = function(dewar) { + this.dewar = dewar; + this.panel = Ext.createWidget({ + xtype : 'container', + layout : 'vbox', width : this.width, - height : this.height, - margin : 10, - border : 1, - defaultType : 'textfield', - items : this._getItems(), - buttons : this._getButtons(), - listeners : { - afterrender : function() { - _this._populate(); + border : 0, + items : [ { + items : { + xtype : "container", + layout : "vbox", + margin : "5 5 5 5", + items : [ this.getInformationPanel(dewar), this.stockSolutionGrid.getPanel() ] } - } + } ] }); + + this.refresh(dewar); return this.panel; -}; -/** Populates could be call when the DOM is not filled yet **/ -FitForm.prototype._populate = function() { }; -/** It populates the form * */ -FitForm.prototype.refresh = function(subtractions) { - this.subtractions = subtractions; - this.fitGrid.refresh(subtractions); -}; +CaseForm.prototype.input = function() { + return { + dewar : { + "dewarId" : 305861, + "code" : "ESRF-TEST", + "comments" : "comments", + "storageLocation" : "FRIDGE", + "dewarStatus" : "opened", + "timeStamp" : null, + "isStorageDewar" : null, + "barCode" : "ESRF305861", + "customsValue" : null, + "transportValue" : null, + "trackingNumberToSynchrotron" : "3333", + "trackingNumberFromSynchrotron" : "224466", + "type" : "Dewar", + "sessionVO" : { + "sessionId" : 31697, + "expSessionPk" : null, + "projectCode" : null, + "startDate" : "2012 07 21", + "endDate" : "2012 07 23", + "beamlineName" : "BM29", + "scheduled" : 1, + "nbShifts" : 2, + "comments" : null, + "beamlineOperator" : "PERNOT P", + "usedFlag" : null, + "sessionTitle" : null, + "structureDeterminations" : null, + "dewarTransport" : null, + "databackupFrance" : null, + "databackupEurope" : null, + "visit_number" : null, + "operatorSiteNumber" : "14061", + "timeStamp" : "2012 04 25" + } + }, + proposal : { + "assemblies" : [], + "sessions" : [ { + "sessionId" : 31697, + "expSessionPk" : null, + "projectCode" : null, + "startDate" : "2012 07 21", + "endDate" : "2012 07 23", + "beamlineName" : "BM29", + "scheduled" : 1, + "nbShifts" : 2, + "comments" : null, + "beamlineOperator" : "PERNOT P", + "usedFlag" : null, + "sessionTitle" : null, + "structureDeterminations" : null, + "dewarTransport" : null, + "databackupFrance" : null, + "databackupEurope" : null, + "visit_number" : null, + "operatorSiteNumber" : "14061", + "timeStamp" : "2012 04 25" + } ], + "labcontacts" : [ { + "labContactId" : 787, + "personVO" : { + "personId" : 304252, + "personUUID" : null, + "familyName" : "KIM", + "givenName" : "Henry", + "title" : null, + "emailAddress" : "henry-sung.kim@ibs.fr", + "phoneNumber" : "", + "login" : "", + "passwd" : "", + "faxNumber" : "" + }, + "cardName" : "KIM-Institut de Bio", + "defaultCourrierCompany" : "22", + "courierAccount" : "", + "billingReference" : "", + "dewarAvgCustomsValue" : 0, + "dewarAvgTransportValue" : 0 + } ], + "buffers" : [ { + "bufferId" : 811, + "proposalId" : 3124, + "safetyLevelId" : null, + "name" : "EDTA", + "acronym" : "EDTA", + "ph" : null, + "composition" : "", + "bufferhasadditive3VOs" : [], + "comments" : "" + }, { + "bufferId" : 810, + "proposalId" : 3124, + "safetyLevelId" : null, + "name" : "HEPES", + "acronym" : "HEPES", + "ph" : null, + "composition" : "", + "bufferhasadditive3VOs" : [], + "comments" : "" + } ], + "shippings" : [ { + "shippingId" : 304107, + "shippingName" : "TEST", + "deliveryAgentAgentName" : null, + "deliveryAgentShippingDate" : null, + "deliveryAgentDeliveryDate" : null, + "deliveryAgentAgentCode" : null, + "deliveryAgentFlightCode" : null, + "shippingStatus" : "opened", + "timeStamp" : "2013 09 25", + "laboratoryId" : null, + "isStorageShipping" : null, + "creationDate" : "2013 09 25", + "comments" : "test", + "sendingLabContactVO" : { + "labContactId" : 787, + "personVO" : { + "personId" : 304252, + "personUUID" : null, + "familyName" : "KIM", + "givenName" : "Henry", + "title" : null, + "emailAddress" : "henry-sung.kim@ibs.fr", + "phoneNumber" : "", + "login" : "", + "passwd" : "", + "faxNumber" : "" + }, + "cardName" : "KIM-Institut de Bio", + "defaultCourrierCompany" : "22", + "courierAccount" : "", + "billingReference" : "", + "dewarAvgCustomsValue" : 0, + "dewarAvgTransportValue" : 0 + }, + "returnLabContactVO" : { + "labContactId" : 787, + "personVO" : { + "personId" : 304252, + "personUUID" : null, + "familyName" : "KIM", + "givenName" : "Henry", + "title" : null, + "emailAddress" : "henry-sung.kim@ibs.fr", + "phoneNumber" : "", + "login" : "", + "passwd" : "", + "faxNumber" : "" + }, + "cardName" : "KIM-Institut de Bio", + "defaultCourrierCompany" : "22", + "courierAccount" : "", + "billingReference" : "", + "dewarAvgCustomsValue" : 0, + "dewarAvgTransportValue" : 0 + }, + "returnCourier" : null, + "dateOfShippingToUser" : null, + "shippingType" : "DewarTracking", + "dewarVOs" : [ { + "dewarId" : 305861, + "code" : "ESRF-TEST", + "comments" : "comments", + "storageLocation" : "FRIDGE", + "dewarStatus" : "opened", + "timeStamp" : null, + "isStorageDewar" : null, + "barCode" : "ESRF305861", + "customsValue" : null, + "transportValue" : null, + "trackingNumberToSynchrotron" : "3333", + "trackingNumberFromSynchrotron" : "224466", + "type" : "Dewar", + "sessionVO" : { + "sessionId" : 31697, + "expSessionPk" : null, + "projectCode" : null, + "startDate" : "2012 07 21", + "endDate" : "2012 07 23", + "beamlineName" : "BM29", + "scheduled" : 1, + "nbShifts" : 2, + "comments" : null, + "beamlineOperator" : "PERNOT P", + "usedFlag" : null, + "sessionTitle" : null, + "structureDeterminations" : null, + "dewarTransport" : null, + "databackupFrance" : null, + "databackupEurope" : null, + "visit_number" : null, + "operatorSiteNumber" : "14061", + "timeStamp" : "2012 04 25" + } + } ] + } ], + "macromolecules" : [ { + "macromoleculeId" : 5933, + "safetylevelId" : null, + "proposalId" : 3124, + "name" : "A", + "acronym" : "A", + "molecularMass" : "", + "extintionCoefficient" : "", + "sequence" : null, + "creationDate" : null, + "comments" : "", + "macromoleculeregion3VOs" : [], + "stoichiometry3VOsForHostMacromoleculeId" : [], + "structure3VOs" : [] + } ], + "stockSolutions" : [ { + "stockSolutionId" : 6, + "proposalId" : 3124, + "macromoleculeId" : 5933, + "bufferId" : 811, + "instructionSet3VO" : null, + "boxId" : 305861, + "storageTemperature" : "20", + "volume" : "300", + "concentration" : "1.2", + "comments" : "Buffer EDTA with A", + "name" : "A_EDTA_1.2", + "samples" : [] + } ] + } -FitForm.prototype.input = function() { - return {}; + }; }; -/** It populates the form **/ -FitForm.prototype.test = function(targetId) { - var macromoleculeForm = new FitForm(); - var panel = macromoleculeForm.getPanel(); - panel.render(targetId); -}; - -/** - * Example form - * - * @witdh - * @height - */ -function RigidBodyModelingResultForm(args) { +CaseForm.prototype.test = function(targetId) { + var caseForm = new CaseForm(); + BIOSAXS.proposal = new Proposal(caseForm.input().proposal); + var panel = caseForm.getPanel(caseForm.input().dewar); + panel.render(targetId); +}; + +/** + * Example form + * + * @witdh + * @height + */ +function ExampleForm(args) { + this.id = BUI.id(); + this.width = 700; + this.height = 500; + + if (args != null) { + if (args.width != null) { + this.width = args.width; + } + if (args.height != null) { + this.height = args.height; + } + } +} + +ExampleForm.prototype._getItems = function() { + return [{ + fieldLabel : 'First Name', + name : 'first', + allowBlank : false + }, { + fieldLabel : 'Last Name', + name : 'last', + allowBlank : false + } ]; +}; + +ExampleForm.prototype._getItems = function() { + return [ ]; +}; + +ExampleForm.prototype._getButtons = function() { + return [ ]; +}; + +ExampleForm.prototype.getPanel = function() { + this.panel = Ext.create('Ext.form.Panel', { + width : this.width, + height : this.height, + margin : 10, + border : 1, + defaultType : 'textfield', + items : this._getItems(), + buttons : this._getButtons() + }); + return this.panel; +}; + +/** It populates the form **/ +ExampleForm.prototype.refresh = function(macromolecule) { +}; + +function ExperimentForm(args) { this.id = BUI.id(); - this.width = null; - this.height = null; if (args != null) { - if (args.width != null) { - this.width = args.width; - } - if (args.height != null) { - this.height = args.height; - } } - var _this = this; - /** Widgets **/ - this.rigidModelGrid = new RigidModelGrid({ - width : null, - height : 200 + this.onSaved = new Event(this); +} + +ExperimentForm.prototype._getItems = function(experiment) { + this.experiment = experiment; + var typeCombo = Ext.create('Ext.form.ComboBox', { + id : this.id + 'type', + fieldLabel : 'Type', + store : [ "STATIC", "CALIBRATION", "HPLC" ], + queryMode : 'local', + labelWidth : 120, + displayField : 'name', + valueField : 'name', + value : experiment.json.type, + disabled : (experiment.json.type == 'TEMPLATE') }); - this.rigidModelGrid.onSelected.attach(function(sender, fits){ - var ids = []; - for ( var i in fits) { - ids.push(fits[i].fitStructureToExperimentalDataId); + var items = []; + + + if (experiment.json.type == "HPLC" ){ + var typeMacromolecule = BIOSAXS_COMBOMANAGER.getComboMacromoleculeByMacromolecules(BIOSAXS.proposal.getMacromolecules(), {labelWidth : 120, width: "120px"}); + if (experiment.getHPLCMacromolecule() != null){ + typeMacromolecule.setValue(experiment.getHPLCMacromolecule().macromoleculeId); + items.push(typeMacromolecule); } - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, data){ -// debugger - - }); - - adapter.getScatteringCurveByFrameIdsList([], [], [], ids); + } + + + items.push(typeCombo, { + id : this.id + 'name', + xtype : 'textfield', + fieldLabel : 'Name', + labelWidth : 120, + width : '100%', + value : experiment.json.name + }, { + id : this.id + 'comments', + xtype : 'textareafield', + name : 'comments', + fieldLabel : 'Comments', + labelWidth : 120, + height : 120, + width : '100%', + value : experiment.json.comments }); - -} - -RigidBodyModelingResultForm.prototype._getItems = function() { - var _this = this; - return [ { - xtype : 'label', - forId : 'myFieldId', - text : 'INLINE HELP: To be updated', - margin : '15 0 20 10', - cls : "inline-help" - }, this.rigidModelGrid.getPanel() ] - -}; - -RigidBodyModelingResultForm.prototype._getButtons = function() { - return []; + return items; }; - -RigidBodyModelingResultForm.prototype.getPanel = function() { +ExperimentForm.prototype.getPanel = function(experiment) { var _this = this; - this.panel = Ext.create('Ext.form.Panel', { - width : this.width, - height : this.height, - margin : 10, - border : 1, - defaultType : 'textfield', - items : this._getItems(), - buttons : this._getButtons(), - listeners : { - afterrender : function() { - _this._populate(); - - } - } + + this.panel = Ext.createWidget({ + xtype : 'container', + layout : 'vbox', + border : 0, + style : { + padding : '10px' + }, + fieldDefaults : { + labelAlign : 'left', + labelWidth : 50 + }, + items : this._getItems(experiment) }); return this.panel; }; -/** Populates could be call when the DOM is not filled yet **/ -RigidBodyModelingResultForm.prototype._populate = function() { -}; - -/** It populates the form * */ -RigidBodyModelingResultForm.prototype.refresh = function(subtractions) { - this.rigidModelGrid.refresh(subtractions); -}; - -RigidBodyModelingResultForm.prototype.input = function() { - return {}; +ExperimentForm.prototype.input = function() { + return new ExperimentHeaderForm().input(); }; -/** It populates the form **/ -RigidBodyModelingResultForm.prototype.test = function(targetId) { - var macromoleculeForm = new RigidBodyModelingResultForm(); - var panel = macromoleculeForm.getPanel(); +ExperimentForm.prototype.test = function(targetId) { + var experimentForm = new ExperimentForm(); + var panel = experimentForm.getPanel(new Experiment(experimentForm.input().experiment)); panel.render(targetId); }; /** - * Example form + * Shows the header for the experiments changing the color and parameters depending on experiment type * - * @witdh - * @height */ -function SuperpositionForm(args) { +function ExperimentHeaderForm(args) { this.id = BUI.id(); - this.width = null; - this.height = null; + this.backgroundColor = '#FFFFFF'; +} - if (args != null) { - if (args.width != null) { - this.width = args.width; - } - if (args.height != null) { - this.height = args.height; +ExperimentHeaderForm.prototype.getHTMLSource = function(experiment) { + var html = BUI.createFormLabel("Name :", experiment.json.name, 75, 400, this.backgroundColor); + if (experiment.json.type == "HPLC") { + if (experiment.getHPLCMacromolecule() != null){ + html = html + BUI.createFormLabel("Molecule :", experiment.getHPLCMacromolecule().acronym, 75, 400, this.backgroundColor); } } + else{ + html = html + BUI.createFormLabel("Type :", experiment.json.type, 75, 400, this.backgroundColor); + } + + html = html + BUI.createFormLabel("Date :", experiment.json.creationDate, 75, 400, this.backgroundColor); + return html; +}; - var _this = this; - - /** Widgets **/ - this.superpositionGrid = new SuperpositionGrid({ - width : null, - height : 200 - }); - - this.superpositionGrid.onSelected.attach(function(sender, superpositions) { - var ids = []; - for ( var i in superpositions) { - ids.push(superpositions[i].superpositionId); - } -// _this._renderAbinitio(ids); -// _this.aprioriPDBViewer.refresh(); - _this._renderAligned(ids); - // getAlignedPDBContentBySuperpositionList - }); - - /** PDB viewer **/ -// this.abinitioPDBViewer = new PDBViewer({ -// width : 860 / 2, -// height : 300 / 2, -// margin : 0 -// }); -// -// /** PDB viewer **/ -// this.aprioriPDBViewer = new StructurePDBViewer({ -// width : 860 / 2, -// height : 300 / 2, -// margin : 0 -// }); +ExperimentHeaderForm.prototype.getHTMLDownload = function(experiment) { + var bgcolor = "background-color:" + this.backgroundColor + ";"; + var html = "
"; + if ((experiment.json.type == "CALIBATION") || (experiment.json.type == "STATIC")) { + html = html + " Download Source File
"; + html = html + + ""; + } + if (experiment.json.type == "TEMPLATE") { + html = html + + " Download Source File"; + } - this.alignedPDBViewer = new AlignedSuperpositionPDBViewer({ - width : 860 , - height : 300 - }); + if (experiment.json.type == "HPLC") { + html = html + " Download h5 File"; + } -} + return html; +}; -SuperpositionForm.prototype._renderAligned = function(modelsIdList) { - /** Trying to plot the PDB file **/ - try { - this.alignedPDBViewer.refresh(modelsIdList); - } catch (e) { - console.log(e); - } +ExperimentHeaderForm.prototype.getTopPanel = function(experiment) { + return { + xtype : 'container', + layout : 'hbox', + items : [ { + margin : "0 0 0 0", + width : 475, + border : 0, + html : this.getHTMLSource(experiment) + }, { + margin : "0 0 0 0", + width : 475, + border : 0, + html : this.getHTMLDownload(experiment) + } ] + }; }; -SuperpositionForm.prototype._renderAbinitio = function(modelsIdList) { - /** Trying to plot the PDB file **/ - try { - var viz = []; - for (var i = 0; i < modelsIdList.length; i++) { - viz.push({ - modelId : modelsIdList[i], - color : "0xFF6600", - opacity : 0.8 +ExperimentHeaderForm.prototype.getButton = function(experiment) { + var _this = this; + return Ext.create('Ext.Button', { + text : 'EDIT', + minWidth : '100', + margin : '10 0 0 30', + handler : function() { + var experimentWindow = new ExperimentWindow(); + experimentWindow.onSaved.attach(function(sender, data) { + _this.experiment.json.name = data.name; + _this.experiment.json.type = data.type; + _this.experiment.json.comments = data.comments; + _this.panel.remove(_this.panel.items.items[0]); + _this.panel.remove(_this.panel.items.items[0]); + _this.panel.insert(_this.getTopPanel(_this.experiment)); + _this.panel.insert(_this.getBottomPanel(_this.experiment)); }); + experimentWindow.show(experiment); } - this.abinitioPDBViewer.refresh(viz); - } catch (e) { - console.log(e); - } + }); }; -SuperpositionForm.prototype._getItems = function() { - var _this = this; - return [ { - xtype : 'label', - forId : 'myFieldId', - text : 'INLINE HELP: To be updated', - margin : '15 0 20 10', - cls : "inline-help" - }, this.superpositionGrid.getPanel(), { +ExperimentHeaderForm.prototype.getBottomPanel = function(experiment) { + return { xtype : 'container', layout : 'hbox', - items : [ -// { -// xtype : 'container', -// layout : 'vbox', -// items : [ this.abinitioPDBViewer.getPanel(), this.aprioriPDBViewer.getPanel() -// -// ] -// }, - - this.alignedPDBViewer.getPanel() ] - } ] - -}; - -SuperpositionForm.prototype._getButtons = function() { - return []; + margin : '10 0 0 0', + items : [ this.getComments(experiment), this.getButton(experiment) ] + }; }; -SuperpositionForm.prototype.getPanel = function() { - var _this = this; - this.panel = Ext.create('Ext.form.Panel', { - width : this.width, - height : this.height, - margin : 10, - border : 1, - defaultType : 'textfield', - items : this._getItems(), - buttons : this._getButtons(), - listeners : { - afterrender : function() { - _this._populate(); - - } - } - }); - return this.panel; +ExperimentHeaderForm.prototype.getComments = function(experiment) { + return { + xtype : 'textareafield', + labelStyle : 'font-weight: bold;', + name : 'comments', + fieldLabel : 'Comments', + labelWidth : 70, + height : 40, + minWidth : '450', + readOnly : true, + value : experiment.json.comments + }; }; -/** Populates could be call when the DOM is not filled yet **/ -SuperpositionForm.prototype._populate = function() { -}; +ExperimentHeaderForm.prototype.getPanel = function(experiment) { + this.experiment = experiment; + + if (experiment.json.type == 'CALIBRATION') { + this.backgroundColor = '#EFFBFB'; + } + if (experiment.json.type == 'TEMPLATE') { + this.backgroundColor = '#E0F8E6'; + } -/** It populates the form * */ -SuperpositionForm.prototype.refresh = function(subtractions) { - this.subtractions = subtractions; - this.superpositionGrid.refresh(subtractions); + this.panel = Ext.create('Ext.container.Container', { + frame : false, + layout : 'vbox', + padding : 5, + bodyPadding : '5 5 0 0', + width : 890, + margin : '0 0 10 0', + height : 120, + style : { + borderColor : '#99bce8', + borderStyle : 'solid', + borderWidth : '1px', + 'background-color' : this.backgroundColor + }, + fieldDefaults : { + msgTarget : 'side', + labelWidth : 100 + }, + items : [ this.getTopPanel(experiment), this.getBottomPanel(experiment) ] + }); + return this.panel; }; -SuperpositionForm.prototype.input = function() { - return {}; +ExperimentHeaderForm.prototype.input = function() { + return { + experiment : DATADOC.getExperiment_10() + }; }; -/** It populates the form **/ -SuperpositionForm.prototype.test = function(targetId) { - var macromoleculeForm = new SuperpositionForm(); - var panel = macromoleculeForm.getPanel(); +ExperimentHeaderForm.prototype.test = function(targetId) { + var experimentHeaderForm = new ExperimentHeaderForm(); + var panel = experimentHeaderForm.getPanel(new Experiment(experimentHeaderForm.input().experiment)); panel.render(targetId); + }; /** - * Example form + * Macromolecule form with the general parameters of a macromolecule * * @witdh * @height + * + * #onSave button save has been clicked + * #onClose button close has been clicked */ -function AssemblyForm(args) { +function MacromoleculeForm(args) { this.id = BUI.id(); this.width = 700; this.height = 500; @@ -6059,15944 +7053,9793 @@ function AssemblyForm(args) { this.height = args.height; } } - - this.molarityGrid = new MolarityGrid({height : this.height - 50}); + + /** Events **/ + this.onSave = new Event(this); + this.onClose = new Event(this); } -AssemblyForm.prototype._getItems = function() { - return [ { - xtype : 'label', - forId : 'myFieldId', - text : 'List of previously defined macromolecules present in the assembly. This information will be used for additional cross-checks where possible', - margin : '15 0 20 10', - cls : "inline-help" - }, this.molarityGrid.getPanel() ]; -}; - -AssemblyForm.prototype._getButtons = function() { - return []; +/** Type : is the Ext type then requiredtext or textfield * */ +MacromoleculeForm.prototype._getFieldTextWithHelp = function(type, fieldLabel, fieldName, help) { + return Ext.create('Ext.container.Container', { + margin : "10 0 0 10", + items : [ { + xtype : type, + fieldLabel : fieldLabel, + name : fieldName, + id : this.id + fieldName + }, { + xtype : 'label', + forId : 'myFieldId', + text : help, + margin : "5 0 0 105", + cls : "inline-help" + } ] + }); }; -AssemblyForm.prototype.getPanel = function() { - this.panel = Ext.create('Ext.form.Panel', { - width : this.width, - height : this.height, - margin : 10, - border : 0, - defaultType : 'textfield', - items : this._getItems(), - buttons : this._getButtons() +MacromoleculeForm.prototype._getNumericWithHelp = function(type, fieldLabel, fieldName, help) { + return Ext.create('Ext.container.Container', { + margin : "0 0 0 10", + items : [ { + xtype : type, + fieldLabel : fieldLabel, + name : fieldName, + id : this.id + fieldName, + decimalPrecision : 6, + width : 220 + }, { + xtype : 'label', + forId : 'myFieldId', + text : help, + margin : "5 0 0 10", + cls : "inline-help" + } ] }); - return this.panel; }; - -/** It populates the form **/ -AssemblyForm.prototype.refresh = function(macromolecule) { - this.macromolecule = macromolecule; - this.molarityGrid.refresh(macromolecule); +MacromoleculeForm.prototype._getButtons = function() { + var _this = this; + return [ { + text : 'Save', + handler : function() { + _this._save(); + } + },{ + text : 'Close', + handler : function() { + _this.onClose.notify(); + + } + } ]; }; -AssemblyForm.prototype.input = function() { - return {}; +/** It persits the macromolecule in the database **/ +MacromoleculeForm.prototype._persist = function(macromoleculeId, acronym, name, molecularMass, extintionCoefficient, comments, refractiveIndex, solventViscosity) { + + /** Checking not duplicated acronym **/ + if (((macromoleculeId == null) && (BIOSAXS.proposal.getMacromoleculeByAcronym(acronym) != null))== true){ + BUI.showError("Duplicated acronym"); + return; + } + + + if (macromoleculeId == null){ + /** new macromolecule **/ + this.macromolecule = {}; + this.macromolecule.macromoleculeId = null; + } + else{ + this.macromolecule.macromoleculeId = macromoleculeId; + } + + this.macromolecule["acronym"] = acronym; + this.macromolecule["name"] = name; + this.macromolecule["molecularMass"] = molecularMass; + this.macromolecule["extintionCoefficient"] = extintionCoefficient; + this.macromolecule["comments"] = comments; + this.macromolecule["symmetry"] = Ext.getCmp(this.id + 'comboSym').getValue(); + this.macromolecule["refractiveIndex"] = refractiveIndex; + this.macromolecule["solventViscosity"] = solventViscosity; + + var _this = this; + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, proposal) { + /** Updating the proposal global object **/ + BIOSAXS.proposal.setItems(proposal); + _this.panel.setLoading(false); + + var saved = BIOSAXS.proposal.getMacromoleculeByAcronym(acronym); + /** Refreshing to check that everything is ok **/ + _this.refresh(saved); + _this.onSave.notify(saved); + }); + + this.panel.setLoading("Saving Macromolecule") + adapter.saveMacromolecule(this.macromolecule); }; - -AssemblyForm.prototype.test = function(targetId) { - var assemblyForm = new AssemblyForm(); +/** Save the macromolecule in the DB **/ +MacromoleculeForm.prototype._save = function() { - var panel = assemblyForm.getPanel(); - panel.render(targetId); -}; -/** - * Edit the information of a buffer - * - * #onSaved - * #onRemoveAdditive - */ -function BufferForm() { var _this = this; + + var acronym = this._getField("acronym"); + var name = this._getField("name"); + var molecularMass = this._getField("molecularMass"); + var extintionCoefficient = this._getField("extintionCoefficient"); + var comments = this._getField("comments"); + + var refractiveIndex = this._getField("refractiveIndex"); + var solventViscosity = this._getField("solventViscosity"); + + /** Checking required fields **/ + if (name == "") { + BUI.showError("Name field is mandatory"); + return; + } + if (acronym == "") { + BUI.showError("Acroynm field is mandatory"); + return; + } - this.additiveGrid = new AdditiveGrid(); - this.additiveGrid.onRemoveButtonClicked.attach(function(sender, args) { - _this.onRemoveAdditive.notify(args); - }); + if (this.macromolecule != null){ + /** Checking if it is a new macromolecule **/ + if (this.macromolecule.macromoleculeId == null){ + /** Check if the acronym exists already **/ + this._persist(null, acronym, name, molecularMass, extintionCoefficient, comments, refractiveIndex, solventViscosity); + } + else{ + /** It is an update **/ + this._persist(this.macromolecule.macromoleculeId, acronym, name, molecularMass, extintionCoefficient, comments, refractiveIndex, solventViscosity); + } + } + else{ + /** It is a new macromolecule **/ + this._persist(null, acronym, name, molecularMass, extintionCoefficient, comments, refractiveIndex, solventViscosity); + } +}; - this.onSaved = new Event(this); - this.onRemoveAdditive = new Event(this); -} -BufferForm.prototype.getBuffer = function() { - this.buffer.name = Ext.getCmp("buffer_name").getValue(); - this.buffer.acronym = Ext.getCmp("buffer_acronym").getValue(); - this.buffer.comments = Ext.getCmp("buffer_comments").getValue(); - this.buffer.ph = Ext.getCmp("buffer_ph").getValue(); - this.buffer.composition = Ext.getCmp("buffer_composition").getValue(); - this.buffer.bufferhasadditive3VOs = this.additiveGrid.getAdditives(); - return this.buffer; -}; -BufferForm.prototype._getTopPanel = function() { - return { - xtype : 'container', - layout : 'hbox', - border : 0, - frame : true, - items : [ { - xtype : 'container', - layout : 'hbox', - items : [ { +MacromoleculeForm.prototype._getItems = function() { + var _this = this; + /** Symmetry combo box **/ + var symmetry = Ext.create('Ext.data.Store', { + fields : [ 's' ], + data : this._getSymmetries() + }); + + this.symmetryComboBox = Ext.create('Ext.form.ComboBox', { + fieldLabel : 'Symmetry', + store : symmetry, + id : this.id + 'comboSym', + queryMode : 'local', + displayField : 's', + valueField : 's', + value : "P1", + margin : "0 0 0 30", + width : 220 + }); + + return [this._getFieldTextWithHelp("requiredtext", "Name", "name", "Long name. i.e: Bovine serum albumin"), + this._getFieldTextWithHelp("requiredtext", "Acronym", "acronym", "Acronym will be used in the files and analisys. i.e: BSA"), + this._getFieldTextWithHelp("textfield", "Mol. Mass (Da)", "molecularMass", "Atomic mass estimation measured in Da"), + { xtype : 'container', - flex : 1, - border : false, - layout : 'anchor', - defaultType : 'textfield', - items : [ { - xtype : 'requiredtext', - id : 'buffer_name', - fieldLabel : 'Name', - name : 'name', - width : '200px', - value : this.buffer.name - }, { - xtype : 'requiredtext', - id : 'buffer_acronym', - fieldLabel : 'Acronym', - name : 'acronym', - width : '200px', - value : this.buffer.acronym - } ] - } ] - }, { - xtype : 'container', - flex : 1, - layout : 'anchor', - defaultType : 'textfield', - margin : '0 0 0 10', - items : [ { - id : 'buffer_ph', - fieldLabel : 'pH', - name : 'ph', - value : this.buffer.ph, - xtype : 'numberfield', - width : 200, - minValue : 0, - maxValue : 15 - }, { - xtype : 'requiredtext', - id : 'buffer_composition', - fieldLabel : 'Composition', - name : 'composition', - width : 200, - value : this.buffer.composition - } ] - } ] - }; + layout : 'hbox', + margin : "10 0 0 0", + items :[ + this._getNumericWithHelp("numberfield", "Extinction coef.", "extintionCoefficient", ""), + this.symmetryComboBox + ] + }, + { + xtype : 'container', + layout : 'hbox', + margin : "5 0 0 0", + items :[ + this._getNumericWithHelp("numberfield", "Refractive Index", "refractiveIndex", "How radiation propagates through the medium"), + this._getNumericWithHelp("numberfield", "Solvent Viscosity", "solventViscosity", "") + ] + }, + { + id : this.id + "comments", + xtype : 'textareafield', + name : 'comments', + margin : '35 0 0 10', + fieldLabel : 'Comments', + width : this.width - 100 + } ]; }; -BufferForm.prototype.getPanel = function(buffer) { - this.buffer = buffer; - this.panel = Ext.createWidget({ - xtype : 'container', - layout : 'vbox', - style : { - padding : '10px' - }, - fieldDefaults : { - labelAlign : 'left', - labelWidth : 50 +MacromoleculeForm.prototype._getSymmetries = function() { + return [ { + "s" : "P1" + }, { + "s" : "P2" + }, { + "s" : "P3" + }, { + "s" : "P4" + }, { + "s" : "P5" + }, { + "s" : "P6" + }, { + "s" : "P32" + }, { + "s" : "P42" + }, { + "s" : "P52" + }, { + "s" : "P62" + }, { + "s" : "P222" + } ] +}; - }, - items : [ this._getTopPanel(), { - id : 'buffer_comments', - xtype : 'textareafield', - name : 'comments', - fieldLabel : 'Comments', - width : '100%', - value : buffer.comments - }, this.additiveGrid.getPanel(buffer) ] +MacromoleculeForm.prototype.getPanel = function() { + this.panel = Ext.create('Ext.form.Panel', { + width : this.width, + height : this.height, + margin : 10, + border : 1, + defaultType : 'textfield', + items : this._getItems(), + buttons : this._getButtons() }); return this.panel; }; -BufferForm.prototype.input = function() { - return { - buffer : { - "bufferId" : 422, - "proposalId" : 10, - "safetyLevelId" : null, - "name" : "B1", - "acronym" : "B1", - "ph" : null, - "composition" : null, - "bufferhasadditive3VOs" : [], - "comments" : null - } - }; + +/** Populates each text field by field name and value **/ +MacromoleculeForm.prototype._populateField = function(fieldName, value) { + if (value != null){ + Ext.getCmp(this.id + fieldName).setValue(value); + } }; -BufferForm.prototype.test = function(targetId) { - var bufferForm = new BufferForm(); - var panel = bufferForm.getPanel(bufferForm.input().buffer); - panel.render(targetId); +/** Gets the value of a textfield **/ +MacromoleculeForm.prototype._getField = function(fieldName) { + return Ext.getCmp(this.id + fieldName).getValue(); }; - -/** - * @showTitle - * - * #onSaved - * #onAddPlates - * #onRemovePlates - **/ -function CaseForm(args) { - this.width = 700; - this.showTitle = true; - if (args != null) { - if (args.showTitle != null) { - this.showTitle = args.showTitle; - } - } - var _this = this; - this.stockSolutionGrid = new StockSolutionGrid({ - width : this.width - 10, - minHeight : 300, - height : 300, - tbar : true, - showTitle : true, - isPackedVisible : false, - btnAddExisting : true, - btnRemoveVisible : false, - btnUnpackVisible : true - }); - /** When selecting existing stock solutions **/ - this.stockSolutionGrid.onStockSolutionSelected.attach(function(sender, stockSolutions) { - if (stockSolutions != null) { - for ( var i = 0; i < stockSolutions.length; i++) { - _this.saveStockSolution(stockSolutions[i]); - } +/** It populates the form **/ +MacromoleculeForm.prototype.refresh = function(macromolecule) { + this.macromolecule = macromolecule; + if (macromolecule != null){ + this._populateField("name", macromolecule.name); + this._populateField("acronym", macromolecule.acronym); + this._populateField("extintionCoefficient", macromolecule.extintionCoefficient); + this._populateField("molecularMass", macromolecule.molecularMass); + this._populateField("comments", macromolecule.comments); + this._populateField("refractiveIndex", macromolecule.refractiveIndex); + this._populateField("solventViscosity", macromolecule.solventViscosity); + if (macromolecule.symmetry != null){ + Ext.getCmp(this.id + 'comboSym').setValue(macromolecule.symmetry); } - }); + } +}; - /** it can be because it has been added a new one or removed **/ - this.stockSolutionGrid.onProposalChanged.attach(function(sender, stockSolution) { - if (stockSolution != null) { - _this.saveStockSolution(stockSolution); - } else { - BIOSAXS.proposal.onInitialized.attach(function() { - _this.stockSolutionGrid.refresh(BIOSAXS.proposal.getStockSolutionsByDewarId(_this.dewar.dewarId)); - _this.stockSolutionGrid.grid.setLoading(false); - }); - BIOSAXS.proposal.init(); +MacromoleculeForm.prototype.input = function() { + return {}; +}; - } +/** It populates the form **/ +MacromoleculeForm.prototype.test = function(targetId) { + var macromoleculeForm = new MacromoleculeForm(); + macromoleculeForm.onClose.attach(function(sender){ + alert("Click on close"); }); + var panel = macromoleculeForm.getPanel(); + panel.render(targetId); +}; - this.onSaved = new Event(this); - this.onAddPlates = new Event(this); - this.onRemovePlates = new Event(this); -} -CaseForm.prototype.saveStockSolution = function(stockSolution) { - var _this = this; - var adapter = new BiosaxsDataAdapter(); - this.stockSolutionGrid.grid.setLoading("ISPyB: setting case to Stock solution"); - adapter.onSuccess.attach(function(sender, stockSolution) { - BIOSAXS.proposal.onInitialized.attach(function() { - _this.stockSolutionGrid.refresh(BIOSAXS.proposal.getStockSolutionsByDewarId(_this.dewar.dewarId)); - _this.stockSolutionGrid.grid.setLoading(false); - }); - BIOSAXS.proposal.init(); - }); - adapter.onError.attach(function(sender, data) { - _this.stockSolutionGrid.grid.setLoading(false); - BUI.showError(data); - }); - stockSolution.boxId = _this.dewar.dewarId; - adapter.saveStockSolution(stockSolution); -}; - -CaseForm.prototype.fillStores = function() { - var _this = this; - this.panel.setLoading("Loading Labcontacts from database"); + - var proposal = BUI.getProposal(); - proposal.onDataRetrieved.attach(function(sender, data) { - _this.labContactForSendingStore.loadData(data, false); - _this.labContactForReturnStore.loadData(data, false); - _this.panel.setLoading(false); - }); - proposal.getLabContactsByProposalId(); +function ModelVisualizerForm(args){ + this.id =BUI.id(); + this.width = 600; + this.height = 400; + if (args!= null){ + if (args.width != null){ + this.width = args.width; + } + if (args.height != null){ + this.height = args.height; + } + } }; -CaseForm.prototype.refresh = function(dewar) { - this.setDewar(dewar); - this.stockSolutionGrid.refresh(BIOSAXS.proposal.getStockSolutionsByDewarId(dewar.dewarId)); +ModelVisualizerForm.prototype._getFirHTML = function(modelId, width, height, type, desc) { + var html = ""; + html = html + ''; + html = html + ''; + html = html + ''; + html = html + '
dammin.' + type + '
' + desc + '
'; + return html; }; -CaseForm.prototype.getDewar = function() { - this.dewar.code = Ext.getCmp("dewar_code").getValue(); - this.dewar.comments = Ext.getCmp("dewar_comments").getValue(); - this.dewar.trackingNumberFromSynchrotron = Ext.getCmp("dewar_trackingNumberFromSynchrotron").getValue(); - this.dewar.trackingNumberToSynchrotron = Ext.getCmp("dewar_trackingNumberToSynchrotron").getValue(); - this.dewar.transportValue = Ext.getCmp("dewar_transportValue").getValue(); - this.dewar.storageLocation = Ext.getCmp("dewar_storageLocation").getValue(); - this.dewar.firstExperimentId = this.macromoleculeCombo.getValue(); - return this.dewar; +ModelVisualizerForm.prototype.getItems = function(modelPanel){ + _this = this; + var height = _this.height/2 - 60; + var width = _this.width/2 - 10; + + return Ext.create('Ext.container.Container', { + layout: { + type: 'vbox', // Arrange child items vertically + }, + items: [ + modelPanel, + { + xtype : 'container', + layout: { + type: 'hbox', // Arrange child items vertically + }, + items : [{ + html : this._getFirHTML("id", width, height, "fir", "Fit of the simulated scattering curve versus a smoothed experimental data (spline interpolation)"), + height :height, + width : width, + padding: 2 + }, + { + html : this._getFirHTML("id", width, height, "fit", "Fit of the simulated scattering curve versus the experimental data."), + height : height, + width : width, + padding: 2 + }] + } + + ] + }); }; -CaseForm.prototype.setDewar = function(dewar) { - this.dewar = dewar; - Ext.getCmp("dewar_code").setValue(this.dewar.code); - Ext.getCmp("dewar_dewarStatus").setText(new String(this.dewar.dewarStatus).toUpperCase()); - Ext.getCmp("dewar_comments").setValue(this.dewar.comments); - Ext.getCmp("dewar_trackingNumberFromSynchrotron").setValue(this.dewar.trackingNumberFromSynchrotron); - Ext.getCmp("dewar_trackingNumberToSynchrotron").setValue(this.dewar.trackingNumberToSynchrotron); - Ext.getCmp("dewar_transportValue").setValue(this.dewar.transportValue); - Ext.getCmp("dewar_storageLocation").setValue(this.dewar.storageLocation); - if (dewar.sessionVO != null) { - this.macromoleculeCombo.setValue(dewar.sessionVO.sessionId); +ModelVisualizerForm.prototype.refresh = function(models){ + var input = []; +// var colors = ["008000", "F0A804", "0000FF", "800080", "C0C0C0"]; + for (var i = 0; i < models.length; i++) { + console.log(BUI.rainbow(models.length, i).replace("#", "0x")); + input.push({ + color: BUI.rainbow(models.length, i).replace("#", "0x"), + modelId: models[i].modelId, + opacity: 0.8, + radius: 3, + title: BUI.getFileNameByPath(models[i].pdbFile), + type: "SHAPEDETERMINATIONMODEL" + + }); } + + this.panel.removeAll(); + this.panel.add( + _this.getItems( + new PDBViewer({ + width : this.width - 10, + height : (this.height/2) - 10, + title : "" + }).draw(input) + ) + ); + + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, data) { + var splitted = data.toString().split("\n"); + var array = []; + for ( var i = 0; i < splitted.length; i++) { + var line = splitted[i].trim(); + var line_splited = line.split(/\s*[\s,]\s*/); + if (line_splited.length == 4) { + array.push([ Number(line_splited[0]), BUI.getStvArray(line_splited[1], line_splited[2]), BUI.getStvArray(line_splited[3], 0) ]); + } + } + + var id = (_this.id + "firid"); + var dygraphWidget = new StdDevDyGraph(id, { + width : (_this.width/2) - 10, + height :(_this.height/2) -110, + xlabel : 'q(nm-1)' + }); + dygraphWidget.draw(array, [ "#FF0000", "#0000FF", "#FF00FF" ], [ 's', 'I(exp)', 'I(sim)' ]); + }); + + adapter.getModelFile("FIR", models[0].modelId); + + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, data) { + var splitted = data.toString().split("\n"); + var array = []; + for ( var i = 0; i < splitted.length; i++) { + var line = splitted[i].trim(); + var line_splited = line.split(/\s*[\s,]\s*/); + if (line_splited.length == 3) { + array.push([ Number(line_splited[0]), BUI.getStvArray(line_splited[1], 0), BUI.getStvArray(line_splited[2], 0) ]); + } + } + + var id = (_this.id + "fitid"); + + var dygraphWidget = new StdDevDyGraph(id, { + width : (_this.width/2) - 10, + height : (_this.height/2) - 110, + xlabel : 'q(nm-1)' + }); + dygraphWidget.draw(array, [ "#FF0000", "#0000FF" ], [ "s", "I(exp)", "I(sim)" ]); + }); + adapter.getModelFile("FIT", models[0].modelId); }; -CaseForm.prototype.getSessionCombo = function() { - this.macromoleculeCombo = BIOSAXS_COMBOMANAGER.getComboSessions(BIOSAXS.proposal.getSessions(), { - labelWidth : 100, - margin : '5 0 00 0', - width : 250 +ModelVisualizerForm.prototype.getPanel = function(modelList){ + _this = this; + this.modelList = modelList; + this.panel = Ext.create('Ext.Panel', { + title: 'Results', + width: this.width, + height: this.height, + layout: { + type: 'vbox', // Arrange child items vertically +// align: 'stretch' // Each takes up full width + }, + items: [ + + ], + listeners : { + afterrender : function(grid, eOpts) { +// alert(_this.modelList) + } + } }); - return this.macromoleculeCombo; + + return this.panel; + }; -CaseForm.prototype.getInformationPanel = function() { - if (this.panel == null) { - this.informationPanel = Ext.create('Ext.form.Panel', { - width : this.width - 10, - border : 0, - items : [ { - xtype : 'container', - margin : "2 2 2 2", - collapsible : false, - defaultType : 'textfield', - layout : 'anchor', - items : [ { - xtype : 'container', - layout : 'hbox', - items : [ { - xtype : 'requiredtext', - fieldLabel : 'Code', - allowBlank : false, - name : 'code', - id : 'dewar_code', - anchor : '50%' - }, { - xtype : 'label', - margin : '0 0 0 20', - readOnly : true, - id : 'dewar_dewarStatus', - anchor : '50%' - } ] - }, this.getSessionCombo(), { - margin : '5 0 0 0', - xtype : 'textareafield', - name : 'comments', - id : 'dewar_comments', - width : this.width - 50, - fieldLabel : 'Comments' - } ] - }, { - xtype : 'fieldset', - title : 'Courier Accounts Details for Return', - collapsible : false, - layout : 'anchor', - defaults : { - anchor : '100%' - }, - items : [ { - xtype : 'container', - layout : 'hbox', - items : [ { - xtype : 'textfield', - labelWidth : 200, - width : 280, - fieldLabel : 'Track Number From Synchrotron', - id : 'dewar_trackingNumberFromSynchrotron' - }, { - xtype : 'numberfield', - width : 190, - labelWidth : 110, - margin : '0 0 0 30', - fieldLabel : 'Transport Value', - id : 'dewar_transportValue' - } - - ] - }, { - xtype : 'container', - layout : 'hbox', - margin : '10 0 0 0', - items : [ - - { - xtype : 'textfield', - labelWidth : 200, - width : 280, - fieldLabel : 'Track Number To Synchrotron', - id : 'dewar_trackingNumberToSynchrotron' - }, { - xtype : 'textfield', - margin : '0 0 0 30', - width : 190, - labelWidth : 110, - fieldLabel : 'Storage Location', - id : 'dewar_storageLocation' - } - - ] - } ] - } ] - }); - } - - return this.informationPanel; -}; - -CaseForm.prototype.getPanel = function(dewar) { - this.dewar = dewar; - this.panel = Ext.createWidget({ - xtype : 'container', - layout : 'vbox', - width : this.width, - border : 0, - items : [ { - items : { - xtype : "container", - layout : "vbox", - margin : "5 5 5 5", - items : [ this.getInformationPanel(dewar), this.stockSolutionGrid.getPanel() ] - - } - } ] - }); - - this.refresh(dewar); - return this.panel; - -}; - -CaseForm.prototype.input = function() { - return { - dewar : { - "dewarId" : 305861, - "code" : "ESRF-TEST", - "comments" : "comments", - "storageLocation" : "FRIDGE", - "dewarStatus" : "opened", - "timeStamp" : null, - "isStorageDewar" : null, - "barCode" : "ESRF305861", - "customsValue" : null, - "transportValue" : null, - "trackingNumberToSynchrotron" : "3333", - "trackingNumberFromSynchrotron" : "224466", - "type" : "Dewar", - "sessionVO" : { - "sessionId" : 31697, - "expSessionPk" : null, - "projectCode" : null, - "startDate" : "2012 07 21", - "endDate" : "2012 07 23", - "beamlineName" : "BM29", - "scheduled" : 1, - "nbShifts" : 2, - "comments" : null, - "beamlineOperator" : "PERNOT P", - "usedFlag" : null, - "sessionTitle" : null, - "structureDeterminations" : null, - "dewarTransport" : null, - "databackupFrance" : null, - "databackupEurope" : null, - "visit_number" : null, - "operatorSiteNumber" : "14061", - "timeStamp" : "2012 04 25" - } - }, - proposal : { - "assemblies" : [], - "sessions" : [ { - "sessionId" : 31697, - "expSessionPk" : null, - "projectCode" : null, - "startDate" : "2012 07 21", - "endDate" : "2012 07 23", - "beamlineName" : "BM29", - "scheduled" : 1, - "nbShifts" : 2, - "comments" : null, - "beamlineOperator" : "PERNOT P", - "usedFlag" : null, - "sessionTitle" : null, - "structureDeterminations" : null, - "dewarTransport" : null, - "databackupFrance" : null, - "databackupEurope" : null, - "visit_number" : null, - "operatorSiteNumber" : "14061", - "timeStamp" : "2012 04 25" - } ], - "labcontacts" : [ { - "labContactId" : 787, - "personVO" : { - "personId" : 304252, - "personUUID" : null, - "familyName" : "KIM", - "givenName" : "Henry", - "title" : null, - "emailAddress" : "henry-sung.kim@ibs.fr", - "phoneNumber" : "", - "login" : "", - "passwd" : "", - "faxNumber" : "" - }, - "cardName" : "KIM-Institut de Bio", - "defaultCourrierCompany" : "22", - "courierAccount" : "", - "billingReference" : "", - "dewarAvgCustomsValue" : 0, - "dewarAvgTransportValue" : 0 - } ], - "buffers" : [ { - "bufferId" : 811, - "proposalId" : 3124, - "safetyLevelId" : null, - "name" : "EDTA", - "acronym" : "EDTA", - "ph" : null, - "composition" : "", - "bufferhasadditive3VOs" : [], - "comments" : "" - }, { - "bufferId" : 810, - "proposalId" : 3124, - "safetyLevelId" : null, - "name" : "HEPES", - "acronym" : "HEPES", - "ph" : null, - "composition" : "", - "bufferhasadditive3VOs" : [], - "comments" : "" - } ], - "shippings" : [ { - "shippingId" : 304107, - "shippingName" : "TEST", - "deliveryAgentAgentName" : null, - "deliveryAgentShippingDate" : null, - "deliveryAgentDeliveryDate" : null, - "deliveryAgentAgentCode" : null, - "deliveryAgentFlightCode" : null, - "shippingStatus" : "opened", - "timeStamp" : "2013 09 25", - "laboratoryId" : null, - "isStorageShipping" : null, - "creationDate" : "2013 09 25", - "comments" : "test", - "sendingLabContactVO" : { - "labContactId" : 787, - "personVO" : { - "personId" : 304252, - "personUUID" : null, - "familyName" : "KIM", - "givenName" : "Henry", - "title" : null, - "emailAddress" : "henry-sung.kim@ibs.fr", - "phoneNumber" : "", - "login" : "", - "passwd" : "", - "faxNumber" : "" - }, - "cardName" : "KIM-Institut de Bio", - "defaultCourrierCompany" : "22", - "courierAccount" : "", - "billingReference" : "", - "dewarAvgCustomsValue" : 0, - "dewarAvgTransportValue" : 0 - }, - "returnLabContactVO" : { - "labContactId" : 787, - "personVO" : { - "personId" : 304252, - "personUUID" : null, - "familyName" : "KIM", - "givenName" : "Henry", - "title" : null, - "emailAddress" : "henry-sung.kim@ibs.fr", - "phoneNumber" : "", - "login" : "", - "passwd" : "", - "faxNumber" : "" - }, - "cardName" : "KIM-Institut de Bio", - "defaultCourrierCompany" : "22", - "courierAccount" : "", - "billingReference" : "", - "dewarAvgCustomsValue" : 0, - "dewarAvgTransportValue" : 0 - }, - "returnCourier" : null, - "dateOfShippingToUser" : null, - "shippingType" : "DewarTracking", - "dewarVOs" : [ { - "dewarId" : 305861, - "code" : "ESRF-TEST", - "comments" : "comments", - "storageLocation" : "FRIDGE", - "dewarStatus" : "opened", - "timeStamp" : null, - "isStorageDewar" : null, - "barCode" : "ESRF305861", - "customsValue" : null, - "transportValue" : null, - "trackingNumberToSynchrotron" : "3333", - "trackingNumberFromSynchrotron" : "224466", - "type" : "Dewar", - "sessionVO" : { - "sessionId" : 31697, - "expSessionPk" : null, - "projectCode" : null, - "startDate" : "2012 07 21", - "endDate" : "2012 07 23", - "beamlineName" : "BM29", - "scheduled" : 1, - "nbShifts" : 2, - "comments" : null, - "beamlineOperator" : "PERNOT P", - "usedFlag" : null, - "sessionTitle" : null, - "structureDeterminations" : null, - "dewarTransport" : null, - "databackupFrance" : null, - "databackupEurope" : null, - "visit_number" : null, - "operatorSiteNumber" : "14061", - "timeStamp" : "2012 04 25" - } - } ] - } ], - "macromolecules" : [ { - "macromoleculeId" : 5933, - "safetylevelId" : null, - "proposalId" : 3124, - "name" : "A", - "acronym" : "A", - "molecularMass" : "", - "extintionCoefficient" : "", - "sequence" : null, - "creationDate" : null, - "comments" : "", - "macromoleculeregion3VOs" : [], - "stoichiometry3VOsForHostMacromoleculeId" : [], - "structure3VOs" : [] - } ], - "stockSolutions" : [ { - "stockSolutionId" : 6, - "proposalId" : 3124, - "macromoleculeId" : 5933, - "bufferId" : 811, - "instructionSet3VO" : null, - "boxId" : 305861, - "storageTemperature" : "20", - "volume" : "300", - "concentration" : "1.2", - "comments" : "Buffer EDTA with A", - "name" : "A_EDTA_1.2", - "samples" : [] - } ] - } - - }; -}; - -CaseForm.prototype.test = function(targetId) { - var caseForm = new CaseForm(); - BIOSAXS.proposal = new Proposal(caseForm.input().proposal); - var panel = caseForm.getPanel(caseForm.input().dewar); - panel.render(targetId); -}; - -/** - * Example form - * - * @witdh - * @height - */ -function ExampleForm(args) { - this.id = BUI.id(); - this.width = 700; - this.height = 500; - - if (args != null) { - if (args.width != null) { - this.width = args.width; - } - if (args.height != null) { - this.height = args.height; - } - } -} - -ExampleForm.prototype._getItems = function() { - return [{ - fieldLabel : 'First Name', - name : 'first', - allowBlank : false - }, { - fieldLabel : 'Last Name', - name : 'last', - allowBlank : false - } ]; -}; - -ExampleForm.prototype._getItems = function() { - return [ ]; -}; - -ExampleForm.prototype._getButtons = function() { - return [ ]; -}; - -ExampleForm.prototype.getPanel = function() { - this.panel = Ext.create('Ext.form.Panel', { - width : this.width, - height : this.height, - margin : 10, - border : 1, - defaultType : 'textfield', - items : this._getItems(), - buttons : this._getButtons() - }); - return this.panel; -}; - -/** It populates the form **/ -ExampleForm.prototype.refresh = function(macromolecule) { -}; - -function ExperimentForm(args) { - this.id = BUI.id(); - - if (args != null) { - } - - this.onSaved = new Event(this); -} - -ExperimentForm.prototype._getItems = function(experiment) { - this.experiment = experiment; - var typeCombo = Ext.create('Ext.form.ComboBox', { - id : this.id + 'type', - fieldLabel : 'Type', - store : [ "STATIC", "CALIBRATION", "HPLC" ], - queryMode : 'local', - labelWidth : 120, - displayField : 'name', - valueField : 'name', - value : experiment.json.type, - disabled : (experiment.json.type == 'TEMPLATE') - }); - - var items = []; - - - if (experiment.json.type == "HPLC" ){ - var typeMacromolecule = BIOSAXS_COMBOMANAGER.getComboMacromoleculeByMacromolecules(BIOSAXS.proposal.getMacromolecules(), {labelWidth : 120, width: "120px"}); - if (experiment.getHPLCMacromolecule() != null){ - typeMacromolecule.setValue(experiment.getHPLCMacromolecule().macromoleculeId); - items.push(typeMacromolecule); - } - } - - - items.push(typeCombo, { - id : this.id + 'name', - xtype : 'textfield', - fieldLabel : 'Name', - labelWidth : 120, - width : '100%', - value : experiment.json.name - }, { - id : this.id + 'comments', - xtype : 'textareafield', - name : 'comments', - fieldLabel : 'Comments', - labelWidth : 120, - height : 120, - width : '100%', - value : experiment.json.comments - }); - return items; -}; -ExperimentForm.prototype.getPanel = function(experiment) { - var _this = this; - - this.panel = Ext.createWidget({ - xtype : 'container', - layout : 'vbox', - border : 0, - style : { - padding : '10px' - }, - fieldDefaults : { - labelAlign : 'left', - labelWidth : 50 - }, - items : this._getItems(experiment) - }); - return this.panel; -}; - -ExperimentForm.prototype.input = function() { - return new ExperimentHeaderForm().input(); -}; - -ExperimentForm.prototype.test = function(targetId) { - var experimentForm = new ExperimentForm(); - var panel = experimentForm.getPanel(new Experiment(experimentForm.input().experiment)); - panel.render(targetId); -}; - -/** - * Shows the header for the experiments changing the color and parameters depending on experiment type - * - */ -function ExperimentHeaderForm(args) { - this.id = BUI.id(); - this.backgroundColor = '#FFFFFF'; -} - -ExperimentHeaderForm.prototype.getHTMLSource = function(experiment) { - var html = BUI.createFormLabel("Name :", experiment.json.name, 75, 400, this.backgroundColor); - if (experiment.json.type == "HPLC") { - if (experiment.getHPLCMacromolecule() != null){ - html = html + BUI.createFormLabel("Molecule :", experiment.getHPLCMacromolecule().acronym, 75, 400, this.backgroundColor); - } - } - else{ - html = html + BUI.createFormLabel("Type :", experiment.json.type, 75, 400, this.backgroundColor); - } - - html = html + BUI.createFormLabel("Date :", experiment.json.creationDate, 75, 400, this.backgroundColor); - return html; -}; - -ExperimentHeaderForm.prototype.getHTMLDownload = function(experiment) { - var bgcolor = "background-color:" + this.backgroundColor + ";"; - var html = "
"; - if ((experiment.json.type == "CALIBATION") || (experiment.json.type == "STATIC")) { - html = html + " Download Source File
"; - html = html - + ""; - } - if (experiment.json.type == "TEMPLATE") { - html = html - + " Download Source File"; - } - - if (experiment.json.type == "HPLC") { - html = html + " Download h5 File"; - } - - return html; -}; - -ExperimentHeaderForm.prototype.getTopPanel = function(experiment) { - return { - xtype : 'container', - layout : 'hbox', - items : [ { - margin : "0 0 0 0", - width : 475, - border : 0, - html : this.getHTMLSource(experiment) - }, { - margin : "0 0 0 0", - width : 475, - border : 0, - html : this.getHTMLDownload(experiment) - } ] - }; -}; - -ExperimentHeaderForm.prototype.getButton = function(experiment) { - var _this = this; - return Ext.create('Ext.Button', { - text : 'EDIT', - minWidth : '100', - margin : '10 0 0 30', - handler : function() { - var experimentWindow = new ExperimentWindow(); - experimentWindow.onSaved.attach(function(sender, data) { - _this.experiment.json.name = data.name; - _this.experiment.json.type = data.type; - _this.experiment.json.comments = data.comments; - _this.panel.remove(_this.panel.items.items[0]); - _this.panel.remove(_this.panel.items.items[0]); - _this.panel.insert(_this.getTopPanel(_this.experiment)); - _this.panel.insert(_this.getBottomPanel(_this.experiment)); - }); - experimentWindow.show(experiment); - } - }); -}; - -ExperimentHeaderForm.prototype.getBottomPanel = function(experiment) { - return { - xtype : 'container', - layout : 'hbox', - margin : '10 0 0 0', - items : [ this.getComments(experiment), this.getButton(experiment) ] - }; -}; - -ExperimentHeaderForm.prototype.getComments = function(experiment) { - return { - xtype : 'textareafield', - labelStyle : 'font-weight: bold;', - name : 'comments', - fieldLabel : 'Comments', - labelWidth : 70, - height : 40, - minWidth : '450', - readOnly : true, - value : experiment.json.comments - }; -}; - -ExperimentHeaderForm.prototype.getPanel = function(experiment) { - this.experiment = experiment; - - if (experiment.json.type == 'CALIBRATION') { - this.backgroundColor = '#EFFBFB'; - } - if (experiment.json.type == 'TEMPLATE') { - this.backgroundColor = '#E0F8E6'; - } - - this.panel = Ext.create('Ext.container.Container', { - frame : false, - layout : 'vbox', - padding : 5, - bodyPadding : '5 5 0 0', - width : 890, - margin : '0 0 10 0', - height : 120, - style : { - borderColor : '#99bce8', - borderStyle : 'solid', - borderWidth : '1px', - 'background-color' : this.backgroundColor - }, - fieldDefaults : { - msgTarget : 'side', - labelWidth : 100 - }, - items : [ this.getTopPanel(experiment), this.getBottomPanel(experiment) ] - }); - return this.panel; -}; - -ExperimentHeaderForm.prototype.input = function() { - return { - experiment : DATADOC.getExperiment_10() - }; -}; - -ExperimentHeaderForm.prototype.test = function(targetId) { - var experimentHeaderForm = new ExperimentHeaderForm(); - var panel = experimentHeaderForm.getPanel(new Experiment(experimentHeaderForm.input().experiment)); - panel.render(targetId); - -}; - -/** - * Macromolecule form with the general parameters of a macromolecule - * - * @witdh - * @height - * - * #onSave button save has been clicked - * #onClose button close has been clicked - */ -function MacromoleculeForm(args) { - this.id = BUI.id(); - this.width = 700; - this.height = 500; - - if (args != null) { - if (args.width != null) { - this.width = args.width; - } - if (args.height != null) { - this.height = args.height; - } - } - - /** Events **/ - this.onSave = new Event(this); - this.onClose = new Event(this); -} - -/** Type : is the Ext type then requiredtext or textfield * */ -MacromoleculeForm.prototype._getFieldTextWithHelp = function(type, fieldLabel, fieldName, help) { - return Ext.create('Ext.container.Container', { - margin : "10 0 0 10", - items : [ { - xtype : type, - fieldLabel : fieldLabel, - name : fieldName, - id : this.id + fieldName - }, { - xtype : 'label', - forId : 'myFieldId', - text : help, - margin : "5 0 0 105", - cls : "inline-help" - } ] - }); -}; - -MacromoleculeForm.prototype._getNumericWithHelp = function(type, fieldLabel, fieldName, help) { - return Ext.create('Ext.container.Container', { - margin : "0 0 0 10", - items : [ { - xtype : type, - fieldLabel : fieldLabel, - name : fieldName, - id : this.id + fieldName, - decimalPrecision : 6, - width : 220 - }, { - xtype : 'label', - forId : 'myFieldId', - text : help, - margin : "5 0 0 10", - cls : "inline-help" - } ] - }); -}; - -MacromoleculeForm.prototype._getButtons = function() { - var _this = this; - return [ { - text : 'Save', - handler : function() { - _this._save(); - } - },{ - text : 'Close', - handler : function() { - _this.onClose.notify(); - - } - } ]; -}; - -/** It persits the macromolecule in the database **/ -MacromoleculeForm.prototype._persist = function(macromoleculeId, acronym, name, molecularMass, extintionCoefficient, comments, refractiveIndex, solventViscosity) { - - /** Checking not duplicated acronym **/ - if (((macromoleculeId == null) && (BIOSAXS.proposal.getMacromoleculeByAcronym(acronym) != null))== true){ - BUI.showError("Duplicated acronym"); - return; - } - - - if (macromoleculeId == null){ - /** new macromolecule **/ - this.macromolecule = {}; - this.macromolecule.macromoleculeId = null; - } - else{ - this.macromolecule.macromoleculeId = macromoleculeId; - } - - this.macromolecule["acronym"] = acronym; - this.macromolecule["name"] = name; - this.macromolecule["molecularMass"] = molecularMass; - this.macromolecule["extintionCoefficient"] = extintionCoefficient; - this.macromolecule["comments"] = comments; - this.macromolecule["symmetry"] = Ext.getCmp(this.id + 'comboSym').getValue(); - this.macromolecule["refractiveIndex"] = refractiveIndex; - this.macromolecule["solventViscosity"] = solventViscosity; - - var _this = this; - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, proposal) { - /** Updating the proposal global object **/ - BIOSAXS.proposal.setItems(proposal); - _this.panel.setLoading(false); - - var saved = BIOSAXS.proposal.getMacromoleculeByAcronym(acronym); - /** Refreshing to check that everything is ok **/ - _this.refresh(saved); - _this.onSave.notify(saved); - }); - - this.panel.setLoading("Saving Macromolecule") - adapter.saveMacromolecule(this.macromolecule); -}; - -/** Save the macromolecule in the DB **/ -MacromoleculeForm.prototype._save = function() { - - var _this = this; - - var acronym = this._getField("acronym"); - var name = this._getField("name"); - var molecularMass = this._getField("molecularMass"); - var extintionCoefficient = this._getField("extintionCoefficient"); - var comments = this._getField("comments"); - - var refractiveIndex = this._getField("refractiveIndex"); - var solventViscosity = this._getField("solventViscosity"); - - /** Checking required fields **/ - if (name == "") { - BUI.showError("Name field is mandatory"); - return; - } - if (acronym == "") { - BUI.showError("Acroynm field is mandatory"); - return; - } - - if (this.macromolecule != null){ - /** Checking if it is a new macromolecule **/ - if (this.macromolecule.macromoleculeId == null){ - /** Check if the acronym exists already **/ - this._persist(null, acronym, name, molecularMass, extintionCoefficient, comments, refractiveIndex, solventViscosity); - } - else{ - /** It is an update **/ - this._persist(this.macromolecule.macromoleculeId, acronym, name, molecularMass, extintionCoefficient, comments, refractiveIndex, solventViscosity); - } - } - else{ - /** It is a new macromolecule **/ - this._persist(null, acronym, name, molecularMass, extintionCoefficient, comments, refractiveIndex, solventViscosity); - } -}; - - - -MacromoleculeForm.prototype._getItems = function() { - var _this = this; - /** Symmetry combo box **/ - var symmetry = Ext.create('Ext.data.Store', { - fields : [ 's' ], - data : this._getSymmetries() - }); - - this.symmetryComboBox = Ext.create('Ext.form.ComboBox', { - fieldLabel : 'Symmetry', - store : symmetry, - id : this.id + 'comboSym', - queryMode : 'local', - displayField : 's', - valueField : 's', - value : "P1", - margin : "0 0 0 30", - width : 220 - }); - - return [this._getFieldTextWithHelp("requiredtext", "Name", "name", "Long name. i.e: Bovine serum albumin"), - this._getFieldTextWithHelp("requiredtext", "Acronym", "acronym", "Acronym will be used in the files and analisys. i.e: BSA"), - this._getFieldTextWithHelp("textfield", "Mol. Mass (Da)", "molecularMass", "Atomic mass estimation measured in Da"), - { - xtype : 'container', - layout : 'hbox', - margin : "10 0 0 0", - items :[ - this._getNumericWithHelp("numberfield", "Extinction coef.", "extintionCoefficient", ""), - this.symmetryComboBox - ] - }, - { - xtype : 'container', - layout : 'hbox', - margin : "5 0 0 0", - items :[ - this._getNumericWithHelp("numberfield", "Refractive Index", "refractiveIndex", "How radiation propagates through the medium"), - this._getNumericWithHelp("numberfield", "Solvent Viscosity", "solventViscosity", "") - ] - }, - { - id : this.id + "comments", - xtype : 'textareafield', - name : 'comments', - margin : '35 0 0 10', - fieldLabel : 'Comments', - width : this.width - 100 - } ]; -}; - -MacromoleculeForm.prototype._getSymmetries = function() { - return [ { - "s" : "P1" - }, { - "s" : "P2" - }, { - "s" : "P3" - }, { - "s" : "P4" - }, { - "s" : "P5" - }, { - "s" : "P6" - }, { - "s" : "P32" - }, { - "s" : "P42" - }, { - "s" : "P52" - }, { - "s" : "P62" - }, { - "s" : "P222" - } ] -}; - -MacromoleculeForm.prototype.getPanel = function() { - this.panel = Ext.create('Ext.form.Panel', { - width : this.width, - height : this.height, - margin : 10, - border : 1, - defaultType : 'textfield', - items : this._getItems(), - buttons : this._getButtons() - }); - return this.panel; -}; - - -/** Populates each text field by field name and value **/ -MacromoleculeForm.prototype._populateField = function(fieldName, value) { - if (value != null){ - Ext.getCmp(this.id + fieldName).setValue(value); - } -}; - -/** Gets the value of a textfield **/ -MacromoleculeForm.prototype._getField = function(fieldName) { - return Ext.getCmp(this.id + fieldName).getValue(); -}; - - -/** It populates the form **/ -MacromoleculeForm.prototype.refresh = function(macromolecule) { - this.macromolecule = macromolecule; - if (macromolecule != null){ - this._populateField("name", macromolecule.name); - this._populateField("acronym", macromolecule.acronym); - this._populateField("extintionCoefficient", macromolecule.extintionCoefficient); - this._populateField("molecularMass", macromolecule.molecularMass); - this._populateField("comments", macromolecule.comments); - this._populateField("refractiveIndex", macromolecule.refractiveIndex); - this._populateField("solventViscosity", macromolecule.solventViscosity); - if (macromolecule.symmetry != null){ - Ext.getCmp(this.id + 'comboSym').setValue(macromolecule.symmetry); - } - } -}; - - -MacromoleculeForm.prototype.input = function() { - return {}; -}; - - -/** It populates the form **/ -MacromoleculeForm.prototype.test = function(targetId) { - var macromoleculeForm = new MacromoleculeForm(); - macromoleculeForm.onClose.attach(function(sender){ - alert("Click on close"); - }); - var panel = macromoleculeForm.getPanel(); - panel.render(targetId); -}; - - - - - -function ModelVisualizerForm(args){ - this.id =BUI.id(); - this.width = 600; - this.height = 400; - if (args!= null){ - if (args.width != null){ - this.width = args.width; - } - if (args.height != null){ - this.height = args.height; - } - } -}; - -ModelVisualizerForm.prototype._getFirHTML = function(modelId, width, height, type, desc) { - var html = ""; - html = html + ''; - html = html + ''; - html = html + ''; - html = html + '
dammin.' + type + '
' + desc + '
'; - return html; -}; - -ModelVisualizerForm.prototype.getItems = function(modelPanel){ - _this = this; - var height = _this.height/2 - 60; - var width = _this.width/2 - 10; - - return Ext.create('Ext.container.Container', { - layout: { - type: 'vbox', // Arrange child items vertically - }, - items: [ - modelPanel, - { - xtype : 'container', - layout: { - type: 'hbox', // Arrange child items vertically - }, - items : [{ - html : this._getFirHTML("id", width, height, "fir", "Fit of the simulated scattering curve versus a smoothed experimental data (spline interpolation)"), - height :height, - width : width, - padding: 2 - }, - { - html : this._getFirHTML("id", width, height, "fit", "Fit of the simulated scattering curve versus the experimental data."), - height : height, - width : width, - padding: 2 - }] - } - - ] - }); -}; - -ModelVisualizerForm.prototype.refresh = function(models){ - var input = []; -// var colors = ["008000", "F0A804", "0000FF", "800080", "C0C0C0"]; - for (var i = 0; i < models.length; i++) { - console.log(BUI.rainbow(models.length, i).replace("#", "0x")); - input.push({ - color: BUI.rainbow(models.length, i).replace("#", "0x"), - modelId: models[i].modelId, - opacity: 0.8, - radius: 3, - title: BUI.getFileNameByPath(models[i].pdbFile), - type: "SHAPEDETERMINATIONMODEL" - - }); - } - - this.panel.removeAll(); - this.panel.add( - _this.getItems( - new PDBViewer({ - width : this.width - 10, - height : (this.height/2) - 10, - title : "" - }).draw(input) - ) - ); - - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, data) { - var splitted = data.toString().split("\n"); - var array = []; - for ( var i = 0; i < splitted.length; i++) { - var line = splitted[i].trim(); - var line_splited = line.split(/\s*[\s,]\s*/); - if (line_splited.length == 4) { - array.push([ Number(line_splited[0]), BUI.getStvArray(line_splited[1], line_splited[2]), BUI.getStvArray(line_splited[3], 0) ]); - } - } - - var id = (_this.id + "firid"); - var dygraphWidget = new StdDevDyGraph(id, { - width : (_this.width/2) - 10, - height :(_this.height/2) -110, - xlabel : 'q(nm-1)' - }); - dygraphWidget.draw(array, [ "#FF0000", "#0000FF", "#FF00FF" ], [ 's', 'I(exp)', 'I(sim)' ]); - }); - - adapter.getModelFile("FIR", models[0].modelId); - - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, data) { - var splitted = data.toString().split("\n"); - var array = []; - for ( var i = 0; i < splitted.length; i++) { - var line = splitted[i].trim(); - var line_splited = line.split(/\s*[\s,]\s*/); - if (line_splited.length == 3) { - array.push([ Number(line_splited[0]), BUI.getStvArray(line_splited[1], 0), BUI.getStvArray(line_splited[2], 0) ]); - } - } - - var id = (_this.id + "fitid"); - - var dygraphWidget = new StdDevDyGraph(id, { - width : (_this.width/2) - 10, - height : (_this.height/2) - 110, - xlabel : 'q(nm-1)' - }); - dygraphWidget.draw(array, [ "#FF0000", "#0000FF" ], [ "s", "I(exp)", "I(sim)" ]); - }); - adapter.getModelFile("FIT", models[0].modelId); -}; - -ModelVisualizerForm.prototype.getPanel = function(modelList){ - _this = this; - this.modelList = modelList; - this.panel = Ext.create('Ext.Panel', { - title: 'Results', - width: this.width, - height: this.height, - layout: { - type: 'vbox', // Arrange child items vertically -// align: 'stretch' // Each takes up full width - }, - items: [ - - ], - listeners : { - afterrender : function(grid, eOpts) { -// alert(_this.modelList) - } - } - }); - - return this.panel; - -}; - - -/** - * Example form - * - * @witdh - * @height - */ -function MolarityForm(args) { - this.id = BUI.id(); - this.width = 700; - this.height = 500; - - if (args != null) { - if (args.width != null) { - this.width = args.width; - } - if (args.height != null) { - this.height = args.height; - } - } - - this.onSave = new Event(this); - this.onClose = new Event(this); -} - - -MolarityForm.prototype._getNumericWithHelp = function(type, fieldLabel, fieldName, help) { - return Ext.create('Ext.container.Container', { - margin : "10 0 0 10", - items : [ { - xtype : type, - fieldLabel : fieldLabel, - name : fieldName, - id : this.id + fieldName, - decimalPrecision : 6 - }, { - xtype : 'label', - forId : 'myFieldId', - text : help, - margin : "5 0 0 105", - cls : "inline-help" - } ] - }); -}; - - -MolarityForm.prototype._getItems = function() { - this.macromoleculeCombo = BIOSAXS_COMBOMANAGER.getComboMacromoleculeByMacromolecules(this.getMacromuleculesCandidates(this.macromolecule), { - width : 250, - labelWidth : 100, - margin : 10 - }); - - return [ { - xtype : 'container', - flex : 1, - margin : '10 0 0 10', - border : 0, - layout : 'anchor', - defaultType : 'requiredtext', - items : [ this.macromoleculeCombo, - this._getNumericWithHelp("textfield", "Ratio", "ratio", "Number in assymmetric units") - ] - } ]; -}; - -MolarityForm.prototype._persist = function() { - var _this = this; - var macromoleculeId = this.macromoleculeCombo.getValue(); - var ratio = Ext.getCmp(this.id + "ratio").getValue(); - var comments = "Not used yet"; - var dataAdapter = new BiosaxsDataAdapter(); - this.panel.setLoading("Saving"); - dataAdapter.onSuccess.attach(function(sender, args) { - _this.onSave.notify(); - }); - dataAdapter.saveStoichiometry(this.macromolecule.macromoleculeId, macromoleculeId, ratio, comments); -}; - -MolarityForm.prototype._getButtons = function() { - var _this = this; - - function onClose() { - _this.onClose.notify(); - } - - return [ { - text : 'Save', - handler : function() { - _this._persist(); - } - }, { - text : 'Cancel', - handler : function() { - onClose(); - } - } ]; -}; - -MolarityForm.prototype.getPanel = function() { - this.panel = Ext.create('Ext.form.Panel', { -// width : null, - height : this.height, - margin : 2, - border : 1, - defaultType : 'requiredtext', - items : this._getItems(), - buttons : this._getButtons() - }); - return this.panel; -}; - -/** macromolecules contains all macromolecules except this one **/ -MolarityForm.prototype.getMacromuleculesCandidates = function(macromolecule) { - var macromolecules = []; - if ( BIOSAXS.proposal.macromolecules){ - for (var i = 0; i < BIOSAXS.proposal.macromolecules.length; i++) { - var m = BIOSAXS.proposal.macromolecules[i]; - if (this.macromolecule != null){ - if (m.macromoleculeId != this.macromolecule.macromoleculeId) { - macromolecules.push(m); - } - } - } - } - return macromolecules; -}; - - -/** It populates the form **/ -MolarityForm.prototype.refresh = function(macromolecule) { - this.macromolecule = macromolecule; - -}; - - -MolarityForm.prototype.input = function() { - return {}; -}; - - -/** It populates the form **/ -MolarityForm.prototype.test = function(targetId) { - var macromoleculeForm = new MolarityForm(); - var panel = macromoleculeForm.getPanel(); - panel.render(targetId); -}; - -///** -// * -// * @witdh -// * @height -// */ -//function MacromoleculeForm(args) { -// this.id = BUI.id(); -// this.width = 700; -// this.height = 500; -// -// if (args != null) { -// if (args.width != null) { -// this.width = args.width; -// } -// if (args.height != null) { -// this.height = args.height; -// } -// } -// -// this.onClose = new Event(this); -//} -// -//MacromoleculeForm.prototype.getMacromolecule = function() { -// this.macromolecule.name = Ext.getCmp(this.panel.getItemId()).getValues().name; -// this.macromolecule.acronym = Ext.getCmp(this.panel.getItemId()).getValues().acronym; -// this.macromolecule.comments = Ext.getCmp(this.panel.getItemId()).getValues().comments; -// this.macromolecule.extintionCoefficient = Ext.getCmp(this.panel.getItemId()).getValues().extintionCoefficient; -// this.macromolecule.molecularMass = Ext.getCmp(this.panel.getItemId()).getValues().molecularMass; -// return this.macromolecule; -//}; -// -//MacromoleculeForm.prototype.setMacromolecule = function(macromolecule) { -// this.pdbStore.loadData(macromolecule.structure3VOs); -// -//}; -// -//MacromoleculeForm.prototype.getForm = function(macromolecule) { -// this.panel = Ext.createWidget('form', { -// frame : false, -// border : 0, -// padding : 15, -// width : 550, -// height : 350, -// items : [ { -// xtype : 'container', -// layout : 'hbox', -// items : [ { -// xtype : 'container', -// flex : 1, -// border : false, -// layout : 'anchor', -// defaultType : 'requiredtext', -// items : [ { -// fieldLabel : 'Name', -// name : 'name', -// anchor : '95%', -// tooltip : "Name of the macromolecule", -// value : macromolecule.name -// }, { -// fieldLabel : 'Acronym', -// name : 'acronym', -// anchor : '95%', -// value : macromolecule.acronym -// } ] -// }, { -// xtype : 'container', -// flex : 1, -// layout : 'anchor', -// defaultType : 'textfield', -// items : [ { -// xtype : 'numberfield', -// fieldLabel : 'Mol. Mass (Da)', -// name : 'molecularMass', -// value : macromolecule.molecularMass, -// decimalPrecision : 6 -// }, { -// xtype : 'numberfield', -// fieldLabel : 'Extinction coef.', -// name : 'extintionCoefficient', -// value : macromolecule.extintionCoefficient, -// decimalPrecision : 6 -// } ] -// } ] -// }, { -// xtype : 'textareafield', -// name : 'comments', -// fieldLabel : 'Comments', -// value : macromolecule.comments, -// width : this.width - 10 -// }, -// -// { -// html : BUI.getWarningHTML("The macromolecule is unsaved. Save the macromolecule to get access to other tabs"), -// margin : "150 10 10 10", -// id : this.id + "unsavedWarning", -// hidden : !(!macromolecule.macromoleculeId) -// } -// -// ] -// }); -// return this.panel; -//}; -// -//MacromoleculeForm.prototype.save = function() { -// var _this = this; -// -// var adapter = new BiosaxsDataAdapter(); -// adapter.onSuccess.attach(function(sender, proposal) { -// BIOSAXS.proposal.setItems(proposal); -// _this.panel.setLoading(false); -// -// Ext.getCmp(_this.id + "assembly").enable() -// Ext.getCmp(_this.id + "advanced").enable(); -// Ext.getCmp(_this.id + "unsavedWarning").hide(); -// }); -// -// if (this.getMacromolecule().name == "") { -// BUI.showError("Name field is mandatory"); -// return; -// } -// if (this.getMacromolecule().acronym == "") { -// BUI.showError("Acroynm field is mandatory"); -// return; -// } -// -// /** Check if acronym is unique * */ -// if (this.getMacromolecule().macromoleculeId == null) { -// if (BIOSAXS.proposal.getMacromoleculeByAcronym(this.getMacromolecule().acronym) == null) { -// this.panel.setLoading("ISPyB: Saving Macromolecule") -// adapter.saveMacromolecule(this.getMacromolecule()); -// } else { -// alert("There is already an existing macromolecule with the same acronym"); -// -// } -// } else { -// this.panel.setLoading("ISPyB: Saving Macromolecule") -// adapter.saveMacromolecule(this.getMacromolecule()); -// } -// -//}; -// -//MacromoleculeForm.prototype.getPanel = function(macromolecule) { -// var _this = this; -// this.macromolecule = macromolecule; -// return Ext.createWidget('tabpanel', { -// height : this.height, -// margin : 5, -// plain : true, -// style : { -// padding : 5 -// }, -// items : [ { -// tabConfig : { -// title : "General", -// disabled : false -// }, -// items : [ this.getForm(macromolecule) ], -// bbar : [ "->", { -// text : 'Save', -// cls : 'btn-with-border', -// style : { -// -// border : 1 -// }, -// handler : function() { -// _this.save(); -// } -// }, { -// text : 'Close', -// cls : 'btn-with-border', -// handler : function() { -// _this.onClose.notify(); -// } -// } ] -// }, { -// tabConfig : { -// id : this.id + "assembly", -// title : "Assembly", -// tooltip : 'Description of subunits present in the macromolecule', -// // hidden : (!macromolecule.macromoleculeId), -// disabled : (!macromolecule.macromoleculeId) -// }, -// items : [ this.getMolarityGrid(macromolecule) ] -// }, { -// tabConfig : { -// id : this.id + "advanced", -// title : "Advanced Modeling", -// // hidden : (!macromolecule.macromoleculeId), -// tooltip : 'Definition of the description contacts and symetries', -// disabled : (!macromolecule.macromoleculeId) -// }, -// items : [ this.getRigidBodyForm(macromolecule) ] -// } ] -// }); -//}; -// -//MacromoleculeForm.prototype.getRigidBodyForm = function(macromolecule) { -// var _this = this; -// -// // [P1, P2, P3, P4 ,P5 ,P6 ,32, P42, P52, P62] + P222 -// var symmetry = Ext.create('Ext.data.Store', { -// fields : [ 's' ], -// data : [ { -// "s" : "P1" -// }, { -// "s" : "P2" -// }, { -// "s" : "P3" -// }, { -// "s" : "P4" -// }, { -// "s" : "P5" -// }, { -// "s" : "P6" -// }, { -// "s" : "P32" -// }, { -// "s" : "P42" -// }, { -// "s" : "P52" -// }, { -// "s" : "P62" -// }, { -// "s" : "P222" -// } ] -// }); -// -// if (macromolecule.symmetry == null) { -// macromolecule.symmetry = "P1"; -// } -// var comboBox = Ext.create('Ext.form.ComboBox', { -// fieldLabel : 'Symmetry', -// store : symmetry, -// id : 'comboSym', -// queryMode : 'local', -// displayField : 's', -// valueField : 's', -// value : macromolecule.symmetry, -// margin : "10 0 0 0", -// listeners : { -// change : function(combo, newValue, oldValue, eOpts) { -// macromolecule.symmetry = newValue; -// } -// } -// }); -// -// this.rigidBodyPanel = Ext.createWidget('form', { -// frame : false, -// border : 0, -// padding : 10, -// width : 550, -// height : 400, -// items : [ { -// xtype : 'container', -// layout : 'hbox', -// items : [ { -// xtype : 'container', -// border : false, -// layout : 'hbox', -// items : [ { -// xtype : 'label', -// forId : 'myFieldId', -// text : 'Contact Desc:', -// width : 105, -// margin : '0 0 0 0' -// }, { -// xtype : 'textfield', -// hideLabel : true, -// id : "contactsDescriptionFilePath", -// margin : '0 0 0 0', -// width : 300, -// value : macromolecule.contactsDescriptionFilePath -// }, { -// text : 'Upload', -// xtype : 'button', -// margin : "0 0 0 20", -// width : 100, -// handler : function() { -// _this._openUploadDialog(macromolecule.macromoleculeId, "CONTACTS", "Upload Contact Description File"); -// } -// } ] -// } ] -// }, -// -// comboBox, { -// xtype : 'checkbox', -// margin : '10 0 0 5', -// boxLabel : "I want rigid body modeling run on this stuff", -// checked : true, -// width : 300 -// }, _this.getPDBGrid(macromolecule) ] -// }); -// return this.rigidBodyPanel; -//}; -// -//MacromoleculeForm.prototype.update = function() { -// var _this = this; -// BIOSAXS.proposal.onInitialized.attach(function() { -// if (BIOSAXS.proposal != null) { -// var macromolecules = BIOSAXS.proposal.macromolecules; -// for (var i = 0; i < macromolecules.length; i++) { -// if (macromolecules[i].macromoleculeId == _this.macromolecule.macromoleculeId) { -// _this.macromolecule = macromolecules[i]; -// _this.setMacromolecule(_this.macromolecule); -// _this.molarityStore.loadData(_this.parseMolarityData(_this.macromolecule)); -// _this.pdbGrid.setLoading(false); -// Ext.getCmp("contactsDescriptionFilePath").setValue(_this.macromolecule.contactsDescriptionFilePath) -// _this.molarityGrid.setLoading(false); -// -// } -// } -// } -// }); -// this.molarityGrid.setLoading("Updating"); -// this.pdbGrid.setLoading("Updating"); -// BIOSAXS.proposal.init(); -//}; -// -///******************************************************************************* -// * MOLARITY GRID -// ******************************************************************************/ -// -//MacromoleculeForm.prototype.parseMolarityData = function(macromolecule) { -// var data = []; -// if (macromolecule.stoichiometry != null) { -// for (var i = 0; i < macromolecule.stoichiometry.length; i++) { -// data.push({ -// ratio : macromolecule.stoichiometry[i].ratio, -// acronym : macromolecule.stoichiometry[i].macromolecule3VO.acronym, -// comments : macromolecule.stoichiometry[i].macromolecule3VO.comments, -// stoichiometryId : macromolecule.stoichiometry[i].stoichiometryId, -// name : macromolecule.stoichiometry[i].macromolecule3VO.name -// }); -// } -// } -// return data; -//}; -// -//MacromoleculeForm.prototype.getMolarityGrid = function(macromolecule) { -// var _this = this; -// -// this.molarityStore = Ext.create('Ext.data.Store', { -// fields : [ 'acronym', 'ratio', 'comments', 'stoichiometryId', 'name' ], -// data : this.parseMolarityData(macromolecule), -// sorters : { -// property : 'ratio', -// direction : 'DESC' -// } -// }); -// -// this.molarityGrid = Ext.create('Ext.grid.Panel', { -// store : this.molarityStore, -// height : 350, -// padding : 5, -// columns : [ -// -// { -// text : 'Subunit', -// columns : [ { -// text : "Acronym", -// width : 100, -// hidden : false, -// dataIndex : 'acronym', -// sortable : true -// }, { -// text : "Name", -// width : 100, -// hidden : false, -// dataIndex : 'name', -// sortable : true -// }, { -// text : "Comments", -// width : 100, -// dataIndex : 'comments', -// sortable : true -// } ] -// }, { -// text : "Number
in assymmetric units", -// width : 150, -// dataIndex : 'ratio', -// tooltip : 'Number of times this subunit is present in the macromolecule', -// sortable : true -// }, { -// id : this.id + 'MOLARITY_REMOVE', -// flex : 0.1, -// sortable : false, -// renderer : function(value, metaData, record, rowIndex, colIndex, store) { -// return BUI.getRedButton('REMOVE'); -// } -// } ], -// listeners : { -// cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { -// if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'MOLARITY_REMOVE') { -// var dataAdapter = new BiosaxsDataAdapter(); -// dataAdapter.onSuccess.attach(function() { -// _this.molarityGrid.setLoading(false); -// _this.update(); -// }); -// dataAdapter.removeStoichiometry(record.data.stoichiometryId); -// _this.molarityGrid.setLoading("Removing Structure"); -// } -// } -// }, -// buttons : [ { -// text : 'Add molarity', -// handler : function() { -// -// function onClose() { -// w.destroy(); -// _this.update(); -// } -// var w = Ext.create('Ext.window.Window', { -// title : 'Molarity', -// height : 300, -// width : 500, -// modal : true, -// buttons : [ { -// text : 'Save', -// handler : function() { -// var macromoleculeId = (_this.macromoleculeCombo.getValue()); -// var ratio = Ext.getCmp(_this.id + "ratio").getValue(); -// var comments = ""; -// var dataAdapter = new BiosaxsDataAdapter(); -// dataAdapter.onSuccess.attach(function(sender, args) { -// _this.update(); -// w.destroy(); -// }); -// dataAdapter.saveStoichiometry(_this.macromolecule.macromoleculeId, macromoleculeId, ratio, comments); -// } -// }, { -// text : 'Cancel', -// handler : function() { -// onClose(); -// } -// } ], -// items : [ _this.getMolarityForm(macromolecule) ], -// listeners : { -// onEsc : function() { -// onClose(); -// }, -// close : function() { -// onClose(); -// } -// } -// }).show(); -// } -// } ] -// }); -// return this.molarityGrid; -//}; -// -///******************************************************************************* -// * PDB GRID -// ******************************************************************************/ -// -//MacromoleculeForm.prototype._openUploadDialog = function(macromoleculeId, type, title) { -// var _this = this; -// function onClose() { -// w.destroy(); -// _this.update(); -// } -// -// var w = Ext.create('Ext.window.Window', { -// title : title, -// height : 200, -// width : 400, -// modal : true, -// buttons : [ { -// text : 'Close', -// handler : function() { -// onClose(); -// } -// } ], -// layout : 'fit', -// items : { -// html : "" -// }, -// listeners : { -// onEsc : function() { -// onClose(); -// }, -// close : function() { -// onClose(); -// } -// } -// }).show(); -//}; -// -//MacromoleculeForm.prototype._getPlugins = function() { -// var _this = this; -// var plugins = []; -// // if (this.updateRowEnabled) { -// plugins.push(Ext.create('Ext.grid.plugin.RowEditing', { -// clicksToEdit : 1, -// listeners : { -// validateedit : function(grid, e) { -// /** Comments are always updatable* */ -// e.record.raw.symmetry = e.newValues.symmetry; -// e.record.raw.multiplicity = e.newValues.multiplicity; -// -// var adapter = new BiosaxsDataAdapter(); -// adapter.onSuccess.attach(function(sender, measurement) { -// // _this.grid.setLoading(false); -// }); -// adapter.onError.attach(function() { -// alert("Error"); -// // _this.grid.setLoading(false); -// }); -// -// // _this.grid.setLoading(); -// adapter.saveStructure(e.record.raw); -// } -// } -// })); -// // } -// return plugins; -//}; -// -//MacromoleculeForm.prototype.getPDBGrid = function(macromolecule) { -// var _this = this; -// -// var data = macromolecule.structure3VOs; -// -// // /** Getting PDB from subunits **/ -// // if (macromolecule != null){ -// // if (macromolecule.stoichiometry != null){ -// // for (var i =0; i < macromolecule.stoichiometry.length; i++){ -// // var stoichiometry = macromolecule.stoichiometry[i]; -// // if (stoichiometry.macromolecule3VO != null){ -// // if (stoichiometry.macromolecule3VO.structure3VOs != null){ -// // for (var j = 0; j < stoichiometry.macromolecule3VO.structure3VOs.length; -// // j++) { -// // var structure = stoichiometry.macromolecule3VO.structure3VOs[j]; -// // structure["isSubunit"] = stoichiometry.macromolecule3VO.acronym; -// // data.push(structure) -// // } -// // } -// // } -// // } -// // } -// // } -// -// this.pdbStore = Ext.create('Ext.data.Store', { -// fields : [ 'filePath', 'structureId', 'structureType', 'symmetry', 'structureId', 'name', 'multiplicity' ], -// data : macromolecule.structure3VOs, -// groupField : 'structureType', -// sorters : { -// property : 'structureId', -// direction : 'DESC' -// } -// }); -// -// var groupingFeature = Ext.create('Ext.grid.feature.Grouping', { -// groupHeaderTpl : Ext.create('Ext.XTemplate', -// "
{name:this.formatName}
", { -// formatName : function(name) { -// return name; -// } -// }), -// hideGroupedHeader : true, -// startCollapsed : false -// }); -// -// this.pdbGrid = Ext.create('Ext.grid.Panel', { -// margin : "15 0 0 5", -// height : 250, -// store : this.pdbStore, -// plugins : _this._getPlugins(), -// buttons : [ { -// // text : 'Add PDB file', -// text : 'Add Modeling Option', -// handler : function() { -// _this._openUploadDialog(macromolecule.macromoleculeId, "PDB", 'Upload PDB File'); -// } -// } -// -// ], -// columns : [ -// { -// text : "structureId", -// flex : 0.2, -// hidden : true, -// dataIndex : 'structureId', -// sortable : true -// }, -// { -// text : "File", -// flex : 0.5, -// dataIndex : 'filePath', -// sortable : true, -// hidden : true -// }, -// { -// text : "PDB", -// flex : 0.4, -// dataIndex : 'name', -// sortable : true -// }, -// { -// text : "Symmetry", -// flex : 0.4, -// dataIndex : 'symmetry', -// sortable : true, -// editor : { -// xtype : 'combobox', -// typeAhead : true, -// triggerAction : 'all', -// selectOnTab : true, -// store : [ [ "P1", "P1" ], [ "P2", "P2" ], [ "P3", "P3" ], [ "P4", "P4" ], [ "P5", "P5" ], [ "P6", "P6" ], [ "P32", "P32" ], [ "P42", "P42" ], -// [ "P52", "P52" ], [ "P62", "P62" ], [ "P222", "P222" ] ], -// } -// }, { -// text : "Multiplicity", -// flex : 0.4, -// dataIndex : 'multiplicity', -// sortable : true, -// editor : { -// xtype : 'textfield' -// } -// -// }, { -// text : "Subunit", -// flex : 0.2, -// dataIndex : 'isSubunit', -// sortable : true, -// hidden : true -// }, { -// text : "Type", -// flex : 0.2, -// dataIndex : 'structureType', -// sortable : true, -// hidden : true -// }, -// -// { -// id : this.id + 'REMOVE', -// flex : 0.2, -// hidden : true, -// sortable : false, -// renderer : function(value, metaData, record, rowIndex, colIndex, store) { -// return BUI.getRedButton('REMOVE'); -// } -// }, ], -// -// listeners : { -// itemdblclick : function(dataview, record, item, e) { -// _this._editExperiment(record.raw.experimentId); -// }, -// cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { -// -// if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'REMOVE') { -// var dataAdapter = new BiosaxsDataAdapter(); -// dataAdapter.onSuccess.attach(function() { -// _this.pdbGrid.setLoading(false); -// _this.update(); -// }); -// dataAdapter.removeStructure(record.data.structureId); -// _this.pdbGrid.setLoading("Removing PDB file"); -// } -// -// } -// }, -// viewConfig : { -// getRowClass : function(record, rowIdx, params, store) { -// if (record.raw.isSubunit != null) { -// return "blue-row"; -// } -// } -// } -// }); -// -// return this.pdbGrid; -//}; -// -//MacromoleculeForm.prototype.getMolarityForm = function(macromolecule) { -// var _this = this; -// var data = []; -// for (var i = 0; i < BIOSAXS.proposal.macromolecules.length; i++) { -// var m = BIOSAXS.proposal.macromolecules[i]; -// if (m.macromoleculeId != macromolecule.macromoleculeId) { -// data.push(m); -// } -// -// } -// this.macromoleculeCombo = BIOSAXS_COMBOMANAGER.getComboMacromoleculeByMacromolecules(data, { -// width : 250, -// labelWidth : 100, -// margin : 10 -// }); -// -// return Ext.createWidget('form', { -// -// frame : false, -// border : 0, -// // padding : 15, -// width : 550, -// height : 350, -// items : [ { -// xtype : 'container', -// flex : 1, -// border : false, -// layout : 'anchor', -// defaultType : 'requiredtext', -// items : [ this.macromoleculeCombo, { -// xtype : 'numberfield', -// name : 'Ratio', -// id : _this.id + "ratio", -// fieldLabel : 'Number in assymmetric units', -// value : 1, -// decimalPrecision : 6, -// margin : 10 -// }, { -// xtype : 'textareafield', -// name : 'comments', -// fieldLabel : 'Comments', -// margin : 10, -// width : 400, -// value : "" -// -// } ] -// } ] -// }); -// -//}; -// -///******************************************************************************* -// * JAVASCRIPT DOC -// ******************************************************************************/ -//MacromoleculeForm.prototype.input = function() { -// return { -// macromolecule : DATADOC.getMacromolecule_10() -// }; -//}; -// -//MacromoleculeForm.prototype.test = function(targetId) { -// var macromoleculeForm = new MacromoleculeForm(); -// var panel = macromoleculeForm.getPanel(macromoleculeForm.input().macromolecule); -// panel.render(targetId); -//}; - -function ResultSummaryForm() { - this.radiationDamageThreshold = BUI.getRadiationDamageThreshold(); - this.qualityThreshold = BUI.getQualityThreshold(); - - /** Data clusters with bufers **/ - this.clusterByBuffers = false; - - /** Visualization are groupeb by concenttrations. Ex: 4.5mg/ml and 4.7mg/ml will be clusterized together **/ - this.collapseConcentrations = true; - - this.summaryHeight = 350; - this.id = BUI.id(); - - var _this = this; - - this.qualityControlResultsWidget = new QualityControlResultsWidget({ - qualityThreshold : this.radiationDamageThreshold, - radiationDamageThreshold : this.qualityThreshold - - }); - this.qualityControlResultsWidget.onQualityChanged.attach(function(sender, value) { - _this.qualityThreshold = value; - _this.thresholdsChanged(); - }); - - this.qualityControlResultsWidget.onRadiationDatamageChanged.attach(function(sender, value) { - _this.radiationDamageThreshold = value; - _this.thresholdsChanged(); - - }); - this.concentrationHTMLTableWidget = new ConcentrationHTMLTableWidget({ - height : 250, - showBufferColumns : this.clusterByBuffers - }); - - this.plot = new BoxWhiskerGraph({ - targetId : _this.id + '_boxPlot', - height : 350, - width : 450, - maxBoxWidth : 25 - }); - - this.rangePlot = new RangeWhiskerGraph({ - targetId : _this.id + '_rangePlot', - height : 350, - width : 450, - maxBoxWidth : 25 - }); -} - -ResultSummaryForm.prototype.thresholdsChanged = function() { - var parsedData = this.prepareData(this.rawData); - - var filtered = JSON.parse(JSON.stringify(parsedData)); - filtered.concentration.concentrations = []; - for ( var conc in parsedData.concentration.concentrations) { - if (parsedData.concentration.concentrations[conc].calculation.rgGnom.validNumber > 0) { - filtered.concentration.concentrations.push(parsedData.concentration.concentrations[conc]); - } - } - - this.plotWhisker(filtered); - this.plotRange(filtered); - - this.concentrationHTMLTableWidget.refresh(parsedData); -}; - -/** Given a frame object (object returned by analyzeFrames()) and depending of the threshold indicates if a datacollection has radiation damage **/ -ResultSummaryForm.prototype.hasRadiationDamage = function(frameObject) { - var bb = frameObject.bufferBeforeFramesMerged / frameObject.framesCount; - var ba = frameObject.bufferAfterFramesMerged / frameObject.framesCount; - var mol = frameObject.framesMerge / frameObject.framesCount; - return !((bb >= this.radiationDamageThreshold) && (ba >= this.radiationDamageThreshold) && (mol >= this.radiationDamageThreshold)); -}; - -/** Return (frameObject) an object with the information about the frames for a data collection**/ -ResultSummaryForm.prototype.analyzeFrames = function(dataCollectionRow) { - return { - bufferAfterFramesMerged : dataCollectionRow.bufferAfterFramesMerged, - bufferBeforeFramesMerged : dataCollectionRow.bufferBeforeFramesMerged, - framesCount : dataCollectionRow.framesCount, - framesMerge : dataCollectionRow.framesMerge - }; -}; - -ResultSummaryForm.prototype.detectDataCollectionWarnings = function(dataCollectionRow) { - var frameObject = this.analyzeFrames(dataCollectionRow); - var warnings = { - count : 0, - type : [] - }; - - if (this.hasRadiationDamage(frameObject)) { - warnings.count = warnings.count + 1; - warnings.type.push("RADIATION DAMAGE"); - } - - if (Number(dataCollectionRow.quality) < this.qualityThreshold) { - warnings.count = 1;//warnings.count + 1; - warnings.type.push("Quality <" + this.qualityThreshold); - } - return warnings; -}; - -/** Return array composed by {concentration} objects **/ -ResultSummaryForm.prototype.getConcentrations = function(data) { - var concentrationsId = {}; - - for ( var i = 0; i < data.length; i++) { - var warning = this.detectDataCollectionWarnings(data[i]); - var id = data[i].conc;// + "_" + data[i].bufferId; - if (this.clusterByBuffers) { - id = data[i].conc + "_" + data[i].bufferId; - } - - if (concentrationsId[id] == null) { - concentrationsId[id] = { - id : id, - concentration : Number(data[i].conc).toFixed(2), - bufferId : data[i].bufferId, - bufferAcronym : data[i].bufferAcronym, - rgGuinier : [], - i0Guinier : [], - rgGnom : [], - dMax : [], - quality : [], - frames : [], - frames_warning : warning.count - }; - } else { - concentrationsId[id].frames_warning = concentrationsId[id].frames_warning + warning.count; - } - - concentrationsId[id].frames.push(data[i]); - - if (warning.count == 0) { - concentrationsId[id].rgGuinier.push(data[i].rgGuinier); - concentrationsId[id].i0Guinier.push(data[i].I0); - concentrationsId[id].quality.push(data[i].quality); - concentrationsId[id].rgGnom.push(data[i].rgGnom); - concentrationsId[id].dMax.push(data[i].dmax); - } - - } - var concentrations = []; - for ( var item in concentrationsId) { - if (concentrationsId.hasOwnProperty(item)) { - concentrations.push({ - concentration : concentrationsId[item].concentration, - id : item, - bufferId : Number(concentrationsId[item].bufferId).toFixed(2), - bufferAcronym : concentrationsId[item].bufferAcronym, - rgGuinier : concentrationsId[item].rgGuinier, - /** Frames **/ - frames : concentrationsId[item].frames, - frames_warning : concentrationsId[item].frames_warning, - /** Calculation **/ - calculation : { - rgGuinier : BUI.getStandardDeviation(concentrationsId[item].rgGuinier), - i0Guinier : BUI.getStandardDeviation(concentrationsId[item].i0Guinier), - quality : BUI.getStandardDeviation(concentrationsId[item].quality), - rgGnom : BUI.getStandardDeviation(concentrationsId[item].rgGnom), - dMax : BUI.getStandardDeviation(concentrationsId[item].dMax) - - } - }); - } - } - - return { - concentrations : concentrations - }; -}; - -ResultSummaryForm.prototype.prepareData = function(data) { - /** Return array composed by {acronym, bufferId} objects **/ - function getBuffers(data) { - var buffersId = {}; - for ( var i = 0; i < data.length; i++) { - buffersId[data[i].bufferId] = data[i].acronym; - } - var buffers = []; - for ( var id in buffersId) { - if (buffersId.hasOwnProperty(id)) { - buffers.push({ - acronym : buffersId[id], - bufferId : id - }); - } - } - return buffers; - } - - /** Get a string with all the concentrations **/ - function getConcentrationString(parseConcentrations) { - var concentrations = []; - for ( var i = 0; i < parseConcentrations.concentrations.length; i++) { - concentrations.push(parseConcentrations.concentrations[i].concentration + " mg/ml "); - } - return concentrations.toString(); - } - - var parseConcentrations = this.getConcentrations(data); - - return { - dataCollectionCount : data.length, - buffers : getBuffers(data), - concentration : parseConcentrations, - concentrationLabel : getConcentrationString(parseConcentrations) - }; -}; - -ResultSummaryForm.prototype.getDataForWhisker = function(data) { - var clusters = []; - - var concentrations = {}; - var i = 0; - var conc = 0; - if (this.collapseConcentrations) { - for (i = 0; i < data.concentration.concentrations.length; i++) { - conc = data.concentration.concentrations[i]; - var concentration = Number(conc.concentration).toFixed(0); - if (concentrations[concentration] == null) { - concentrations[concentration] = {}; - concentrations[concentration].concentration = concentration; - concentrations[concentration].calculation = {}; - concentrations[concentration].calculation.rgGuinier = {}; - concentrations[concentration].calculation.rgGuinier.values = []; - concentrations[concentration].calculation.rgGuinier.values = conc.calculation.rgGuinier.values; - concentrations[concentration].calculation.rgGnom = {}; - concentrations[concentration].calculation.rgGnom.values = []; - concentrations[concentration].calculation.rgGnom.values = conc.calculation.rgGnom.values; - } else { - concentrations[concentration].calculation.rgGuinier.values = concentrations[concentration].calculation.rgGuinier.values - .concat(conc.calculation.rgGuinier.values); - concentrations[concentration].calculation.rgGnom.values = concentrations[concentration].calculation.rgGnom.values - .concat(conc.calculation.rgGnom.values); - } - } - - /** From object to array **/ - var array = []; - for ( var key in concentrations) { - if (concentrations.hasOwnProperty(key)) { - array.push(concentrations[key]); - } - } - data.concentration.concentrations = array; - } - - for (i = 0; i < data.concentration.concentrations.length; i++) { - conc = data.concentration.concentrations[i]; - clusters.push({ - name : conc.concentration + "mg/ml", - concentration : Number(conc.concentration), - x : Number(conc.concentration), - classes : [] - }); - clusters[clusters.length - 1].classes.push({ - name : "Guinier", - color : '#9A2EFE', - values : conc.calculation.rgGuinier.values - - }); - clusters[clusters.length - 1].classes.push({ - name : "P(r)", - color : '#2E64FE', - values : conc.calculation.rgGnom.values - - }); - } - return { - clusters : clusters.sort(function(a, b) { - return a.concentration - b.concentration; - }) - }; -}; - -ResultSummaryForm.prototype.getDataForWhiskerQuality = function(data) { - var clusters = []; - - for ( var i = 0; i < data.concentration.concentrations.length; i++) { - var conc = data.concentration.concentrations[i]; - clusters.push({ - name : conc.concentration + "mg/ml", - classes : [] - }); - clusters[clusters.length - 1].classes.push({ - name : "Quality", - values : conc.calculation.quality.values - - }); - } - - return { - clusters : clusters - }; -}; - -ResultSummaryForm.prototype.plotQualityWhisker = function(parsedData) { - this.qualityPlot.refresh(this.getDataForWhiskerQuality(parsedData)); -}; - -ResultSummaryForm.prototype.plotWhisker = function(parsedData) { - this.plot.refresh(this.getDataForWhisker(parsedData)); -}; - -ResultSummaryForm.prototype.plotRange = function(parsedData) { - this.rangePlot.refresh(this.getDataForWhisker(parsedData)); -}; - -ResultSummaryForm.prototype.getPanel = function(data) { - var _this = this; - _this.rawData = data; - var parsedData = this.prepareData(data); - - this.panel = Ext - .createWidget( - 'form', - { - bodyPadding : 20, - frame : false, - border : 0, - width : this.width, - height : this.summaryHeight + 1000, - items : [ { - xtype : 'container', - layout : 'hbox', - items : [ { - xtype : 'container', - flex : 1, - border : false, - layout : 'anchor', - defaultType : 'textfield', - items : [ - _this.qualityControlResultsWidget.getPanel(), - { - xtype : 'fieldset', - width : 950, - margin : "20, 0 0 0", - height : this.summaryHeight + 500, - items : [ - { - html : "
Concentration Analysis
", - border : 0, - width : 900 - - }, - { - html : this.concentrationHTMLTableWidget.getPanel(parsedData), - border : 0, - width : 900, - margin : "10, 0 0 10" - - }, - { - xtype : 'container', - layout : 'vbox', - border : 5, - items : [ - { - html : "
Rg based on Guinier and Gnom
", - border : 8, - width : 900, - margin : "20 0 0 10" - - }, { - xtype : 'container', - layout : 'hbox', - items : [ { - xtype : 'container', - layout : 'vbox', - items : [ { - html : "
", - border : 0, - width : this.plot.width, - padding : "10 0 0 20", - listeners : { - afterrender : function() { - _this.plotWhisker(parsedData); - - } - } - }, { - html : "
Concentration (mg/ml)
", - width : 450, - border : 0, - margin : "-50 0 0 0" - - } ] - }, { - xtype : 'container', - layout : 'vbox', - items : [ { - html : "
", - border : 0, - width : this.rangePlot.width, - padding : "10 0 0 10", - listeners : { - afterrender : function() { - _this.plotRange(parsedData); - } - } - }, { - html : "
Concentration (mg/ml)
", - width : 450, - border : 0, - margin : "-50 0 0 0" - - } ] - } ] - } ] - } ] - } ] - } - - ] - } ] - }); - return this.panel; - -}; - -ResultSummaryForm.prototype.input = function() { - return { - data : DATADOC.getData_3367() - }; -}; - -ResultSummaryForm.prototype.test = function(targetId) { - var resultSummaryForm = new ResultSummaryForm(); - var panel = resultSummaryForm.getPanel(resultSummaryForm.input().data); - panel.render(targetId); - -}; - -/** - * Example form - * - * @witdh - * @height - */ -function RigibBodyModelingForm(args) { - this.id = BUI.id(); - this.width = 700; - this.height = 500; - - if (args != null) { - if (args.width != null) { - this.width = args.width; - } - if (args.height != null) { - this.height = args.height; - } - } - - var _this = this; - this.rigidBodyGrid = new AprioriRigidBodyGrid(); - - this.rigidBodyGrid.onUploadFile.attach(function(sender, type, title){ - _this._openUploadDialog(_this.macromolecule.macromoleculeId, type, title); - }); - - this.rigidBodyGrid.onRemove.attach(function(sender, type, title){ - _this._update(); - }); - - this.onSave = new Event(this); - -} - -RigibBodyModelingForm.prototype._getItems = function() { - var _this = this; - - - return [ { - xtype : 'label', - forId : 'myFieldId', - text : 'Information for model fit, mixture analysis and rigid body modeling', - margin : '15 0 20 10', - cls : "inline-help" - }, - this.rigidBodyGrid.getPanel(), - { - xtype : 'label', - forId : 'myFieldId', - text : 'Distance restraints may be imposed on the model using contacts conditions file (OPTIONAL)', - margin : '25 0 5 10', - cls : "inline-help" - }, - { - xtype : 'container', - layout : 'hbox', - items : [ { - xtype : 'container', - border : false, - layout : 'hbox', - items : [ { - xtype : 'label', - forId : 'myFieldId', - text : 'Contact Description File: (Optional)', - width : 150, - margin : '10 0 0 10' - }, { - id : this.id + "contactsDescriptionFilePath", - xtype : 'textfield', - hideLabel : true, - margin : '10 0 0 0', - width : 200, - disabled: true - }, { - text : 'Upload', - xtype : 'button', - margin : "10 0 0 10", - width : 80, - handler : function() { - - _this._openUploadDialog(_this.macromolecule.macromoleculeId, "CONTACTS", "Upload Contact Description File"); - } - }, { - text : 'Remove', - id : _this.id + "_remove", - xtype : 'button', - margin : "10 0 0 10", - width : 80, - handler : function() { - _this.macromolecule.contactsDescriptionFilePath = null; - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, proposal) { - /** Updating the proposal global object **/ - BIOSAXS.proposal.setItems(proposal); - _this.panel.setLoading(false); - - var saved = BIOSAXS.proposal.getMacromoleculeByAcronym(_this.macromolecule.acronym); - /** Refreshing to check that everything is ok **/ - _this.refresh(saved); - _this.onSave.notify(saved); - }); - - _this.panel.setLoading("Saving Macromolecule") - adapter.saveMacromolecule(_this.macromolecule); - } - } ] - } ] - }, { - xtype : 'panel', - html : "Go to SASREF manual for further information", - margin : "10 0 0 160", - border : 0 - - }, - { - xtype : 'checkbox', - margin : '10 0 0 5', - boxLabel : "I want rigid body modeling run on this stuff", - checked : true, - width : 300 - } ] - -}; - -/** Because update is a jsp page we don't know if the user has uploaded a file or not then we need to refresh **/ -RigibBodyModelingForm.prototype._update = function(macromoleculeId, type, title) { - var _this = this; - BIOSAXS.proposal.onInitialized.attach(function() { - if (BIOSAXS.proposal != null) { - _this.refresh(BIOSAXS.proposal.getMacromoleculeById(_this.macromolecule.macromoleculeId)); - _this.panel.setLoading(false); - } - }); - this.panel.setLoading(); - BIOSAXS.proposal.init(); -}; - -RigibBodyModelingForm.prototype._openUploadDialog = function(macromoleculeId, type, title) { - var _this = this; - function onClose() { - w.destroy(); - _this._update(); - - } - - var w = Ext.create('Ext.window.Window', { - title : title, - height : 200, - width : 400, - modal : true, - buttons : [ { - text : 'Close', - handler : function() { - onClose(); - } - } ], - layout : 'fit', - items : { - html : "" - }, - listeners : { - onEsc : function() { - onClose(); - }, - close : function() { - onClose(); - } - } - }).show(); -}; - -RigibBodyModelingForm.prototype._getButtons = function() { - return []; -}; - -RigibBodyModelingForm.prototype.getPanel = function() { - var _this = this; - this.panel = Ext.create('Ext.form.Panel', { - width : this.width, - height : this.height, - margin : 10, - border : 1, - defaultType : 'textfield', - items : this._getItems(), - buttons : this._getButtons(), - listeners : { - afterrender : function(){ - _this._populate(); - - } - } - }); - return this.panel; -}; - -/** Populates could be call when the DOM is not filled yet **/ -RigibBodyModelingForm.prototype._populate = function() { - if (this.macromolecule != null){ - if (Ext.getCmp(this.id + "contactsDescriptionFilePath") != null){ - if (this.macromolecule.contactsDescriptionFilePath != null){ - Ext.getCmp(this.id + "contactsDescriptionFilePath").setValue(this.macromolecule.contactsDescriptionFilePath); - Ext.getCmp(this.id + "_remove").enable(); - } - else{ - Ext.getCmp(this.id + "_remove").disable(); - Ext.getCmp(this.id + "contactsDescriptionFilePath").setValue(""); - - } - } - } -}; - -/** It populates the form * */ -RigibBodyModelingForm.prototype.refresh = function(macromolecule) { - this.macromolecule = macromolecule; - this.rigidBodyGrid.refresh(macromolecule); - this._populate(); -}; - -RigibBodyModelingForm.prototype.input = function() { - return {}; -}; - - -/** It populates the form **/ -RigibBodyModelingForm.prototype.test = function(targetId) { - var macromoleculeForm = new RigibBodyModelingForm(); - var panel = macromoleculeForm.getPanel(); - panel.render(targetId); -}; - - -/** - * Same form as MX part - * - * @creationMode if true a create button appears instead of save - * @showTitle true or false - */ -function ShipmentForm(args) { - this.id = BUI.id(); - - this.creationMode = false; - this.showTitle = true; - if (args != null) { - if (args.creationMode != null) { - this.creationMode = args.creationMode; - } - if (args.showTitle != null) { - this.showTitle = args.showTitle; - } - } - - this.onSaved = new Event(this); -} - -ShipmentForm.prototype.fillStores = function() { - this.panel.setLoading("Loading Labcontacts from database"); - this.labContactForSendingStore.loadData(BIOSAXS.proposal.getLabcontacts(), false); - this.labContactForReturnStore.loadData(BIOSAXS.proposal.getLabcontacts(), false); - this.panel.setLoading(false); - if (this.shipment != null) { - this.setShipment(this.shipment); - } -}; - -ShipmentForm.prototype.draw = function(targetId) { - this.getPanel().render(targetId); -}; - -ShipmentForm.prototype.setShipment = function(shipment) { - this.shipment = shipment; - var _this = this; - Ext.getCmp(_this.id + "shippingName").setValue(shipment.json.shippingName); - Ext.getCmp(_this.id + "shippingStatus").setValue(shipment.json.shippingStatus); - Ext.getCmp(_this.id + "comments").setValue(shipment.json.comments); - if (shipment.json.sendingLabContactVO != null) { - this.labContactsSendingCombo.setValue(shipment.json.sendingLabContactVO.labContactId); - } - if (shipment.json.returnLabContactVO != null) { - this.labContactsReturnCombo.setValue(shipment.json.returnLabContactVO.labContactId); - } - -}; - -ShipmentForm.prototype._saveShipment = function() { - var _this = this; - var shippingId = null; - if (this.shipment != null) { - shippingId = this.shipment.json.shippingId; - } - var json = { - shippingId : shippingId, - name : Ext.getCmp(_this.id + "shippingName").getValue(), - status : Ext.getCmp(_this.id + "shippingStatus").getValue(), - sendingLabContactId : Ext.getCmp(_this.id + "shipmentform_sendingLabContactId").getValue(), - returnLabContactId : Ext.getCmp(_this.id + "returnLabContactId").getValue(), - returnCourier : Ext.getCmp(_this.id + "returnCourier").getValue(), - courierAccount : Ext.getCmp(_this.id + "courierAccount").getValue(), - BillingReference : Ext.getCmp(_this.id + "BillingReference").getValue(), - dewarAvgCustomsValue : Ext.getCmp(_this.id + "dewarAvgCustomsValue").getValue(), - dewarAvgTransportValue : Ext.getCmp(_this.id + "dewarAvgTransportValue").getValue(), - comments : Ext.getCmp(_this.id + "comments").getValue() - }; - - var dataAdapter = new BiosaxsDataAdapter(); - dataAdapter.onSuccess.attach(function(sender, shipment) { - window.location = BUI.getShippingURL(shipment.shippingId); - }); - - dataAdapter.onError.attach(function(sender, error) { - _this.onError.notify(error); - }); - - /** Cheking params **/ - if (json.name == "") { - BUI.showError("Name field is mandatory"); - return; - } - - if (json.sendingLabContactId == null) { - BUI.showError("Lab contact for sending field is mandatory"); - return; - } - - if (json.returnLabContactId == null) { - BUI.showError("Lab contact for return field is mandatory"); - return; - } - - dataAdapter.createShipment(json.shippingId, json.name, json.status, json.comments, json.sendingLabContactId, json.returnLabContactId, - json.returnCourier, json.courierAccount, json.BillingReference, json.dewarAvgCustomsValue, json.dewarAvgTransportValue); - -}; - -ShipmentForm.prototype.getPanel = function(shipment) { - var _this = this; - this.shipment = shipment; - var required = '*'; - var buttons = []; - - if (_this.creationMode) { - buttons.push({ - text : 'Create', - scope : this, - handler : function() { - _this._saveShipment(); - } - }); - } else { - buttons.push({ - text : 'Save', - scope : this, - handler : function() { - _this._saveShipment(); - } - }); - - } - - this.labContactForSendingStore = Ext.create('Ext.data.Store', { - fields : [ 'cardName', 'labContactId' ] - }); - - this.labContactForReturnStore = Ext.create('Ext.data.Store', { - fields : [ 'cardName', 'labContactId' ] - }); - - // Create the combo box, attached to the states data store - this.labContactsSendingCombo = Ext.create('Ext.form.ComboBox', { - id : _this.id + "shipmentform_sendingLabContactId", - fieldLabel : 'Lab contact for sending', - afterLabelTextTpl : required, - store : this.labContactForSendingStore, - queryMode : 'local', - labelWidth : 200, - displayField : 'cardName', - valueField : 'labContactId' - }); - - this.labContactsReturnCombo = Ext.create('Ext.form.ComboBox', { - id : _this.id + "returnLabContactId", - fieldLabel : 'If No, Lab-Contact for Return', - afterLabelTextTpl : required, - store : this.labContactForReturnStore, - queryMode : 'local', - labelWidth : 200, - displayField : 'cardName', - valueField : 'labContactId', - listeners : { - change : function(x, newValue) { - for ( var i = 0; i < x.getStore().data.items.length; i++) { - if (x.getStore().data.items[i].raw.labContactId == newValue) { - Ext.getCmp(_this.id + "returnCourier").setValue(x.getStore().data.items[i].raw.defaultCourrierCompany); - Ext.getCmp(_this.id + "courierAccount").setValue(x.getStore().data.items[i].raw.courierAccount); - Ext.getCmp(_this.id + "BillingReference").setValue(x.getStore().data.items[i].raw.billingReference); - Ext.getCmp(_this.id + "dewarAvgCustomsValue").setValue(x.getStore().data.items[i].raw.dewarAvgCustomsValue); - Ext.getCmp(_this.id + "dewarAvgTransportValue").setValue(x.getStore().data.items[i].raw.dewarAvgTransportValue); - } - } - } - } - }); - - if (this.panel == null) { - this.panel = Ext.create('Ext.form.Panel', { - bodyPadding : 5, - width : 600, - border : 1, - items : [ { - xtype : 'fieldset', - title : 'Details', - collapsible : false, - defaultType : 'textfield', - layout : 'anchor', - defaults : { - anchor : '100%' - }, - items : [ { - xtype : 'requiredtext', - fieldLabel : 'Shipment Label', - allowBlank : false, - name : 'shippingName', - id : _this.id + 'shippingName', - value : '', - anchor : '50%' - }, { - - xtype : 'textareafield', - name : 'comments', - id : _this.id + 'comments', - fieldLabel : 'Comments', - value : '' - }, { - fieldLabel : 'Status', - readOnly : true, - id : _this.id + 'shippingStatus', - value : 'Opened', - anchor : '50%' - } ] - }, { - xtype : 'fieldset', - title : 'Lab-Contacts', - collapsible : false, - defaultType : 'textfield', - layout : 'anchor', - defaults : { - anchor : '100%' - }, - items : [ this.labContactsSendingCombo, this.labContactsReturnCombo ] - }, { - border : 0, - html : BUI.getWarningHTML("These informations are relevant for all shipments") - }, { - xtype : 'fieldset', - title : 'Courier accounts details for return', - collapsible : false, - layout : 'anchor', - defaults : { - anchor : '100%' - }, - items : [ { - xtype : 'textfield', - labelWidth : 400, - fieldLabel : 'Courier company for return (if ESRF sends a dewar back)', - id : _this.id + 'returnCourier', - value : '' - }, { - xtype : 'textfield', - labelWidth : 400, - fieldLabel : 'Courier account', - id : _this.id + 'courierAccount', - value : '' - }, { - xtype : 'textfield', - labelWidth : 400, - fieldLabel : 'Billing reference', - id : _this.id + 'BillingReference', - value : '' - }, { - xtype : 'numberfield', - labelWidth : 400, - fieldLabel : 'Average Customs value of a dewar (Euro)', - id : _this.id + 'dewarAvgCustomsValue', - value : '' - }, { - xtype : 'numberfield', - labelWidth : 400, - fieldLabel : 'Average Transport value of a dewar (Euro)', - id : _this.id + 'dewarAvgTransportValue', - value : '' - } ] - } ], - buttons : buttons - }); - } - this.fillStores(); - if (this.showTitle) { - this.panel.setTitle('Create a new Shipment'); - } - return this.panel; -}; - -ShipmentForm.prototype.input = function() { - return { - proposal : DATADOC.getProposal_10() - - }; -}; - -ShipmentForm.prototype.test = function(targetId) { - var shipmentForm = new ShipmentForm({ - creationMode : true - - }); - BIOSAXS.proposal = new Proposal(shipmentForm.input().proposal); - shipmentForm.getPanel().render(targetId); -}; - -/** - * #onSaved - */ -function StockSolutionForm(args) { - this.id = BUI.id(); - this.actions = []; - - if (args != null) { - if (args.actions != null) { - this.actions = args.actions; - } - } - this.onSaved = new Event(this); -} - -StockSolutionForm.prototype.getStockSolution = function() { - if (this.stockSolution != null) { - this.stockSolution.concentration = Ext.getCmp(this.id + "stockSolution_concentration").getValue(); - this.stockSolution.storageTemperature = Ext.getCmp(this.id + "stockSolution_storageTemperature").getValue(); - this.stockSolution.volume = Ext.getCmp(this.id + "stockSolution_volume").getValue(); - this.stockSolution.comments = Ext.getCmp(this.id + "stockSolution_comments").getValue(); - this.stockSolution.name = Ext.getCmp(this.id + "stockSolution_name").getValue(); - this.stockSolution.bufferId = this.bufferCombo.getValue(); - - if (this.macromoleculeCombo.getValue() != null) { - this.stockSolution.macromoleculeId = this.macromoleculeCombo.getValue(); - } else { - this.stockSolution.macromolecule3VO = null; - } - - } else { - return { - concentration : Ext.getCmp(this.id + "stockSolution_concentration").getValue(), - storageTemperature : Ext.getCmp(this.id + "stockSolution_storageTemperature").getValue(), - volume : Ext.getCmp(this.id + "stockSolution_volume").getValue(), - comments : Ext.getCmp(this.id + "stockSolution_comments").getValue(), - name : Ext.getCmp(this.id + "stockSolution_name").getValue(), - bufferId : this.bufferCombo.getValue(), - macromoleculeId : this.macromoleculeCombo.getValue() - }; - } - return this.stockSolution; -}; - -StockSolutionForm.prototype.setStockSolution = function(stockSolution) { - if (stockSolution != null) { - if (stockSolution.macromoleculeId != null) { - this.macromoleculeCombo.setValue(stockSolution.macromoleculeId); - } - this.bufferCombo.setValue(stockSolution.bufferId); - Ext.getCmp(this.id + "stockSolution_concentration").setValue(this.stockSolution.concentration); - Ext.getCmp(this.id + "stockSolution_storageTemperature").setValue(this.stockSolution.storageTemperature); - Ext.getCmp(this.id + "stockSolution_volume").setValue(this.stockSolution.volume); - Ext.getCmp(this.id + "stockSolution_name").setValue(this.stockSolution.name); - Ext.getCmp(this.id + "stockSolution_comments").setValue(this.stockSolution.comments); - } -}; - -StockSolutionForm.prototype.getBufferCombo = function() { - this.bufferCombo = BIOSAXS_COMBOMANAGER.getComboBuffers(BIOSAXS.proposal.getBuffers(), { - labelWidth : 120, - margin : '0 0 10 0', - width : 220 - - }); - return this.bufferCombo; -}; - -StockSolutionForm.prototype.getMacromoleculeCombo = function() { - this.macromoleculeCombo = BIOSAXS_COMBOMANAGER.getComboMacromoleculeByMacromolecules(BIOSAXS.proposal.getMacromolecules(), { - labelWidth : 120, - margin : '0 0 10 0', - width : 220 - - }); - return this.macromoleculeCombo; -}; - -StockSolutionForm.prototype.refresh = function() { -}; - -StockSolutionForm.prototype._getTopPanel = function() { - return { - xtype : 'container', - layout : 'hbox', - border : 0, - items : [ { - xtype : 'container', - layout : 'hbox', - items : [ { - xtype : 'container', - flex : 1, - border : false, - layout : 'anchor', - defaultType : 'textfield', - items : [ - - this.getMacromoleculeCombo(), { - xtype : 'requiredtext', - id : this.id + 'stockSolution_name', - fieldLabel : 'Acronym', - labelWidth : 120, - width : 250 - }, { - xtype : 'requiredtext', - id : this.id + 'stockSolution_concentration', - fieldLabel : 'Conc. (mg/ml)', - labelWidth : 120, - width : 250 - }, - - { - id : this.id + 'stockSolution_storageTemperature', - fieldLabel : 'Storage Temp.(C)', - labelWidth : 120, - width : 250 - }, { - xtype : 'requiredtext', - id : this.id + 'stockSolution_volume', - fieldLabel : 'Volume in Well (µl)', - labelWidth : 120, - width : 250 - } ] - } ] - }, { - xtype : 'container', - flex : 1, - layout : 'anchor', - defaultType : 'textfield', - margin : '0 0 0 10', - items : [ this.getBufferCombo() ] - } ] - }; - -}; - -StockSolutionForm.prototype.getPanel = function(stockSolution) { - this.stockSolution = stockSolution; - this.panel = Ext.createWidget({ - xtype : 'container', - layout : 'vbox', - border : 0, - style : { - padding : '10px' - }, - fieldDefaults : { - labelAlign : 'left', - labelWidth : 50 - }, - items : [ this._getTopPanel(stockSolution), { - id : this.id + 'stockSolution_comments', - xtype : 'textareafield', - name : 'comments', - fieldLabel : 'Comments', - labelWidth : 120, - width : '100%' - } ] - }); - - this.setStockSolution(stockSolution); - return this.panel; -}; - -StockSolutionForm.prototype.input = function() { - return { - stock : { - "stockSolutionId" : 6, - "proposalId" : 3124, - "macromoleculeId" : 5933, - "bufferId" : 811, - "instructionSet3VO" : null, - "boxId" : 305861, - "storageTemperature" : "20", - "volume" : "300", - "concentration" : "1.2", - "comments" : "Buffer EDTA with A", - "name" : "A_EDTA_1.2", - "samples" : [], - "buffer" : "EDTA", - "macromolecule" : "A" - }, - proposal : new MeasurementGrid().input().proposal - }; -}; - -StockSolutionForm.prototype.test = function(targetId) { - var stockSolutionForm = new StockSolutionForm(); - BIOSAXS.proposal = new Proposal(stockSolutionForm.input().proposal); - var panel = stockSolutionForm.getPanel(new Shipment(stockSolutionForm.input().stock)); - panel.render(targetId); -}; - - -BoxWhiskerGraph.prototype.cleanArray = GenericGraph.prototype.cleanArray; -BoxWhiskerGraph.prototype.plotAxes = GenericGraph.prototype.plotAxes; -BoxWhiskerGraph.prototype.plotRuler = GenericGraph.prototype.plotRuler; -BoxWhiskerGraph.prototype.drawSVGVerticalText = GenericGraph.prototype.drawSVGVerticalText; -BoxWhiskerGraph.prototype.getClassColor = GenericGraph.prototype.getClassColor; -BoxWhiskerGraph.prototype.getDimensions = GenericGraph.prototype.getDimensions; -BoxWhiskerGraph.prototype.calculate = GenericGraph.prototype.calculate; -BoxWhiskerGraph.prototype.getMedian = GenericGraph.prototype.getMedian; -BoxWhiskerGraph.prototype.pointToPixel = GenericGraph.prototype.pointToPixel; -BoxWhiskerGraph.prototype.maxValueWhisker = GenericGraph.prototype.maxValueWhisker; -BoxWhiskerGraph.prototype.refresh = GenericGraph.prototype.refresh; - -/** - * Subclass of GenericGraph - * - * @maxBoxWidth - */ -function BoxWhiskerGraph(args){ - this.maxBoxWidth = 25; - - if (args == null){ - args = new Object(); - } - args["plotHorizontalByCluster"] = true; - - GenericGraph.call(this, args); - - if (args.maxBoxWidth != null){ - this.maxBoxWidth = args.maxBoxWidth; - } -}; - - - -/** -There are several different methods for calculating quartiles.[1] This calculator uses a method described by Moore and McCabe to find quartile values. -The same method also used by The TI-83 to calculate quartile values. -With this method, the first quartile is the median of the numbers below the median, the third quartile is the median of the numbers above the median. - -http://www.miniwebtool.com/quartile-calculator/ -http://www.alcula.com/calculators/statistics/box-plot/ -**/ -BoxWhiskerGraph.prototype.getQ1 = function(array){ - array = array.slice(0, array.length/2); - return this.getMedian(array); -}; - -BoxWhiskerGraph.prototype.getQ3 = function(array){ - array = array.slice((array.length + 1)/2); - return this.getMedian(array); - -}; - -BoxWhiskerGraph.prototype.getQ2 = function(array){ - return this.getMedian(array); -}; - -BoxWhiskerGraph.prototype.getBelowOutliers = function(belowLimit, array){ - var points = new Array(); - for (var i = 0; i < array.length; i++){ - if (array[i] <= belowLimit){ - points.push(array[i]); - } - } - return points; -}; - -BoxWhiskerGraph.prototype.getAboveOutliers = function(aboveLimit, array){ - var points = new Array(); - for (var i = 0; i < array.length; i++){ - if (array[i] >= aboveLimit){ - points.push(array[i]); - } - } - return points; -}; - - -BoxWhiskerGraph.prototype.minValueWhisker = function(belowLimit, q1, array){ - var points = new Array(); - - for (var i = 0; i < array.length; i++){ - if ((array[i] < q1) && (array[i]) > belowLimit){ - points.push(array[i]); - } - } - - if (points.length > 0){ - points.sort(function(a, b){return a - b;}); - return points[0]; - } - return null; -}; - -BoxWhiskerGraph.prototype.isNumber = function(value){ - if (value=="") return false; - - var d = parseInt(value); - if (!isNaN(d)) return true; else return false; - -}; - -BoxWhiskerGraph.prototype.plotBoxWhisker = function(boxProperties){ - if (this.maxBoxWidth != null){ - if (boxProperties.width > this.maxBoxWidth){ - boxProperties.x = boxProperties.x + (boxProperties.width/2) - (this.maxBoxWidth/2); - boxProperties.width = this.maxBoxWidth; - } - - } - - if (this.plotPoints){ - for(var i = 0; i < boxProperties.values.length; i++){ - var value = boxProperties.values[i]; - var toPixel = this.pointToPixel(value, boxProperties); - SVG.drawCircle(boxProperties.x + (boxProperties.width/2) + 2, toPixel, this.pointRadius, this.svg, [["fill", "green"], ["fill-opacity", this.fillOpacityPoint],['stroke-opacity', this.strokeOpacityPoint], ["stroke", "black"]]); - } - } - - - var boxColor = this.getClassColor(boxProperties.name); - var result = this.calculate(boxProperties.values); - /** Q1 **/ - - if (this.isNumber(result.Q1)){ - SVG.drawLine(boxProperties.x, this.pointToPixel(result.Q1, boxProperties), boxProperties.x + boxProperties.width, this.pointToPixel(result.Q1, boxProperties), this.svg, [["stroke", boxColor]]); - } - /** Q2 **/ - if (this.isNumber(result.Q2)){ - SVG.drawLine(boxProperties.x, this.pointToPixel(result.Q2, boxProperties), boxProperties.x + boxProperties.width, this.pointToPixel(result.Q2, boxProperties), this.svg, [["stroke", "blue"], ["stroke-width", "2"]]); - } - - /** Q3 **/ - if (this.isNumber(result.Q3)){ - SVG.drawLine(boxProperties.x, this.pointToPixel(result.Q3, boxProperties), boxProperties.x + boxProperties.width, this.pointToPixel(result.Q3, boxProperties), this.svg, [["stroke", boxColor]]); - } - - /** Concenting Q1 and Q3 **/ - if (this.isNumber(result.Q1)&&this.isNumber(result.Q3)){ - SVG.drawLine(boxProperties.x, this.pointToPixel(result.Q1, boxProperties), boxProperties.x, this.pointToPixel(result.Q3, boxProperties), this.svg, [["stroke", boxColor]]); - SVG.drawLine(boxProperties.x + boxProperties.width, this.pointToPixel(result.Q1, boxProperties), boxProperties.x + boxProperties.width, this.pointToPixel(result.Q3, boxProperties), this.svg, [["stroke", boxColor]]); - } - - /** min-whisker **/ - if (result["min-whisker"] != null){ - if (this.isNumber(result.Q1)){ - SVG.drawLine(boxProperties.x + (boxProperties.width/2) + 2, this.pointToPixel(result.Q1, boxProperties), boxProperties.x + (boxProperties.width/2) + 2, this.pointToPixel(result["min-whisker"], boxProperties), this.svg, [["stroke", boxColor]]); - } - SVG.drawLine(boxProperties.x,this.pointToPixel(result["min-whisker"], boxProperties), boxProperties.x + boxProperties.width, this.pointToPixel(result["min-whisker"], boxProperties) , this.svg, [["stroke", boxColor]]); - - } - - /** max-whisker **/ - if (result["max-whisker"] != null){ - if (this.isNumber(result.Q3)){ - SVG.drawLine(boxProperties.x + (boxProperties.width/2) + 2, this.pointToPixel(result.Q3, boxProperties), boxProperties.x + (boxProperties.width/2) + 2, this.pointToPixel(result["max-whisker"], boxProperties), this.svg, [["stroke", boxColor]]); - } - SVG.drawLine(boxProperties.x , this.pointToPixel(result["max-whisker"], boxProperties), boxProperties.x + boxProperties.width, this.pointToPixel(result["max-whisker"], boxProperties), this.svg, [["stroke", boxColor]]); - - } - - /** outliners **/ - if (result["above-outliers"] != null){ - for (var point in result["above-outliers"]){ - SVG.drawCircle(boxProperties.x + (boxProperties.width/2) + 2, this.pointToPixel(result["above-outliers"][point], boxProperties), this.pointRadius, this.svg, [["fill", "red"], ["fill-opacity", "1"]]); - //SVG.drawText(boxProperties.x + (boxProperties.width/2) - 2, this.pointToPixel(result["above-outliers"][point], boxProperties) + 2, "x",this.svg, [["fill", "black"], ["font-size", "x-small"]]); - } - } - if (result["below-outliers"] != null){ - for (var point in result["below-outliers"]){ - SVG.drawCircle(boxProperties.x + (boxProperties.width/2) + 2, this.pointToPixel(result["below-outliers"][point], boxProperties), this.pointRadius, this.svg, [["fill", "red"], ["fill-opacity", "1"]]); - //SVG.drawText(boxProperties.x + (boxProperties.width/2) - 2, this.pointToPixel(result["below-outliers"][point], boxProperties) + 2, "x",this.svg, [["fill", "black"], ["font-size", "x-small"]]); - } - } -}; - - - -BoxWhiskerGraph.prototype.plotClusterTitles = function(properties){ - /** Cluster Titles **/ - var posX = this.left + this.rulerWidth; - var data = this.data; - for(var i = 0; i < data.clusters.length; i++ ){ - /** title for the clusters **/ - posX = posX + this.interClustersSpace; - - /** Drawing title of classes **/ - var cluster = data.clusters[i]; - var posXClasses = posX; - for(var j = 0; j < cluster.classes.length; j++){ - //SVG.drawRectangle(posXClasses, this.height - (this.bottom + this.clusterTitleHeight + this.rulerHeight), properties.classWidth, this.rulerHeight, this.svg, [["fill", "green"]]); -// SVG.drawText(posXClasses + 1/6*properties.classWidth, this.height - (this.bottom + this.clusterTitleHeight + (this.rulerHeight*1/4)), cluster.classes[j].name, this.svg, [["style", "font-size:xx-small"]]); - var color = cluster.classes[j].color; - this.drawSVGVerticalText(posXClasses + 1/2*(properties.classWidth), this.height - (this.bottom - this.clusterTitleHeight + (this.rulerHeight)), cluster.classes[j].name , [["style", "font-size:x-small;"], ["fill", color]]); - - // SVG.drawRectangle(posXClasses, this.height - (this.bottom + this.clusterTitleHeight), properties.classWidth, 2, this.svg, [["fill", "black"]]); - posXClasses = posXClasses + properties.classWidth + this.interClassesSpace; - } - - var clusterTitleSpace = (data.clusters[i].classes.length * properties.classWidth) + ((data.clusters[i].classes.length - 1) * this.interClassesSpace); - //SVG.drawRectangle(posX, this.height - (this.bottom + this.clusterTitleHeight), clusterTitleSpace, this.clusterTitleHeight, this.svg, [["fill", "pink"]]); - SVG.drawRectangle(posX, this.height - (this.clusterTitleHeight+this.bottom), clusterTitleSpace, 1, this.svg, []); - -// var transform = ["translate", "(" + posX + 1/3*clusterTitleSpace +", " + this.height - (this.bottom + (this.clusterTitleHeight*1/4)) +")"], ["transform", "rotate(180)"]; -// var transform = ["transform", "translate(" + (posX + 1/3*clusterTitleSpace) +", " + (this.height - (this.bottom + (this.clusterTitleHeight*1/4))) + "), rotate(-90) "]; -// this.drawSVGVerticalText(posX + 1/3*clusterTitleSpace, this.height - (this.bottom + (this.clusterTitleHeight*1/4)),data.clusters[i].name, [["style", "font-size:x-small"]]); - SVG.drawText(posX , this.height - (this.bottom + (this.clusterTitleHeight*1/4)),data.clusters[i].name ,this.svg, [["style", "font-size:x-small"]]); - posX = posX + clusterTitleSpace; - } -}; - -BoxWhiskerGraph.prototype.plotWhisters = function(data, properties){ - var colors = ["yellow", "orange", "green"]; - var posX = this.left + this.rulerWidth; - for(var i = 0; i < data.clusters.length; i++ ){ - var cluster = data.clusters[i]; - /** inter cluster space **/ - //SVG.drawRectangle(posX, this.top, this.interClustersSpace, this.height - this.top - this.bottom - this.clusterTitleHeight - this.rulerHeight, this.svg, [["fill", "black"]]); - posX = posX + this.interClustersSpace; - - for (var j = 0; j < cluster.classes.length; j ++){ - - //SVG.drawRectangle(posX, this.top, properties.classWidth, this.height - this.top - this.bottom - this.clusterTitleHeight - this.rulerHeight, this.svg, [["fill", colors[j]]]); - this.plotBoxWhisker( - { - name : cluster.classes[j].name, - values : cluster.classes[j].values, - minPoint : properties.minPoint, - maxPoint : properties.maxPoint, - x : posX, - y : this.top, - width : properties.classWidth, - height : this.height - this.top - this.bottom - this.clusterTitleHeight - this.rulerHeight - - - } - ); - posX = posX + properties.classWidth; - //SVG.drawRectangle(posX, this.top, this.interClassesSpace, this.height - this.top - this.bottom - this.clusterTitleHeight - this.rulerHeight, this.svg, [["fill", "black"]]); - if (j < cluster.classes.length - 1){ - posX = posX + this.interClassesSpace; - } - } - } -}; - -BoxWhiskerGraph.prototype.draw = function(targetId, data){ - this.targetId = targetId; - var properties = (this.getDimensions(data)); - this.svg = SVG.createSVGCanvas(document.getElementById(this.targetId), [["width", this.width], ["height", this.height]]); - this.plotAxes(properties); - this.plotClusterTitles(properties); - this.plotWhisters(data,properties); -}; - -BoxWhiskerGraph.prototype.input = function(){ - return DATADOC.getBoxWhikerData(); -}; - -BoxWhiskerGraph.prototype.test = function(targetId){ - var plot = new BoxWhiskerGraph( - { - targetId : targetId, - height : 350, - width : 450, - maxBoxWidth : 25 - } - ); - plot.refresh(this.input()); -}; - - - - - - - -function DataSet(){ - this.json = null; - -}; - - -DataSet.prototype.loadFromJSON = function(json){ - if(json != null) { - if(this.validate(json)) { - this.json = json; - } - } -}; - - -DataSet.prototype.toJSON = function(json){ - return this.json; -}; - - -/** Abstract method to be override on childs classes **/ -DataSet.prototype.validate = function(json){ - if (true){ - return true; - } - /*else{ - throw "Data validation failed"; - }*/ -}; - - - - -Edge.prototype.getName = GraphItem.prototype.getName; -Edge.prototype.setName = GraphItem.prototype.setName; -Edge.prototype.getId = GraphItem.prototype.getId; - -function Edge(id, name, nodeSource, nodeTarget, args){ - GraphItem.prototype.constructor.call(this, id, name, args); - - this.sourceNode = nodeSource; - this.targetNode = nodeTarget; - -}; - -Edge.prototype.toJSON = function(){ - return {"index": this.id,"sourceIndex":this.sourceNode.getId(),"targetIndex":this.targetNode.getId(),"args":this.args}; -}; - -Edge.prototype.getNodeSource = function(){ - return this.sourceNode; -}; - -Edge.prototype.getNodeTarget = function(){ - return this.targetNode; -}; - -Edge.prototype.remove = function(){ - //Remove edge object in the nodes - this.getNodeSource().removeEdge(this); - this.getNodeTarget().removeEdge(this); - - this.deleted.notify(this); -}; - - -EdgeGraphFormatter.prototype.setProperties = ItemGraphFormatter.prototype.setProperties; -EdgeGraphFormatter.prototype.getDefault = ItemGraphFormatter.prototype.getDefault; -EdgeGraphFormatter.prototype.getSelected = ItemGraphFormatter.prototype.getSelected; -EdgeGraphFormatter.prototype.getOver = ItemGraphFormatter.prototype.getOver; -EdgeGraphFormatter.prototype.getDragging = ItemGraphFormatter.prototype.getDragging; -EdgeGraphFormatter.prototype.getId = ItemGraphFormatter.prototype.getId; -EdgeGraphFormatter.prototype.toJSON = ItemGraphFormatter.prototype.toJSON; -EdgeGraphFormatter.prototype.loadFromJSON = ItemGraphFormatter.prototype.loadFromJSON; -EdgeGraphFormatter.prototype._setEvents = ItemGraphFormatter.prototype._setEvents; - -function EdgeGraphFormatter(id, defaultFormat, selectedFormat, overFormat, dragginFormat){ - ItemGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, dragginFormat); - - this.getDefault().args.type = "EdgeGraphFormatter"; -}; - - - - -LineEdgeGraphFormatter.prototype.setProperties = EdgeGraphFormatter.prototype.setProperties; -LineEdgeGraphFormatter.prototype.getDefault = EdgeGraphFormatter.prototype.getDefault; -LineEdgeGraphFormatter.prototype.getSelected = EdgeGraphFormatter.prototype.getSelected; -LineEdgeGraphFormatter.prototype.getOver = EdgeGraphFormatter.prototype.getOver; -LineEdgeGraphFormatter.prototype.getDragging = EdgeGraphFormatter.prototype.getDragging; -LineEdgeGraphFormatter.prototype.getId = EdgeGraphFormatter.prototype.getId; -LineEdgeGraphFormatter.prototype.toJSON = EdgeGraphFormatter.prototype.toJSON; -LineEdgeGraphFormatter.prototype.loadFromJSON = EdgeGraphFormatter.prototype.loadFromJSON; -LineEdgeGraphFormatter.prototype._setEvents = EdgeGraphFormatter.prototype._setEvents; - -function LineEdgeGraphFormatter(id, defaultFormat, selectedFormat, overFormat, dragginFormat){ - EdgeGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, dragginFormat); - this.args.type = "LineEdgeGraphFormatter"; -}; - -/** DIRECTED **/ -DirectedLineEdgeGraphFormatter.prototype.setProperties = EdgeGraphFormatter.prototype.setProperties; -DirectedLineEdgeGraphFormatter.prototype.getDefault = EdgeGraphFormatter.prototype.getDefault; -DirectedLineEdgeGraphFormatter.prototype.getSelected = EdgeGraphFormatter.prototype.getSelected; -DirectedLineEdgeGraphFormatter.prototype.getOver = EdgeGraphFormatter.prototype.getOver; -DirectedLineEdgeGraphFormatter.prototype.getDragging = EdgeGraphFormatter.prototype.getDragging; -DirectedLineEdgeGraphFormatter.prototype.getId = EdgeGraphFormatter.prototype.getId; -DirectedLineEdgeGraphFormatter.prototype.toJSON = EdgeGraphFormatter.prototype.toJSON; -DirectedLineEdgeGraphFormatter.prototype.loadFromJSON = EdgeGraphFormatter.prototype.loadFromJSON; -DirectedLineEdgeGraphFormatter.prototype._setEvents = EdgeGraphFormatter.prototype._setEvents; - -function DirectedLineEdgeGraphFormatter(id, defaultFormat, selectedFormat, overFormat, dragginFormat){ - EdgeGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, dragginFormat); - this.args.type = "DirectedLineEdgeGraphFormatter"; - this.args.arrowSize = "3"; -}; -DirectedLineEdgeGraphFormatter.prototype.getArrowSize = function(){ - return this.args.arrowSize; -}; - -OdirectedLineEdgeGraphFormatter.prototype.setProperties = EdgeGraphFormatter.prototype.setProperties; -OdirectedLineEdgeGraphFormatter.prototype.getDefault = EdgeGraphFormatter.prototype.getDefault; -OdirectedLineEdgeGraphFormatter.prototype.getSelected = EdgeGraphFormatter.prototype.getSelected; -OdirectedLineEdgeGraphFormatter.prototype.getOver = EdgeGraphFormatter.prototype.getOver; -OdirectedLineEdgeGraphFormatter.prototype.getDragging = EdgeGraphFormatter.prototype.getDragging; -OdirectedLineEdgeGraphFormatter.prototype.getId = EdgeGraphFormatter.prototype.getId; -OdirectedLineEdgeGraphFormatter.prototype.toJSON = EdgeGraphFormatter.prototype.toJSON; -OdirectedLineEdgeGraphFormatter.prototype.loadFromJSON = EdgeGraphFormatter.prototype.loadFromJSON; -OdirectedLineEdgeGraphFormatter.prototype._setEvents = EdgeGraphFormatter.prototype._setEvents; - -function OdirectedLineEdgeGraphFormatter(id, defaultFormat, selectedFormat, overFormat, dragginFormat){ - EdgeGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, dragginFormat); - this.args.type = "DirectedLineEdgeGraphFormatter"; - this.args.arrowSize = "3"; -}; - -OdirectedLineEdgeGraphFormatter.prototype.getArrowSize = function(){ - return this.args.arrowSize; -}; - - -/** CUT (inhibition) **/ -CutDirectedLineEdgeGraphFormatter.prototype.setProperties = EdgeGraphFormatter.prototype.setProperties; -CutDirectedLineEdgeGraphFormatter.prototype.getDefault = EdgeGraphFormatter.prototype.getDefault; -CutDirectedLineEdgeGraphFormatter.prototype.getSelected = EdgeGraphFormatter.prototype.getSelected; -CutDirectedLineEdgeGraphFormatter.prototype.getOver = EdgeGraphFormatter.prototype.getOver; -CutDirectedLineEdgeGraphFormatter.prototype.getDragging = EdgeGraphFormatter.prototype.getDragging; -CutDirectedLineEdgeGraphFormatter.prototype.getId = EdgeGraphFormatter.prototype.getId; -CutDirectedLineEdgeGraphFormatter.prototype.toJSON = EdgeGraphFormatter.prototype.toJSON; -CutDirectedLineEdgeGraphFormatter.prototype.loadFromJSON = EdgeGraphFormatter.prototype.loadFromJSON; -CutDirectedLineEdgeGraphFormatter.prototype._setEvents = EdgeGraphFormatter.prototype._setEvents; - -function CutDirectedLineEdgeGraphFormatter(id, defaultFormat, selectedFormat, overFormat, dragginFormat){ - EdgeGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, dragginFormat); - this.args.type = "CutDirectedLineEdgeGraphFormatter"; -}; - - - - -BezierEdgeGraphFormatter.prototype.setProperties = EdgeGraphFormatter.prototype.setProperties; -BezierEdgeGraphFormatter.prototype.setProperties = EdgeGraphFormatter.prototype.setProperties; -BezierEdgeGraphFormatter.prototype.getDefault = EdgeGraphFormatter.prototype.getDefault; -BezierEdgeGraphFormatter.prototype.getSelected = EdgeGraphFormatter.prototype.getSelected; -BezierEdgeGraphFormatter.prototype.getOver = EdgeGraphFormatter.prototype.getOver; -BezierEdgeGraphFormatter.prototype.getDragging = EdgeGraphFormatter.prototype.getDragging; -BezierEdgeGraphFormatter.prototype.getId = EdgeGraphFormatter.prototype.getId; -BezierEdgeGraphFormatter.prototype.toJSON = EdgeGraphFormatter.prototype.toJSON; -BezierEdgeGraphFormatter.prototype.loadFromJSON = EdgeGraphFormatter.prototype.loadFromJSON; -BezierEdgeGraphFormatter.prototype._setEvents = EdgeGraphFormatter.prototype._setEvents; - -function BezierEdgeGraphFormatter(id, defaultFormat, selectedFormat, overFormat, dragginFormat){ - EdgeGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, dragginFormat); - this.args.type = "BezierEdgeGraphFormatter"; -}; - - - -DotDirectedLineEdgeGraphFormatter.prototype.setProperties = EdgeGraphFormatter.prototype.setProperties; -DotDirectedLineEdgeGraphFormatter.prototype.getDefault = EdgeGraphFormatter.prototype.getDefault; -DotDirectedLineEdgeGraphFormatter.prototype.getSelected = EdgeGraphFormatter.prototype.getSelected; -DotDirectedLineEdgeGraphFormatter.prototype.getOver = EdgeGraphFormatter.prototype.getOver; -DotDirectedLineEdgeGraphFormatter.prototype.getDragging = EdgeGraphFormatter.prototype.getDragging; -DotDirectedLineEdgeGraphFormatter.prototype.getId = EdgeGraphFormatter.prototype.getId; -DotDirectedLineEdgeGraphFormatter.prototype.toJSON = EdgeGraphFormatter.prototype.toJSON; -DotDirectedLineEdgeGraphFormatter.prototype.loadFromJSON = EdgeGraphFormatter.prototype.loadFromJSON; -DotDirectedLineEdgeGraphFormatter.prototype._setEvents = EdgeGraphFormatter.prototype._setEvents; - -function DotDirectedLineEdgeGraphFormatter(id, defaultFormat, selectedFormat, overFormat, dragginFormat){ - EdgeGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, dragginFormat); - this.args.type = "DotDirectedLineEdgeGraphFormatter"; -}; - - -OdotDirectedLineEdgeGraphFormatter.prototype.setProperties = EdgeGraphFormatter.prototype.setProperties; -OdotDirectedLineEdgeGraphFormatter.prototype.getDefault = EdgeGraphFormatter.prototype.getDefault; -OdotDirectedLineEdgeGraphFormatter.prototype.getSelected = EdgeGraphFormatter.prototype.getSelected; -OdotDirectedLineEdgeGraphFormatter.prototype.getOver = EdgeGraphFormatter.prototype.getOver; -OdotDirectedLineEdgeGraphFormatter.prototype.getDragging = EdgeGraphFormatter.prototype.getDragging; -OdotDirectedLineEdgeGraphFormatter.prototype.getId = EdgeGraphFormatter.prototype.getId; -OdotDirectedLineEdgeGraphFormatter.prototype.toJSON = EdgeGraphFormatter.prototype.toJSON; -OdotDirectedLineEdgeGraphFormatter.prototype.loadFromJSON = EdgeGraphFormatter.prototype.loadFromJSON; -OdotDirectedLineEdgeGraphFormatter.prototype._setEvents = EdgeGraphFormatter.prototype._setEvents; - -function OdotDirectedLineEdgeGraphFormatter(id, defaultFormat, selectedFormat, overFormat, dragginFormat){ - EdgeGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, dragginFormat); - this.args.type = "OdotDirectedLineEdgeGraphFormatter"; -}; - - - -function GraphDataset(){ - DataSet.prototype.constructor.call(this); - this.edges = new Object(); - this.vertices = new Object(); - this.verticesIndex = new Object(); - - //Events - this.newVertex = new Event(this); - this.vertexNameChanged = new Event(this); - this.vertexDeleted = new Event(this); - - this.newEdge = new Event(this); - this.edgeNameChanged = new Event(this); - this.edgeDeleted = new Event(this); - - this.json = new Object(); - this.json.vertices = new Array(); - this.json.edges = new Array(); - this.json.relations = new Array(); -}; - -GraphDataset.prototype.loadFromJSON = DataSet.prototype.loadFromJSON; -GraphDataset.prototype.toJSON = DataSet.prototype.toJSON; -GraphDataset.prototype.validate = DataSet.prototype.validate; - -/** Devuelve el numero de edges incidentes sobre el nodo con mas edges **/ -GraphDataset.prototype.getMaxClass = function(){ - var maxClassNode = 0; - for ( var node in this.vertices) { - if (this.vertices[node].getEdgesCount() > maxClassNode){ - maxClassNode = this.vertices[node].getEdgesCount(); - } - } - return maxClassNode; -}; - -/** Devuelve el numero de edges incidentes sobre el nodo con mas edges **/ -GraphDataset.prototype.getMinClass = function(){ - var minClassNode = Math.min(); - for ( var node in this.vertices) { - if (this.vertices[node].getEdgesCount() < minClassNode){ - minClassNode = this.vertices[node].getEdgesCount(); - } - } - return minClassNode; -}; - -GraphDataset.prototype.getVertexByName = function(nodeName){ - var results = new Array(); - - for (var vertexId in this.verticesIndex[nodeName]){ - var vertexByid = this.getVertexById(this.verticesIndex[nodeName][vertexId]); - results.push(vertexByid); - //* añadido nuevo porque fallaba el anterior codigo - return vertexByid - } - - if (results <= 1){ - return this.getVertexById(this.verticesIndex[nodeName]); - } - else{ - return results; - } -}; - -GraphDataset.prototype.getVertexById = function(id){ - return this.vertices[id]; -}; - -GraphDataset.prototype.toSIF = function(){ - var sifDataAdapter = new SifFileDataAdapter(); - return sifDataAdapter.toSIF(this); -}; - -GraphDataset.prototype.toSIFID = function(){ - var sifDataAdapter = new SifFileDataAdapter(); - return sifDataAdapter.toSIFID(this); -}; - -GraphDataset.prototype.toDOT = function(){ - var dotFileDataAdapter = new DotFileDataAdapter(); - return dotFileDataAdapter.toDOT(this); -}; - -GraphDataset.prototype.toDOTID = function(){ - var dotFileDataAdapter = new DotFileDataAdapter(); - return dotFileDataAdapter.toDOTID(this); -}; - -GraphDataset.prototype._addNode = function(nodeName, args){ - return new Vertex(this._getVerticesCount()-1, nodeName, args); -}; - -GraphDataset.prototype.addNode = function(nodeName, args){ - this.json.vertices.push(nodeName); - this._addVerticesIndex(nodeName, this._getVerticesCount() - 1); - var vertex = this._addNode(nodeName, args); - this.vertices[this._getVerticesCount()-1] = vertex; - this._setNodeEvents(vertex); - this.newVertex.notify(vertex); -}; - -GraphDataset.prototype._addVerticesIndex = function(nodeName, id){ - if (this.verticesIndex[nodeName] == null){ - this.verticesIndex[nodeName] = new Array(); - } - this.verticesIndex[nodeName].push(id); -}; - -GraphDataset.prototype.addEdge = function(edgeName, nodeSourceId, nodeTargetId, args){ - this.json.edges.push(edgeName); - var nodeSource = this.getVertexById(nodeSourceId); - var nodeTarget = this.getVertexById(nodeTargetId); - var index = this.getEdgesCount() - 1; - this.edges[index] = new Edge(index, edgeName, nodeSource, nodeTarget, args); - this.json.relations.push({"index": index, "sourceIndex": nodeSourceId, "targetIndex": nodeTargetId, "args": args }); - - nodeSource.addEdge(this.edges[index]); - nodeTarget.addEdge(this.edges[index]); - this._setEdgeEvents(this.edges[index]); - this.newEdge.notify(this.edges[index]); -}; - -GraphDataset.prototype.getVertices = function(){ - return this.vertices; -}; - -GraphDataset.prototype.getEdges = function(){ - return this.edges; -}; - -GraphDataset.prototype.getEdgeById = function(edgeId){ - return this.edges[edgeId]; -}; - -GraphDataset.prototype._getVerticesCount = function(){ - return this.json.vertices.length; -}; - - -GraphDataset.prototype.getVerticesCount = function(){ - var count = 0; - for ( var vertex in this.getVertices()) { - count ++; - } - return count; -}; - - -GraphDataset.prototype.getEdgesCount = function(){ - return this.json.edges.length; -}; - -GraphDataset.prototype.init = function(){ - this.edges = new Object(); - this.vertices = new Object(); -}; - -GraphDataset.prototype._setNodeEvents = function(node){ - var _this = this; - //NODE EVENTS - node.deleted.attach(function (sender, node){ - _this._removeNode(node); - }); - - node.nameChanged.attach(function (sender, args){ - var item = args.item; - var newName = item.name; - var indexes = _this.verticesIndex[args.previousName]; - for(var i = 0; i < indexes.length; i++){ - if(indexes[i] == item.id) - indexes.splice(i,1); - } - if(indexes.length == 0){ - delete _this.verticesIndex[args.previousName]; - } - _this._addVerticesIndex(newName, item.id); - _this.json.vertices[item.id] = newName; - _this.vertexNameChanged.notify(args); - }); -}; - -GraphDataset.prototype._setEdgeEvents = function(edge){ - var _this = this; - //EDGE EVENTS - edge.nameChanged.attach(function (sender, edge){ - _this.edgeNameChanged.notify(edge); - - }); - - edge.deleted.attach(function (sender, edge){ - _this._removeEdge(edge); - }); -}; - -GraphDataset.prototype._connectVerticesByName = function(nodeNameSource, nodeNameTarget){ - var source = this.getVertexByName(nodeNameSource); - var target = this.getVertexByName(nodeNameTarget); - - if ((source != null)&&(target!=null)){ - this.addEdge(source.getName() +"_" + target.getName(), source.getId(), target.getId(), {}); - } - else{ - if (source == null){ - console.log("No encontrado: " + nodeNameSource) - } - if (target == null){ - console.log("No encontrado: " + nodeNameTarget) - } - } -}; - -GraphDataset.prototype.loadFromJSON = function(json){ - var json = json; - this.init(); - this.json = new Object(); - this.json.vertices = new Array(); - this.json.edges = new Array(); - this.json.relations = new Array(); - - for ( var i = 0; i < json.nodes.length; i++) { - if (json.nodes[i] != null){ - var name = json.nodes[i]; - this.addNode(name); - } - else{ - this.json.vertices.push(null); - } - } - - for ( var i = 0; i < json.edges.length; i++) { - if (json.edges[i] != null){ - if (json.relations[i] != null){ - var name = json.edges[i]; - this.addEdge(name, json.relations[i].sourceIndex, json.relations[i].targetIndex, json.relations[i].args); - } - } - else{ - this.json.edges.push(null); - this.json.relations.push(null); - } - } -}; - -GraphDataset.prototype.prettyPrint = function(){ - for ( var node in this.vertices) { - console.log(this.vertices[node].getName() ); - for ( var j = 0; j < this.vertices[node].getEdgesIn().length; j++) { - console.log(" --> " + this.vertices[node].getEdgesIn()[j].getNodeTarget().getName() ); - } - } -}; - -GraphDataset.prototype._removeEdge = function(edge){ - this.json.edges[edge.getId()] = null; - this.json.relations[edge.getId()] = null; - - delete this.edges[edge.getId()]; - this.edgeDeleted.notify(edge); - - -}; - -GraphDataset.prototype._removeNode = function(node){ - this.json.vertices[node.getId()] = null; - delete this.vertices[node.getId()]; - this.vertexDeleted.notify(node); -}; - -GraphDataset.prototype.toJSON = function(){ - var json = new Object(); - var nodes = new Array(); - json.nodes = this.json.vertices; //nodes; - json.edges = this.json.edges; //edges; - json.relations = this.json.relations; - return json; -}; - -GraphDataset.prototype.clone = function(){ - var dsDataset = new GraphDataset(); - dsDataset.loadFromJSON(this.toJSON()); - return dsDataset; -}; -//GraphDataset.prototype.test = function(){ -// this.loadFromJSON(this.toJSON()); -//}; - -function labels(){ - var names = new Array(); - - var dataset = interactomeViewer.graphEditorWidget.dataset; - var layout = interactomeViewer.graphEditorWidget.layout; - - for ( var vertexId in interactomeViewer.graphEditorWidget.dataset.getVertices()) { - names.push(interactomeViewer.graphEditorWidget.dataset.getVertexById(vertexId).getName()); - } - - var sorted = (names.sort()); - console.log(sorted) - var distance = 0.01; - var altura = 0.6; - for ( var i = 0; i < names.length; i++) { - var id =dataset.getVertexByName(names[i]).getId(); - - layout.getNodeById(id).setCoordenates(distance, altura); - - - distance = parseFloat(distance) + parseFloat(0.03); - - altura = parseFloat(altura) + parseFloat(0.02); - - if (parseFloat(altura) == 0.9800000000000003){ - - altura = 0.6; - distance = distance - 0.51; - } - - } - - -}; - -function GraphItem(id, name, args){ - this.id = id; - this.name = name; - this.type = "NONE"; - - this.args = new Object(); - - - if (args!=null){ - this.args = args; - if (args.type !=null){ - this.type = args.type; - } - } - - //Events - this.nameChanged = new Event(this); - this.deleted = new Event(this); -} - -GraphItem.prototype.getName = function(){ - return this.name; -}; - -GraphItem.prototype.getId = function(){ - return this.id; -}; - -GraphItem.prototype.setName = function(name){ - var oldName = this.getName(); - this.name = name; - this.nameChanged.notify({"item": this, "previousName" : oldName}); -}; - - - - - -function LayoutDataset(){ - this.dataset = null; - this.vertices = new Object(); - this.changed = new Event(this); - - - this.args = new Object(); - - //RANDOM, CIRCLE - this.args.type = "CIRCLE"; -}; - -LayoutDataset.prototype.loadFromJSON = function(dataset, json){ - var _this = this; - this.vertices = new Object(); - this.dataset = dataset; //new GraphDataset(); - for ( var vertex in json) { - this.vertices[vertex] = new NodeLayout(vertex, json[vertex].x, json[vertex].y); - this.vertices[vertex].changed.attach(function (sender, item){ - _this.changed.notify(item); - }); - } - this._attachDatasetEvents(); -}; - - -LayoutDataset.prototype.toJSON = function(){ - var serialize = new Object(); - for ( var vertex in this.vertices) { - serialize[vertex] = new Object(); - serialize[vertex].x = this.vertices[vertex].x; - serialize[vertex].y = this.vertices[vertex].y; - } - serialize.dataset = new Object(); - serialize.dataset =this.dataset.toJSON(); - return serialize; -}; - -LayoutDataset.prototype.dataBind = function(graphDataset){ - this.dataset = graphDataset; - this._attachDatasetEvents(); - this._calculateLayout(); -}; - -LayoutDataset.prototype._removeVertex = function(vertexId){ - delete this.vertices[vertexId]; -}; - -LayoutDataset.prototype._attachDatasetEvents = function(){ - var _this = this; - - this.dataset.vertexDeleted.attach(function (sender, item){ - _this._removeVertex(item.getId()); - }); - - this.dataset.newVertex.attach(function (sender, item){ - _this.vertices[item.getId()] = new NodeLayout(item.getId(), 0.5, 0.5); - _this.vertices[item.getId()].changed.attach(function (sender, item){ - _this.changed.notify(item); - }); - }); -}; - -LayoutDataset.prototype.getType = function(){ - return this.args.type; -}; - -LayoutDataset.prototype._calculateLayoutVertices = function(type, count){ - - if (type == "CIRCLE"){ - var radius = 0.4; - var centerX = 0.5; - var centerY = 0.5; - var verticesCoordinates = new Array(); - for(var i = 0; i < count; i++){ - x = centerX + radius * Math.sin(i * 2 * Math.PI/count); - y = centerY + radius * Math.cos(i * 2 * Math.PI/count); - verticesCoordinates.push({'x':x,'y':y}); - } - return verticesCoordinates; - } -}; - - -LayoutDataset.prototype._calculateLayout = function(){ - var _this = this; - if (this.getType() == "RANDOM"){ - for ( var vertex in this.dataset.getVertices()) { - if (this.vertices[vertex] == null){ - this.vertices[vertex] = new NodeLayout(vertex, 0, 0); - } - this.vertices[vertex].setCoordinates(Math.random(), Math.random()); - this.vertices[vertex].changed.attach(function (sender, item){ - _this.changed.notify(item); - }); - } - } - - if ( this.getType() == "CIRCLE"){ - - var count = this.dataset._getVerticesCount(); - var verticesCoordinates = this._calculateLayoutVertices(this.getType(), count); - - var aux = 0; - for ( var vertex in this.dataset.getVertices()) { - if (this.vertices[vertex] == null){ - this.vertices[vertex] = new NodeLayout(vertex, 0, 0); - } - this.vertices[vertex].setCoordinates(verticesCoordinates[aux].x, verticesCoordinates[aux].y);//{"x":, "y":}; - aux++; - this.vertices[vertex].changed.attach(function (sender, item){ - _this.changed.notify(item); - }); - } - } - - - if (this.getType() == "SQUARE"){ - - var count = this.dataset._getVerticesCount(); - var xMin = 0.1; - var xMax = 0.9; - var yMin = 0.1; - var yMax = 0.9; - - var rows = Math.sqrt(count); - var step = (xMax - xMin) / rows; - - var verticesCoordinates = new Array(); - for(var i = 0; i < rows; i ++){ - for ( var j = 0; j < rows; j++) { - x = i * step + xMin; - y = j * step + yMin; - verticesCoordinates.push({'x':x,'y':y}); - } - } - - var aux = 0; - for ( var vertex in this.dataset.getVertices()) { - if (this.vertices[vertex] == null){ - this.vertices[vertex] = new NodeLayout(vertex, 0, 0); - } - this.vertices[vertex].setCoordinates(verticesCoordinates[aux].x, verticesCoordinates[aux].y);//{"x":, "y":}; - aux++; - this.vertices[vertex].changed.attach(function (sender, item){ - _this.changed.notify(item); - }); - } - } - -}; - -LayoutDataset.prototype.getNodeById = function(id){ - return this.vertices[id]; -}; - -LayoutDataset.prototype.getVerticesByArea = function(x1, y1, x2, y2){ - var vertices = new Array(); - for ( var vertex in this.dataset.getVertices()) { - if ((this.vertices[vertex].x >= x1)&&(this.vertices[vertex].x <= x2)){ - if ((this.vertices[vertex].y >= y1)&&(this.vertices[vertex].y <= y2)){ - vertices.push(this.vertices[vertex]); - } - } - } - return vertices; -}; - - - - -LayoutDataset.prototype.getLayout = function(type){ - - if (type == "CIRCLE"){ - this.args.type = "CIRCLE"; - this._calculateLayout(); - return; - } - - if (type == "SQUARE"){ - this.args.type = "SQUARE"; - this._calculateLayout(); - return; - } - - if (type == "RANDOM"){ - this.args.type = "RANDOM"; - this._calculateLayout(); - return; - } - - - var dotText = this.dataset.toDOTID(); - var url = "http://bioinfo.cipf.es/utils/ws/rest/network/layout/"+type+".coords"; - var _this = this; - - $.ajax({ - async: true, - type: "POST", - url: url, - dataType: "text", - data: { - dot :dotText - }, - cache: false, - success: function(data){ - var response = JSON.parse(data); - for ( var vertexId in response) { - _this.vertices[vertexId].setCoordinates(response[vertexId].x, response[vertexId].y); - } - } - }); - -// $.ajax({ -// async: true, -// type: "POST", -// url: url, -// dataType: "script", -// data: { -// dot :dotText -// }, -// cache: false, -// success: function(data){ -// var response = JSON.parse(data); -// for ( var vertexId in response) { -// _this.vertices[vertexId].setCoordinates(response[vertexId].x, response[vertexId].y); -// } -// } -// }); - -}; - -function NodeLayout(id, x, y, args){ - this.id = id; - this.x = x; - this.y = y; - this.changed = new Event(this); -}; - -NodeLayout.prototype.getId = function(id){ - return this.id; -}; - -NodeLayout.prototype.setCoordinates = function(x, y){ - this.x = x; - this.y = y; - this.changed.notify(this); -}; - - - -function NetworkDataSetFormatter(vertexFormatProperties, defaultEdgeProperties, args){ - DataSet.prototype.constructor.call(this); - - this.args = new Object(); - - this.vertices = new Object(); - this.edges = new Object(); - this.dataset = null; - this.maxClass = 0; - this.minClass = 0; - - /** Coordenates with default Setting */ - this.args.width = 1100; - this.args.height = 500; - - /** Optional parameters */ - this.args.backgroundColor = "#FFFFFF"; - this.args.backgroundImage = null; - this.args.backgroundImageHeight = null; - this.args.backgroundImageWidth = null; - this.args.backgroundImageX = null; - this.args.backgroundImageY = null; - - - this.args.balanceNodes = false; - this.args.nodesMaxSize = 3; - this.args.nodesMinSize = 0.5; - - - /** Zoom **/ - this.args.zoomScale = 1; - this.args.zoomScaleStepFactor = 0.4; - - if (args != null){ - if (args.backgroundImage != null){ - this.args.backgroundImage = args.backgroundImage; - } - - if (args.width != null){ - this.args.width = args.width; - } - - if (args.height != null){ - this.args.height = args.height; - this.args.svgHeight = args.height; - } - - if (args.svgHeight != null){ - this.args.svgHeight = args.svgHeight; - } - - if (args.backgroundColor != null){ - this.args.backgroundColor = args.backgroundColor; - } - - if(args.balanceNodes != null){ - this.args.balanceNodes = args.balanceNodes; - } - - if(args.nodesMaxSize != null){ - this.args.nodesMaxSize = args.nodesMaxSize; - } - if(args.nodesMinSize != null){ - this.args.nodesMinSize = args.nodesMinSize; - } - } - - this.args.defaultEdgeProperties = {"fill":"#000000", "radius":"1", "stroke":"#000000", "size":1, "title":{"fontSize":10, "fontColor":"#000000"}}; - this.args.vertexFormatProperties = { "fill":"#000000", "stroke":"#000000", "stroke-opacity":"#000000"}; - - if (vertexFormatProperties!= null){ - this.args.vertexFormatProperties = vertexFormatProperties; - } - if (defaultEdgeProperties!= null){ - this.args.defaultEdgeProperties = defaultEdgeProperties; - } - - /** Events **/ - this.changed = new Event(this); - this.edgeChanged = new Event(this); - this.resized = new Event(this); - this.backgroundImageChanged= new Event(this); - this.backgroundColorChanged= new Event(this); -}; - -NetworkDataSetFormatter.prototype.loadFromJSON = function(dataset, json){ - this.args = new Object(); - this.vertices = new Object(); - this.args = json; - this._setDataset(dataset); - var _this = this; - - for ( var vertex in json.vertices) { - this.addVertex(vertex, json.vertices[vertex]); - } - - for ( var edgeId in json.edges) { - this.addEdge(edgeId, json.edges[edgeId]); - } -}; - - -NetworkDataSetFormatter.prototype.toJSON = function(){ - - -// this.args.vertices = new Object(); -// this.args.edges = new Object(); -// -// for ( var vertex in this.vertices) { -// this.args.vertices[vertex] = this.getVertexById(vertex).toJSON(); -// } -// for ( var edge in this.edges) { -// this.args.edges[edge] = this.getEdgeById(edge).toJSON(); -// } -// -// return (this.args); - - - var serialize = new Object(); - serialize = JSON.parse(JSON.stringify(this.args)); - serialize.vertices = new Object(); - serialize.edges = new Object(); - - for ( var vertex in this.vertices) { - serialize.vertices[vertex] = this.getVertexById(vertex).toJSON(); - } - for ( var edge in this.edges) { - serialize.edges[edge] = this.getEdgeById(edge).toJSON(); - } - - return (serialize); -}; - - - -NetworkDataSetFormatter.prototype._getNodeSize = function(nodeId){ - if (this.isVerticesBalanced()){ - var total = this.maxClass - this.minClass; - if (total == 0){ total = 1;} - var nodeConnection = this.dataset.getVertexById(nodeId).getEdges().length; - return ((nodeConnection*this.args.nodesMaxSize)/total) + this.args.nodesMinSize; - } - return this.getVertexById(nodeId).getDefault().getSize(); -}; - -NetworkDataSetFormatter.prototype._recalculateSize = function(){ - if (this.isVerticesBalanced()){ - this.maxClass = this.dataset.getMaxClass(); - this.minClass = this.dataset.getMinClass(); - for ( var vertexIdAll in this.vertices) { - var size = this._getNodeSize(vertexIdAll); - this.vertices[vertexIdAll].getDefault().setSize(size); - this.vertices[vertexIdAll].getSelected().setSize(size); - this.vertices[vertexIdAll].getOver().setSize(size); - } - } -}; - - -NetworkDataSetFormatter.prototype.addVertex = function(vertexId, json){ - - - if (json == null){ - this.vertices[vertexId] = new CircleVertexGraphFormatter(vertexId, this.args.vertexFormatProperties.defaultFormat, this.args.vertexFormatProperties.selected, this.args.vertexFormatProperties.over, this.args.vertexFormatProperties.dragging); - } - else{ - - /** Cargo los attributos desde el json **/ - if (json.type == null){ - this.vertices[vertexId] = new CircleVertexGraphFormatter(vertexId, this.args.vertexFormatProperties.defaultFormat, this.args.vertexFormatProperties.selected, this.args.vertexFormatProperties.over, this.args.vertexFormatProperties.dragging); - } - - if ((json.type == "SquareVertexGraphFormatter")||(json.type == "SquareVertexNetworkFormatter")){ - this.vertices[vertexId] = new SquareVertexGraphFormatter(vertexId); - this.vertices[vertexId].loadFromJSON(json); - } - - if ((json.type == "CircleVertexGraphFormatter")||(json.type == "CircleVertexNetworkFormatter")){ - this.vertices[vertexId] = new CircleVertexGraphFormatter(vertexId); - this.vertices[vertexId].loadFromJSON(json); - } - - if ((json.type == "EllipseVertexGraphFormatter")||(json.type == "EllipseVertexNetworkFormatter")){ - this.vertices[vertexId] = new EllipseVertexGraphFormatter(vertexId); - this.vertices[vertexId].loadFromJSON(json); - } - - if ((json.type == "RectangleVertexGraphFormatter")||(json.type == "RectangleVertexNetworkFormatter")){ - this.vertices[vertexId] = new RectangleVertexGraphFormatter(vertexId); - this.vertices[vertexId].loadFromJSON(json); - } - - if ((json.type == "RoundedVertexGraphFormatter")||(json.type == "RoundedVertexNetworkFormatter")){ - this.vertices[vertexId] = new RoundedVertexGraphFormatter(vertexId); - this.vertices[vertexId].loadFromJSON(json); - } - - } - - - var _this = this; - this.vertices[vertexId].stateChanged.attach(function (sender, item){ - _this.changed.notify(item); - }); - - var size = this._getNodeSize(vertexId); - this.vertices[vertexId].defaultFormat.args.size = size; - this.vertices[vertexId].selected.args.size = size; - this.vertices[vertexId].over.args.size = size; - -}; - -NetworkDataSetFormatter.prototype.addEdge = function(edgeId, json){ - - /** Es un edge nuevo que le doy los atributos por defecto **/ - if (json == null){ - if (this.dataset.getEdgeById(edgeId).getNodeSource().getId() == this.dataset.getEdgeById(edgeId).getNodeTarget().getId()){ - this.edges[edgeId] = new BezierEdgeGraphFormatter(edgeId, this.args.defaultEdgeProperties.defaultFormat, this.args.defaultEdgeProperties.selected, this.args.defaultEdgeProperties.over, this.args.defaultEdgeProperties.dragging); - }else{ - this.edges[edgeId] = new DirectedLineEdgeGraphFormatter(edgeId, this.args.defaultEdgeProperties.defaultFormat, this.args.defaultEdgeProperties.selected, this.args.defaultEdgeProperties.over, this.args.defaultEdgeProperties.dragging); - } - } - else{ - /** Cargo los attributos desde el json **/ - if (json.type == null){ - this.edges[edgeId] = new LineEdgeGraphFormatter(edgeId, this.args.defaultEdgeProperties.defaultFormat, this.args.defaultEdgeProperties.selected, this.args.defaultEdgeProperties.over, this.args.defaultEdgeProperties.dragging); - } - - if ((json.type == "LineEdgeGraphFormatter")||(json.type == "LineEdgeNetworkFormatter")){ - this.edges[edgeId] = new LineEdgeGraphFormatter(edgeId); - this.edges[edgeId].loadFromJSON(json); - } - if ((json.type == "DirectedLineEdgeGraphFormatter")||(json.type == "DirectedLineEdgeNetworkFormatter")){ - this.edges[edgeId] = new DirectedLineEdgeGraphFormatter(edgeId); - this.edges[edgeId].loadFromJSON(json); - } - - if ((json.type == "BezierEdgeGraphFormatter")||(json.type == "BezierEdgeNetworkFormatter")){ - this.edges[edgeId] = new BezierEdgeGraphFormatter(edgeId); - this.edges[edgeId].loadFromJSON(json); - } - - - if ((json.type == "CutDirectedLineEdgeGraphFormatter")||(json.type == "CutDirectedLineEdgeNetworkFormatter")){ - this.edges[edgeId] = new CutDirectedLineEdgeGraphFormatter(edgeId); - this.edges[edgeId].loadFromJSON(json); - } - - if ((json.type == "DotDirectedLineEdgeGraphFormatter")||(json.type == "DotDirectedLineEdgeNetworkFormatter")){ - this.edges[edgeId] = new DotDirectedLineEdgeGraphFormatter(edgeId); - this.edges[edgeId].loadFromJSON(json); - } - if ((json.type == "OdotDirectedLineEdgeGraphFormatter")||(json.type == "OdotDirectedLineEdgeNetworkFormatter")){ - this.edges[edgeId] = new OdotDirectedLineEdgeGraphFormatter(edgeId); - this.edges[edgeId].loadFromJSON(json); - } - - if ((json.type == "OdirectedLineEdgeGraphFormatter")||(json.type == "OdirectedLineEdgeNetworkFormatter")){ - this.edges[edgeId] = new OdirectedLineEdgeGraphFormatter(edgeId); - this.edges[edgeId].loadFromJSON(json); - } - } - - var _this = this; - this.edges[edgeId].stateChanged.attach(function (sender, item){ - _this.edgeChanged.notify(item); - }); - - this._recalculateSize(); -}; - -NetworkDataSetFormatter.prototype._setDataset = function(dataset){ - this.dataset = dataset; - this.maxClass = dataset.getMaxClass(); - this.minClass = dataset.getMinClass(); - this._attachDatasetEvents(); -}; - -NetworkDataSetFormatter.prototype.changeEdgeType = function(edgeId, type){ - if ((type == "LineEdgeGraphFormatter")||(type == "LineEdgeNetworkFormatter")){ - this.edges[edgeId] = new LineEdgeGraphFormatter(edgeId, this.args.defaultEdgeProperties.defaultFormat, this.args.defaultEdgeProperties.selected, this.args.defaultEdgeProperties.over, this.args.defaultEdgeProperties.dragging); - - } - if ((type == "DirectedLineEdgeGraphFormatter")||(type == "DirectedLineEdgeNetworkFormatter")){ - this.edges[edgeId] = new DirectedLineEdgeGraphFormatter(edgeId, this.args.defaultEdgeProperties.defaultFormat, this.args.defaultEdgeProperties.selected, this.args.defaultEdgeProperties.over, this.args.defaultEdgeProperties.dragging); - } - - if ((type == "CutDirectedLineEdgeGraphFormatter")||(type == "CutDirectedLineEdgeNetworkFormatter")){ - this.edges[edgeId] = new CutDirectedLineEdgeGraphFormatter(edgeId, this.args.defaultEdgeProperties.defaultFormat, this.args.defaultEdgeProperties.selected, this.args.defaultEdgeProperties.over, this.args.defaultEdgeProperties.dragging); - } - - if ((type == "DotDirectedLineEdgeGraphFormatter")||(type == "DotDirectedLineEdgeNetworkFormatter")){ - this.edges[edgeId] = new DotDirectedLineEdgeGraphFormatter(edgeId, this.args.defaultEdgeProperties.defaultFormat, this.args.defaultEdgeProperties.selected, this.args.defaultEdgeProperties.over, this.args.defaultEdgeProperties.dragging); - } - - if ((type == "OdotDirectedLineEdgeGraphFormatter")||(type == "OdotDirectedLineEdgeNetworkFormatter")){ - this.edges[edgeId] = new OdotDirectedLineEdgeGraphFormatter(edgeId, this.args.defaultEdgeProperties.defaultFormat, this.args.defaultEdgeProperties.selected, this.args.defaultEdgeProperties.over, this.args.defaultEdgeProperties.dragging); - } - - if ((type == "OdirectedLineEdgeGraphFormatter")||(type == "OdirectedLineEdgeNetworkFormatter")){ - this.edges[edgeId] = new OdirectedLineEdgeGraphFormatter(edgeId, this.args.defaultEdgeProperties.defaultFormat, this.args.defaultEdgeProperties.selected, this.args.defaultEdgeProperties.over, this.args.defaultEdgeProperties.dragging); - } - - - - var _this = this; - this.edges[edgeId].stateChanged.attach(function (sender, item){ - _this.edgeChanged.notify(item); - }); - _this.edgeChanged.notify(this.edges[edgeId]); -}; -/* -classe = "SquareNetworkNodeFormatter"; -} -if (value == "circle"){ - classe = "CircleNetworkNodeFormatter"; - **/ -NetworkDataSetFormatter.prototype.changeNodeType = function(vertexId, type){ - var defaultFormat = JSON.parse(JSON.stringify(this.vertices[vertexId].getDefault())); - var selectedFormat = JSON.parse(JSON.stringify(this.vertices[vertexId].getSelected())); - var defaultFormat = JSON.parse(JSON.stringify(this.vertices[vertexId].getDefault())); - var defaultFormat = JSON.parse(JSON.stringify(this.vertices[vertexId].getDefault())); - - if ((type == "SquareVertexGraphFormatter")||(type == "SquareVertexNetworkFormatter")){ - this.vertices[vertexId] = new SquareVertexGraphFormatter(vertexId,defaultFormat, this.args.vertexFormatProperties.selected, this.args.vertexFormatProperties.over, this.args.vertexFormatProperties.dragging); - } - - if ((type == "CircleVertexGraphFormatter")||(type == "CircleVertexNetworkFormatter")){ - this.vertices[vertexId] = new CircleVertexGraphFormatter(vertexId, defaultFormat, this.args.vertexFormatProperties.selected, this.args.vertexFormatProperties.over, this.args.vertexFormatProperties.dragging); - } - - if ((type == "EllipseVertexGraphFormatter")||(type == "EllipseVertexNetworkFormatter")){ - this.vertices[vertexId] = new EllipseVertexGraphFormatter(vertexId, defaultFormat, this.args.vertexFormatProperties.selected, this.args.vertexFormatProperties.over, this.args.vertexFormatProperties.dragging); - } - - if ((type == "RectangleVertexGraphFormatter")||(type == "RectangleVertexNetworkFormatter")){ - this.vertices[vertexId] = new RectangleVertexGraphFormatter(vertexId, defaultFormat, this.args.vertexFormatProperties.selected, this.args.vertexFormatProperties.over, this.args.vertexFormatProperties.dragging); - } - - if ((type == "RoundedVertexGraphFormatter")||(type == "RoundedVertexNetworkFormatter")){ - this.vertices[vertexId] = new RoundedVertexGraphFormatter(vertexId, defaultFormat, this.args.vertexFormatProperties.selected, this.args.vertexFormatProperties.over, this.args.vertexFormatProperties.dragging); - } - - if ((type == "OctagonVertexGraphFormatter")||(type == "OctagonVertexNetworkhFormatter")){ - this.vertices[vertexId] = new OctagonVertexGraphFormatter(vertexId, defaultFormat, this.args.vertexFormatProperties.selected, this.args.vertexFormatProperties.over, this.args.vertexFormatProperties.dragging); - } - - - var _this = this; - this.vertices[vertexId].stateChanged.attach(function (sender, item){ - _this.changed.notify(item); - }); - _this.changed.notify(this.vertices[vertexId]); -}; - - - -NetworkDataSetFormatter.prototype.dataBind = function(networkDataSet){ - var _this = this; - this._setDataset(networkDataSet); - - for ( var vertex in this.dataset.getVertices()) { - this.addVertex(vertex); - } - - for ( var edge in this.dataset.getEdges()) { - this.addEdge(edge); - } -}; - -NetworkDataSetFormatter.prototype._removeEdge = function(edgeId){ - delete this.edges[edgeId]; -}; - -NetworkDataSetFormatter.prototype._removeVertex = function(vertexId){ - delete this.vertices[vertexId]; -}; - -NetworkDataSetFormatter.prototype._attachDatasetEvents = function(id){ - var _this = this; - this.dataset.vertexDeleted.attach(function (sender, item){ - _this._removeVertex(item.getId()); - }); - - this.dataset.edgeDeleted.attach(function (sender, item){ - _this._removeEdge(item.getId()); - }); - - this.dataset.newVertex.attach(function (sender, item){ - _this.addVertex(item.getId()); - }); - - this.dataset.newEdge.attach(function (sender, item){ - _this.addEdge(item.getId()); - }); -}; - - -NetworkDataSetFormatter.prototype.getVertexById = function(id){ - return this.vertices[id]; -}; - -NetworkDataSetFormatter.prototype.getEdgeById = function(id){ - return this.edges[id]; -}; - -NetworkDataSetFormatter.prototype.makeLabelsBigger = function(){ -for ( var vertexId in this.vertices) { - var fontSize = this.vertices[vertexId].getDefault().getFontSize() + 2; - this.vertices[vertexId].getDefault().setFontSize(fontSize); - } -}; - -NetworkDataSetFormatter.prototype.makeLabelsSmaller = function(){ - for ( var vertexId in this.vertices) { - var fontSize = this.vertices[vertexId].getDefault().getFontSize() - 2; - this.vertices[vertexId].getDefault().setFontSize(fontSize); - } -}; - - -NetworkDataSetFormatter.prototype.resize = function(width, height){ - this.args.width = width; - this.args.height = height; - this.resized.notify(); -}; - -/** ZOOM GETTERS **/ -NetworkDataSetFormatter.prototype.getZoomScaleStepFactor = function(){return this.args.zoomScaleStepFactor;}; -NetworkDataSetFormatter.prototype.setZoomScaleStepFactor = function(scaleFactor){this.args.zoomScaleStepFactor = scaleFactor;}; -NetworkDataSetFormatter.prototype.getZoomScale = function(){return this.args.zoomScale;}; -NetworkDataSetFormatter.prototype.setZoomScale = function(scale){this.args.zoomScale = scale;}; - -NetworkDataSetFormatter.prototype.getNodesMaxSize = function(){return this.args.nodesMaxSize;}; -NetworkDataSetFormatter.prototype.getNodesMinSize = function(){return this.args.nodesMinSize;}; - -/** SIZE SETTERS AND GETTERS **/ -NetworkDataSetFormatter.prototype.isVerticesBalanced = function(){return this.args.balanceNodes;}; -NetworkDataSetFormatter.prototype.getWidth = function(){return this.args.width;}; -NetworkDataSetFormatter.prototype.getHeight = function(){return this.args.height;}; - -/** OPTIONAL PARAMETERS GETTERS AND SETTERS **/ -NetworkDataSetFormatter.prototype.getBackgroundImage = function(){return this.args.backgroundImage;}; -NetworkDataSetFormatter.prototype.setBackgroundImage = function(value){this.args.backgroundImage = value; this.backgroundImageChanged.notify(this);}; - - -NetworkDataSetFormatter.prototype.getBackgroundImageWidth = function(){return this.args.backgroundImageWidth;}; -NetworkDataSetFormatter.prototype.setBackgroundImageWidth = function(value){this.args.backgroundImageWidth = value; this.backgroundImageChanged.notify(this);}; - -NetworkDataSetFormatter.prototype.getBackgroundImageHeight = function(){return this.args.backgroundImageHeight;}; -NetworkDataSetFormatter.prototype.setBackgroundImageHeight = function(value){this.args.backgroundImageHeight = value; this.backgroundImageChanged.notify(this);}; - -NetworkDataSetFormatter.prototype.getBackgroundImageX = function(){return this.args.backgroundImageX;}; -NetworkDataSetFormatter.prototype.setBackgroundImageX = function(value){this.args.backgroundImageX = value; this.backgroundImageChanged.notify(this);}; - -NetworkDataSetFormatter.prototype.getBackgroundImageY = function(){return this.args.backgroundImageX;}; -NetworkDataSetFormatter.prototype.setBackgroundImageY = function(value){this.args.backgroundImageY = value; this.backgroundImageChanged.notify(this);}; - - - -NetworkDataSetFormatter.prototype.getBackgroundColor = function(){return this.args.backgroundColor;}; -NetworkDataSetFormatter.prototype.setBackgroundColor = function(value){this.args.backgroundColor = value; this.backgroundColorChanged.notify(this);}; - -NetworkDataSetFormatter.prototype.getWidth = function(){return this.args.width;}; -NetworkDataSetFormatter.prototype.setWidth = function(value){this.args.width = value;}; - -NetworkDataSetFormatter.prototype.getHeight = function(){return this.args.height;}; -NetworkDataSetFormatter.prototype.setHeight = function(value){this.args.height = value;}; - - - - -Vertex.prototype.getName = GraphItem.prototype.getName; -Vertex.prototype.setName = GraphItem.prototype.setName; -Vertex.prototype.getId = GraphItem.prototype.getId; - -function Vertex(id, name, args){ - GraphItem.prototype.constructor.call(this, id, name, args); - this.edgesIn= new Array(); - this.edgesOut= new Array(); -}; - -Vertex.prototype.getEdges = function(){ - return this.edgesIn.concat(this.edgesOut); -}; - -Vertex.prototype.getEdgesCount = function(){ - return this.getEdges().length; -}; - -Vertex.prototype.getEdgesIn = function(){ - return this.edgesIn; -}; - -Vertex.prototype.getEdgesOut = function(){ - return this.edgesOut; -}; - -Vertex.prototype.addEdge = function(edge){ - if (edge.getNodeSource().getId() == this.id){ - this.edgesIn.push(edge); - } - else{ - this.edgesOut.push(edge); - } -}; - -Vertex.prototype.removeEdge = function(edge){ - for ( var i = 0; i < this.getEdgesIn().length; i++) { - var edgeIn = this.edgesIn[i]; - if (edgeIn.getId() == edge.getId()){ - this.edgesIn.splice(i, 1); - } - } - for ( var i = 0; i < this.getEdgesOut().length; i++) { - var edgeIn = this.edgesOut[i]; - if (edgeIn.getId() == edge.getId()){ - this.edgesOut.splice(i, 1); - } - } -}; - -Vertex.prototype.remove = function(){ - var edges = this.getEdges(); - for ( var i = 0; i < edges.length; i++) { - var edge = edges[i]; - edge.remove(); - } - this.deleted.notify(this); -}; - - - - - -VertexGraphFormatter.prototype.getDefault = ItemGraphFormatter.prototype.getDefault; -VertexGraphFormatter.prototype.getSelected = ItemGraphFormatter.prototype.getSelected; -VertexGraphFormatter.prototype.getOver = ItemGraphFormatter.prototype.getOver; -VertexGraphFormatter.prototype.getDragging = ItemGraphFormatter.prototype.getDragging; -VertexGraphFormatter.prototype.getId = ItemGraphFormatter.prototype.getId; -VertexGraphFormatter.prototype.toJSON = ItemGraphFormatter.prototype.toJSON; -VertexGraphFormatter.prototype.loadFromJSON = ItemGraphFormatter.prototype.loadFromJSON; -VertexGraphFormatter.prototype._setEvents = ItemGraphFormatter.prototype._setEvents; - - -function VertexGraphFormatter(defaultFormat, selectedFormat, overFormat, draggingFormat){ - ItemGraphFormatter.prototype.constructor.call(this, defaultFormat, selectedFormat, overFormat, draggingFormat); -}; - - -CircleVertexGraphFormatter.prototype.getDefault = VertexGraphFormatter.prototype.getDefault; -CircleVertexGraphFormatter.prototype.getSelected = VertexGraphFormatter.prototype.getSelected; -CircleVertexGraphFormatter.prototype.getOver = VertexGraphFormatter.prototype.getOver; -CircleVertexGraphFormatter.prototype.getDragging = VertexGraphFormatter.prototype.getDragging; -CircleVertexGraphFormatter.prototype.getId = VertexGraphFormatter.prototype.getId; -CircleVertexGraphFormatter.prototype.toJSON = VertexGraphFormatter.prototype.toJSON; -CircleVertexGraphFormatter.prototype.loadFromJSON = VertexGraphFormatter.prototype.loadFromJSON; -CircleVertexGraphFormatter.prototype._setEvents = VertexGraphFormatter.prototype._setEvents; - -function CircleVertexGraphFormatter(id, defaultFormat, selectedFormat, overFormat, draggingFormat){ - VertexGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, draggingFormat); - this.args.type = "CircleVertexGraphFormatter"; - if (defaultFormat != null){ - if (defaultFormat.radius != null){ - this.defaultFormat.args.radius = defaultFormat.radius; - } - } - - if (selectedFormat != null){ - if (selectedFormat.radius != null){ - this.selected.args.radius = selectedFormat.radius; - } - } - - if (overFormat != null){ - if (overFormat.radius != null){ - this.over.args.radius = overFormat.radius; - } - } - - if (draggingFormat != null){ - if (draggingFormat.radius != null){ - this.dragging.args.draggingFormat = draggingFormat.radius; - } - } -}; - - - -SquareVertexGraphFormatter.prototype.getDefault = VertexGraphFormatter.prototype.getDefault; -SquareVertexGraphFormatter.prototype.getSelected = VertexGraphFormatter.prototype.getSelected; -SquareVertexGraphFormatter.prototype.getOver = VertexGraphFormatter.prototype.getOver; -SquareVertexGraphFormatter.prototype.getDragging = VertexGraphFormatter.prototype.getDragging; -SquareVertexGraphFormatter.prototype.getId = VertexGraphFormatter.prototype.getId; -SquareVertexGraphFormatter.prototype.toJSON = VertexGraphFormatter.prototype.toJSON; -SquareVertexGraphFormatter.prototype.loadFromJSON = VertexGraphFormatter.prototype.loadFromJSON; -SquareVertexGraphFormatter.prototype._setEvents = VertexGraphFormatter.prototype._setEvents; - -function SquareVertexGraphFormatter(id, defaultFormat, selectedFormat, overFormat, draggingFormat){ - VertexGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, draggingFormat); - this.args.type = "SquareVertexGraphFormatter"; - if (defaultFormat != null){ - if (defaultFormat.radius != null){ - this.defaultFormat.args.radius = defaultFormat.radius; - } - } - - if (selectedFormat != null){ - if (selectedFormat.radius != null){ - this.selected.args.radius = selectedFormat.radius; - } - } - - if (overFormat != null){ - if (overFormat.radius != null){ - this.over.args.radius = overFormat.radius; - } - } - - if (draggingFormat != null){ - if (draggingFormat.radius != null){ - this.dragging.args.draggingFormat = draggingFormat.radius; - } - } -}; - - -EllipseVertexGraphFormatter.prototype.getDefault = VertexGraphFormatter.prototype.getDefault; -EllipseVertexGraphFormatter.prototype.getSelected = VertexGraphFormatter.prototype.getSelected; -EllipseVertexGraphFormatter.prototype.getOver = VertexGraphFormatter.prototype.getOver; -EllipseVertexGraphFormatter.prototype.getDragging = VertexGraphFormatter.prototype.getDragging; -EllipseVertexGraphFormatter.prototype.getId = VertexGraphFormatter.prototype.getId; -EllipseVertexGraphFormatter.prototype.toJSON = VertexGraphFormatter.prototype.toJSON; -EllipseVertexGraphFormatter.prototype.loadFromJSON = VertexGraphFormatter.prototype.loadFromJSON; -EllipseVertexGraphFormatter.prototype._setEvents = VertexGraphFormatter.prototype._setEvents; - -function EllipseVertexGraphFormatter(id, defaultFormat, selectedFormat, overFormat, draggingFormat){ - VertexGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, draggingFormat); - this.args.type = "EllipseVertexGraphFormatter"; - if (defaultFormat != null){ - if (defaultFormat.radius != null){ - this.defaultFormat.args.radius = defaultFormat.radius; - } - } - - if (selectedFormat != null){ - if (selectedFormat.radius != null){ - this.selected.args.radius = selectedFormat.radius; - } - } - - if (overFormat != null){ - if (overFormat.radius != null){ - this.over.args.radius = overFormat.radius; - } - } - - if (draggingFormat != null){ - if (draggingFormat.radius != null){ - this.dragging.args.draggingFormat = draggingFormat.radius; - } - } -}; - - - -RectangleVertexGraphFormatter.prototype.getDefault = ItemGraphFormatter.prototype.getDefault; -RectangleVertexGraphFormatter.prototype.getSelected = ItemGraphFormatter.prototype.getSelected; -RectangleVertexGraphFormatter.prototype.getOver = ItemGraphFormatter.prototype.getOver; -RectangleVertexGraphFormatter.prototype.getDragging = ItemGraphFormatter.prototype.getDragging; -RectangleVertexGraphFormatter.prototype.getId = ItemGraphFormatter.prototype.getId; -RectangleVertexGraphFormatter.prototype.toJSON = ItemGraphFormatter.prototype.toJSON; -RectangleVertexGraphFormatter.prototype.loadFromJSON = ItemGraphFormatter.prototype.loadFromJSON; -RectangleVertexGraphFormatter.prototype._setEvents = ItemGraphFormatter.prototype._setEvents; - -function RectangleVertexGraphFormatter(id, defaultFormat, selectedFormat, overFormat, draggingFormat){ - VertexGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, draggingFormat); - this.args.type = "RectangleVertexGraphFormatter"; - if (defaultFormat != null){ - if (defaultFormat.radius != null){ - this.defaultFormat.args.radius = defaultFormat.radius; - } - } - - if (selectedFormat != null){ - if (selectedFormat.radius != null){ - this.selected.args.radius = selectedFormat.radius; - } - } - - if (overFormat != null){ - if (overFormat.radius != null){ - this.over.args.radius = overFormat.radius; - } - } - - if (draggingFormat != null){ - if (draggingFormat.radius != null){ - this.dragging.args.draggingFormat = draggingFormat.radius; - } - } -}; - - - -RoundedVertexGraphFormatter.prototype.getDefault = ItemGraphFormatter.prototype.getDefault; -RoundedVertexGraphFormatter.prototype.getSelected = ItemGraphFormatter.prototype.getSelected; -RoundedVertexGraphFormatter.prototype.getOver = ItemGraphFormatter.prototype.getOver; -RoundedVertexGraphFormatter.prototype.getDragging = ItemGraphFormatter.prototype.getDragging; -RoundedVertexGraphFormatter.prototype.getId = ItemGraphFormatter.prototype.getId; -RoundedVertexGraphFormatter.prototype.toJSON = ItemGraphFormatter.prototype.toJSON; -RoundedVertexGraphFormatter.prototype.loadFromJSON = ItemGraphFormatter.prototype.loadFromJSON; -RoundedVertexGraphFormatter.prototype._setEvents = ItemGraphFormatter.prototype._setEvents; - -function RoundedVertexGraphFormatter(id, defaultFormat, selectedFormat, overFormat, draggingFormat){ - VertexGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, draggingFormat); - this.args.type = "RoundedVertexGraphFormatter"; - if (defaultFormat != null){ - if (defaultFormat.radius != null){ - this.defaultFormat.args.radius = defaultFormat.radius; - } - } - - if (selectedFormat != null){ - if (selectedFormat.radius != null){ - this.selected.args.radius = selectedFormat.radius; - } - } - - if (overFormat != null){ - if (overFormat.radius != null){ - this.over.args.radius = overFormat.radius; - } - } - - if (draggingFormat != null){ - if (draggingFormat.radius != null){ - this.dragging.args.draggingFormat = draggingFormat.radius; - } - } -}; - - - -OctagonVertexGraphFormatter.prototype.getDefault = ItemGraphFormatter.prototype.getDefault; -OctagonVertexGraphFormatter.prototype.getSelected = ItemGraphFormatter.prototype.getSelected; -OctagonVertexGraphFormatter.prototype.getOver = ItemGraphFormatter.prototype.getOver; -OctagonVertexGraphFormatter.prototype.getDragging = ItemGraphFormatter.prototype.getDragging; -OctagonVertexGraphFormatter.prototype.getId = ItemGraphFormatter.prototype.getId; -OctagonVertexGraphFormatter.prototype.toJSON = ItemGraphFormatter.prototype.toJSON; -OctagonVertexGraphFormatter.prototype.loadFromJSON = ItemGraphFormatter.prototype.loadFromJSON; -OctagonVertexGraphFormatter.prototype._setEvents = ItemGraphFormatter.prototype._setEvents; - -function OctagonVertexGraphFormatter(id, defaultFormat, selectedFormat, overFormat, draggingFormat){ - VertexGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, draggingFormat); - this.args.type = "OctagonVertexGraphFormatter"; - if (defaultFormat != null){ - if (defaultFormat.radius != null){ - this.defaultFormat.args.radius = defaultFormat.radius; - } - } - - if (selectedFormat != null){ - if (selectedFormat.radius != null){ - this.selected.args.radius = selectedFormat.radius; - } - } - - if (overFormat != null){ - if (overFormat.radius != null){ - this.over.args.radius = overFormat.radius; - } - } - - if (draggingFormat != null){ - if (draggingFormat.radius != null){ - this.dragging.args.draggingFormat = draggingFormat.radius; - } - } -}; - - - -var Colors = new function() -{ - this.hashColor = []; - this.getColorByScoreArrayValue = function (arrayScore) - { - var array = new Array(); - - for (var i = 0; i< arrayScore.length; i++) - { - - var color = this.getColorByScoreValue(arrayScore[i]) - array.push( color); - - } - return array; - }; - - this.getHexStringByScoreArrayValue = function (arrayScore) - { - var arrayColor = this.getColorByScoreArrayValue(arrayScore); - var arrayHex = new Array(); - for (var i = 0; i< arrayColor.length; i++) - { - arrayHex.push( arrayColor[i].HexString()); - } - return arrayHex; - }; - - this.getColorByScoreValue = function (score) - { - - var truncate = score.toString().substr(0,4); - if (this.hashColor[truncate]!=null) - { - return this.hashColor[truncate]; - } - - - if(isNaN(score)) { - return Colors.ColorFromRGB(0,0,0); - } - var value; - - var from, to; - if(score < 0.5) { - from = Colors.ColorFromRGB(0,0,255); - to = Colors.ColorFromRGB(255,255,255); - value = (score * 2); - } else { - from = Colors.ColorFromRGB(255,255,255); - to = Colors.ColorFromRGB(255,0,0); - value = (score * 2) - 1; - } - - var x = value; - var y = 1.0 - value; - var color = Colors.ColorFromRGB(y * from.Red() + x * to.Red(), y * from.Green() + x * to.Green(), y * from.Blue() + x * to.Blue()); - - this.hashColor[truncate] = color; - - return color; - }; - - this.ColorFromHSV = function(hue, sat, val) - { - var color = new Color(); - color.SetHSV(hue,sat,val); - return color; - }; - - this.ColorFromRGB = function(r, g, b) - { - var color = new Color(); - color.SetRGB(r,g,b); - return color; - }; - - this.ColorFromHex = function(hexStr) - { - var color = new Color(); - color.SetHexString(hexStr); - return color; - }; - - function Color() { - //Stored as values between 0 and 1 - var red = 0; - var green = 0; - var blue = 0; - - //Stored as values between 0 and 360 - var hue = 0; - - //Strored as values between 0 and 1 - var saturation = 0; - var value = 0; - - this.SetRGB = function(r, g, b) - { - red = r/255.0; - green = g/255.0; - blue = b/255.0; - calculateHSV(); - }; - - this.Red = function() { return Math.round(red*255); }; - - this.Green = function() { return Math.round(green*255); }; - - this.Blue = function() { return Math.round(blue*255); }; - - this.SetHSV = function(h, s, v) - { - hue = h; - saturation = s; - value = v; - calculateRGB(); - }; - - this.Hue = function() - { return hue; }; - - this.Saturation = function() - { return saturation; }; - - this.Value = function() - { return value; }; - - this.SetHexString = function(hexString) - { - if(hexString == null || typeof(hexString) != "string") - { - this.SetRGB(0,0,0); - return; - } - - if (hexString.substr(0, 1) == '#') - hexString = hexString.substr(1); - - if(hexString.length != 6) - { - this.SetRGB(0,0,0); - return; - } - - var r = parseInt(hexString.substr(0, 2), 16); - var g = parseInt(hexString.substr(2, 2), 16); - var b = parseInt(hexString.substr(4, 2), 16); - if (isNaN(r) || isNaN(g) || isNaN(b)) - { - this.SetRGB(0,0,0); - return; - } - - this.SetRGB(r,g,b); - }; - - this.HexString = function() - { - - var rStr = this.Red().toString(16); - if (rStr.length == 1) - rStr = '0' + rStr; - var gStr = this.Green().toString(16); - if (gStr.length == 1) - gStr = '0' + gStr; - var bStr = this.Blue().toString(16); - if (bStr.length == 1) - bStr = '0' + bStr; - return ('#' + rStr + gStr + bStr).toUpperCase(); - }; - - this.Complement = function() - { - var newHue = (hue >= 180) ? hue - 180 : hue + 180; - var newVal = (value * (saturation - 1) + 1); - var newSat = (value*saturation) / newVal; - var newColor = new Color(); - newColor.SetHSV(newHue, newSat, newVal); - return newColor; - } ; - - function calculateHSV() - { - var max = Math.max(Math.max(red, green), blue); - var min = Math.min(Math.min(red, green), blue); - - value = max; - - saturation = 0; - if(max != 0) - saturation = 1 - min/max; - - hue = 0; - if(min == max) - return; - - var delta = (max - min); - if (red == max) - hue = (green - blue) / delta; - else if (green == max) - hue = 2 + ((blue - red) / delta); - else - hue = 4 + ((red - green) / delta); - hue = hue * 60; - if(hue < 0) - hue += 360; - } - - function calculateRGB() - { - red = value; - green = value; - blue = value; - - if(value == 0 || saturation == 0) - return; - - var tHue = (hue / 60); - var i = Math.floor(tHue); - var f = tHue - i; - var p = value * (1 - saturation); - var q = value * (1 - saturation * f); - var t = value * (1 - saturation * (1 - f)); - switch(i) - { - case 0: - red = value; green = t; blue = p; - break; - case 1: - red = q; green = value; blue = p; - break; - case 2: - red = p; green = value; blue = t; - break; - case 3: - red = p; green = q; blue = value; - break; - case 4: - red = t; green = p; blue = value; - break; - default: - red = value; green = p; blue = q; - break; - } - } - } -} -(); -/* - * Clase gestiona la insercción, eliminación de los elementos en el DOM - * - * Vital hacerla SIEMPRE compatible hacia atrás - * - * Last update: 28-10-2010 - * - */ - - -var DOM = {}; - -DOM.createNewElement = function(type, nodeParent, attributes) -{ - - var node = document.createElement(type); - for (var i=0; i0) - { - parent.removeChild(parent.childNodes[0]); - - } -}; - -DOM.select = function(targetID) -{ - return document.getElementById(targetID); -// return $("#"+targetID); -}; -var Geometry = -{ - - /** From tow points obtains the angles formed with the cartesian side **/ - getAngleBetweenTwoPoints : function(x1, y1, x2, y2){ - //var m = (y1 - y2)/ (x1 - x2); - return Math.atan2(y2-y1, x2-x1); - }, - - getAdjacentSideOfRectangleRight : function(angle, hypotenuse){ - return Math.abs(Math.cos(angle)*hypotenuse); - }, - - getOppositeSideOfRectangleRight : function(angle, hypotenuse){ - return Math.abs(Math.sin(angle)*hypotenuse); - }, - - toDegree: function(radian){ - return radian*180/Math.PI; - - } - - -}; - -var SVG = -{ - svgns : 'http://www.w3.org/2000/svg', - xlinkns : "http://www.w3.org/1999/xlink", - - createSVGCanvas: function(parentNode, attributes) - { - - attributes.push( ['xmlns', SVG.svgns], ['xmlns:xlink', 'http://www.w3.org/1999/xlink']); - var svg = document.createElementNS("http://www.w3.org/2000/svg", "svg"); - this._setProperties(svg, attributes); - parentNode.appendChild( svg); - return svg; - - }, - - createRectangle : function (x, y, width, height, attributes){ - var rect = document.createElementNS(this.svgns, "rect"); - rect.setAttribute("x",x); - rect.setAttribute("y",y); - rect.setAttribute("width",width); - rect.setAttribute("height",height); - SVG._setProperties(rect, attributes); - return rect; - }, - - drawImage64 : function (x, y, width, height, base64, svgNode, attributes) { - var node = SVG.createImage64(x, y, width, height, base64, attributes); - svgNode.appendChild(node); - return node; - }, - - createImage64 : function (x, y, width, height, base64, attributes) { - var img = document.createElementNS(this.svgns, "image"); - img.setAttribute("x",x); - img.setAttribute("y",y); - img.setAttribute("width",width); - img.setAttribute("height",height); - img.setAttribute("xlink:href",base64); - SVG._setProperties(img, attributes); - return img; - }, - - createLine: function (x1, y1, x2, y2, attributes){ - var line = document.createElementNS(this.svgns,"line"); - line.setAttribute("x1",x1); - line.setAttribute("y1",y1); - line.setAttribute("x2", x2); - line.setAttribute("y2", y2); - SVG._setProperties(line, attributes); - return line; - }, - - createClip: function (id, nodeToClip, attributes){ - var clip = document.createElementNS(this.svgns,"clipPath"); - clip.setAttribute("id",id); - clip.appendChild(nodeToClip); - return clip; - }, - - drawClip : function (id, nodeToClip, svgNode) { - var node = SVG.createClip(id, nodeToClip); - svgNode.appendChild(node); - return node; - }, - - drawRectangle : function (cx, cy, width, height, svgNode, attributes) { - try{ - var node = SVG.createRectangle(cx, cy, width, height, attributes); - svgNode.appendChild(node); - } - catch(e){ - - console.log("-------------------- "); - console.log("Error on drawRectangle " + e); - console.log(attributes); - console.log("-------------------- "); - } - return node; - }, - - createEllipse : function (x, y, rx, ry, attributes){ - var rect = document.createElementNS(this.svgns, "ellipse"); - rect.setAttribute("cx",x); - rect.setAttribute("cy",y); - rect.setAttribute("rx",rx); - rect.setAttribute("ry",ry); - SVG._setProperties(rect, attributes); - return rect; - }, - - drawEllipse : function (cx, cy, rx, ry, svgNode, attributes) { - var node = SVG.createEllipse(cx, cy, rx, ry, attributes); - svgNode.appendChild(node); - return node; - }, - - drawImage : function (x, y, canvasSVG, attributes) { - var image = document.createElementNS(this.svgns, "image"); - image.setAttribute("x",x); - image.setAttribute("y",y); - canvasSVG.appendChild(image); - SVG._setProperties(image, attributes); - }, - - drawLine : function (x1, y1, x2, y2, nodeSVG, attributes) { - try{ - var line = SVG.createLine(x1, y1, x2, y2, attributes); - nodeSVG.appendChild(line); - }catch(e){ - } - return line; - }, - - - drawPath: function (d, nodeSVG, attributes) { - var path = SVG.createPath(d, attributes); - nodeSVG.appendChild(path); - return path; - }, - - createPoligon : function (points, attributes){ - var poligon = document.createElementNS(this.svgns, "polygon"); - poligon.setAttribute("points",points); - SVG._setProperties(poligon, attributes); - return poligon; - }, - - drawPoligon : function (points, canvasSVG, attributes){ - var poligon = SVG.createPoligon(points, attributes); - canvasSVG.appendChild(poligon); - return poligon; - }, - // - - createPath : function (d, attributes){ - var path = document.createElementNS(this.svgns, "path"); - path.setAttribute("d",d); - SVG._setProperties(path, attributes); - return path; - }, - - drawCircle : function (x, y, radio, canvasSVG, attributes) { - - var newText = document.createElementNS(this.svgns,"circle"); - newText.setAttribute("cx",x); - newText.setAttribute("cy",y); - newText.setAttribute("r",radio); - - canvasSVG.appendChild(newText); - attributes["cursor"] = "pointer"; - this._setProperties(newText, attributes); - return newText; - }, - - - _setProperties: function(node, attributes) - { - if (attributes instanceof Array){ - for (var i=0; i< attributes.length; i++) - { - node.setAttribute(attributes[i][0], attributes[i][1]); - } - } - else{ - for ( var key in attributes){ - node.setAttribute(key, attributes[key]); - } - } - }, - -/* drawPath: function(pointsArray, canvasSVG, attributes){ - var path = document.createElementNS(this.svgns,"polyline"); - path.setAttribute ('id', id); - - var d= pointsArray[0].x+ " "+ pointsArray[0].y; - for (var i=1; i< pointsArray.length; i++) - { - d=d+" "+pointsArray[i].x+" "+pointsArray[i].y; - } - path.setAttribute ('points', d); - canvasSVG.appendChild(path); - },*/ - - createText : function (x, y, text, attributes) { - var node = document.createElementNS(this.svgns,"text"); - node.setAttributeNS(null , "x",x); - node.setAttributeNS(null, "y",y); - - var textNode = document.createTextNode(text); - node.appendChild(textNode); - - this._setProperties(node, attributes); - return node; - }, - - drawText : function (x, y, text, canvasSVG, attributes) { - var text = SVG.createText(x, y, text, attributes); - canvasSVG.appendChild(text); - return text; - }, - - - - drawGroup: function(svgNode, attributes) - { - var group = SVG.createGroup(attributes); - svgNode.appendChild(group); - return group; - }, - - createGroup: function(attributes){ - var group = document.createElementNS(this.svgns,"g"); - this._setProperties(group, attributes); - return group; - } - -}; - - - -var CanvasToSVG = { - - convert: function(sourceCanvas, targetSVG, x, y, id, attributes) { - - var img = this._convert(sourceCanvas, targetSVG, x, y, id); - - for (var i=0; i< attributes.length; i++) - { - img.setAttribute(attributes[i][0], attributes[i][1]); - } - }, - - _convert: function(sourceCanvas, targetSVG, x, y, id) { - var svgNS = "http://www.w3.org/2000/svg"; - var xlinkNS = "http://www.w3.org/1999/xlink"; - // get base64 encoded png from Canvas - var image = sourceCanvas.toDataURL(); - - // must be careful with the namespaces - var svgimg = document.createElementNS(svgNS, "image"); - - svgimg.setAttribute('id', id); - - //svgimg.setAttribute('class', class); - //svgimg.setAttribute('xlink:href', image); - svgimg.setAttributeNS(xlinkNS, 'xlink:href', image); - - - - - svgimg.setAttribute('x', x ? x : 0); - svgimg.setAttribute('y', y ? y : 0); - svgimg.setAttribute('width', sourceCanvas.width); - svgimg.setAttribute('height', sourceCanvas.height); - //svgimg.setAttribute('cursor', 'pointer'); - svgimg.imageData = image; - - targetSVG.appendChild(svgimg); - return svgimg; - }, - - importSVG: function(sourceSVG, targetCanvas) { - svg_xml = sourceSVG;//(new XMLSerializer()).serializeToString(sourceSVG); - var ctx = targetCanvas.getContext('2d'); - - var img = new Image(); - img.src = "data:image/svg+xml;base64," + btoa(svg_xml); -// img.onload = function() { - ctx.drawImage(img, 0, 0); -// }; - } - -}; -/* -Graph.prototype.importSVG = function(sourceSVG, targetCanvas) { - sourceSVG = this._svg; - targetCanvas = document.createElementNS('canvas'); - // https://developer.mozilla.org/en/XMLSerializer - svg_xml = (new XMLSerializer()).serializeToString(sourceSVG); - var ctx = targetCanvas.getContext('2d'); - // this is just a JavaScript (HTML) image - var img = new Image(); - // http://en.wikipedia.org/wiki/SVG#Native_support - // https://developer.mozilla.org/en/DOM/window.btoa - img.src = "data:image/svg+xml;base64," + btoa(svg_xml); - img.onload = function() { - // after this, Canvas’ origin-clean is DIRTY - ctx.drawImage(img, 0, 0); - } -}; -*/ - -/* - -Normalizacion de datos para dibujar colores -Issues: - No sé como debería llamarse esta libreria - No sé si ya existe una funciçon en javascript que lo haga - - -*/ - - -var Normalizer = new function() -{ - this.normalizeArray = function (arrayData) - { - - return this.standardizeArray(this.normal(arrayData)); - -// var result = this._getMaxAndMin(arrayData); -// var min =result[0]; -// var max = result[1]; -// -// -// //los hacemos todos positivos -// for (var i = 0; i< arrayData.length; i++) -// { -// arrayData[i]= Math.abs(min) + parseFloat(arrayData[i]); -// } -// -// var result = this._getMaxAndMin(arrayData); -// var min =result[0]; -// var max = result[1]; -// -// -// var resultArray = new Array(); -// for (var i = 0; i< arrayData.length; i++) -// { -// resultArray.push(arrayData[i]*1/max); -// } -// return resultArray; - }; - - this.normal = function(arrayData){ - var mean = this._getMean(arrayData); - var deviation = this._getStdDeviation(arrayData); - var result = this._getMaxAndMin(arrayData); - var min = result[0]; - var max = result[1]; - - var resultArray = new Array(); - for (var i = 0; i< arrayData.length; i++) { - if (deviation!=0){ - resultArray.push((arrayData[i]-mean)/deviation); - }else{ - resultArray.push(arrayData[i]); - } - } - return resultArray; - }; - - this.standardizeArray = function(arrayData) - { - var result = this._getMaxAndMin(arrayData); - var min = result[0]; - var max = result[1]; - - var offset = ( min <= 0 ) ? Math.abs(min) : (-1 * min); - var resultArray = new Array(); - for (var i = 0; i< arrayData.length; i++) { - if(max + offset!=0){ - resultArray.push((arrayData[i] + offset) / (max + offset)); - }else{ - resultArray.push(arrayData[i]+offset); - } - } - return resultArray; - }; - - - this._getMean = function(arrayData) { - var sum = 0; - for (var i = 0; i< arrayData.length; i++) { - sum = sum + parseFloat(arrayData[i]); - } - return sum/arrayData.length; - }; - - this._getStdDeviation = function(arrayData) { - var mean = this._getMean(arrayData); - var acum = 0.0; - for(var i=0; i max) max = parseFloat(arrayData[i]); - } - - return [min, max]; - }; -}; -function GraphCanvas(componentID, targetNode, args) { - this.args = {}; - /** target */ - this.targetID = targetNode.id; - - /** id manage */ - this.id = componentID; - this.args.idGraph = this.id + "main"; - this.args.idBackgroundNode = this.id + "background"; - - this.args.idEdgesGraph = this.id + "edges"; - this.args.idNodesGraph = this.id + "vertices"; - this.args.idLabelGraph = this.id + "label"; - this.args.idBackground = this.id + "background"; - - /** Objects Graph **/ - this.dataset = null; - this.formatter = null; - this.layout = null; - - /** Drawing **/ - this.circleDefaultRadius = 2; - this.squareDefaultSide = this.circleDefaultRadius * 1.5; - - /** Directed Arrow **/ - this.arrowDefaultSize = this.circleDefaultRadius; - - /** Groups **/ - this.GraphGroup = null; - this.GraphNodeGroup = null; - this.GraphLabelGroup = null; - this.GraphBackground = null; - - /** SETTINGS FLAGS **/ - this.args.draggingCanvasEnabled = false; //Flag to set if the canvas can be dragged - this.args.multipleSelectionEnabled = false; - this.args.interactive = false; - this.args.labeled = false; - this.args.linkEnabled = false; - - /** If numberEdge > maxNumberEdgesMoving then only it will move edges when mouse up **/ - this.args.maxNumberEdgesMoving = 3; - this.args.maxNumberEdgesFiringEvents = 50; - - /** Linking edges **/ - this.args.linking = false; - this.linkStartX = 0; - this.linkStartY = 0; - this.linkSVGNode = null; - this.linkNodeSource = null; - this.linkNodeTarget = null; - - /** Dragging Control **/ - this.draggingElement = null; - this.dragging = false; - this.nMouseOffsetX = 0; - this.nMouseOffsetY = 0; - this.dragStartX = 0; - this.dragStartY = 0; - this.desplazamientoX = 0; - this.desplazamientoY = 0; - - /** Selection Control **/ - this.selecting = false; - this.selectorX = null; - this.selectorY = null; - this.selectorSVGNode = null; - - /** Node status **/ - this.args.isVertexSelected = {}; - this.args.selectedVertices = []; - - /** Edges status **/ - this.args.isEdgeSelected = {}; - //this.args.selectedEdges = []; - - if (args != null) { - if (args.multipleSelectionEnabled != null) { - this.args.multipleSelectionEnabled = args.multipleSelectionEnabled; - this.args.draggingCanvasEnabled = !(this.args.multipleSelectionEnabled); - } - if (args.draggingCanvasEnabled != null) { - this.args.draggingCanvasEnabled = args.draggingCanvasEnabled; - this.args.multipleSelectionEnabled = !(this.args.draggingCanvasEnabled); - } - if (args.interactive != null) { - this.args.interactive = args.interactive; - } - if (args.labeled != null) { - this.args.labeled = args.labeled; - } - - } - - /** Hashmap with the svg node labels **/ - this.svgLabels = {}; - - /** EVENTS **/ - this.onVertexOut = new Event(this); - this.onVertexOver = new Event(this); - this.onVertexSelect = new Event(this); - this.onEdgeSelect = new Event(this); - this.onCanvasClicked = new Event(this); - this.onVertexUp = new Event(this); -} - -GraphCanvas.prototype.showLabels = function(value) { - this.args.labeled = value; - this.removeLabels(); - if (value) { - this.renderLabels(); - } -}; - -GraphCanvas.prototype.getSelectedVertices = function() { - return this.args.selectedVertices; -}; - -GraphCanvas.prototype.getSelectedEdges = function() { - var selected = []; - for ( var selectedEdge in this.args.isEdgeSelected) { - selected.push(selectedEdge); - } - return selected; -}; - -GraphCanvas.prototype.createSVGDom = function(targetID, id, width, height, backgroundColor) { - var container = document.getElementById(targetID); - this._svg = SVG.createSVGCanvas(container, [ - [ "style", "background-color:" + backgroundColor + ";" ], [ "id", id ], [ "dragx", 0 ], [ "dragy", 0 ], - [ "height", this.getFormatter().getHeight() ], [ "width", this.getFormatter().getWidth() ] ]); - return this._svg; -}; - -/** MULTIPLE SELECTION **/ -GraphCanvas.prototype.isMultipleSelectionEnabled = function() { - return this.args.multipleSelectionEnabled; -}; - -GraphCanvas.prototype.setMultipleSelection = function(value) { - this.args.multipleSelectionEnabled = value; - this.args.draggingCanvasEnabled = (!value); -}; - -GraphCanvas.prototype.setSelecting = function(value) { - this.selecting = value; -}; - -/** linking **/ -GraphCanvas.prototype.setLinking = function(value) { - this.args.linkEnabled = value; - this.selecting = !value; - this.dragging = !value; -}; - -/** CANVAS MOVING **/ -GraphCanvas.prototype.setDraggingCanvas = function(value) { - this.args.draggingCanvasEnabled = value; - this.args.multipleSelectionEnabled = !value; -}; - -GraphCanvas.prototype.isDraggingCanvasEnabled = function() { - return this.args.draggingCanvasEnabled; -}; -/** ZOOM **/ -GraphCanvas.prototype.getScale = function() { - return this.getFormatter().getZoomScale(); -}; - -GraphCanvas.prototype.setScale = function(scale) { - var graphNode = document.getElementById(this.args.idGraph); - graphNode.setAttribute("transform", graphNode.getAttribute("transform").replace("scale(" + this.getScale() + ")", "scale(" + scale + ")")); - this.getFormatter().setZoomScale(scale); -}; - -GraphCanvas.prototype.zoomIn = function() { - this.setScale(this.getScale() + this.getFormatter().getZoomScaleStepFactor()); -}; - -GraphCanvas.prototype.zoomOut = function() { - this.setScale(this.getScale() - this.getFormatter().getZoomScaleStepFactor()); - -}; - -/** SVG COORDENATES **/ -GraphCanvas.prototype.getSVGCoordenates = function(evt) { - var p = this._svg.createSVGPoint(); - p.x = evt.clientX; - p.y = evt.clientY; - - var m = this._svg.getScreenCTM(document.documentElement); - p = p.matrixTransform(m.inverse()); - return p; -}; - -/** SVG EVENTS **/ -GraphCanvas.prototype.mouseClick = function(event) { - if (event.button == 0) { - if (!this.args.interactive) { - return; - } - - if (this.isVertex(event.target)) { - this.clickNode(this.getVertexIdFromSVGId(event.target.id)); - } - /** Como el evento mouseClick viene despues del mouse up es aqui donde manejo el tema de deseccionar los elementos que estoy dragging **/ - if (this.dragging) { - this.dragging = false; - } - } -}; - -GraphCanvas.prototype.mouseMove = function(evt) { - if (this.selecting) { - this.clearLabels(); - - var width = (this.getSVGCoordenates(evt).x - this.selectorX); - var height = (this.getSVGCoordenates(evt).y - this.selectorY); - if ((width > 0) && (height > 0)) { - this.displaySelection(this.selectorX, this.selectorY, width, height); - } - if ((width > 0) && (height < 0)) { - this.displaySelection(this.selectorX, this.getSVGCoordenates(evt).y, width, Math.abs(height)); - } - if ((width < 0) && (height < 0)) { - this.displaySelection(this.getSVGCoordenates(evt).x, this.getSVGCoordenates(evt).y, Math.abs(width), Math.abs(height)); - } - if ((width < 0) && (height > 0)) { - this.displaySelection(this.selectorX + width, this.selectorY, Math.abs(width), Math.abs(height)); - } - - var x1 = (parseFloat(this.selectorSVGNode.getAttribute("x")) - DOM.select(this.id).getAttribute("dragx")) / this.getFormatter().getWidth(); - var y1 = (parseFloat(this.selectorSVGNode.getAttribute("y")) - DOM.select(this.id).getAttribute("dragy")) / this.getFormatter().getHeight(); - var x2 = (x1 + parseFloat(this.selectorSVGNode.getAttribute("width") / this.getFormatter().getWidth())); - var y2 = (y1 + parseFloat(this.selectorSVGNode.getAttribute("height") / this.getFormatter().getHeight())); - - this.deselectNodes(this.getLayout()); - var verticesSelected = this.getLayout().getVerticesByArea(x1 / this.getFormatter().getZoomScale(), y1 / this.getFormatter().getZoomScale(), - x2 / this.getFormatter().getZoomScale(), y2 / this.getFormatter().getZoomScale()); - for ( var i = 0; i < verticesSelected.length; i++) { - this.selectNode(verticesSelected[i].getId()); - this.renderLabel(verticesSelected[i].getId()); - } - - } - var p = null; - if (this.args.linking) { - p = this.getSVGCoordenates(evt); - if (this.linkSVGNode != null) { - this.linkSVGNode.setAttribute("x2", p.x - 2); - this.linkSVGNode.setAttribute("y2", p.y - 2); - } - } - - if (this.dragging) { - p = this.getSVGCoordenates(evt); - p.x -= this.nMouseOffsetX; - p.y -= this.nMouseOffsetY; - this.desplazamientoX = (this.getSVGCoordenates(evt).x - this.dragStartX);// + parseFloat(DOM.select(this.id).getAttribute("dragx")); - this.desplazamientoY = (this.getSVGCoordenates(evt).y - this.dragStartY);// + parseFloat(DOM.select(this.id).getAttribute("dragy")); - - if (this.draggingElement != null) { - /** Click sobre el recct del banground que provoca que mueva todo el canvas **/ - if (this.isNodeCanvas(this.draggingElement)) { - - p = this.getSVGCoordenates(evt); - p.x = this.desplazamientoX; - p.y = this.desplazamientoY; - - this.draggingElement.setAttribute("dragx", p.x); - this.draggingElement.setAttribute("dragy", p.y); - this.draggingElement = document.getElementById(this.args.idGraph); - this.draggingElement.setAttribute("transform", "translate(" + p.x + "," + p.y + "), scale(" + this.getScale() + ")"); - - DOM.select(this.id).setAttribute("dragx", p.x); - DOM.select(this.id).setAttribute("dragy", p.y); - - if (this.NodeSVGbackgroundImage != null) { - this.NodeSVGbackgroundImage.setAttribute("dragx", p.x); - this.NodeSVGbackgroundImage.setAttribute("dragy", p.y); - } - } else { - if (this.isVertex(this.draggingElement)) { - this.selectNode(this.getVertexIdFromSVGId(this.draggingElement.id)); - this.desplazamientoX = this.desplazamientoX / this.getFormatter().getZoomScale(); - this.desplazamientoY = this.desplazamientoY / this.getFormatter().getZoomScale(); - this.moveSelectedNodes(this.desplazamientoX, this.desplazamientoY); - - this.dragStartX = this.getSVGCoordenates(evt).x; - this.dragStartY = this.getSVGCoordenates(evt).y; - } else { - if (this.isNodeBackground(this.draggingElement)) { - - this.draggingElement.setAttribute("dragx", p.x); - this.draggingElement.setAttribute("dragy", p.y); - this.draggingElement = document.getElementById(this.args.idGraph); - this.draggingElement.setAttribute("transform", "translate(" + p.x + "," + p.y + "), scale(" + this.getScale() + ")"); - } else { - this.draggingElement.setAttribute("transform", "translate(" + p.x + "," + p.y + ")"); - } - } - } - } - } -}; - -GraphCanvas.prototype.moveSelectedNodes = function(offsetX, offsetY) { - for ( var i = 0; i < this.getSelectedVertices().length; i++) { - - var nodeId = this.getSelectedVertices()[i]; - var svgNodeId = this.getSVGNodeId(nodeId); - - var x = parseFloat(DOM.select(svgNodeId).getAttribute("dragx")) + parseFloat(offsetX);// - parseFloat(DOM.select(this.id).getAttribute("dragx")); - var y = parseFloat(DOM.select(svgNodeId).getAttribute("dragy")) + parseFloat(offsetY);// + parseFloat(DOM.select(this.id).getAttribute("dragy")); - - this._movingNode(DOM.select(svgNodeId), x, y); - } -}; - -GraphCanvas.prototype.mouseDown = function(evt) { - if (event.button == 0) { - - /** if !no interactive mouse events do anything **/ - if (!this.args.interactive) { - return; - } - - var p = this.getSVGCoordenates(evt); - - /** When click on canvas or background deselect all **/ - if (this.isNodeCanvas(evt.target) || this.isNodeBackground(evt.target)) { - this.deselectNodes(); - this.deselectEdges(); - this.onCanvasClicked.notify(); - } - - /** if I am linking vertices **/ - if (this.args.linkEnabled) { - - if (!this.args.linking) { - this.args.linking = true; - if (this.isVertex(evt.target)) { - this.linkStartX = p.x; - this.linkStartY = p.y; - this.linkSVGNode = SVG.drawLine(p.x, p.y, p.x, p.y, this._svg, { - "stroke" : "#FF0000" - }); - this.linkNodeSource = this.getVertexIdFromSVGId(evt.target.id); - } - } else { - this.linkNodeTarget = this.getVertexIdFromSVGId(evt.target.id); - this.args.linking = false; - this.args.linkEnabled = false; - if (this.isVertex(evt.target)) { - this.getDataset().addEdge(this.linkNodeSource + "_" + this.linkNodeTarget, this.linkNodeSource, this.linkNodeTarget, {}); - } - this.linkSVGNode.parentNode.removeChild(this.linkSVGNode); - } - return; - } - - /** Id is a vertex or the canvas **/ - if (this.isVertex(evt.target) || this.isNodeCanvas(evt.target) || this.isNodeBackground(evt.target)) { - this._startDragging(evt); - } - /** if i is edge **/ - if (this.isEdge(evt.target)) { - this.selectEdge(this.getEdgeIdFromSVGId(evt.target.getAttribute("id"))); - } - - if (this.args.multipleSelectionEnabled) { - if (!this.dragging) { - this.setSelecting(true); - this.selectorX = p.x; - this.selectorY = p.y; - this.displaySelection(p.x, p.y, 1, 1); - } - } - - } - if (event.button == 1) { - this.setLinking(false); - this.setMultipleSelection(false); - this.selecting = false; - - /** Id is a vertex or the canvas **/ - if (this.isVertex(evt.target) || this.isNodeCanvas(evt.target) || this.isNodeBackground(evt.target)) { - this._startDragging(evt); - } - } -}; - -GraphCanvas.prototype.mouseUp = function(event) { - if (!this.args.interactive) { - return; - } - - if (this.dragging) { - this._stopDragging(event); - if (this.isVertex(event.target)) { - var vertexId = this.getVertexIdFromSVGId(event.target.id); - if (this.getDataset().getVertexById(vertexId).getEdges().length >= this.args.maxNumberEdgesMoving) { - this.moveEdge(vertexId); - } - } - } - - if (this.selecting) { - this.setSelecting(false); - - var x1 = (parseFloat(this.selectorSVGNode.getAttribute("x")) - DOM.select(this.id).getAttribute("dragx")) / this.getFormatter().getWidth(); - var y1 = (parseFloat(this.selectorSVGNode.getAttribute("y")) - DOM.select(this.id).getAttribute("dragy")) / this.getFormatter().getHeight(); - var x2 = (x1 + parseFloat(this.selectorSVGNode.getAttribute("width") / this.formatter.getWidth())); - var y2 = (y1 + parseFloat(this.selectorSVGNode.getAttribute("height") / this.formatter.getHeight())); - - var verticesSelected = this.getLayout().getVerticesByArea(x1 / this.getFormatter().getZoomScale, y1 / this.getFormatter().getZoomScale, - x2 / this.getFormatter().getZoomScale, y2 / this.getFormatter().getZoomScale); - - for ( var i = 0; i < verticesSelected.length; i++) { - this.selectNode(verticesSelected[i].getId()); - } - - if (this.selectorSVGNode != null) { - this._svg.removeChild(this.selectorSVGNode); - } - - if (this.args.labeled) { - this.clearLabels(); - this.renderLabels(); - } - - this.selectorSVGNode = null; - // this.renderLabels(); - } -}; - -/** SELECTION **/ -GraphCanvas.prototype.displaySelection = function(x, y, width, height) { - if (this.selectorSVGNode != null) { - this.selectorSVGNode.setAttribute("x", x); - this.selectorSVGNode.setAttribute("y", y); - this.selectorSVGNode.setAttribute("width", width); - this.selectorSVGNode.setAttribute("height", height); - } else { - this.selectorSVGNode = SVG.drawRectangle(x, y, width, height, this._svg, { - "fill" : "red", - "stroke" : "black", - "opacity" : "0.2", - "stroke-opacity" : "1" - }); - } -}; - -/** DRAGGING **/ -GraphCanvas.prototype._startDragging = function(evt) { - if (!this.isDraggingCanvasEnabled()) { - if (this.isNodeCanvas(evt.target)) { - this.draggingElement = null; - } - } - - if (this.isVertex(evt.target) || (this.isNodeBackground(evt.target) && (this.isDraggingCanvasEnabled()))|| (this.isNodeCanvas(evt.target) && (this.isDraggingCanvasEnabled()))) { - this.clearLabels(); - this.draggingElement = evt.target; - this.dragging = true; - var p = this.getSVGCoordenates(evt); - - this.nMouseOffsetX = p.x - parseInt(evt.target.getAttribute("dragx")); - this.nMouseOffsetY = p.y - parseInt(evt.target.getAttribute("dragy")); - - if (this.isVertex(evt.target)) { - this.dragStartX = parseInt(this.draggingElement.getAttribute("dragx")) * this.getFormatter().getZoomScale() - + parseFloat(DOM.select(this.id).getAttribute("dragx")); - this.dragStartY = parseInt(this.draggingElement.getAttribute("dragy")) * this.getFormatter().getZoomScale() - + parseFloat(DOM.select(this.id).getAttribute("dragy")); - } else { - this.dragStartX = p.x - parseInt(this.draggingElement.getAttribute("dragx"));// + parseFloat(DOM.select(this.id).getAttribute("dragx")); - this.dragStartY = p.y - parseInt(this.draggingElement.getAttribute("dragy"));// + parseFloat(DOM.select(this.id).getAttribute("dragy")); - } - } -}; - -GraphCanvas.prototype._stopDragging = function(event) { - this.nMouseOffsetX = 0; - this.nMouseOffsetX = 0; - /** despues del evento up viene el evento click entonces acabo el dragging en el mouseclick **/ - this.dragging = false; - this.draggingElement = null; - this.renderLabels(); - - this.setLinking(false); - this.setMultipleSelection(true); - this.selecting = false; - -}; - -/** Move the edges of the vertex with the vertexId indicado **/ -GraphCanvas.prototype.moveEdge = function(vertexId) { - var x = this.getLayout().getNodeById(vertexId).x * this.getFormatter().getWidth(); - var y = this.getLayout().getNodeById(vertexId).y * this.getFormatter().getHeight(); - - /** Moving edges out **/ - for ( var i = 0; i < this.getDataset().getVertexById(vertexId).getEdgesOut().length; i++) { - var edgeId = this.getDataset().getVertexById(vertexId).getEdgesOut()[i].getId(); - var svgEdgeId = this.getSVGEdgeId(edgeId); - var edgeFormatter = this.getFormatter().getEdgeById(edgeId); - if (edgeFormatter instanceof LineEdgeGraphFormatter) { - DOM.select(svgEdgeId + "_shadow").setAttribute("x2", x); - DOM.select(svgEdgeId + "_shadow").setAttribute("y2", y); - DOM.select(svgEdgeId).setAttribute("x2", x); - DOM.select(svgEdgeId).setAttribute("y2", y); - } - - if ((edgeFormatter instanceof DirectedLineEdgeGraphFormatter) || (edgeFormatter instanceof OdirectedLineEdgeGraphFormatter) - || (edgeFormatter instanceof OdotDirectedLineEdgeGraphFormatter) || (edgeFormatter instanceof DotDirectedLineEdgeGraphFormatter) - || (edgeFormatter instanceof CutDirectedLineEdgeGraphFormatter)) { - this.removeEdge(edgeId); - this.renderEdge(edgeId); - } - } - - /** Moving edges in **/ - for ( var i = 0; i < this.getDataset().getVertexById(vertexId).getEdgesIn().length; i++) { - var edgeId = this.getDataset().getVertexById(vertexId).getEdgesIn()[i].getId(); - var svgEdgeId = this.getSVGEdgeId(edgeId); - var edgeFormatter = this.getFormatter().getEdgeById(edgeId); - if (edgeFormatter instanceof LineEdgeGraphFormatter) { - DOM.select(svgEdgeId).setAttribute("x1", x); - DOM.select(svgEdgeId).setAttribute("y1", y); - DOM.select(svgEdgeId + "_shadow").setAttribute("x1", x); - DOM.select(svgEdgeId + "_shadow").setAttribute("y1", y); - } - - if ((edgeFormatter instanceof DirectedLineEdgeGraphFormatter) || (edgeFormatter instanceof OdirectedLineEdgeGraphFormatter) - || (edgeFormatter instanceof OdotDirectedLineEdgeGraphFormatter) || (edgeFormatter instanceof DotDirectedLineEdgeGraphFormatter) - || (edgeFormatter instanceof CutDirectedLineEdgeGraphFormatter)) { - this.removeEdge(edgeId); - this.renderEdge(edgeId); - } - - if (edgeFormatter instanceof BezierEdgeGraphFormatter) { - var radius = this.getFormatter().getVertexById(vertexId).getDefault().getSize() * this.getFormatter().getNodesMaxSize(); - var d = this.calculateCoordenatesBezier(radius, x, y); - DOM.select(svgEdgeId).setAttribute("d", d); - } - } -}; - -GraphCanvas.prototype.moveNode = function(vertexId) { - var x = this.getLayout().getNodeById(vertexId).x * this.getFormatter().getWidth(); - var y = this.getLayout().getNodeById(vertexId).y * this.getFormatter().getHeight(); - var svgNodeElement = DOM.select(this.getSVGNodeId(vertexId)); - - svgNodeElement.setAttribute("dragx", x); - svgNodeElement.setAttribute("dragy", y); - svgNodeElement.setAttribute("transform", "translate(" + x + "," + y + ")"); - - if (this.getDataset().getVertexById(vertexId).getEdges().length < this.args.maxNumberEdgesMoving) { - this.moveEdge(vertexId); - } -}; - -GraphCanvas.prototype._movingNode = function(svgNodeElement, x, y) { - var vertexId = this.getVertexIdFromSVGId(svgNodeElement.getAttribute("id")); - this.getLayout().getNodeById(vertexId).setCoordinates(x / this.getFormatter().getWidth(), y / this.getFormatter().getHeight()); - this.desplazamientoX = 0; - this.desplazamientoY = 0; - this.removeLabel(vertexId); - this.renderLabel(vertexId); -}; - -/** INIT **/ -GraphCanvas.prototype.init = function() { - - this._svg = this.createSVGDom(this.targetID, this.id, this.getFormatter().getWidth(), this.getFormatter().getHeight(), this.getFormatter() - .getBackgroundColor()); - this.GraphGroup = SVG.drawGroup(this._svg, [ [ "id", this.args.idGraph ], [ "transform", "translate(0,0), scale(1)" ] ]); - this.GraphBackground = SVG.drawGroup(this.GraphGroup, [ [ "id", this.args.idBackground ] ]); - this.GraphEdgeGroup = SVG.drawGroup(this.GraphGroup, [ [ "id", this.args.idEdgesGraph ] ]); - this.GraphNodeGroup = SVG.drawGroup(this.GraphGroup, [ [ "id", this.args.idNodesGraph ] ]); - this.GraphLabelGroup = SVG.drawGroup(this.GraphGroup, [ [ "id", this.args.idLabelGraph ] ]); - - if ((this.getFormatter().getBackgroundImage() != null) && (this.getFormatter().getBackgroundImage() != "")) { - this.setBackgroundImage(this.getFormatter().getBackgroundImage()); - } - /** SVG Events listener */ - var _this = this; - this._svg.addEventListener("click", function(event) { - _this.mouseClick(event); - }, false); - this._svg.addEventListener("mousemove", function(event) { - _this.mouseMove(event, _this); - }, false); - this._svg.addEventListener("mousedown", function(event) { - _this.mouseDown(event, _this); - }, false); - this._svg.addEventListener("mouseup", function(event) { - _this.mouseUp(event, _this); - }, false); -}; - -/* - GraphCanvas.prototype.backgroungToSVG = function(){ - var _this = this; - var canvas = document.createElement('canvas'); - canvas.setAttribute("id", "canvas"); - canvas.width = this.formatter.getWidth(); - canvas.height = this.formatter.getHeight(); - - this._svg.parentNode.parentNode.appendChild(canvas); - var ctx = document.getElementById('canvas').getContext('2d'); - var img = new Image(); - - img.src = this.formatter.getBackgroundImage(); - ctx.drawImage(img,0,0 ,_this.formatter.getWidth(), _this.formatter.getHeight()); - - - img.onload = function() { - canvas.parentNode.removeChild(canvas); - } - - this.NodeSVGbackgroundImage.setAttribute("xlink:href", document.getElementById("canvas").toDataURL()); - this.NodeSVGbackgroundImage.removeAttribute("href"); - - // - - };*/ - -GraphCanvas.prototype.setBackgroundImage = function() { - if (this.NodeSVGbackgroundImage != null) { - this.NodeSVGbackgroundImage.parentNode.removeChild(this.NodeSVGbackgroundImage); - } - $('#' + this.targetID).svg(); - $('#' + this.targetID).svg("get"); - - $('#' + this.targetID).svg("get")._svg = document.getElementById(this.id); - - var svg = $('#' + this.targetID).svg("get"); - this.NodeSVGbackgroundImage = svg.image(0, 0, this.getFormatter().getWidth(), this.getFormatter().getHeight(), this.getFormatter() - .getBackgroundImage()); - this.NodeSVGbackgroundImage.setAttribute("id", this.args.idBackgroundNode); - - this.NodeSVGbackgroundImage.setAttribute("x", 0); - this.NodeSVGbackgroundImage.setAttribute("y", 0); - - this.NodeSVGbackgroundImage.setAttribute("dragx", 0); - this.NodeSVGbackgroundImage.setAttribute("dragy", 0); - - if (this.getFormatter().args.backgroundImageHeight != null) { - this.NodeSVGbackgroundImage.setAttribute("height", this.getFormatter().args.backgroundImageHeight); - } - if (this.getFormatter().args.backgroundImageWidth != null) { - this.NodeSVGbackgroundImage.setAttribute("width", this.getFormatter().args.backgroundImageWidth); - } - - if (this.getFormatter().args.backgroundImageX != null) { - this.NodeSVGbackgroundImage.setAttribute("x", this.getFormatter().args.backgroundImageX); - } - if (this.getFormatter().args.backgroundImageY != null) { - this.NodeSVGbackgroundImage.setAttribute("y", this.getFormatter().args.backgroundImageY); - } - - this.GraphBackground.appendChild(this.NodeSVGbackgroundImage); - this.NodeSVGbackgroundImage.removeAttribute("href"); - this.NodeSVGbackgroundImage.setAttribute("xlink:href", this.getFormatter().getBackgroundImage()); -}; - -GraphCanvas.prototype.removeBackgroundImage = function() { - if (this.NodeSVGbackgroundImage != null) { - this.NodeSVGbackgroundImage.parentNode.removeChild(this.NodeSVGbackgroundImage); - } -}; - -GraphCanvas.prototype._setBackgroundColor = function(color) { - var attributes = [ [ "fill", color ] ]; - SVG.drawRectangle(0, 0, this.getFormatter().getWidth(), this.getFormatter().getHeight(), this.GraphBackground, attributes); -}; - -/** Serialize **/ -GraphCanvas.prototype.toJSON = function() { - var json = {}; - json.dataset = {}; - json.formatter = {}; - json.layout = {}; - json.dataset = this.getDataset().toJSON(); - json.formatter = this.getFormatter().toJSON(); - json.layout = this.getLayout().toJSON(); - return json; -}; - -GraphCanvas.prototype.toHTML = function() { - //this.backgroungToSVG(); - var html = this._svg.parentElement.innerHTML; - - var start = html.indexOf("") + 6; - - return html.substr(start, end); -}; - -/** DRAW **/ -GraphCanvas.prototype.draw = function(graphdataset, graphformatter, graphlayout) { - this.setDataset(graphdataset); - this.setFormatter(graphformatter); - this.setLayout(graphlayout); - - var _this = this; - this.getFormatter().changed.attach(function(sender, item) { - _this.removeNode(item.getId()); - _this.renderNode(item.getId()); - if (_this.args.labeled) { - _this.removeLabel(item.getId()); - _this.renderLabel(item.getId()); - } - - }); - //TODO - this.getFormatter().edgeChanged.attach(function(sender, item) { - _this.removeEdge(item.getId()); - _this.renderEdge(item.getId()); - }); - - this.getFormatter().resized.attach(function(sender, item) { - _this.resize(_this.getFormatter().getWidth(), _this.getFormatter().getHeight()); - }); - - this.getFormatter().backgroundImageChanged.attach(function(sender, item) { - _this.setBackgroundImage(_this.getFormatter().getBackgroundImage()); - }); - - this.getFormatter().backgroundColorChanged.attach(function(sender, item) { - _this._setBackgroundColor(_this.getFormatter().getBackgroundColor()); - }); - - this.getLayout().changed.attach(function(sender, item) { - _this.moveNode(item.getId()); - _this.moveEdge(item.getId()); - if (_this.args.labeled) { - _this.removeLabel(item.getId()); - _this.renderLabel(item.getId()); - } - }); - - this.getDataset().newVertex.attach(function(sender, item) { - - _this.renderNode(item.getId()); - if (_this.args.labeled) { - _this.renderLabel(item.getId()); - } - }); - - this.getDataset().newEdge.attach(function(sender, item) { - _this.renderEdge(item.getId()); - }); - - this.getDataset().vertexDeleted.attach(function(sender, item) { - _this.removeNode(item.getId()); - if (_this.args.labeled) { - _this.removeLabel(item.getId()); - } - }); - - this.getDataset().edgeDeleted.attach(function(sender, item) { - _this.removeEdge(item.getId()); - }); - - this.getDataset().vertexNameChanged.attach(function(sender, args) { - if (_this.args.labeled) { - _this.removeLabel(args.item.getId()); - _this.removeLabel(args.item.getId()); - _this.renderLabel(args.item.getId()); - } - }); - this.init(); - this.render(); -}; - -GraphCanvas.prototype.render = function() { - for ( var id in this.getDataset().getVertices()) { - this.renderNode(id); - } - this.renderLabels(); - this.renderEdges(); -}; - -GraphCanvas.prototype.renderLabels = function() { - if (this.args.labeled) { - for ( var id in this.getDataset().getVertices()) { - this.renderLabel(id); - } - } -}; - -GraphCanvas.prototype.removeLabels = function() { - for ( var id in this.getDataset().getVertices()) { - this.removeLabel(id); - } -}; - -/** Utilities method for nodes **/ -GraphCanvas.prototype.isNodeCanvas = function(node) { - return ((node.id == this.args.idGraph) || (node.id == this.id)); -}; - -GraphCanvas.prototype.isNodeBackground = function(node) { - return ((node.id == this.args.idBackgroundNode)); -}; - -GraphCanvas.prototype.isVertex = function(node) { - if (node.getAttribute("id") != null) { - if (node.getAttribute("id").indexOf("_v_") != -1) { - return true; - } - } - return false; -}; - -GraphCanvas.prototype.isLabel = function(node) { - if (node.getAttribute("id") != null) { - if (node.getAttribute("id").indexOf("_l_") != -1) { - return true; - } - } - return false; -}; - -GraphCanvas.prototype.isEdge = function(node) { - if (node.getAttribute("id") != null) { - if (node.getAttribute("id").indexOf("_e_") != -1) { - return true; - } - } - return false; -}; - -/** Resize **/ -GraphCanvas.prototype.resize = function(width, height) { - // this._svg.setAttribute("width", width); - // this._svg.setAttribute("height", height); - if (this.NodeSVGbackgroundImage != null) { - this.NodeSVGbackgroundImage.setAttribute("width", width); - this.NodeSVGbackgroundImage.setAttribute("height", height); - } - - this._svg.setAttribute("width", width); - this._svg.setAttribute("height", height); - - this.clearCanvas(); - this.render(); -}; - -GraphCanvas.prototype.clearCanvas = function() { - DOM.removeChilds(this.GraphEdgeGroup.getAttribute("id")); - DOM.removeChilds(this.GraphNodeGroup.getAttribute("id")); - this.clearLabels(); -}; - -GraphCanvas.prototype.clearLabels = function() { - DOM.removeChilds(this.GraphLabelGroup.getAttribute("id")); -}; - -/** ID'S converter **/ -GraphCanvas.prototype.getSVGNodeId = function(nodeId) { - return this.id + "_v_" + nodeId; -}; - -GraphCanvas.prototype.getSVGEdgeId = function(edgeId) { - return this.id + "_e_" + edgeId; -}; - -GraphCanvas.prototype.getSVGArrowEdgeId = function(edgeId) { - return this.id + "_arrow_" + edgeId; -}; - -GraphCanvas.prototype.getSVGLabelId = function(edgeId) { - return this.id + "_l_" + edgeId; -}; - -GraphCanvas.prototype.blinkVertexById = function(vertexId) { - $("#" + this.getSVGNodeId(vertexId)).fadeIn().fadeOut().fadeIn().fadeOut().fadeIn().fadeOut(); -}; - -GraphCanvas.prototype.getVertexIdFromSVGId = function(svgVertexId) { - return svgVertexId.replace(this.id, "").replace("_v_", ""); -}; - -GraphCanvas.prototype.getEdgeIdFromSVGId = function(svgEdgeId) { - return svgEdgeId.replace(this.id, "").replace("_e_", ""); -}; - -/** VERTEX **/ -GraphCanvas.prototype.getVertexById = function(id) { - return document.getElementById(this.getSVGNodeId(id)); -}; - -GraphCanvas.prototype.renderNodes = function() { - for ( var id in this.getDataset().getVertices()) { - this.renderNode(id); - } -}; - -GraphCanvas.prototype.overNode = function(nodeId) { - if (!this.args.interactive) { - return; - } - /** If selected we don't change the format **/ - if (this.args.isVertexSelected[nodeId] == null) { - var args = this.getFormatter().getVertexById(nodeId).getOver(); - args.args["cursor"] = 'pointer'; - this.changeVertexFormat(nodeId, args); - } -}; - -GraphCanvas.prototype.outNode = function(nodeId) { - if (!this.args.interactive) { - return; - } - - /** If selected we don't change the format **/ - if (this.args.isVertexSelected[nodeId] == null) { - this.changeVertexFormat(nodeId, this.getFormatter().getVertexById(nodeId).getDefault()); - } -}; - -GraphCanvas.prototype.overLabel = function(nodeId) { - this.overNode(nodeId); - // this.svgLabels[nodeId].setAttribute("cursor", "pointer"); -}; - -GraphCanvas.prototype.outLabel = function(nodeId) { - this.outNode(nodeId); - // this.svgLabels[nodeId].setAttribute("cursor", ""); -}; - -GraphCanvas.prototype.clickNode = function(nodeId) { - if (!this.args.interactive) { - return; - } - - /** si el evento se dispara oprque estaba dragging entonces no activo nada **/ - if (this.args.isVertexSelected[nodeId] == null) { - this.selectNode(nodeId); - } else { - this.deselectNode(nodeId); - } -}; - -GraphCanvas.prototype.selectNode = function(nodeId) { - for ( var i = 0; i < this.args.selectedVertices.length; i++) { - var format = this.getFormatter().getVertexById(nodeId).getSelected(); - format.opacity = 0.2; - this.changeVertexFormat(nodeId, this.getFormatter().getVertexById(nodeId).getSelected()); - } - - if (this.args.isVertexSelected[nodeId] == null) { - var format = this.getFormatter().getVertexById(nodeId).getSelected(); - format.opacity = 1; - this.changeVertexFormat(nodeId, this.getFormatter().getVertexById(nodeId).getSelected()); - this.args.selectedVertices.push(nodeId); - this.args.isVertexSelected[nodeId] = this.args.selectedVertices.length - 1; - this.onVertexSelect.notify(nodeId); - } -}; - -GraphCanvas.prototype.selectAllEdges = function() { - this.deselectNodes(); - this.deselectEdges(); - - for ( var edgesId in this.getDataset().edges) { - this.selectEdge(edgesId); - } -}; - -GraphCanvas.prototype.selectAllNodes = function() { - this.deselectNodes(); - this.deselectEdges(); - - for ( var vertexId in this.getDataset().vertices) { - this.selectNode(vertexId); - } -}; - -GraphCanvas.prototype.selectAll = function() { - this.deselectNodes(); - this.deselectEdges(); - - for ( var vertexId in this.getDataset().vertices) { - this.selectNode(vertexId); - } - - for ( var edgesId in this.getDataset().edges) { - this.selectEdge(edgesId); - } -}; - -GraphCanvas.prototype.selectEdge = function(edgeId) { - if (this.args.isEdgeSelected[edgeId] == null) { - this.changeEdgeFormat(edgeId, this.getFormatter().getEdgeById(edgeId).getSelected()); - //this.args.selectedEdges.push(edgeId); - this.args.isEdgeSelected[edgeId] = true; //this.args.selectedEdges.length - 1; - this.onEdgeSelect.notify(edgeId); - } -}; - -GraphCanvas.prototype.selectEdges = function(edges) { - - for ( var i = 0; i < edges.length; i++) { - this.selectEdge(edges[i]); - } -}; - -GraphCanvas.prototype.deselectNode = function(nodeId) { - if (this.args.isVertexSelected[nodeId] != null) { - this.changeVertexFormat(nodeId, this.getFormatter().getVertexById(nodeId).getDefault()); - this.args.selectedVertices.splice(this.args.isVertexSelected[nodeId], 1); - var index = this.args.isVertexSelected[nodeId]; - delete this.args.isVertexSelected[nodeId]; - - for ( var vertex in this.args.isVertexSelected) { - if (this.args.isVertexSelected[vertex] > index) { - this.args.isVertexSelected[vertex] = this.args.isVertexSelected[vertex] - 1; - } - } - } -}; - -GraphCanvas.prototype.deselectNodes = function() { - var selected = JSON.parse(JSON.stringify(this.getSelectedVertices())); - for ( var i = 0; i < selected.length; i++) { - this.deselectNode(selected[i]); - } -}; -GraphCanvas.prototype.selectNodes = function(idNodes) { - - for ( var i = 0; i < idNodes.length; i++) { - this.selectNode(idNodes[i]); - } - - // for ( var vertex in this.args.isVertexSelected) { - // if (this.args.isVertexSelected[vertex] > index){ - // this.args.isVertexSelected[vertex] = this.args.isVertexSelected[vertex] - 1; - // } - // } - -}; - -GraphCanvas.prototype.changeVertexFormat = function(nodeId, format) { - var svgNode = DOM.select(this.getSVGNodeId(nodeId)); - if (svgNode != null) { - var properties = format.toJSON(); - for ( var item in properties) { - svgNode.setAttribute(item, properties[item]); - } - - if (this.getFormatter().getVertexById(nodeId) instanceof CircleVertexGraphFormatter) { - var transform = "translate(" + svgNode.getAttribute("dragx") + "," + svgNode.getAttribute("dragy") + "), scale(" + format.getSize() + ")"; - svgNode.setAttribute("transform", transform); - } - } -}; - -GraphCanvas.prototype.renderLabel = function(nodeId) { - var x = Math.ceil(this.getLayout().getNodeById(nodeId).x * this.getFormatter().getWidth()); - var y = Math.ceil(this.getLayout().getNodeById(nodeId).y * this.getFormatter().getHeight()); - - var svgAttributesNode = JSON.parse(JSON.stringify(this.getFormatter().getVertexById(nodeId).getDefault().toJSON().title)); - svgAttributesNode.id = this.getSVGLabelId(this.getDataset().getVertexById(nodeId).getId()); - svgAttributesNode.dx = (-1) * (this.getDataset().getVertexById(nodeId).getName().length * svgAttributesNode["font-size"]) / 4 - 4; - - svgAttributesNode.dy = parseFloat((this.getFormatter().getVertexById(nodeId).getDefault().getSize())) - + parseFloat(svgAttributesNode["font-size"]) + parseFloat(this.getFormatter().getVertexById(nodeId).getDefault().getStrokeWidth()) - 4; - svgAttributesNode.dragx = Math.ceil(this.getLayout().getNodeById(nodeId).x * this.getFormatter().getWidth()); - - var gragy = parseFloat(this.getFormatter().getVertexById(nodeId).getDefault().getSize()) - + Math.ceil(this.getLayout().getNodeById(nodeId).y * this.getFormatter().getHeight()); - svgAttributesNode.dragy = gragy; - svgAttributesNode.transform = "translate(" + svgAttributesNode.dragx + "," + svgAttributesNode.dragy + ")";//, scale("+this.formatter.getVertexById(nodeId).getDefault().getSize()+")"; - - var nodeSVG = SVG.drawText(0, 0, this.getDataset().getVertexById(nodeId).getName(), this.GraphLabelGroup, svgAttributesNode); - - this.svgLabels[nodeId] = nodeSVG; - - /** Events for the SVG node **/ - var _this = this; - if (nodeSVG != null) { - nodeSVG.addEventListener("mouseover", function() { - _this.overLabel(nodeId); - }, false); - nodeSVG.addEventListener("mouseout", function() { - _this.outLabel(nodeId); - }, false); - } - -}; - -GraphCanvas.prototype.removeLabel = function(labelId) { - if (DOM.select(this.getSVGLabelId(labelId)) != null) { - DOM.select(this.getSVGLabelId(labelId)).parentNode.removeChild(DOM.select(this.getSVGLabelId(labelId))); - } -}; - -GraphCanvas.prototype.renderNode = function(nodeId) { - var svgAttributesNode = JSON.parse(JSON.stringify(this.getFormatter().getVertexById(nodeId).getDefault().toJSON())); - svgAttributesNode.dragx = Math.ceil(this.getLayout().getNodeById(nodeId).x * this.getFormatter().getWidth()); - svgAttributesNode.dragy = Math.ceil(this.getLayout().getNodeById(nodeId).y * this.getFormatter().getHeight()); - svgAttributesNode.transform = "translate(" + svgAttributesNode.dragx + "," + svgAttributesNode.dragy + ")"; - svgAttributesNode.id = this.getSVGNodeId(nodeId); - /*svgAttributesNode["stroke-width"] = 3 ; - svgAttributesNode["stroke-opacity"] = 1 ; - svgAttributesNode["fill-opacity"] = svgAttributesNode["opacity"] ; - svgAttributesNode["opacity"] = 1 ;*/ - this.circleDefaultRadius = this.getFormatter().getVertexById(nodeId).getDefault().getSize(); - var nodeSVG; - - if (this.getFormatter().getVertexById(nodeId) instanceof CircleVertexGraphFormatter) { - nodeSVG = SVG.drawCircle(0, 0, this.circleDefaultRadius, this.GraphNodeGroup, svgAttributesNode); - } - - if (this.getFormatter().getVertexById(nodeId) instanceof SquareVertexGraphFormatter) { - //nodeSVG = SVG.drawRectangle(0 - (this.circleDefaultRadius) ,0 - (this.formatter.getVertexById(nodeId).getDefault().getSize()) , (this.getFormatter().getVertexById(nodeId).getDefault().getSize()*2), (this.getFormatter().getVertexById(nodeId).getDefault().getSize()*2), this.GraphNodeGroup, svgAttributesNode); - nodeSVG = SVG.drawRectangle(0 - (this.circleDefaultRadius), 0 - (this.circleDefaultRadius), (this.circleDefaultRadius * 2), - (this.circleDefaultRadius * 2), this.GraphNodeGroup, svgAttributesNode); - } - - if (this.getFormatter().getVertexById(nodeId) instanceof EllipseVertexGraphFormatter) { - nodeSVG = SVG.drawEllipse(0, 0, this.circleDefaultRadius * 1.5, this.circleDefaultRadius, this.GraphNodeGroup, svgAttributesNode); - } - - if (this.getFormatter().getVertexById(nodeId) instanceof RectangleVertexGraphFormatter) { - //nodeSVG = SVG.drawRectangle(0 - (this.circleDefaultRadius) ,0 - ((this.circleDefaultRadius*2)/2) , (this.circleDefaultRadius*2), (this.circleDefaultRadius), this.GraphNodeGroup, svgAttributesNode); - nodeSVG = SVG.drawRectangle(0 - (this.circleDefaultRadius * 1.5), 0 - (this.circleDefaultRadius), (this.circleDefaultRadius * 2 * 1.5), - (this.circleDefaultRadius * 2), this.GraphNodeGroup, svgAttributesNode); - - } - - if (this.getFormatter().getVertexById(nodeId) instanceof RoundedVertexGraphFormatter) { - svgAttributesNode.ry = 2;// this.formatter.getVertexById(nodeId).getDefault().getSize()/4; - svgAttributesNode.rx = 2;// this.formatter.getVertexById(nodeId).getDefault().getSize()/4; - nodeSVG = SVG.drawRectangle(0 - (this.circleDefaultRadius * 1.5), 0 - (this.circleDefaultRadius), (this.circleDefaultRadius * 2 * 1.5), - (this.circleDefaultRadius * 2), this.GraphNodeGroup, svgAttributesNode); - } - - // - - if (this.getFormatter().getVertexById(nodeId) instanceof OctagonVertexGraphFormatter) { - svgAttributesNode.ry = 2; - svgAttributesNode.rx = 2; - nodeSVG = SVG.drawRectangle(0 - (this.circleDefaultRadius * 1.5), 0 - (this.circleDefaultRadius), (this.circleDefaultRadius * 2 * 1.5), - (this.circleDefaultRadius * 2), this.GraphNodeGroup, svgAttributesNode); - } - - nodeSVG.internalId = nodeId; - // - var _this = this; - - /** Events for the SVG node **/ - if (nodeSVG != null) { - nodeSVG.addEventListener("mouseover", function() { - _this.onVertexOver.notify(nodeId); - _this.overNode(nodeId); - }, false); - nodeSVG.addEventListener("mouseout", function() { - _this.onVertexOut.notify(nodeId); - _this.outNode(nodeId); - }, false); - //nodeSVG.addEventListener("click", function(){_this.clickNode(nodeId);}, false); - // - nodeSVG.addEventListener("mouseup", function() { - _this.onVertexUp.notify(nodeId); - }, false); - } -}; - -GraphCanvas.prototype.removeNode = function(nodeId) { - DOM.select(this.getSVGNodeId(nodeId)).parentNode.removeChild(DOM.select(this.getSVGNodeId(nodeId))); - if (this.args.labeled) { - this.removeLabel(nodeId); - } -}; - -/** REMOVING **/ -GraphCanvas.prototype.removeSelected = function() { - /** El orden importa **/ - this.removeSelectedEdges(); - this.removeSelectedNode(); - -}; - -GraphCanvas.prototype.removeSelectedNode = function() { - var selected = JSON.parse(JSON.stringify(this.getSelectedVertices())); - this.deselectNodes(); - var sorted = selected.sort(function(a, b) { - return a - b - }); - for ( var i = 0; i < sorted.length; i++) { - if (this.getDataset().getVertexById(sorted[i]) != null) { - this.getDataset().getVertexById(sorted[i]).remove(); - } - } -}; - -/** EDGES **/ -GraphCanvas.prototype.removeEdge = function(edgeId) { - if (DOM.select(this.getSVGEdgeId(edgeId)) != null) { - DOM.select(this.getSVGEdgeId(edgeId)).parentNode.removeChild(DOM.select(this.getSVGEdgeId(edgeId))); - } - - if (DOM.select(this.getSVGEdgeId(edgeId) + "_shadow") != null) { - DOM.select(this.getSVGEdgeId(edgeId) + "_shadow").parentNode.removeChild(DOM.select(this.getSVGEdgeId(edgeId) + "_shadow")); - } - - if (DOM.select(this.getSVGArrowEdgeId(edgeId)) != null) { - DOM.select(this.getSVGArrowEdgeId(edgeId)).parentNode.removeChild(DOM.select(this.getSVGArrowEdgeId(edgeId))); - } -}; - -GraphCanvas.prototype.overEdge = function(edgeId) { - if ((!this.args.interactive) || this.dragging || this.selecting) { - return; - } - - /** If selected we don't change the format **/ - if (this.args.isEdgeSelected[edgeId] == null) { - var format = this.getFormatter().getEdgeById(edgeId).getOver(); - format.args["cursor"] = "pointer"; - this.changeEdgeFormat(edgeId, format); - } -}; - -GraphCanvas.prototype.outEdge = function(edgeId) { - if (!this.args.interactive) { - return; - } - - /** If selected we don't change the format **/ - if (this.args.isEdgeSelected[edgeId] == null) { - this.changeEdgeFormat(edgeId, this.getFormatter().getEdgeById(edgeId).getDefault()); - } -}; - -GraphCanvas.prototype.changeEdgeFormat = function(edgeId, format) { - var svgEdge = DOM.select(this.getSVGEdgeId(edgeId) + "_shadow"); - if (svgEdge != null) { - var properties = format.toJSON(); - for ( var item in properties) { - svgEdge.setAttribute(item, properties[item]); - } - } -}; - -GraphCanvas.prototype.deselectEdge = function(edgeID) { - if (this.args.isEdgeSelected[edgeID] != null) { - this.changeEdgeFormat(edgeID, this.getFormatter().getEdgeById(edgeID).getDefault()); - var index = this.args.isEdgeSelected[edgeID]; - delete this.args.isEdgeSelected[edgeID]; - } -}; - -GraphCanvas.prototype.deselectEdges = function() { - var selected = JSON.parse(JSON.stringify(this.getSelectedEdges())); - for ( var i = 0; i < selected.length; i++) { - this.deselectEdge(selected[i]); - } -}; - -GraphCanvas.prototype.removeSelectedEdges = function() { - var selected = JSON.parse(JSON.stringify(this.getSelectedEdges())); - this.deselectEdges(); - for ( var i = 0; i < selected.length; i++) { - if (this.getDataset().getEdgeById(selected[i]) != null) { - this.getDataset().getEdgeById(selected[i]).remove(); - } - } -}; - -GraphCanvas.prototype.renderEdge = function(edgeId) { - var svgAttributesEdge = this.getFormatter().getEdgeById(edgeId).getDefault().toJSON(); - var edge = this.getDataset().getEdgeById(edgeId); - - var svgNodeTarget = this.getVertexById(edge.getNodeTarget().getId()); - var svgNodeSource = this.getVertexById(edge.getNodeSource().getId()); - svgAttributesEdge.id = this.getSVGEdgeId(edge.getId()) + "_shadow"; - - var svgEdge = null; - - if (this.getFormatter().getEdgeById(edgeId) instanceof LineEdgeGraphFormatter) { - var coordenateSourceX = svgNodeSource.getAttribute("dragx"); - var coordenateSourceY = svgNodeSource.getAttribute("dragy"); - var coordenateTargetX = svgNodeTarget.getAttribute("dragx"); - var coordenateTargetY = svgNodeTarget.getAttribute("dragy"); - - SVG.drawLine(coordenateSourceX, coordenateSourceY, coordenateTargetX, coordenateTargetY, this.GraphEdgeGroup, svgAttributesEdge); - var attributesShadow = {}; - attributesShadow.id = this.getSVGEdgeId(edge.getId()); - attributesShadow["stroke-opacity"] = 0; - attributesShadow["stroke-width"] = 4; - attributesShadow["stroke"] = "black"; - svgEdge = SVG.drawLine(svgNodeSource.getAttribute("dragx"), svgNodeSource.getAttribute("dragy"), svgNodeTarget.getAttribute("dragx"), - svgNodeTarget.getAttribute("dragy"), this.GraphEdgeGroup, attributesShadow); - } - - if (this.getFormatter().getEdgeById(edgeId) instanceof BezierEdgeGraphFormatter) { - var nodeId = edge.getNodeTarget().getId(); - var nodeSize = this.formatter.getVertexById(nodeId).getDefault().getSize() * this.getFormatter().getNodesMaxSize(); - svgAttributesEdge.fill = "none"; - svgAttributesEdge.id = this.getSVGEdgeId(edgeId); - var d = this.calculateCoordenatesBezier(nodeSize, svgNodeSource.getAttribute("dragx"), svgNodeSource.getAttribute("dragy")); - svgEdge = SVG.drawPath(d, this.GraphEdgeGroup, svgAttributesEdge); - } - ; - - if ((this.getFormatter().getEdgeById(edgeId) instanceof DirectedLineEdgeGraphFormatter) - || (this.getFormatter().getEdgeById(edgeId) instanceof CutDirectedLineEdgeGraphFormatter) - || (this.getFormatter().getEdgeById(edgeId) instanceof DotDirectedLineEdgeGraphFormatter) - || (this.getFormatter().getEdgeById(edgeId) instanceof OdotDirectedLineEdgeGraphFormatter) - || (this.getFormatter().getEdgeById(edgeId) instanceof OdirectedLineEdgeGraphFormatter)) { - var coordenateSourceX = svgNodeSource.getAttribute("dragx"); - var coordenateSourceY = svgNodeSource.getAttribute("dragy"); - var coordenateTargetX = svgNodeTarget.getAttribute("dragx"); - var coordenateTargetY = svgNodeTarget.getAttribute("dragy"); - - var offset = parseFloat(this.getFormatter().getVertexById(this.getDataset().getEdgeById(edgeId).getNodeTarget().getId()).getDefault() - .getSize() - * this.circleDefaultRadius); - var point = this._calculateEdgePointerPosition(coordenateSourceX, coordenateSourceY, coordenateTargetX, coordenateTargetY, offset); - coordenateTargetX = point.x; - coordenateTargetY = point.y; - SVG.drawLine(coordenateSourceX, coordenateSourceY, coordenateTargetX, coordenateTargetY, this.GraphEdgeGroup, svgAttributesEdge); - - var attributesShadow = {}; - attributesShadow.id = this.getSVGEdgeId(edge.getId()); - attributesShadow["stroke-opacity"] = 0; - attributesShadow["stroke-width"] = 4; - attributesShadow["stroke"] = "black"; - svgEdge = SVG.drawLine(coordenateSourceX, coordenateSourceY, coordenateTargetX, coordenateTargetY, this.GraphEdgeGroup, attributesShadow); - } - - if (this.getFormatter().getEdgeById(edgeId) instanceof DirectedLineEdgeGraphFormatter - || (this.getFormatter().getEdgeById(edgeId) instanceof OdirectedLineEdgeGraphFormatter)) { - var coordenateSourceX = svgNodeSource.getAttribute("dragx"); - var coordenateSourceY = svgNodeSource.getAttribute("dragy"); - var coordenateTargetX = svgNodeTarget.getAttribute("dragx"); - var coordenateTargetY = svgNodeTarget.getAttribute("dragy"); - - var point = this._calculateEdgePointerPosition(coordenateSourceX, coordenateSourceY, coordenateTargetX, coordenateTargetY, offset); - coordenateTargetX = point.x; - coordenateTargetY = point.y; - - var angle = Geometry.toDegree(point.angle) + 90; - this.arrowDefaultSize = this.getFormatter().getEdgeById(edgeId).getArrowSize(); //getDefault().getArrowSize(); - var d = "-" + this.arrowDefaultSize + ",0 0,-" + parseFloat(this.arrowDefaultSize) * 2 + " " + this.arrowDefaultSize + ",0"; - - var attributes; - - if (this.getFormatter().getEdgeById(edgeId) instanceof DirectedLineEdgeGraphFormatter) { - attributes = [ - [ "fill", this.getFormatter().getEdgeById(edgeId).getDefault().getStroke() ], - [ "stroke", this.getFormatter().getEdgeById(edgeId).getDefault().getStroke() ], [ "id", this.getSVGArrowEdgeId(edgeId) ] ]; - } else { - attributes = [ - [ "fill", "#FFFFFF" ], [ "stroke", this.getFormatter().getEdgeById(edgeId).getDefault().getStroke() ], - [ "id", this.getSVGArrowEdgeId(edgeId) ] ]; - } - - var flechaSVGNode = SVG.drawPoligon(d, this.GraphEdgeGroup, attributes);//, ["transform", "rotate("+angle+"), translate(0,0)"]]); - flechaSVGNode.setAttribute("transform", " translate(" + coordenateTargetX + ", " + coordenateTargetY + "), rotate(" + angle + ")"); - } - ; - - if (this.getFormatter().getEdgeById(edgeId) instanceof CutDirectedLineEdgeGraphFormatter) { - var coordenateSourceX = svgNodeSource.getAttribute("dragx"); - var coordenateSourceY = svgNodeSource.getAttribute("dragy"); - var coordenateTargetX = svgNodeTarget.getAttribute("dragx"); - var coordenateTargetY = svgNodeTarget.getAttribute("dragy"); - - var point = this._calculateEdgePointerPosition(coordenateSourceX, coordenateSourceY, coordenateTargetX, coordenateTargetY, offset); - coordenateTargetX = point.x; - coordenateTargetY = point.y; - - var angle = Geometry.toDegree(point.angle) + 90; - - //this.arrowDefaultSize = 2; //getDefault().getArrowSize(); - var d = "-4,0 4,0 4,-2 -4,-2"; - - var flechaSVGNode = SVG.drawPoligon(d, this.GraphEdgeGroup, [ - [ "fill", this.getFormatter().getEdgeById(edgeId).getDefault().getStroke() ], - [ "stroke", this.getFormatter().getEdgeById(edgeId).getDefault().getStroke() ], [ "id", this.getSVGArrowEdgeId(edgeId) ] ]);//, ["transform", "rotate("+angle+"), translate(0,0)"]]); - flechaSVGNode.setAttribute("transform", " translate(" + coordenateTargetX + ", " + coordenateTargetY + "), rotate(" + angle + ")"); - } - ; - - if ((this.getFormatter().getEdgeById(edgeId) instanceof DotDirectedLineEdgeGraphFormatter) - || (this.getFormatter().getEdgeById(edgeId) instanceof OdotDirectedLineEdgeGraphFormatter)) { - var coordenateSourceX = svgNodeSource.getAttribute("dragx"); - var coordenateSourceY = svgNodeSource.getAttribute("dragy"); - var coordenateTargetX = svgNodeTarget.getAttribute("dragx"); - var coordenateTargetY = svgNodeTarget.getAttribute("dragy"); - var point = this._calculateEdgePointerPosition(coordenateSourceX, coordenateSourceY, coordenateTargetX, coordenateTargetY, offset); - coordenateTargetX = point.x; - coordenateTargetY = point.y; - var angle = Geometry.toDegree(point.angle) + 90; - // this.arrowDefaultSize = this.formatter.getEdgeById(edgeId).getArrowSize(); //getDefault().getArrowSize(); - var attributes = []; - if (this.getFormatter().getEdgeById(edgeId) instanceof OdotDirectedLineEdgeGraphFormatter) { - attributes = [ - [ "fill", "#FFFFFF" ], [ "stroke", this.getFormatter().getEdgeById(edgeId).getDefault().getStroke() ], - [ "id", this.getSVGArrowEdgeId(edgeId) ] ]; - } else { - attributes = [ - [ "fill", this.getFormatter().getEdgeById(edgeId).getDefault().getStroke() ], - [ "stroke", this.getFormatter().getEdgeById(edgeId).getDefault().getStroke() ], [ "id", this.getSVGArrowEdgeId(edgeId) ] ]; - } - var flechaSVGNode = SVG.drawCircle(0, 0, 4, this.GraphEdgeGroup, attributes); - flechaSVGNode.setAttribute("transform", " translate(" + coordenateTargetX + ", " + coordenateTargetY + "), rotate(" + angle + ")"); - } - ; - - var _this = this; - /** Events for the SVG edge **/ - if (svgEdge != null) { - if (this.getDataset().getEdgesCount() < this.args.maxNumberEdgesFiringEvents) { - svgEdge.addEventListener("mouseover", function() { - _this.overEdge(edgeId); - }, false); - svgEdge.addEventListener("mouseout", function() { - _this.outEdge(edgeId); - }, false); - } - } -}; - -GraphCanvas.prototype._calculateEdgePointerPosition = function(sourceX, sourceY, targetX, targetY, radius) { - var angle = Geometry.getAngleBetweenTwoPoints(sourceX, sourceY, targetX, targetY); - - /** Suponiendo el node source que este a la derecha **/ - if ((targetX - sourceX) < 0) { - var b = Geometry.getAdjacentSideOfRectangleRight(angle, radius); - targetX = parseFloat(targetX) + parseFloat(b); - arrowX = parseFloat(targetX) + parseFloat(b) + this.arrowDefaultSize / 2; - } else { - var b = Geometry.getAdjacentSideOfRectangleRight(angle, radius); - targetX = parseFloat(targetX) - parseFloat(b); - arrowX = parseFloat(targetX) - parseFloat(b) - this.arrowDefaultSize / 2; - } - - /** Suponiendo el node source que este a la arriba **/ - if ((targetY - sourceY) > 0) { - var a = Geometry.getOppositeSideOfRectangleRight(angle, radius); - targetY = parseFloat(targetY) - parseFloat(a); - arrowY = parseFloat(targetY) - parseFloat(a) - this.arrowDefaultSize / 2; - } else { - var a = Geometry.getOppositeSideOfRectangleRight(angle, radius); - targetY = parseFloat(targetY) + parseFloat(a); - arrowY = parseFloat(targetY) + parseFloat(a) - this.arrowDefaultSize / 2; - - } - - return { - "x" : arrowX, - "y" : arrowY, - "angle" : angle - }; -}; - -GraphCanvas.prototype.calculateCoordenatesBezier = function(nodeSize, x1, y1) { - var x11 = x1 - (nodeSize / 2); - var y11 = y1 - (nodeSize / 2); - - var x12 = parseFloat(x1) + parseFloat(nodeSize / 2); - var y12 = y1 - (nodeSize / 2); - - var curvePointX = (x12 - x11) / 2 + x11; - var curvePointY = y1 - (nodeSize * 2); - var d = "M" + x11 + "," + y11 + " T" + curvePointX + "," + curvePointY + " " + x12 + "," + y12; - return d; - -}; - -GraphCanvas.prototype.renderEdges = function() { - for ( var edge in this.getDataset().getEdges()) { - this.renderEdge(this.getDataset().getEdgeById(edge).getId()); - - } -}; - -GraphCanvas.prototype.getLastSelectedNode = function() { - var node = null; - if (this.getSelectedVertices().length > 0) { - var nodeId = this.getSelectedVertices()[this.getSelectedVertices().length - 1]; - node = this.getDataset().getVertexById(nodeId); - } - return node; -}; -/* - GraphCanvas.prototype.getNodeByNameAndIndex = function(node, index){ - var nodeId = this.getDataset().verticesIndex[node][index]; - var nodeItem = this.getDataset().getVertexById(nodeId); - return nodeItem; - }; - */ - -GraphCanvas.prototype.setDataset = function(dataset) { - this.dataset = dataset; -}; - -GraphCanvas.prototype.setFormatter = function(formatter) { - this.formatter = formatter; -}; - -GraphCanvas.prototype.setLayout = function(layout) { - this.layout = layout; -}; - -/** API **/ -GraphCanvas.prototype.getDataset = function() { - return this.dataset; -}; - -GraphCanvas.prototype.getFormatter = function() { - return this.formatter; -}; - -GraphCanvas.prototype.getLayout = function() { - return this.layout; -}; - -/** API DATASET **/ -GraphCanvas.prototype.addVertex = function(name, args) { - this.getDataset().addNode(name, args); -}; - -GraphCanvas.prototype.removeVertex = function(vertexId) { - this.getDataset().getVertexById(vertexId).remove(); -}; - -GraphCanvas.prototype.addEdge = function(edgeName, nodeSourceId, nodeTargetId, args) { - this.getDataset().addEdge(edgeName, nodeSourceId, nodeTargetId, args); -}; -/* - GraphCanvas.prototype.removeEdge = function(edgeId){ - this.getDataset().getEdgeById(edgeId).remove(); - }; - */ - -/** API FORMATTER **/ -GraphCanvas.prototype.getWidth = function() { - return this.getFormatter().getWidth(); -}; - -GraphCanvas.prototype.getHeight = function() { - return this.getFormatter().getHeight(); -}; - -GraphCanvas.prototype.getBackgroundImage = function() { - return this.getFormatter().getBackgroundImage(); -}; - -//GraphCanvas.prototype.setBackgroundImage = function(value){ -// this.getFormatter().setBackgroundImage(value); -//}; - -GraphCanvas.prototype.getBackgroundColor = function() { - return this.getFormatter().getBackgroundColor(); -}; - -GraphCanvas.prototype.setBackgroundColor = function() { - this.getFormatter().setBackgroundColor(value); -}; - -//GraphCanvas.prototype.setEdgeFill = function(edgeId, value){ -// this.getFormatter().getEdgeById(edgeId).getDefault().setFill(value); -//}; -// -//GraphCanvas.prototype.getEdgeFill = function(edgeId){ -// return this.getFormatter().getEdgeById(edgeId).getDefault().getFill(); -//}; - -/** VERTICES FORMATTER **/ -GraphCanvas.prototype.setVertexSize = function(vertexId, value) { - this.getFormatter().getVertexById(vertexId).getDefault().setSize(value); -}; - -GraphCanvas.prototype.getVertexSize = function(vertexId) { - return this.getFormatter().getVertexById(vertexId).getDefault().getSize(); -}; - -GraphCanvas.prototype.setVertexStroke = function(vertexId, value) { - this.getFormatter().getVertexById(vertexId).getDefault().setStroke(value); -}; - -GraphCanvas.prototype.getVertexStroke = function(vertexId) { - return this.getFormatter().getVertexById(vertexId).getDefault().getStroke(); -}; - -GraphCanvas.prototype.setVertexStrokeOpacity = function(vertexId, value) { - this.getFormatter().getVertexById(vertexId).getDefault().setStrokeOpacity(value); -}; - -GraphCanvas.prototype.getVertexStrokeOpacity = function(vertexId) { - return this.getFormatter().getVertexById(vertexId).getDefault().getStrokeOpacity(); -}; - -GraphCanvas.prototype.setVertexOpacity = function(vertexId, value) { - this.getFormatter().getVertexById(vertexId).getDefault().setOpacity(value); -}; - -GraphCanvas.prototype.getVertexOpacity = function(vertexId) { - return this.getFormatter().getVertexById(vertexId).getDefault().getOpacity(); -}; - -GraphCanvas.prototype.setVertexFill = function(vertexId, color) { - this.getFormatter().getVertexById(vertexId).getDefault().setFill(color); -}; - -GraphCanvas.prototype.getVertexFill = function(vertexId) { - return this.getFormatter().getVertexById(vertexId).getDefault().getFill(); -}; - -/** EDGES FORMATTER **/ -GraphCanvas.prototype.setEdgeSize = function(edgeId, value) { - this.getFormatter().getEdgeById(edgeId).getDefault().setSize(value); -}; - -GraphCanvas.prototype.getEdgeSize = function(edgeId) { - return this.getFormatter().getEdgeById(edgeId).getDefault().getSize(); -}; - -GraphCanvas.prototype.setEdgeStroke = function(edgeId, value) { - this.getFormatter().getEdgeById(edgeId).getDefault().setStroke(value); -}; - -GraphCanvas.prototype.getEdgeStroke = function(edgeId) { - return this.getFormatter().getEdgeById(edgeId).getDefault().getStroke(); -}; - -GraphCanvas.prototype.setEdgeStrokeOpacity = function(edgeId, value) { - this.getFormatter().getEdgeById(edgeId).getDefault().setStrokeOpacity(value); -}; - -GraphCanvas.prototype.getEdgeStrokeOpacity = function(edgeId) { - return this.getFormatter().getEdgeById(edgeId).getDefault().getStrokeOpacity(); -}; - -GraphCanvas.prototype.setEdgeFill = function(edgeId, color) { - this.getFormatter().getEdgeById(edgeId).getDefault().setFill(color); -}; - -GraphCanvas.prototype.getEdgeFill = function(edgeId) { - return this.getFormatter().getEdgeById(edgeId).getDefault().getFill(); -}; - -/** API LAYOUT **/ -GraphCanvas.prototype.setCoordinates = function(vertexId, x, y) { - return this.getLayout().getEdgeById(vertexId).setCoordinates(x, y); -}; - - - -function HPLCGraph(args) { - this.width = 600; - this.height = 600; - this.title = ''; - this.bbar = false; - this.plotInnerPanelPadding = 10; - this.plotPanelPadding = 5; - this.id = BUI.id(); - - this.hidePlots = null; - this.xlabel = ""; - this.scaled = false; - this.xParam = null; - this.showRangeSelector = true; - this.interactionModel = null; - - /** for each stat the max and minimum value when it is scaled in order to show correctly in the legend **/ - this.ranges = {}; - if (args != null) { - if (args.interactionModel != null) { - this.interactionModel = args.interactionModel; - } - if (args.width != null) { - this.width = args.width; - } - if (args.height != null) { - this.height = args.height; - } - if (args.bbar != null) { - this.bbar = args.bbar; - } - if (args.title != null) { - this.title = args.title; - } - if (args.plots != null) { - this.plots = args.plots; - } - - if (args.scaled != null) { - this.scaled = args.scaled; - } - if (args.xlabel != null) { - this.xlabel = args.xlabel; - } - if (args.xParam != null) { - this.xParam = args.xParam; - } - if (args.showRangeSelector != null) { - this.showRangeSelector = args.showRangeSelector; - } - } - - this.onZoomX = new Event(this); - this.onResetZoom = new Event(this); - this.dblclick = new Event(this); -} - -HPLCGraph.prototype.getMenu = function () { - var _this = this; - /** Actions buttons **/ - var actions = []; - - function toggle(item, pressed) { - if (pressed) { - _this.plots[item.param] = true; - } else { - delete _this.plots[item.param]; - } - _this.reloadData(this.hplcData); - } - - for (var i = 0; i < this.hplcData.length; i++) { - if (this.hplcData[i].showOnMenu != false) { - var param = this.hplcData[i].param; - var style = "style='padding:0 0px 0 5px;'"; - actions.push({ - text : "
" + BUI.getRectangleColorDIV(this.hplcData[i].color, 10, 10) + " " + this.hplcData[i].label + "
", - id : _this.id + param, - param : param, - enableToggle : true, - scope : this, - toggleHandler : toggle, - pressed : (_this.plots[param] != null) - }); - } - } - actions.push("-"); - - actions.push({ - text : "Scale", - enableToggle : true, - scope : this, - pressed : this.scaled, - icon : '../images/icon_graph.png', - toggleHandler : function (item, pressed) { - _this.scaled = pressed; - _this.reloadData(this.hplcData); - } - }); - - actions.push("->"); - actions.push({ - text : "Save", - scope : this, - icon : '../images/save.gif', - handler : function (item, pressed) { - var largeImage = document.createElement("img"); - largeImage.style.display = 'block'; - largeImage.style.width = 200 + "px"; - largeImage.style.height = 200 + "px"; - largeImage.setAttribute('src', Dygraph.Export.asCanvas(this.dygraphObject.dygraph).toDataURL()); - window.open(Dygraph.Export.asCanvas(this.dygraphObject.dygraph).toDataURL(), 'Image', ''); - } - }); - - return actions; -}; - -/** Looks for the maximum value and then divide everything but that value **/ -HPLCGraph.prototype.scaledData = function (data) { - for (var i = 0; i < data.length; i++) { - var values = this.getMaxAndMinValue(data[i]); - data[i] = this.divideValuesByMax(data[i], values.max); - this.ranges[data[i].label] = values; - } - return data; -}; - - -/** Given a stat float[] and a max number it will divide each value by max **/ -HPLCGraph.prototype.divideValuesByMax = function (stat, max) { - for (var j = 0; j < stat.data.length; j++) { - if (max != 0) { - stat.data[j] = Number(stat.data[j]) / max; - stat.std[j] = Number(stat.std[j]) / max; - } - } - return stat; -}; - -/** returns max value of a stat **/ -HPLCGraph.prototype.getMaxAndMinValue = function (stat) { - var max = 0; - var min = stat.data[0]; - for (var j = 0; j < stat.data.length; j++) { - if (Number(stat.data[j]) > max) { - max = Number(stat.data[j]); - } - if (Number(stat.std[j]) > max) { - max = Number(stat.std[j]); - } - if (Number(stat.data[j]) < min) { - min = Number(stat.data[j]); - } - } - return { - max : Number(max), - min : Number(min) - }; -}; - -HPLCGraph.prototype.getPoint = function (data, i) { - var point = [ 10, 10, 10 ]; - var y = parseFloat(data.data[i]); - var error = parseFloat(data.std[i]); - if (data.fdata == null) { - return [ y - error, y, y + error ]; - } else { - if (data.fstd != null) { - return [ data.fstd(y - error), data.fdata(y), data.fstd(y + error) ]; - } - return [ data.fdata(y) - error, data.fdata(y), data.fdata(y) + error ]; - } - return point; -}; - -HPLCGraph.prototype.reloadData = function(hplcData) { - this.panel.setLoading(false); - this.hplcData = hplcData; - - var data = hplcData; - - - /** In case of having peaks **/ - if (this.peaks != null) { - for (var peak in this.peaks) { - var values = []; - var std = []; - for (var i = 0; i < this.peaks[peak].length; i++) { - values.push(this.peaks[peak][i][1]); - std.push(this.peaks[peak][i][2]); - } - data.push({ - param : peak, - data : values, - showOnMenu : false, - fdata : function (a) { - var value = (Math.log(parseFloat(a))); - if (isNumber(value)) { - return value; - } - }, - fstd : function (a) { - var value = Math.log(Math.abs(parseFloat(a))); - if (isNumber(value)) - return value; - }, - std : std, - color : this.colorPeak[peak], - label : peak - }); - - } - } - - - if (this.scaled) { - data = this.scaledData(JSON.parse(JSON.stringify(hplcData))); - } - - var paramIndex = {}; - var parsed = []; - var j = 0; - for (var i = 0; i < data[0].data.length - 1; i++) { - var aux = []; - for (j = 0; j < data.length; j++) { - if (this.plots != null) { - if (this.plots[data[j].param] != null) { - aux.push(this.getPoint(data[j], i)); - paramIndex[data[j].param] = aux.length - 1; - } - } else { - aux.push([ data[j].data[i] - data[j].std[i], data[j].data[i], data[j].data[i] + data[j].std[i] ]); - } - } - parsed.push([]); - - var index = i; - if (this.xParam != null) { - index = parseFloat(data[this.xParam].data[i]); - } - - parsed[parsed.length - 1].push(index); - - for (j = 0; j < data.length; j++) { - if (this.plots != null) { - if (this.plots[data[j].param] != null) { - parsed[parsed.length - 1].push(aux[paramIndex[data[j].param]]); - } - } else { - parsed[parsed.length - 1].push(aux[j]); - } - } - } - - var colors = []; - var labels = [ "" ]; - for (j = 0; j < data.length; j++) { - if (this.plots != null) { - if (this.plots[data[j].param] != null) { - colors.push(data[j].color); - labels.push(data[j].label); - } - } else { - parsed[parsed.length - 1].push(aux[j]); - } - } - - this._renderDygraph(parsed, colors, labels); -}; - -HPLCGraph.prototype._renderDygraph = function (parsed, colors, labels) { - this.dygraphObject = new StdDevDyGraph(this.id, { - width : this.width, - height : this.height - 10, - xlabel : this.xlabel, - showRangeSelector : this.showRangeSelector, - interactionModel : this.interactionModel, - scaled : this.scaled, - ranges : this.ranges - }); - this.dygraphObject.draw(parsed, colors, labels); - - var _this = this; - this.dygraphObject.onZoomX.attach(function (sender, args) { - try { - _this.onZoomX.notify(args); - } catch (e) { - } - }); - - this.dygraphObject.onResetZoom.attach(function (sender, args) { - try { - _this.onResetZoom.notify(args); - } catch (e) { - } - }); - - this.dygraphObject.dblclick.attach(function (sender, args) { - try { - _this.dblclick.notify(args); - } catch (e) { - } - }); - -}; - -HPLCGraph.prototype.loadData = function (data) { - var _this = this; - this.reloadData(data); - this.panel.addDocked({ - xtype : 'toolbar', - items : this.getMenu() - }); - - - if (this.bbar == true){ - this.panel.addDocked({ - xtype : 'toolbar', - dock: 'bottom', - items : [ - { - xtype: 'numberfield', - id: 'main_field_start', - fieldLabel: 'Range from', - width: 170, - labelWidth : 70, - value: 0, - minValue: 0 - }, - { - xtype: 'numberfield', - id: 'main_field_end', - fieldLabel: 'to', - width: 130, - labelWidth : 30, - value: 0, - minValue: 0 - }, - { - xtype: 'button', - text: 'Go', - handler: function () { - var start = parseFloat(Ext.getCmp("main_field_start").getValue()); - var end = parseFloat(Ext.getCmp("main_field_end").getValue()); - - if (start < 0) { - start = 0; - } - if (end < 0) { - end = 0; - } - if (start > end) { - var aux = end; - end = start; - start = aux; - } - - _this.dygraphObject.dygraph.updateOptions({ isZoomedIgnoreProgrammaticZoom: true, dateWindow: [start, end] }); - } - } - ] - }); - } -}; - -HPLCGraph.prototype.getPanel = function () { - this.panel = Ext.create('Ext.panel.Panel', { - padding : this.plotPanelPadding, - width : this.width + 4 * this.plotInnerPanelPadding, - height : this.height + 4 * this.plotInnerPanelPadding, - items : [ { - html : "", - id : this.id, - width : this.width, - height : this.height - } ] - }); - - return this.panel; -}; - -HPLCGraph.prototype.input = function () { - return DATADOC.getHPLCData(); -}; - -HPLCGraph.prototype.getDataByFrameNumber = function (frameNumber) { - var data = {}; - data.frameNumber = frameNumber; - for (var key in this.hplcData){ - data[this.hplcData[key].label] = this.hplcData[key].data[frameNumber]; - } - console.log(data); - return data; -}; - - -HPLCGraph.prototype.test = function (targetId) { - var mainPlotPanel = new HPLCGraph({ - title : 'I0', - width : 800, - height : 400, - plots : { - "I0" : true, - "Rg" : true, - "Mass" : true - }, - xlabel : "HPLC Frames", - scaled : this.scaled, - interactionModel : { - 'dblclick' : function (event, g, context) { - } - } - }); - mainPlotPanel.getPanel().render(targetId); - mainPlotPanel.loadData(mainPlotPanel.input()); - -}; - - -function MergesHPLCGraph(args) { - HPLCGraph.prototype.constructor.call(this, args); - -// this.peakColors = ["#00FB42", "#00BA31", "#007C21", "#003E10"]; - this.peakColors = ["#DEBD00", "#6D9100", "#872900", "#0092CC"]; -} - - -MergesHPLCGraph.prototype.scaledData = HPLCGraph.prototype.scaledData; -MergesHPLCGraph.prototype.divideValuesByMax = HPLCGraph.prototype.divideValuesByMax; -MergesHPLCGraph.prototype.getMaxAndMinValue = HPLCGraph.prototype.getMaxAndMinValue; -MergesHPLCGraph.prototype.getPoint = HPLCGraph.prototype.getPoint; -MergesHPLCGraph.prototype.reloadData = HPLCGraph.prototype.reloadData; -MergesHPLCGraph.prototype._renderDygraph = HPLCGraph.prototype._renderDygraph; -MergesHPLCGraph.prototype.loadData = HPLCGraph.prototype.loadData; -MergesHPLCGraph.prototype.getPanel = HPLCGraph.prototype.getPanel; -MergesHPLCGraph.prototype.getDataByFrameNumber = HPLCGraph.prototype.getDataByFrameNumber; - - -MergesHPLCGraph.prototype.setPeaks = function (data) { - this.peaks = data; - /** get size of peaks **/ - this.peakKeys = []; - this.colorPeak = {}; - var colorCount = 1; - for (var key in this.peaks) { - if (this.peaks.hasOwnProperty(key)) { - var color = this.peakColors[colorCount % this.peakColors.length]; - colorCount = colorCount + 1; - this.peakKeys.push(key); - this.colorPeak[key] = color; - } - } - this.peakKeys.sort(); -}; - - -MergesHPLCGraph.prototype.getMenu = function () { - var _this = this; - /** Actions buttons **/ - var actions = []; - - function toggle(item, pressed) { - if (pressed) { - _this.plots[item.param] = true; - } else { - delete _this.plots[item.param]; - } - _this.reloadData(_this.hplcData); - } - - - /** Toolbar for peaks Average **/ - if (this.peaks != null) { - var items = []; - for (var i = 0; i < this.peakKeys.length; i++) { - var color = this.colorPeak[this.peakKeys[i]]; - items.push({ - text: "Peak #" + i + " " + this.peakKeys[i].replace("- ", " to #").replace(".0", "").replace(".0", "") + "", - peakid : this.peakKeys[i], - checked: false, - checkHandler: function (sender, pressed) { - var item = new Object(); - item.param = sender.peakid; - toggle(item, pressed); - } - }); - } - - var menu = Ext.create('Ext.menu.Menu', { - id: 'mainMenu', - style: { - overflow: 'visible' - }, - items: items - }); - var tb = Ext.create('Ext.toolbar.Toolbar'); - tb.add({ - text: 'Peaks Avg.', - menu: menu - }); - actions.push(tb); - } - - - - for (var i = 0; i < this.hplcData.length; i++) { - if (this.hplcData[i].showOnMenu != false) { - var param = this.hplcData[i].param; - var style = "style='padding:0 0px 0 5px;'"; - actions.push({ - text : "
" + BUI.getRectangleColorDIV(this.hplcData[i].color, 10, 10) + " " + this.hplcData[i].label + "
", - id : _this.id + param, - param : param, - enableToggle : true, - scope : this, - margin : 5, - toggleHandler : toggle, - pressed : (_this.plots[param] != null) - }); - } - } - - actions.push("->"); - actions.push({ - text : "Save", - scope : this, - icon : '../images/save.gif', - handler : function (item, pressed) { - var largeImage = document.createElement("img"); - largeImage.style.display = 'block'; - largeImage.style.width = 200 + "px"; - largeImage.style.height = 200 + "px"; - largeImage.setAttribute('src', Dygraph.Export.asCanvas(this.dygraphObject.dygraph).toDataURL()); - window.open(Dygraph.Export.asCanvas(this.dygraphObject.dygraph).toDataURL(), 'Image', ''); - } - }); - - return actions; -}; - - -MergesHPLCGraph.prototype.input = function () { - return DATADOC.getScatteringHPLCFrameData(); -}; - -MergesHPLCGraph.prototype.test = function (targetId) { - var mainPlotPanel = new MergesHPLCGraph({ - title : 'Scattering', - width : this.plotWidth, - height : 500, - showRangeSelector : false, - xParam : 0, - xlabel : "scattering_I", - plots : { - "scattering_I" : true, - "subtracted_I" : true, - "buffer_I" : true - } - }); - mainPlotPanel.getPanel().render(targetId); - mainPlotPanel.loadData(mainPlotPanel.input()); -}; - - -function NetworkWidget(args) { - this.id = "NetworkViewer_" + Math.random().toString().replace(".", "_"); - - this.label = true; - if (args != null) { - if (args.targetId != null) { - this.targetId = args.targetId; - } - if (args.label != null) { - this.label = args.label; - } - } - - this.onVertexOver = new Event(this); - this.onVertexOut = new Event(this); -} - -NetworkWidget.prototype.draw = function(dataset, formatter, layout) { - - this.graphCanvas = new GraphCanvas(this.id, document.getElementById(this.targetId), { - "labeled" : this.label, - "multipleSelectionEnabled" : false, - "draggingCanvasEnabled" : false - }); - this.graphCanvas.draw(dataset, formatter, layout); - - var _this = this; - this.graphCanvas.onVertexOver.attach(function(sender, nodeId) { - _this.onVertexOver.notify(nodeId); - }); - - this.graphCanvas.onVertexOut.attach(function(sender, nodeId) { - _this.onVertexOut.notify(nodeId); - }); -}; - -/** SELECT VERTICES BY NAME * */ -NetworkWidget.prototype.selectVertexByName = function(vertexName) { - var vertices = this.getDataset().getVertexByName(vertexName); - if (vertices != null) { - for ( var nodeId in vertices) { - if (vertices.hasOwnProperty(nodeId)) { - var vertexId = vertices[nodeId].getId(); - this.selectVertexById(vertexId); - } - } - } -}; - -NetworkWidget.prototype.selectVerticesByName = function(verticesName) { - for ( var i = 0; i < verticesName.length; i++) { - this.selectVertexByName(verticesName[i]); - } -}; - -/** SELECT VERTICES BY ID * */ -NetworkWidget.prototype.selectVertexById = function(vertexId) { - this.graphCanvas.selectNode(vertexId); - this.blinkVertexById(vertexId); -}; - -NetworkWidget.prototype.selectVerticesById = function(verticesId) { - for ( var i = 0; i < verticesId.length; i++) { - this.selectVertexById(verticesId[i]); - } -}; - -/** VECINDARIO * */ -NetworkWidget.prototype.selectNeighbourhood = function() { - this.selectEdgesFromVertices(); - this.selectAdjacent(); -}; - -/** DESELECT * */ -NetworkWidget.prototype.deselectNodes = function() { - this.graphCanvas.deselectNodes(); -}; - -/** SELECT ALL NODES * */ -NetworkWidget.prototype.selectAllNodes = function() { - this.getGraphCanvas().selectAllNodes(); -}; - -/** SELECT EVERYTHING * */ -NetworkWidget.prototype.selectAll = function() { - this.getGraphCanvas().selectAll(); -}; - -/** SELECT ALL EDGES * */ -NetworkWidget.prototype.selectAllEdges = function() { - this.getGraphCanvas().selectAllEdges(); -}; - -/** ZOOM * */ -NetworkWidget.prototype.setScale = function(value) { - this.graphCanvas.setScale(value); -}; - -NetworkWidget.prototype.getScale = function() { - return this.graphCanvas.getScale(value); -}; - -/** SELECT ADJACENT VERTICES FROM SELECTED VERTICES * */ -NetworkWidget.prototype.selectAdjacent = function() { - var selectedVertices = this.getGraphCanvas().getSelectedVertices(); - var edges = []; - for ( var i = 0; i < selectedVertices.length; i++) { - edges = edges.concat(this.getGraphCanvas().getDataset().getVertexById(selectedVertices[i]).getEdges()); - } - var vertices = []; - for ( i = 0; i < edges.length; i++) { - vertices.push(edges[i].getNodeSource().getId()); - vertices.push(edges[i].getNodeTarget().getId()); - } - - this.selectVerticesById(vertices); -}; - -/** SELECT EDGES FROM SELECTED VERTICES * */ -NetworkWidget.prototype.selectEdgesFromVertices = function() { - var selectedVertices = this.getGraphCanvas().getSelectedVertices(); - var edges = []; - for ( var i = 0; i < selectedVertices.length; i++) { - edges = edges.concat(this.getGraphCanvas().getDataset().getVertexById(selectedVertices[i]).getEdges()); - } - var edgesId = []; - for ( i = 0; i < edges.length; i++) { - edgesId.push(edges[i].getId()); - } - this.getGraphCanvas().selectEdges(edgesId); -}; - -/** BLINKING * */ -NetworkWidget.prototype.blinkVertexById = function(vertexId) { - this.graphCanvas.blinkVertexById(vertexId); -}; - -/** COMPONENTE CONEXA DE LOS NODOS SELECCIONADOS * */ -NetworkWidget.prototype.selectConnectedComponent = function() { - var elements = this.getConnectedComponent(); - this.selectVerticesById(elements.nodes); - this.graphCanvas.selectEdges(elements.edges); -}; - -NetworkWidget.prototype.getConnectedComponent = function() { - var nodosVisitados = {}; - var aristasVisitadas = {}; - var arrNodos = []; - var arrAristas = []; - var selectedGraphNodesId = this.getGraphCanvas().getSelectedVertices(); - for ( var i = 0; i < selectedGraphNodesId.length; i++) { - this.visitNode(selectedGraphNodesId[i], nodosVisitados, aristasVisitadas, arrNodos, arrAristas); - } - return { - nodes : arrNodos, - edges : arrAristas - }; -}; - -NetworkWidget.prototype.visitNode = function(nodeId, nodosVisitados, aristasVisitadas, arrNodos, arrAristas) { - nodosVisitados[nodeId] = true; - arrNodos.push(nodeId); - var nodeEdges = this.getDataset().getVertexById(nodeId).getEdges(); - for ( var j = 0; j < nodeEdges.length; j++) { - var edge = nodeEdges[j]; - var edgeId = edge.getId(); - if (aristasVisitadas[edgeId] == null) { - aristasVisitadas[edgeId] = true; - arrAristas.push(edgeId); - var nodeTargetId = edge.getNodeTarget().getId(); - if (nodosVisitados[nodeTargetId] == null) { - this.visitNode(nodeTargetId, nodosVisitados, aristasVisitadas, arrNodos, arrAristas); - } - var nodeSourceId = edge.getNodeSource().getId(); - if (nodosVisitados[nodeSourceId] == null) { - this.visitNode(nodeSourceId, nodosVisitados, aristasVisitadas, arrNodos, arrAristas); - } - } - } -}; - -/** COLLAPSE SELECTED VERTICES * */ -NetworkWidget.prototype.collapse = function() { - var selectedVertices = this.getGraphCanvas().getSelectedVertices(); - var xMin = -Infinity; - var xMax = Infinity; - var yMin = -Infinity; - var yMax = Infinity; - - for ( var i = 0; i < selectedVertices.length; i++) { - var vertex = this.getGraphCanvas().getLayout().getNodeById(selectedVertices[i]); - if (xMin < vertex.x) { - xMin = vertex.x; - } - if (xMax > vertex.x) { - xMax = vertex.x; - } - if (yMin < vertex.y) { - yMin = vertex.y; - } - if (yMax > vertex.y) { - yMax = vertex.y; - } - } - - var centerX = xMin - xMax; - var centerY = yMin - yMax; - var radius = (xMax - xMin) / 4; - - var count = selectedVertices.length; - var verticesCoordinates = []; - - for ( i = 0; i < selectedVertices.length; i++) { - x = centerX + radius * Math.sin(i * 2 * Math.PI / count); - y = centerY + radius * Math.cos(i * 2 * Math.PI / count); - verticesCoordinates.push({ - 'x' : x, - 'y' : y - }); - } - - for ( i = 0; i < selectedVertices.length; i++) { - this.getGraphCanvas().getLayout().getNodeById(selectedVertices[i]).setCoordinates(verticesCoordinates[i].x, verticesCoordinates[i].y); - } -}; - -/** SETTER FONT SIZE * */ -NetworkWidget.prototype.setVerticesFontSize = function(value) { - for ( var nodeId in this.getDataset().getVertices()) { - if (this.getDataset().getVertices().hasOwnProperty(nodeId)) { - this.getFormatter().getVertexById(nodeId).getDefault().setFontSize(value); - } - } -}; - -/** GETTERS * */ -NetworkWidget.prototype.getFormatter = function() { - return this.getGraphCanvas().getFormatter(); -}; -NetworkWidget.prototype.getLayout = function() { - return this.getGraphCanvas().getLayout(); -}; - -NetworkWidget.prototype.getDataset = function() { - return this.getGraphCanvas().getDataset(); -}; - -NetworkWidget.prototype.getGraphCanvas = function() { - return this.graphCanvas; -}; - -RangeWhiskerGraph.prototype.cleanArray = GenericGraph.prototype.cleanArray; -RangeWhiskerGraph.prototype.plotAxes = GenericGraph.prototype.plotAxes; -RangeWhiskerGraph.prototype.plotRuler = GenericGraph.prototype.plotRuler; -RangeWhiskerGraph.prototype.drawSVGVerticalText = GenericGraph.prototype.drawSVGVerticalText; -RangeWhiskerGraph.prototype.getClassColor = GenericGraph.prototype.getClassColor; -RangeWhiskerGraph.prototype.getDimensions = GenericGraph.prototype.getDimensions; -RangeWhiskerGraph.prototype.calculate = GenericGraph.prototype.calculate; -RangeWhiskerGraph.prototype.getMedian = GenericGraph.prototype.getMedian; -RangeWhiskerGraph.prototype.pointToPixel = GenericGraph.prototype.pointToPixel; -RangeWhiskerGraph.prototype.maxValueWhisker = GenericGraph.prototype.maxValueWhisker; -RangeWhiskerGraph.prototype.refresh = GenericGraph.prototype.refresh; - -/** - * Subclass of GenericGraph - * - * @plotHorizontalByCluster - */ -function RangeWhiskerGraph(args) { - this.maxBoxWidth = 25; - - if (args == null) { - args = {}; - } - args.plotHorizontalByCluster = false; - - GenericGraph.call(this, args); - - if (args.maxBoxWidth != null) { - this.maxBoxWidth = args.maxBoxWidth; - } -} - -RangeWhiskerGraph.prototype.refresh = function(data) { - document.getElementById(this.targetId).innerHTML = ""; - this.draw(this.targetId, data); -}; - -RangeWhiskerGraph.prototype.isNumber = function(value) { - if (value == "") - return false; - - var d = parseInt(value); - if (!isNaN(d)) - return true; - else - return false; - -}; - -/** - There are several different methods for calculating quartiles.[1] This calculator uses a method described by Moore and McCabe to find quartile values. - The same method also used by The TI-83 to calculate quartile values. - With this method, the first quartile is the median of the numbers below the median, the third quartile is the median of the numbers above the median. - - http://www.miniwebtool.com/quartile-calculator/ - http://www.alcula.com/calculators/statistics/box-plot/ - - **/ -RangeWhiskerGraph.prototype.getQ1 = function(array) { - array = array.slice(0, array.length / 2); - return this.getMedian(array); -}; - -RangeWhiskerGraph.prototype.getQ3 = function(array) { - array = array.slice((array.length + 1) / 2); - return this.getMedian(array); - -}; - -RangeWhiskerGraph.prototype.getQ2 = function(array) { - return this.getMedian(array); -}; - -RangeWhiskerGraph.prototype.getBelowOutliers = function(belowLimit, array) { - var points = []; - for ( var i = 0; i < array.length; i++) { - if (array[i] <= belowLimit) { - points.push(array[i]); - } - } - return points; -}; - -RangeWhiskerGraph.prototype.getAboveOutliers = function(aboveLimit, array) { - var points = []; - for ( var i = 0; i < array.length; i++) { - if (array[i] >= aboveLimit) { - points.push(array[i]); - } - } - return points; -}; - -RangeWhiskerGraph.prototype.minValueWhisker = function(belowLimit, q1, array) { - var points = []; - - for ( var i = 0; i < array.length; i++) { - if ((array[i] < q1) && (array[i]) > belowLimit) { - points.push(array[i]); - } - } - - if (points.length > 0) { - points.sort(function(a, b) { - return a - b; - }); - return points[0]; - } - return null; -}; - -//RangeWhiskerGraph.prototype.maxValueWhisker = function(aboveLimit, q3, array){ -// var points = []; -// for (var i = 0; i < array.length; i++){ -// if ((array[i] > q3) && (array[i]) <= aboveLimit){ -// points.push(array[i]); -// } -// } -// if (points.length > 0){ -// points.sort(function(a, b){return a - b;}); -// return points[points.length - 1]; -// } -// return null; -//}; - -RangeWhiskerGraph.prototype.drawPoints = function(boxProperties) { - if (this.plotPoints) { - for ( var i = 0; i < boxProperties.values.length; i++) { - var value = boxProperties.values[i]; - var toPixel = this.pointToPixel(value, boxProperties); - SVG.drawCircle(boxProperties.x, toPixel, this.pointRadius, this.svg, - [ - [ "fill", "green" ], [ "fill-opacity", this.fillOpacityPoint ], [ 'stroke-opacity', this.strokeOpacityPoint ], - [ "stroke", "black" ] ]); - } - } -}; - -RangeWhiskerGraph.prototype.plotRangeQ1Q3 = function(data, properties) { - var posX = this.left + this.rulerWidth; - - var boxBordersPointsQ1 = []; - var boxBordersPointsQ3 = []; - var Q2 = []; - - for ( var i = 0; i < data.clusters.length; i++) { - var cluster = data.clusters[i]; - /** inter cluster space **/ - posX = posX + this.interClustersSpace; - - for ( var j = 0; j < cluster.classes.length; j++) { - var ratio = properties.width / (properties.xValues.range); - var coorX = (cluster.x - properties.xValues.min) * ratio + this.rulerWidth; - var boxProperties = { - name : cluster.classes[j].name, - values : cluster.classes[j].values, - minPoint : properties.minPoint, - maxPoint : properties.maxPoint, - x : coorX + this.left, - y : this.top, - width : properties.classWidth, - height : this.height - this.top - this.bottom - this.clusterTitleHeight - this.rulerHeight - }; - - var result = this.calculate(boxProperties.values); - var boxColor = this.getClassColor(boxProperties.name); - - if (this.isNumber(result.Q1) && this.isNumber(result.Q3)) { - var x = boxProperties.x; - var y = this.pointToPixel(result.Q1, boxProperties); - if (boxBordersPointsQ1[cluster.classes[j].name] == null) { - boxBordersPointsQ1[cluster.classes[j].name] = []; - } - boxBordersPointsQ1[cluster.classes[j].name].push({ - x : x, - y : y - }); - - x = boxProperties.x; - y = this.pointToPixel(result.Q3, boxProperties); - if (boxBordersPointsQ3[cluster.classes[j].name] == null) { - boxBordersPointsQ3[cluster.classes[j].name] = []; - } - boxBordersPointsQ3[cluster.classes[j].name].push({ - x : x, - y : y - }); - } else { - - if (this.isNumber(result.Q2)) { - - if (boxBordersPointsQ1[cluster.classes[j].name] == null) { - boxBordersPointsQ1[cluster.classes[j].name] = []; - } - - if (boxBordersPointsQ3[cluster.classes[j].name] == null) { - boxBordersPointsQ3[cluster.classes[j].name] = []; - } - - boxBordersPointsQ1[cluster.classes[j].name].push({ - x : boxProperties.x, - y : this.pointToPixel(result.Q2, boxProperties) - }); - boxBordersPointsQ3[cluster.classes[j].name].push({ - x : boxProperties.x, - y : this.pointToPixel(result.Q2, boxProperties) - }); - } - } - } - } - for ( var classe in boxBordersPointsQ1) { - var points = boxBordersPointsQ1[classe]; - var pointsSVG = ""; - for (var k = 0; k < points.length; k++) { - pointsSVG = Number(points[k].x).toFixed(1) + "," + Number(points[k].y).toFixed(1) + " " + pointsSVG; - } - - points = boxBordersPointsQ3[classe]; - for (var z = points.length - 1; z >= 0; z--) { - pointsSVG = Number(points[z].x).toFixed(1) + "," + Number(points[z].y).toFixed(1) + " " + pointsSVG; - } - - SVG.drawPoligon(pointsSVG, this.svg, [ - [ "fill", this.getClassColor(classe) ], [ "opacity", "0.3" ], [ "stroke", "black" ], [ "stroke-width", 1 ] ]); - } -}; - -RangeWhiskerGraph.prototype.plotWhisters = function(data, properties) { - var colors = [ "yellow", "orange", "green" ]; - - this.plotRangeQ1Q3(data, properties); - var Q2 = {}; - - for ( var i = 0; i < data.clusters.length; i++) { - var cluster = data.clusters[i]; - for ( var j = 0; j < cluster.classes.length; j++) { - var ratio = properties.width / (properties.xValues.range); - var coorX = (cluster.x - properties.xValues.min) * ratio + this.left + this.rulerWidth - this.rulerStroke; - var boxProperties = { - name : cluster.classes[j].name, - values : cluster.classes[j].values, - minPoint : properties.minPoint, - maxPoint : properties.maxPoint, - x : coorX, - y : this.top, - width : properties.classWidth, - height : this.height - this.top - this.bottom - this.clusterTitleHeight - this.rulerHeight - }; - - this.drawPoints(boxProperties); - - /** PLOTTING Q2 **/ - var result = this.calculate(boxProperties.values); - var boxColor = this.getClassColor(boxProperties.name); - if (this.isNumber(result.Q2)) { - if (Q2[boxProperties.name] != null) { - SVG.drawLine(boxProperties.x, this.pointToPixel(result.Q2, boxProperties), Q2[boxProperties.name].x, Q2[boxProperties.name].y, - this.svg, [ [ "stroke", boxColor ], [ "stroke-width", "2" ] ]); - } - Q2[boxProperties.name] = { - x : boxProperties.x, - y : this.pointToPixel(result.Q2, boxProperties) - } - } - } - } - -}; - -RangeWhiskerGraph.prototype.draw = function(targetId, data) { - //this.calculate(data); - this.targetId = targetId; - var properties = (this.getDimensions(data)); - this.svg = SVG.createSVGCanvas(document.getElementById(this.targetId), [ [ "width", this.width ], [ "height", this.height ] ]); - this.plotAxes(properties); - this.plotWhisters(data, properties); -}; - -RangeWhiskerGraph.prototype.input = function() { - return DATADOC.getBoxWhikerData(); -}; - -RangeWhiskerGraph.prototype.test = function(targetId) { - var plot = new RangeWhiskerGraph({ - targetId : targetId, - height : 350, - width : 450, - maxBoxWidth : 25 - }); - plot.refresh(this.input()); -}; - -StdDevDyGraph.prototype.dblclick = DygraphWidget.prototype.dblclick; -StdDevDyGraph.prototype._createHTLMWrapper = DygraphWidget.prototype._createHTLMWrapper; -StdDevDyGraph.prototype.draw = DygraphWidget.prototype.draw; - -function StdDevDyGraph(targetId, args) { - this.scaled = false; - if (args == null) { - args = {}; - } - args.customBars = true; - DygraphWidget.prototype.constructor.call(this, targetId, args); -} - -StdDevDyGraph.prototype.input = function () { - return { - data : [ [ 1, [ 2, 3, 3.5 ], [ 4, 4.2, 5 ] ], [ 2, [ 5, 5.5, 5.7 ], [ 4, 4.2, 5 ] ] ], - colors : [ "blue", "red" ], - labels : [ "", 'data1', 'data2' ] - }; -}; - -StdDevDyGraph.prototype.test = function (targetId) { - var dygraphObject = new StdDevDyGraph(targetId, { - width : 500, - height : 400, - xlabel : "xLabel", - showRangeSelector : false - }); - - dygraphObject.draw(dygraphObject.input().data, dygraphObject.input().colors, dygraphObject.input().labels); -}; - - - -function AbinitioGrid(args) { - this.height = null; - this.width = null; - this.id = BUI.id(); - - if (args != null) { - if (args.height != null) { - this.height = args.height; - - if (args.width != null) { - this.width = args.width; - } - } - } - - this.onSelected = new Event(this); -}; - - -AbinitioGrid.prototype.refresh = function(subtractions){ - this.store.loadData(this._prepareData(subtractions)); -}; - -AbinitioGrid.prototype._prepareData = function(subtractions){ - /** Parsing data * */ - var models = []; - for (var l = 0; l < subtractions.length; l++) { - var subtraction = subtractions[l]; - for (var k = 0; k < subtraction.substractionToAbInitioModel3VOs.length; k++) { - var data = subtraction.substractionToAbInitioModel3VOs[k].abinitiomodel3VO; - if (data.averagedModel != null) { - models.push(data.averagedModel); - models[models.length - 1].type = "Reference"; - } - - if (data.shapeDeterminationModel != null) { - models.push(data.shapeDeterminationModel); - models[models.length - 1].type = "Refined"; - } - - if (data.modelList3VO != null) { - if (data.modelList3VO.modeltolist3VOs != null) { - for (var i = 0; i < data.modelList3VO.modeltolist3VOs.length; i++) { - models.push(data.modelList3VO.modeltolist3VOs[i].model3VO); - models[models.length - 1].type = "Model"; - } - } - } - } - } - return models; -}; - -AbinitioGrid.prototype.getPanel = function(){ - var _this = this; - - - var modelFields = [ "modelId", "type", "chiSqrt", "dmax", "firFile", "logFile", "fitFile", "pdbFile", "rfactor", "rg", "volume" ]; - Ext.define('AbinitioModel', { - extend : 'Ext.data.Model', - fields : modelFields - - }); - - /** - * Store in Memory - */ - this.store = Ext.create('Ext.data.Store', { - model : 'AbinitioModel', - autoload : true, - groupField : 'type' - }); - - - var groupingFeature = Ext.create('Ext.grid.feature.Grouping',{ - groupHeaderTpl: '{name} ({rows.length} model{[values.rows.length > 1 ? "s" : ""]})', - startCollapsed: true, - collapsible : true - }); - - this.grid = Ext.create('Ext.grid.Panel', { - collapsible : false, - resizable : true, - features: [groupingFeature], - autoscroll : true, - multiSelect : true, - store : this.store, - height : this.height, - width : this.width, - margin : 10, - columns : [ { - text : "Type", - dataindex : "type", - hidden : true, - renderer : function(a, b, record) { - return record.data.type; - }, - flex : 1 - }, - { - text : "ModelId", - dataindex : "modelId", - hidden : true, - renderer : function(a, b, record) { - return record.data.modelId; - - }, - flex : 1 - }, - - { - text : "chiSqrt", - dataindex : "chiSqrt", - renderer : function(a, b, record) { - if (record.data.dmax != null) { - return BUI.formatValuesUnits(record.data.chiSqrt, "", 12, this.decimals); - } - - }, - flex : 1 - }, - { - text : "Dmax", - dataindex : "dmax", - renderer : function(a, b, record) { - if (record.data.dmax != null) { - return BUI.formatValuesUnits(record.data.dmax, "nm", 12, this.decimals); - } - - }, - flex : 1 - }, { - text : "rFactor", - dataindex : "rfactor", - hidden : true, - renderer : function(a, b, record) { - if (record.data.rfactor != null) { - return record.data.rfactor; - } - }, - flex : 1 - }, { - text : "Rg", - dataindex : "rg", - renderer : function(a, b, record) { - if (record.data.rg != null) { - return BUI.formatValuesUnits(record.data.rg, "nm", 12, this.decimals); - } - - }, - flex : 1 - }, - { - text : "Volume", - dataindex : "volume", - renderer : function(a, b, record) { - if (record.raw.volume != null){ - return BUI.formatValuesUnits(record.raw.volume, '') + " nm3"; - } - }, - flex : 1 - }, - { - text : "PDB", - dataindex : "pdbFile", - renderer : function(a, b, record) { - if (record.data.pdbFile != null){ - return record.data.pdbFile.split("/")[record.data.pdbFile.split("/").length - 1]; - } - }, - flex : 1 - }, { - text : "Fir", - dataindex : "firFile", - renderer : function(a, b, record) { - if (record.data.firFile != null){ - return record.data.firFile.split("/")[record.data.firFile.split("/").length - 1]; - } - }, - flex : 1 - }, { - text : "LOG", - dataindex : "logFile", - hidden : true, - renderer : function(a, b, record) { - if (record.data.logFile != null){ - return record.data.logFile.split("/")[record.data.logFile.split("/").length - 1]; - } - }, - flex : 1 - } - ], - viewConfig : { - enableTextSelection : true, - preserveScrollOnRefresh : true, - stripeRows : true, - listeners : { - 'celldblclick' : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { - }, - 'cellclick' : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { - var models = []; - for (var i = 0; i < grid.getSelectionModel().selected.items.length; i++) { - models.push(grid.getSelectionModel().selected.items[i].raw); - } - _this.onSelected.notify(models); - } - } - } - }); - return this.grid; - -}; - - -function AdditiveGrid(args) { - this.onRemoveButtonClicked = new Event(this); -} - -AdditiveGrid.prototype.getBuffer = function() { - return this.buffer; -}; - -AdditiveGrid.prototype._getActions = function() { - var _this = this; - /** Actions buttons **/ - var actions = []; - - actions.push(Ext.create('Ext.Action', { - icon : '../images/add.png', - text : 'Add', - disabled : true, - alwaysEnabled : true, - handler : function(widget, event) { - _this.buffer.bufferhasadditive3VOs.push(BIOSAXS_BEANS.getBufferhasAdditive3VO()); - _this.refresh(_this.buffer, _this.experiment); - } - })); - - return actions; -}; - -AdditiveGrid.prototype.refresh = function(buffer, experiment) { - this.buffer = buffer; - this.experiment = experiment; - if (buffer) { - if (buffer.bufferhasadditive3VOs) { - this.features = buffer.bufferhasadditive3VOs; - } - } - this.experiment = experiment; - this.store.loadData(this._prepareData(), false); -}; - -AdditiveGrid.prototype.getAdditives = function() { - var additives = []; - for ( var i = 0; i < this.store.getCount(); i++) { - var bufferHasAdditive = BIOSAXS_BEANS.getBufferhasAdditive3VO(); - var data = this.store.getAt(i).getData(); - - bufferHasAdditive.additive3VO.name = data.name; - bufferHasAdditive.additive3VO.comments = data.comments; - bufferHasAdditive.additive3VO.additiveType = data.additiveType; - - bufferHasAdditive.bufferId = this.buffer.bufferId; - bufferHasAdditive.bufferHasAdditiveId = data.bufferHasAdditiveId; - bufferHasAdditive.quantity = data.quantity; - additives.push(bufferHasAdditive); - } - - return additives; -}; - -AdditiveGrid.prototype._getFields = function(buffers) { - var columns = [ - - { - header : 'Name', - dataIndex : 'name', - type : 'string', - editor : { - allowBlank : true - }, - flex : 1 - }, - { - header : 'Type', - name : 'additiveType', - dataIndex : 'additiveType', - editor : { - xtype : 'combobox', - typeAhead : true, - triggerAction : 'all', - selectOnTab : true, - store : BIOSAXS.proposal.getAdditiveTypes(), - lazyRender : true, - listClass : 'x-combo-list-small' - }, - flex : 0.6 - }, - { - header : 'Quantity', - dataIndex : 'quantity', - name : 'quantity', - editor : { - allowBlank : true - }, - type : 'string', - flex : 1 - }, - { - xtype : 'actioncolumn', - items : [ { - icon : '../images/cancel.png', - tooltip : 'Delete additive', - scope : this, - handler : function(grid, rowIndex, colIndex) { - if ((grid.getStore().getAt(rowIndex).data.bufferHasAdditiveId == null)|| (grid.getStore().getAt(rowIndex).data.bufferHasAdditiveId == "")) { - grid.getStore().removeAt(rowIndex); - } else { - this.onRemoveButtonClicked.notify({ - 'bufferId' : this.buffer.bufferId, - 'bufferHasAdditiveId' : this.store.getAt(rowIndex).data.bufferHasAdditiveId - }); - } - } - } ] - } ]; - - return columns; -}; - -AdditiveGrid.prototype._prepareData = function() { - var data = []; - if (this.features == null) { - this.features = []; - } - for (var i = 0; i < this.features.length; i++) { - var object = this.features[i]; - object.name = this.features[i].additive3VO.name; - object.additiveType = this.features[i].additive3VO.additiveType; - object.comments = this.features[i].additive3VO.comments; - object.additiveId = this.features[i].additive3VO.additiveId; - data.push(object); - } - return data; -}; - -AdditiveGrid.prototype.getPanel = function(buffer, experiment) { - this.buffer = buffer; - this.features = buffer.bufferhasadditive3VOs; - this.experiment = experiment; - return this._renderGrid(); -}; - -AdditiveGrid.prototype.getStore = function() { - var _this = this; - var store = Ext.create('Ext.data.Store', { - fields : [ "name", "additiveType", "comments", "additiveId", "bufferHasAdditiveId", "quantity" ], - autoload : false, - data : this._prepareData(), - listeners : { - update : function(store, record) { - record.index = _this.grid.getSelectionModel().getCurrentPosition().row; - _this.buffer.bufferhasadditive3VOs[record.index].additive3VO.name = record.data.name; - _this.buffer.bufferhasadditive3VOs[record.index].additive3VO.additiveType = record.data.additiveType; - _this.buffer.bufferhasadditive3VOs[record.index].additive3VO.comments = record.data.comments; - _this.buffer.bufferhasadditive3VOs[record.index].quantity = record.data.quantity; - } - } - }); - - // store.sort('bufferHasAdditiveId', 'ASC'); - store.loadData(this._prepareData(), false); - return store; -}; - -AdditiveGrid.prototype._renderGrid = function() { - var _this = this; - this.store = this.getStore(); - - var cellEditing = Ext.create('Ext.grid.plugin.CellEditing', { - clicksToEdit : 1 - }); - - this.grid = Ext.create('Ext.grid.Panel', { - dockedItems : [ { - xtype : 'toolbar', - items : this._getActions() - } ], - store : this.store, - height : 230, - title : "Additives", - width : "100%", - columns : _this._getFields(), - plugins : [ cellEditing ], - viewConfig : { - stripeRows : true, - listeners : { - itemcontextmenu : function(view, rec, node, index, e) { - e.stopEvent(); - contextMenu.showAt(e.getXY()); - return false; - } - } - }, - selModel : { - mode : 'SINGLE' - } - }); - - return this.grid; -}; - -AdditiveGrid.prototype.input = function() { -}; - -AdditiveGrid.prototype.test = function(targetId) { - var grid = new AdditiveGrid(); - var panel = grid.getPanel([]); - panel.render(targetId); - -}; - -/** AnalysisGrid **/ -function AnalysisGrid(args) { - var _this = this; - - this.id = BUI.id(); - if (Ext.get("mainPanel")){ - this.width = Ext.get("mainPanel").getWidth(); - } - else{ - this.width = this.maxWidth; - } - this.maxWidth = 1500; - - /** Visibles **/ - this.isGuinierTabVisible = true; - this.isGnomTabVisible = true; - this.isPorodTabVisible = true; - this.isScatteringPlotVisible = true; - this.isAbinitioTabVisible = true; - this.showButtonsVisible = true; - this.isI0Visible = true; - - this.margin = null; - this.grouped = true; - - this.hideNulls = false; - this.decimals = 4; - this.height = null; - this.sorters = [ { - property : 'conc', - direction : 'ASC' - } ]; - - if (args != null) { - if (args.grouped != null) { - this.grouped = args.grouped; - } - - if (args.showButtonsVisible != null) { - this.showButtonsVisible = args.showButtonsVisible; - } - if (args.isI0Visible != null) { - this.isI0Visible = args.isI0Visible; - } - if (args.sorters != null) { - this.sorters = args.sorters; - } - if (args.isScatteringPlotVisible != null) { - this.isScatteringPlotVisible = args.isScatteringPlotVisible; - } - if (args.isGuinierTabVisible != null) { - this.isGuinierTabVisible = args.isGuinierTabVisible; - } - if (args.isGnomTabVisible != null) { - this.isGnomTabVisible = args.isGnomTabVisible; - } - if (args.isPorodTabVisible != null) { - this.isPorodTabVisible = args.isPorodTabVisible; - } - if (args.hideNulls != null) { - this.hideNulls = args.hideNulls; - } - if (args.height != null) { - this.height = args.height; - } - if (args.width != null) { - this.width = args.width; - } - if (args.maxWidth != null) { - this.maxWidth = args.maxWidth; - } - } -} - -AnalysisGrid.prototype.refresh = function(data, args) { - this.store.loadData(this._prepareData(data), false); - if (args != null){ - if (args.experiment != null){ - this.experiment = args.experiment; - } - } -}; - -AnalysisGrid.prototype._getPorod = function() { - return { - text : 'Porod', - name : 'Porod', - columns : [ - { - text : 'Volume', - dataIndex : 'volumeEdna', - width : 75, - sortable : true, - hidden : !this.isPorodTabVisible, - renderer : function(val, y, sample) { - if (sample.raw.volume != null) - return BUI.formatValuesUnits(sample.raw.volume, '') + " nm3"; - } - }, - { - text : 'MM Vol. est.', - dataIndex : 'volumeEdna', - tooltip : '[Volume/2 - Volume/1.5] (Guinier)', - sortable : true, - width : 95, - hidden : !this.isPorodTabVisible, - renderer : function(val, y, sample) { - if (sample.raw.volume != null) - return Number(sample.raw.volume / 2).toFixed(1) + " - " + Number(sample.raw.volume / 1.5).toFixed(1)+ "kD"; - } - } ] - }; -}; - - -AnalysisGrid.prototype._getFramesColumn = function() { - var _this = this; - return { - text : 'Frames (Averaged/Total)', - dataIndex : 'datacollection', - name : 'datacollection', - sortable : true, - width : 150, - renderer : function(dataCollections, y, data) { - /** Bug of Webservices: frames count were swapped with frames averages **/ - if (data.raw.bufferAfterFramesCount){ - if (data.raw.bufferAfterFramesMerged){ - if (parseInt(data.raw.bufferAfterFramesMerged) > parseInt(data.raw.bufferAfterFramesCount)){ - var aux = parseInt(data.raw.bufferAfterFramesCount); - data.raw.bufferAfterFramesCount= data.raw.bufferAfterFramesMerged; - data.raw.bufferAfterFramesMerged = aux; - } - } - } - - if (data.raw.bufferBeforeFramesCount){ - if (data.raw.bufferBeforeFramesMerged){ - if (parseInt(data.raw.bufferBeforeFramesMerged) > parseInt(data.raw.bufferBeforeFramesCount)){ - var aux = parseInt(data.raw.bufferBeforeFramesCount); - data.raw.bufferBeforeFramesCount= data.raw.bufferBeforeFramesMerged; - data.raw.bufferBeforeFramesMerged = aux; - } - } - - } - - var bufferAcronym = data.raw.bufferAcronym; - var macromoleculeAcronym = data.raw.macromoleculeAcronym; - var bbmerges = data.raw.bufferBeforeFramesMerged; - var molmerges = data.raw.framesMerge; - var bamerges = data.raw.bufferAfterFramesMerged; - var totalframes = data.raw.framesCount; - var bufferId = data.raw.bufferId; - var macromoleculeId = data.raw.macromoleculeId; - var macromoleculeColor = null; - if (_this.experiment != null){ - macromoleculeColor = _this.experiment.macromoleculeColors[data.raw.macromoleculeId]; - } - - /** BUG in the database to be fixed **/ - try{ - if (totalframes != null){ - if(molmerges != null){ - if (parseFloat(totalframes) < parseFloat(molmerges)){ - var aux = totalframes; - totalframes = molmerges; - molmerges = aux; - } - } - } - } - catch(e){ - - } - - return BUI.getHTMLTableForFrameAveraged(bufferAcronym, - macromoleculeAcronym, - bbmerges, - molmerges, - bamerges, - totalframes, - bufferId, - macromoleculeId, - macromoleculeColor); - } - }; -}; - -AnalysisGrid.prototype._getColumns = function() { - var _this = this; - return [ - { - name : 'groupeField', - dataIndex : 'groupeField', - hidden : true - - }, - { - "header" : "subtractionId", - hidden : true, - "dataIndex" : "subtractionId", - "name" : "subtractionId" - }, - { - header : "Macromolecule", - dataIndex : "macromoleculeAcronym", - name : "macromoleculeAcronym", - renderer : function(val, y, sample) { - return '
' + val + '
' + - BUI.formatValuesUnits(sample.raw.conc, "mg/ml", 10, this.decimals) + '
' + - BUI.formatValuesUnits(sample.raw.exposureTemperature, "C", 10, this.decimals) + '
' - } - }, - { - header : "Order", - dataIndex : "priorityLevelId", - name : "priorityLevelId", - hidden : true - }, - { - header : "Code", - dataIndex : "code", - name : "code", - hidden : true - }, - { - header : "Conc.", - dataIndex : "conc", - width : 80, - name : "conc", - type : "number", - hidden : true, - renderer : function(val, y, sample) { - return BUI.formatValuesUnits(val, "mg/ml", 10, this.decimals); - } - }, - { - text : 'Scattering', - width : 100, - dataIndex : 'subtractionId', - name : 'subtractionId', - hidden : !this.isScatteringPlotVisible, - renderer : function(val, y, sample) { - var url = BUI.getURL() + '&type=scattering&subtractionId=' + sample.raw.subtractionId; - var event = "OnClick= window.open('" + url + "')"; - return ''; - } - }, - { - text : 'Kratky', - width : 100, - dataIndex : 'subtractionId', - hidden : true, - name : 'subtractionId', - renderer : function(val, y, sample) { - var url = BUI.getURL() + '&type=kratky&subtractionId=' + sample.raw.subtractionId; - var event = "OnClick= window.open('" + url + "')"; - return ''; - } - }, - this._getFramesColumn(), - { - text : 'File Name', - dataIndex : 'averageFilePath', - name : 'averageFilePath', - sortable : true, - width : 150, - hidden : true, - renderer : function(dataCollections, y, data) { - return BUI.getHTMLTableForPrefixes(data.raw.bufferBeforeAverageFilePath, data.raw.averageFilePath, - data.raw.bufferAfterAverageFilePath); - } - }, - - { - text : 'Guinier', - name : 'Guinier', - columns : [ - { - text : 'Rg', - dataIndex : 'rgGuinier', - name : 'rgGuinier', - hidden : !this.isGuinierTabVisible, - width : 75, - tooltip : 'In polymer physics, the radius of gyration is used to describe the dimensions of a polymer chain.', - sortable : true, - renderer : function(val, y, sample) { - if (sample.raw.rgGuinier != null) { - /** Show warning if rgGuinier and rgGnom differ more than 10% **/ - if (Math.abs(sample.raw.rgGuinier - sample.raw.rgGnom) > (sample.raw.rgGuinier * 0.1)) { - return "" + BUI.formatValuesUnits(sample.raw.rgGuinier, "") + ""; - - } - return BUI.formatValuesUnits(sample.raw.rgGuinier, "nm", 12, this.decimals); - } - } - }, - { - text : 'Points', - dataIndex : 'points', - sortable : true, - width : 100, - type : 'string', - hidden : !this.isGuinierTabVisible, - renderer : function(val, y, sample) { - if ((sample.raw.firstPointUsed == "") || (sample.raw.firstPointUsed == null)) - return; - return "" + sample.raw.firstPointUsed + " - " + sample.raw.lastPointUsed + " (" + - (sample.raw.lastPointUsed - sample.raw.firstPointUsed) + " )"; - } - }, - { - text : 'Quality', - dataIndex : 'quality', - hidden : !this.isGuinierTabVisible, - tooltip : 'Estimated data quality. 1.0 - means ideal quality, 0.0 - unusable data. In table format it is given in percent (100% - ideal quality, 0% - unusable data). Please note that this estimation is based only on the Guinier interval (very low angles).', - width : 60, - sortable : true, - renderer : function(val, y, sample) { - if (sample.raw.quality != null) { - val = sample.raw.quality; - if ((val != null) && (val != "")) { - return "" + (Number(val) * 100).toFixed(2) + " %"; - } - } - } - }, { - text : 'I(0)', - dataIndex : 'I0', - sortable : true, - hidden : !this.isI0Visible, - tooltip : 'Extrapolated scattering intensity at zero angle I(0) (forward scattering)', - width : 75, - type : 'string', - renderer : function(val, y, sample) { - if (sample.raw.I0 != null) { - return BUI.formatValuesErrorUnitsScientificFormat(sample.raw.I0, sample.raw.i0stdev, ""); - } - } - }, { - text : 'Aggregated', - tooltip : "If aggregation was detected from the slope of the data curve at low angles the value is '1', otherwise '0'.", - dataIndex : 'isagregated', - hidden : true, - width : 75, - renderer : function(val, y, sample) { - if ((sample.raw.isagregated != null)) { - if (val == true) { - return "Yes"; - } else { - return "No"; - } - } - } - }, { - text : 'Guinier', - sortable : true, - dataIndex : 'subtractionId', - type : 'string', - width : 100, - hidden : true, - renderer : function(val, y, sample) { - - if (sample.raw.subtractionId != null) { - var url = BUI.getURL() + '&type=guinier&subtractionId=' + sample.raw.subtractionId; - var event = "OnClick= window.open('" + url + "')"; - return ''; - } - } - } ] - }, - { - text : 'Gnom', - name : 'Gnom', - - columns : [ { - text : 'Rg', - dataIndex : 'rgGnom', - type : 'string', - width : 65, - hidden : !this.isGnomTabVisible, - sortable : true, - renderer : function(val, y, sample) { - /** Show warning if rgGuinier and rgGnom differ more than 10% **/ - if (sample.raw.rgGnom != null) { - if (Math.abs(sample.raw.rgGuinier - sample.raw.rgGnom) > (sample.raw.rgGuinier * 0.1)) { - return "" + BUI.formatValuesUnits(sample.raw.rgGnom, "") + ""; - - } - return BUI.formatValuesUnits(sample.raw.rgGnom, "nm"); - } - } - }, { - text : 'Total', - dataIndex : 'total', - width : 65, - hidden : !this.isGnomTabVisible, - sortable : true, - renderer : function(val, y, sample) { - if (sample.raw.total != null) - return BUI.formatValuesUnits(sample.raw.total, ''); - } - }, { - text : 'Dmax', - dataIndex : 'dmax', - sortable : true, - width : 75, - hidden : !this.isGnomTabVisible, - renderer : function(val, y, sample) { - if (sample.raw.dmax != null) - return BUI.formatValuesUnits(sample.raw.dmax, "") + " nm"; - } - }, { - text : 'P(r)', - sortable : true, - hidden : true, - width : 100, - dataIndex : 'subtractionId', - type : 'string', - renderer : function(val, y, sample) { - var url = BUI.getURL() + '&type=gnom&subtractionId=' + sample.raw.subtractionId; - var event = "OnClick= window.open('" + url + "')"; - return ''; - } - } ] - }, - this._getPorod(), - { - text : 'AbInitio Modeling', - name : 'AbInitio_modeling', - - columns : [ - { - text : 'NSD', - dataIndex : 'volumeEdna', - width : 100, - sortable : true, - hidden : true, - renderer : function(val, y, sample) { - var url = BUI.getNSDImageURL(sample.raw.modelListId); - var event = "OnClick= window.open('" + url + "')"; - return ''; - } - }, - { - text : 'Chi2', - dataIndex : 'volumeEdna', - sortable : true, - hidden : true, - width : 100, - renderer : function(val, y, sample) { - var url = BUI.getCHI2ImageURL(sample.raw.modelListId); - var event = "OnClick= window.open('" + url + "')"; - return ''; - } - }, - { - text : 'Pdb', - dataIndex : 'volumeEdna', - tooltip : 'Damaver: The program suite DAMAVER is a set of programs to align ab initio models, select the most typical one and build an averaged model. Dammif : Rapid ab initio shape determination by simulated annealing using a single phase dummy atom model. Dammin :Ab initio shape determination by simulated annealing using a single phase dummy atom model', - sortable : true, - width : 125, - hidden : !this.isAbinitioTabVisible, - renderer : function(val, y, sample) { - var html = new String(); - var split = null; - var url = null; - var file = sample.raw.averagedModel; - if (file != null) { - split = file.split("/"); - if (split.length > 0) { - url = BUI.getPdbURL() + '&type=AVERAGED&abInitioModelId=' + sample.raw.abInitioModelId; - html = html + ''+ split[split.length - 1] + '

'; - - } - } - - file = sample.raw.rapidShapeDeterminationModel; - if (file != null) { - split = file.split("/"); - if (split.length > 0) { - url = BUI.getPdbURL() + '&type=RAPIDSHAPEDETERMINATIONMODEL&abInitioModelId=' + sample.raw.abInitioModelId; - html = html + '' + split[split.length - 1] + '

'; - } - } - - file = sample.raw.shapeDeterminationModel; - if (file != null) { - split = file.split("/"); - if (split.length > 0) { - url = BUI.getPdbURL() + '&type=SHAPEDETERMINATIONMODEL&abInitioModelId=' + sample.raw.abInitioModelId; - html = html + ''+ split[split.length - 1] + ''; - } - } - return html; - } - }, - { - text : 'Damaver', - dataIndex : 'volumeEdna', - hidden : true, - tooltip : 'Damaver: The program suite DAMAVER is a set of programs to align ab initio models, select the most typical one and build an averaged model. ', - sortable : true, - width : 125, - renderer : function(val, y, sample) { - var file = sample.raw.averagedModel; - if (file != null) { - var split = file.split("/"); - if (split.length > 0) { - var url = BUI.getPdbURL() + '&type=AVERAGED&abInitioModelId=' + sample.raw.abInitioModelId; - return '' + split[split.length - 1]+ ''; - } - } - return sample.raw.averagedModel; - } - }, - { - text : 'Dammif', - dataIndex : 'volumeEdna', - hidden : true, - tooltip : 'Dammif : Rapid ab initio shape determination by simulated annealing using a single phase dummy atom model', - sortable : true, - width : 125, - renderer : function(val, y, sample) { - var file = sample.raw.rapidShapeDeterminationModel; - if (file != null) { - var split = file.split("/"); - if (split.length > 0) { - var url = BUI.getPdbURL() + '&type=RAPIDSHAPEDETERMINATIONMODEL&abInitioModelId=' + sample.raw.abInitioModelId; - return '' + split[split.length - 1] + ''; - } - } - return sample.raw.averagedModel; - } - }, - { - text : 'Dammin', - dataIndex : 'volumeEdna', - hidden : true, - tooltip : 'Dammin :Ab initio shape determination by simulated annealing using a single phase dummy atom model', - sortable : true, - width : 125, - renderer : function(val, y, sample) { - var file = sample.raw.shapeDeterminationModel; - if (file != null) { - var split = file.split("/"); - if (split.length > 0) { - var url = BUI.getPdbURL() + '&type=SHAPEDETERMINATIONMODEL&abInitioModelId=' + sample.raw.abInitioModelId; - return '' + split[split.length - 1]+ ''; - } - } - return sample.raw.averagedModel; - } - } ] - }, { - text : 'Time', - dataIndex : 'substractionCreationTime', - name : 'substractionCreationTime', - hidden : true, - width : 80, - sortable : true, - renderer : function(value, metaData, record, rowIndex, colIndex, store) { - try { - if (record.raw.substractionCreationTime != null) { - return moment(record.raw.substractionCreationTime).format('h:mm:ss a'); - } - } catch (e) { - return "NA"; - } - } - }, { - text : 'Date', - dataIndex : 'substractionCreationTime', - name : 'substractionCreationTime', - hidden : true, - width : 80, - sortable : true, - renderer : function(value, metaData, record, rowIndex, colIndex, store) { - try { - if (record.raw.substractionCreationTime != null) { - return moment(record.raw.substractionCreationTime).format("LL"); - } - } catch (e) { - return "NA"; - } - } - }, - this._getButtons()]; -}; - -AnalysisGrid.prototype._getButtons = function() { - return { - id : this.id + 'buttonPlot', - name : 'buttonPlot', - hidden : !this.showButtonsVisible, - width : 110, - sortable : false, - renderer : function(value, metaData, sample, rowIndex, colIndex, store) { - var html = ""; - - if (sample.raw.subtractionId) { - html = html + ""; - if (sample.raw.rapidShapeDeterminationModelId != null) { - html = html + ""; - } - } - return html + "
" + BUI.getGreenButton('Calibration', { - height : 20, - width : 100 - }) + "
" + BUI.getGreenButton('Primary Data Proc.', { - height : 20, - width : 100 - }) + "
" + BUI.getGreenButton('AbInitio Modeling', { - height : 20, - width : 100 - }) + "
"; - } - }; -}; -AnalysisGrid.prototype._prepareData = function(data) { - if (this.hideNulls) { - var result = []; - for ( var i = 0; i < data.length; i++) { - if (data[i].subtractionId != null) { - data[i].groupeField = data[i].macromoleculeAcronym + " " + data[i].bufferAcronym; - result.push(data[i]); - } - } - return result; - } else { - return data; - } -}; - -AnalysisGrid.prototype.getPanel = function(data) { - var _this = this; - var columns = this._getColumns(); - - var fields = JSON.parse(JSON.stringify(columns)); - fields.push({ - name : 'experimentId', - dataIndex : 'experimentId' - - }); - this.store = Ext.create('Ext.data.Store', { - fields : fields, - autoload : true, - data : this._prepareData(data), - groupField : 'groupeField' - }); - - this.store.sort(this.sorters); - - var features = []; - - if (this.grouped) { - features.push({ - ftype : 'grouping', - groupHeaderTpl : '{name}', - hideGroupedHeader : false, - startCollapsed : false - }); - } - - this.grid = Ext.create('Ext.grid.Panel', { - style : { - padding : 5 - }, - maxWidth : this.maxWidth, - width : this.width, - height : this.height, - store : this.store, - columns : columns, - resizable : true, - features : features, - viewConfig : { - preserveScrollOnRefresh : true, - stripeRows : true, - listeners : { - cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { - - if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonPlot') { - if (e.target.defaultValue == 'AbInitio Modeling') { - var url = BUI.getPDBVisualizerURL(record.raw.averagedModelId, record.raw.subtractionId, record.raw.experimentId); - window.open(url, "_blank"); - } - - if (e.target.defaultValue == 'Primary Data Proc.') { - _this._edit(record.raw); - } - - if (e.target.defaultValue == 'Calibration') { - _this._showCalibration(record.raw); - } - } - } - } - }, - selModel : { - mode : 'SINGLE' - } - }); - - return this.grid; - -}; - -AnalysisGrid.prototype._openVisualizarBySubstractionId = function(record) { - //experimentId, subtractionId) { - var experimentId = record.experimentId; - var subtractionId = record.subtractionId; - - var _this = this; - var adapter = new BiosaxsDataAdapter(); - this.subtractionId = subtractionId; - - adapter.onSuccess.attach(function(sender, data) { - var experiment = new Experiment(data); - var experimentList = new ExperimentList([ experiment ]); - var subtraction = experiment.getSubtractionById(_this.subtractionId); - _this.grid.setLoading(false); - _this._openVisualizer(experimentList, subtraction, record); - }); - this.grid.setLoading("ISPyB: Fetching experiment"); - adapter.getExperimentById(experimentId, "MEDIUM"); -}; - -AnalysisGrid.prototype._edit = function(record) { - var experimentId = record.experimentId; - var _this = this; - - if (BIOSAXS.proposal.macromolecules == null) { - this.grid.setLoading("ISPyB: Fetching proposal"); - BIOSAXS.proposal.onInitialized.attach(function(sender, data) { - _this._openVisualizarBySubstractionId(record); - }); - BIOSAXS.proposal.init(); - } else { - this._openVisualizarBySubstractionId(record); - } -}; - - -AnalysisGrid.prototype._showCalibration = function(row) { - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, data) { - Ext.create('Ext.window.Window', { - title : 'Calibration', - width : 900, - resizable : true, - height : 400, - modal : true, - frame : false, - draggable : true, - closable : true, - autoscroll : true, - paddin : 5, - layout : { - type : 'vbox', - align : 'stretch' - }, - items : [ new AnalysisGrid({ - isGuinierTabVisible : false, - isGnomTabVisible : false, - isPorodTabVisible : false, - isAbinitioTabVisible : false, - isI0Visible : true, - showButtonsVisible : false, - height : 350, - sorters : [ { - property : 'experimentId', - direction : 'DESC' - } ] - }).getPanel(data) ] - }).show(); - }); - adapter.getAnalysisCalibrationByProposalId(); -}; - -AnalysisGrid.prototype._openVisualizer = function(experimentList, subtraction, record) { - var merges = experimentList.getMergesByDataCollectionId(subtraction.dataCollectionId); - var mergeIdList = []; - for ( var i = 0; i < merges.length; i++) { - mergeIdList.push(merges[i].mergeId); - } - - var dataAdapter = new BiosaxsDataAdapter(); - dataAdapter.onSuccess.attach(function(sender, merges) { - var dataCollectionCurveVisualizer = new DataCollectionCurveVisualizer(); - dataCollectionCurveVisualizer.draw(); - dataCollectionCurveVisualizer.refresh(merges, experimentList, subtraction.dataCollectionId, record); - }); - - dataAdapter.onError.attach(function(sender, error) { - }); - - dataAdapter.getMergesByIdsList(mergeIdList); -}; - -AnalysisGrid.prototype.input = function() { - return { - data : DATADOC.getData_3367().slice(20, 30),//[{"total":"0.447505552082","bufferBeforeMeasurementId":22150,"sampleMergeId":12373,"averagedModelId":43636,"I0":"32.50776","proposalCode":"OPD","averagedModel":"/data/pyarch/bm29/opd29/1137/1d/damaver.pdb","bufferAfterMergeId":12374,"framesCount":"10","bufferBeforeMergeId":12372,"averageFilePath":" /data/pyarch/bm29/opd29/1137/1d/MG386_016_ave.dat","bufferAfterMeasurementId":22152,"rgGuinier":"2.96883","subtractedFilePath":" /data/pyarch/bm29/opd29/1137/1d/MG386_016_sub.dat","firstPointUsed":"30","chi2RgFilePath":"/data/pyarch/bm29/opd29/1137/1d/chi2_R.png","abInitioModelId":272,"macromoleculeId":112,"code":"_20.0_1.25","transmission":"100.0","timeStart":"2013-07-17 17:10:25.743734","bufferAcronym":"MES","quality":"0.87091","shapeDeterminationModelId":43638,"expermientComments":"[BsxCube] Generated from BsxCube","bufferId":707,"isagregated":"False","dmax":"10.390905","bufferBeforeAverageFilePath":" /data/pyarch/bm29/opd29/1137/1d/MG386_015_ave.dat","exposureTemperature":"20.0","subtractionId":5165,"conc":"1.25","rapidShapeDeterminationModel":"/data/pyarch/bm29/opd29/1137/1d/damfilt.pdb","experimentCreationDate":"Jul 17, 2013 5:08:37 PM","lastPointUsed":"92","modelListId":272,"framesMerge":"10","rapidShapeDeterminationModelId":43637,"bufferAfterAverageFilePath":" /data/pyarch/bm29/opd29/1137/1d/MG386_017_ave.dat","nsdFilePath":"/data/pyarch/bm29/opd29/1137/1d/nsd.png","bufferAfterFramesMerged":"10","experimentId":1137,"macromoleculeAcronym":"MG386","i0stdev":"0.05726128","sampleMeasurementId":22151,"measurementComments":"[1] MES","priorityLevelId":2,"bufferBeforeFramesMerged":"10","substractionCreationTime":"Jul 17, 2013 5:12:32 PM","proposalNumber":"29","rgGnom":"3.05242714339","volume":"44.1406","shapeDeterminationModel":"/data/pyarch/bm29/opd29/1137/1d/dammin.pdb","comments":null},{"total":"0.582641941147","bufferBeforeMeasurementId":22152,"sampleMergeId":12375,"averagedModelId":43809,"I0":"31.4366153846","proposalCode":"OPD","averagedModel":"/data/pyarch/bm29/opd29/1137/1d/damaver.pdb","bufferAfterMergeId":12376,"framesCount":"10","bufferBeforeMergeId":12374,"averageFilePath":" /data/pyarch/bm29/opd29/1137/1d/MG386_018_ave.dat","bufferAfterMeasurementId":22155,"rgGuinier":"2.95541","subtractedFilePath":" /data/pyarch/bm29/opd29/1137/1d/MG386_018_sub.dat","firstPointUsed":"21","chi2RgFilePath":"/data/pyarch/bm29/opd29/1137/1d/chi2_R.png","abInitioModelId":273,"macromoleculeId":112,"code":"_20.0_0.65000000000000002","transmission":"100.0","timeStart":"2013-07-17 17:12:55.837462","bufferAcronym":"MES","quality":"0.870164","shapeDeterminationModelId":43811,"expermientComments":"[BsxCube] Generated from BsxCube","bufferId":707,"isagregated":"False","dmax":"10.343935","bufferBeforeAverageFilePath":" /data/pyarch/bm29/opd29/1137/1d/MG386_017_ave.dat","exposureTemperature":"20.0","subtractionId":5166,"conc":"0.65000000000000002","rapidShapeDeterminationModel":"/data/pyarch/bm29/opd29/1137/1d/damfilt.pdb","experimentCreationDate":"Jul 17, 2013 5:08:37 PM","lastPointUsed":"80","modelListId":273,"framesMerge":"10","rapidShapeDeterminationModelId":43810,"bufferAfterAverageFilePath":" /data/pyarch/bm29/opd29/1137/1d/MG386_019_ave.dat","nsdFilePath":"/data/pyarch/bm29/opd29/1137/1d/nsd.png","bufferAfterFramesMerged":"10","experimentId":1137,"macromoleculeAcronym":"MG386","i0stdev":"0.100250461538","sampleMeasurementId":22154,"measurementComments":"[2] MES","priorityLevelId":4,"bufferBeforeFramesMerged":"10","substractionCreationTime":"Jul 17, 2013 5:15:02 PM","proposalNumber":"29","rgGnom":"2.9963404542","volume":"42.2255","shapeDeterminationModel":"/data/pyarch/bm29/opd29/1137/1d/dammin.pdb","comments":null}], - proposal : new ResultsAssemblyGrid().input().proposal - }; -}; - -AnalysisGrid.prototype.test = function(targetId) { - var analysisGrid = new AnalysisGrid(); - BIOSAXS.proposal = new Proposal(analysisGrid.input().proposal); - var panel = analysisGrid.getPanel(analysisGrid.input().data); - panel.render(targetId); -}; - - -function HPLCAnalysisGrid(args) { - AnalysisGrid.prototype.constructor.call(this, args); -} - -HPLCAnalysisGrid.prototype._edit = AnalysisGrid.prototype._edit; -HPLCAnalysisGrid.prototype._getColumns = AnalysisGrid.prototype._getColumns; -HPLCAnalysisGrid.prototype._openVisualizarBySubstractionId = AnalysisGrid.prototype._openVisualizarBySubstractionId; -HPLCAnalysisGrid.prototype._openVisualizer = AnalysisGrid.prototype._openVisualizer; -HPLCAnalysisGrid.prototype._prepareData = AnalysisGrid.prototype._prepareData; -HPLCAnalysisGrid.prototype._showCalibration = AnalysisGrid.prototype._showCalibration; -HPLCAnalysisGrid.prototype.getPanel = AnalysisGrid.prototype.getPanel; -HPLCAnalysisGrid.prototype.input = AnalysisGrid.prototype.input; -HPLCAnalysisGrid.prototype.test = AnalysisGrid.prototype.test; -HPLCAnalysisGrid.prototype.refresh = AnalysisGrid.prototype.refresh; -HPLCAnalysisGrid.prototype._getPorod = AnalysisGrid.prototype._getPorod; - -HPLCAnalysisGrid.prototype._getButtons = function() { - return { - id : this.id + 'buttonPlot', - name : 'buttonPlot', - hidden : !this.showButtonsVisible, - width : 110, - sortable : false, - renderer : function(value, metaData, sample, rowIndex, colIndex, store) { - var html = ""; - - if (sample.raw.subtractionId) { - if (sample.raw.rapidShapeDeterminationModelId != null) { - html = html + ""; - } - } - return html + "
" + BUI.getGreenButton('AbInitio Modeling', { - height : 20, - width : 100 - }) + "
"; - } - }; -}; - -HPLCAnalysisGrid.prototype._getFramesColumn = function() { - return { - text : 'Frames', - dataIndex : 'datacollection', - name : 'datacollection', - sortable : true, - width : 150, - renderer : function(dataCollections, y, data) { - molmerges = data.raw.framesMerge; - totalframes = data.raw.framesCount; - return molmerges +" - "+ totalframes; - } - }; -}; - - - - -/** - * Rigid body grid to show PDB, symmetry and multiplicity - * - * - * #onUploadFile click on upload file - */ -function AprioriRigidBodyGrid(args) { - - this.height = 250; - this.btnEditVisible = true; - this.btnRemoveVisible = true; - - if (args != null) { - if (args.height != null) { - this.height = args.height; - } - if (args.btnEditVisible != null) { - this.btnEditVisible = args.btnEditVisible; - } - if (args.btnRemoveVisible != null) { - this.btnRemoveVisible = args.btnRemoveVisible; - } - } - - /** Events **/ - this.onUploadFile = new Event(this); - this.onRemove = new Event(this); -} - -AprioriRigidBodyGrid.prototype._getColumns = function() { -}; - -AprioriRigidBodyGrid.prototype._getTopButtons = function() { - var _this = this; - /** Actions buttons **/ - var actions = []; - - /** ADD BUTTON **/ - actions.push(Ext.create('Ext.Action', { - icon : '../images/add.png', - text : 'Add', - disabled : false, - handler : function(widget, event) { - _this.onAddButtonClicked.notify(); - } - })); - - return actions; -}; - -AprioriRigidBodyGrid.prototype.refresh = function(macromolecule) { - this.macromolecule = macromolecule; - if (macromolecule != null){ - this.pdbStore.loadData(macromolecule.structure3VOs); - } -}; - -AprioriRigidBodyGrid.prototype._prepareData = function() { - var data = []; - for ( var i = 0; i < this.features.length; i++) { - data.push(this.features[i]); - } - return data; -}; - -AprioriRigidBodyGrid.prototype._getPlugins = function() { - var _this = this; - var plugins = []; - plugins.push(Ext.create('Ext.grid.plugin.RowEditing', { - clicksToEdit : 1, - listeners : { - validateedit : function(grid, e) { - /** Comments are always updatable* */ - e.record.raw.symmetry = e.newValues.symmetry; - e.record.raw.multiplicity = e.newValues.multiplicity; - - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, proposal) { - BIOSAXS.proposal.setItems(proposal); - _this.refresh(BIOSAXS.proposal.getMacromoleculeById(_this.macromolecule.macromoleculeId)); - _this.panel.setLoading(false); - }); - adapter.onError.attach(function() { - alert("Error"); - }); - - _this.panel.setLoading(); - adapter.saveStructure(e.record.raw); - } - } - })); - return plugins; -}; - -AprioriRigidBodyGrid.prototype.getPanel = function() { - var _this = this; - - this.pdbStore = Ext.create('Ext.data.Store', { - fields : [ 'filePath', 'structureId', 'structureType', 'symmetry', 'structureId', 'name', 'multiplicity' ], - groupField : 'structureType', - sorters : { - property : 'structureId', - direction : 'DESC' - } - }); - - var groupingFeature = Ext.create('Ext.grid.feature.Grouping', { - groupHeaderTpl : Ext.create('Ext.XTemplate', - "
{name:this.formatName}
", { - formatName : function(name) { - return name; - } - }), - hideGroupedHeader : true, - startCollapsed : false - }); - - this.panel = Ext.create('Ext.grid.Panel', { - margin : "15 10 0 10", - height : this.height, - store : this.pdbStore, - plugins : _this._getPlugins(), - tbar : [ { - text : 'Add Modeling Option (PDB)', - icon : '../images/add.png', - handler : function() { - _this.onUploadFile.notify('PDB', 'Upload PDB File'); - } - } - - ], - columns : [ - { - text : "structureId", - flex : 0.2, - hidden : true, - dataIndex : 'structureId', - sortable : true - }, - { - text : "File", - flex : 0.5, - dataIndex : 'filePath', - sortable : true, - hidden : true - }, - { - text : "PDB", - flex : 0.4, - dataIndex : 'name', - sortable : true - }, - { - text : "Symmetry", - flex : 0.2, - dataIndex : 'symmetry', - sortable : true, - editor : { - xtype : 'combobox', - typeAhead : true, - triggerAction : 'all', - selectOnTab : true, - store : [ [ "P1", "P1" ], [ "P2", "P2" ], [ "P3", "P3" ], [ "P4", "P4" ], [ "P5", "P5" ], [ "P6", "P6" ], [ "P32", "P32" ], [ "P42", "P42" ], - [ "P52", "P52" ], [ "P62", "P62" ], [ "P222", "P222" ] ], - } - }, { - text : "Multiplicity", - flex : 0.2, - dataIndex : 'multiplicity', - sortable : true, - editor : { - xtype : 'textfield' - } - - }, { - text : "Subunit", - flex : 0.2, - dataIndex : 'isSubunit', - sortable : true, - hidden : true - }, { - text : "Type", - flex : 0.2, - dataIndex : 'structureType', - sortable : true, - hidden : true - }, - - { - id : this.id + 'REMOVE', - flex : 0.2, - sortable : false, - renderer : function(value, metaData, record, rowIndex, colIndex, store) { - return BUI.getRedButton('REMOVE'); - } - }, ], - - listeners : { - itemdblclick : function(dataview, record, item, e) { - _this._editExperiment(record.raw.experimentId); - }, - cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { - - if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'REMOVE') { - var dataAdapter = new BiosaxsDataAdapter(); - dataAdapter.onSuccess.attach(function() { - _this.panel.setLoading(false); - _this.onRemove.notify(); - }); - _this.panel.setLoading("Removing PDB file"); - dataAdapter.removeStructure(record.data.structureId); - } - - } - }, - viewConfig : { - getRowClass : function(record, rowIdx, params, store) { - if (record.raw.isSubunit != null) { - return "blue-row"; - } - } - } - }); - - return this.panel; -}; - - - - -AprioriRigidBodyGrid.prototype.input = function() { - return { - proposal : DATADOC.getProposal_10(), - dewars : DATADOC.getDewars_10() - - }; -}; - -AprioriRigidBodyGrid.prototype.test = function(targetId) { - var AprioriRigidBodyGrid = new AprioriRigidBodyGrid({ - height : 150 - }); - BIOSAXS.proposal = new Proposal(AprioriRigidBodyGrid.input().proposal); - var panel = AprioriRigidBodyGrid.getPanel(AprioriRigidBodyGrid.input().dewars); - panel.render(targetId); - -}; - -/** - * It shows buffer grid with a top bar with "Add" button - * - * @height - * @searchBar - * @collapsed - * @width - */ -function BufferGrid(args) { - this.height = 500; - this.searchBar = false; - this.tbar = false; - this.collapsed = false; - this.id = BUI.id(); - - if (args != null) { - if (args.height != null) { - this.height = args.height; - } - if (args.searchBar != null) { - this.searchBar = args.searchBar; - } - - if (args.tbar != null) { - this.tbar = args.tbar; - } - - if (args.collapsed != null) { - this.collapsed = args.collapsed; - } - - if (args.width != null) { - this.width = args.width; - } - } -} - -BufferGrid.prototype._edit = function(bufferId) { - var _this = this; - var window = new BufferWindow(); - window.onSuccess.attach(function(sender, proposal) { - _this.store.loadData(BIOSAXS.proposal.getBuffers()); - }); - window.draw(BIOSAXS.proposal.getBufferById(bufferId)); -}; - -BufferGrid.prototype.refresh = function(buffers) { - this.store.loadData(this._prepareData(buffers), false); -}; - -BufferGrid.prototype._prepareData = function(buffers) { - return buffers; -}; - -BufferGrid.prototype._getTbar = function() { - var _this = this; - var actions = []; - - actions.push(Ext.create('Ext.Action', { - icon : '../images/add.png', - text : 'Add buffer', - disabled : false, - handler : function(widget, event) { - var window = new BufferWindow(); - window.onSuccess.attach(function(sender) { - _this.store.loadData(BIOSAXS.proposal.getBuffers()); - }); - window.draw({}); - } - })); - return actions; -}; - -BufferGrid.prototype.getPanel = function(buffers) { - var _this = this; - - this.store = Ext.create('Ext.data.Store', { - fields : [ 'bufferId', 'acronym', 'name', 'composition' ], - data : buffers - }); - - this.store.sort('acronym'); - - var type = 'Ext.grid.Panel'; - if (this.searchBar == true) { - type = 'Ext.ux.LiveSearchGridPanel'; - } - - this.grid = Ext.create(type, { - title : 'Buffers', - collapsible : true, - collapsed : this.collapsed, - store : this.store, - height : this.height, - width : this.width, - columns : [ - /*{ - text : '', - dataIndex : 'bufferId', - width : 20, - renderer : function(val, y, sample) { - return BUI.getRectangleColorDIV(BIOSAXS.proposal.bufferColors[val], 10, 10); - } - },*/ - { - text : 'Acronym', - dataIndex : 'acronym', - flex : 1 - }, { - text : 'Name', - dataIndex : 'name', - flex : 1, - hidden : true - }, { - text : 'Composition', - dataIndex : 'composition', - flex : 1, - hidden : true - }, { - id : _this.id + 'buttonEditBuffer', - width : 80, - sortable : false, - renderer : function(value, metaData, record, rowIndex, colIndex, store) { - return BUI.getGreenButton('EDIT'); - } - }, { - id : _this.id + 'buttonRemoveBuffer', - width : 85, - hidden : true, - sortable : false, - renderer : function(value, metaData, record, rowIndex, colIndex, store) { - return BUI.getRedButton('REMOVE'); - } - } ], - flex : 1, - viewConfig : { - stripeRows : true, - listeners : { - 'celldblclick' : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { - _this._edit(record.data.bufferId); - }, - 'cellclick' : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { - if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonEditBuffer') { - _this._edit(record.data.bufferId); - } - - if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonRemoveBuffer') { - BUI.showBetaWarning(); - } - } - - } - } - }); - - /** Adding the tbar **/ - if (this.tbar) { - this.grid.addDocked({ - xtype : 'toolbar', - items : this._getTbar() - }); - } - return this.grid; -}; - -BufferGrid.prototype.input = function() { - return new MacromoleculeGrid().input(); -}; - -BufferGrid.prototype.test = function(targetId) { - var bufferGrid = new BufferGrid({ - width : 800, - height : 350, - collapsed : false, - tbar : true - }); - - BIOSAXS.proposal = new Proposal(bufferGrid.input().proposal); - var panel = bufferGrid.getPanel(BIOSAXS.proposal.macromolecules); - panel.render(targetId); -}; - -/** - * A shipment may contains one or more cases where stock solutions and sample plates are stored - * - * @height - * @btnEditVisible - * @btnRemoveVisible - * - * #onEditButtonClicked - * #onAddButtonClicked - * #onRemoveButtonClicked - * #onDuplicateButtonClicked - */ -function CaseGrid(args) { - - this.height = 100; - this.btnEditVisible = true; - this.btnRemoveVisible = true; - - if (args != null) { - if (args.height != null) { - this.height = args.height; - } - if (args.btnEditVisible != null) { - this.btnEditVisible = args.btnEditVisible; - } - if (args.btnRemoveVisible != null) { - this.btnRemoveVisible = args.btnRemoveVisible; - } - } - - /** Events **/ - this.onEditButtonClicked = new Event(this); - this.onAddButtonClicked = new Event(this); - this.onRemoveButtonClicked = new Event(this); - this.onDuplicateButtonClicked = new Event(this); -} - -CaseGrid.prototype._getColumns = function() { - var _this = this; - - function actionItemRenderer(value, meta, record, rowIx, ColIx, store) { - /*if (record.raw.specimen3VOs.length > 0){ - return 'x-hide-display'; - }*/ - } - - var columns = [ - { - header : 'Code', - dataIndex : 'code', - name : 'code', - type : 'string', - flex : 1 - }, - { - header : 'Bar Code', - dataIndex : 'barCode', - name : 'barCode', - type : 'string', - flex : 1, - hidden : true - }, - { - header : 'BeamLine', - dataIndex : 'barCode', - type : 'string', - flex : 1, - renderer : function(comp, val, record) { - if (record.raw.sessionVO != null) { - return "" + record.raw.sessionVO.beamlineName + ""; - } - } - }, - { - header : 'Session', - dataIndex : 'barCode', - type : 'string', - flex : 1, - renderer : function(comp, val, record) { - if (record.raw.sessionVO != null) { - return "" + moment(record.raw.sessionVO.startDate).format("MMM Do YY") + ""; - } - } - }, - { - header : 'Status', - dataIndex : 'dewarStatus', - name : 'dewarStatus', - type : 'string', - flex : 1, - renderer : function(comp, val, record) { - if (new String(record.raw.dewarStatus).toUpperCase() == 'OPENED') { - return "" + new String(record.raw.dewarStatus).toUpperCase() + ""; - } - return "" + new String(record.raw.dewarStatus).toUpperCase() + ""; - } - }, - { - header : 'Type', - dataIndex : 'type', - name : 'type', - type : 'string', - flex : 1, - hidden : true - }, - { - header : 'Sample Plates', - dataIndex : 'plates', - name : 'plates', - type : 'string', - flex : 1, - hidden : true - }, - { - header : 'Stock Solutions', - dataIndex : 'plates', - name : 'plates', - type : 'string', - width : 100, - renderer : function(comp, val, record) { - var html = "
"; - var stockSolutions = BIOSAXS.proposal.getStockSolutionsByDewarId(record.raw.dewarId); - html = html + "" + stockSolutions.length + " x"; - return html + "
"; - } - }, { - header : 'isStorageDewar', - dataIndex : 'isStorageDewar', - name : 'isStorageDewar', - type : 'string', - flex : 1, - hidden : true - }, { - header : 'Tracking Number From Synchrotron', - dataIndex : 'trackingNumberFromSynchrotron', - name : 'trackingNumberFromSynchrotron', - type : 'string', - flex : 1, - hidden : true - }, { - header : 'Tracking Number To Synchrotron', - dataIndex : 'trackingNumberToSynchrotron', - name : 'trackingNumberToSynchrotron', - type : 'string', - flex : 1, - hidden : true - }, { - header : 'Storage Location', - dataIndex : 'storageLocation', - name : 'storageLocation', - type : 'string', - flex : 1, - hidden : false - }, { - header : 'Comments', - dataIndex : 'comments', - name : 'comments', - type : 'string', - flex : 1, - hidden : false - } - ]; - - if (this.btnEditVisible) { - columns.push({ - id : _this.id + 'buttonEdit', - text : '', - hidden : !_this.btnEditVisible, - width : 85, - sortable : false, - renderer : function(value, metaData, record, rowIndex, colIndex, store) { - return BUI.getGreenButton('EDIT'); - } - }); - } - - if (this.btnRemoveVisible) { - columns.push({ - id : _this.id + 'buttonRemove', - width : 85, - sortable : false, - renderer : function(value, metaData, record, rowIndex, colIndex, store) { - if (_this.btnRemoveVisible) { - return BUI.getRedButton('REMOVE'); - } - } - }); - } - - columns.push({ - dataIndex : 'comments', - type : 'string', - width : 85, - hidden : false, - renderer : function(comp, val, record) { - return BUI.getBlueButton("LABELS", { - href : BUI.getPrintcomponentURL(record.raw.dewarId) - }); - } - }); - - return columns; -}; - -CaseGrid.prototype._getTopButtons = function() { - var _this = this; - /** Actions buttons **/ - var actions = []; - - /** ADD BUTTON **/ - actions.push(Ext.create('Ext.Action', { - icon : '../images/add.png', - text : 'Add', - disabled : false, - handler : function(widget, event) { - _this.onAddButtonClicked.notify(); - } - })); - - return actions; -}; - -CaseGrid.prototype.refresh = function(dewars) { - this.features = dewars; - this.store.loadData(this._prepareData(dewars), false); -}; - -CaseGrid.prototype._sort = function(store) { - store.sort('dewarId', 'DESC'); -}; - -CaseGrid.prototype._prepareData = function() { - var data = []; - for ( var i = 0; i < this.features.length; i++) { - data.push(this.features[i]); - } - return data; -}; - -CaseGrid.prototype.getPanel = function(dewars, plates) { - this.features = dewars; - this.plates = plates; - return this._renderGrid(); -}; - -CaseGrid.prototype._edit = function(dewar) { - var _this = this; - var caseWindow = new CaseWindow(); - /**SAVED **/ - caseWindow.onSaved.attach(function(sender, dewar) { - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, shipment) { - _this.refresh(shipment.dewarVOs); - }); - adapter.saveCase(BIOSAXS.proposal.getShipmentByDewarId(dewar.dewarId).shippingId, dewar); - }); - caseWindow.draw(dewar); -}; - -CaseGrid.prototype._getStoreFields = function(data) { - return [ { - name : 'dewarId', - type : 'string' - }, { - name : 'barCode', - type : 'string' - }, { - name : 'code', - type : 'string' - }, { - name : 'comments', - type : 'string' - }, { - name : 'dewarStatus', - type : 'string' - }, { - name : 'isStorageDewar', - type : 'string' - }, { - name : 'plates', - type : 'string' - }, { - name : 'transportValue', - type : 'string' - }, { - name : 'trackingNumberFromSynchrotron', - type : 'string' - }, { - name : 'trackingNumberToSynchrotron', - type : 'string' - }, { - name : 'timeStamp', - type : 'string' - }, { - name : 'storageLocation', - type : 'string' - }, { - name : 'type', - type : 'string' - } - - ]; -}; - -CaseGrid.prototype._renderGrid = function() { - var _this = this; - - /** Store **/ - var data = this._prepareData(); - this.store = Ext.create('Ext.data.Store', { - fields : this._getStoreFields(data), - autoload : true, - data : data - }); - this._sort(this.store); - - this.store.loadData(data, false); - - this.grid = Ext.create('Ext.grid.Panel', { - style : { - padding : 5 - }, - height : this.height, - store : this.store, - columns : this._getColumns(), - viewConfig : { - stripeRows : true, - listeners : { - itemdblclick : function(dataview, record, item, e) { - _this._edit(record.raw); - }, - cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { - if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonEdit') { - _this._edit(record.raw); - } - - if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonRemove') { - _this.onRemoveButtonClicked.notify(_this.store.getAt(rowIndex).raw.dewarId); - } - } - } - }, - selModel : { - mode : 'SINGLE' - } - }); - - var actions = _this._getTopButtons(); - this.grid.addDocked({ - xtype : 'toolbar', - items : actions - }); - - this.grid.getSelectionModel().on({ - selectionchange : function(sm, selections) { - if (selections.length) { - for ( var i = 0; i < actions.length; i++) { - if (actions[i].enable) { - actions[i].enable(); - } - } - } else { - for ( var i = 0; i < actions.length; i++) { - if (actions[i].alwaysEnabled == false) { - if (actions[i].disable) { - actions[i].disable(); - } - } - } - } - } - }); - return this.grid; -}; - -CaseGrid.prototype.input = function() { - return { - proposal : DATADOC.getProposal_10(), - dewars : DATADOC.getDewars_10() - - }; -}; - -CaseGrid.prototype.test = function(targetId) { - var CaseGrid = new CaseGrid({ - height : 150 - }); - BIOSAXS.proposal = new Proposal(CaseGrid.input().proposal); - var panel = CaseGrid.getPanel(CaseGrid.input().dewars); - panel.render(targetId); - -}; - -/** - * A shipment may contains one or more cases where stock solutions and sample plates are stored - * - * @height - * @btnEditVisible - * @btnRemoveVisible - * - * #onEditButtonClicked - * #onAddButtonClicked - * #onRemoveButtonClicked - * #onDuplicateButtonClicked - */ -function ExampleGrid(args) { - - this.height = 100; - this.btnEditVisible = true; - this.btnRemoveVisible = true; - - if (args != null) { - if (args.height != null) { - this.height = args.height; - } - if (args.btnEditVisible != null) { - this.btnEditVisible = args.btnEditVisible; - } - if (args.btnRemoveVisible != null) { - this.btnRemoveVisible = args.btnRemoveVisible; - } - } - - /** Events **/ - this.onEditButtonClicked = new Event(this); - this.onAddButtonClicked = new Event(this); - this.onRemoveButtonClicked = new Event(this); - this.onDuplicateButtonClicked = new Event(this); -} - -ExampleGrid.prototype._getColumns = function() { - var _this = this; - - function actionItemRenderer(value, meta, record, rowIx, ColIx, store) { - /*if (record.raw.specimen3VOs.length > 0){ - return 'x-hide-display'; - }*/ - } - - var columns = [ - { - header : 'Code', - dataIndex : 'code', - name : 'code', - type : 'string', - flex : 1 - }, - { - header : 'Bar Code', - dataIndex : 'barCode', - name : 'barCode', - type : 'string', - flex : 1, - hidden : true - }, - { - header : 'BeamLine', - dataIndex : 'barCode', - type : 'string', - flex : 1, - renderer : function(comp, val, record) { - if (record.raw.sessionVO != null) { - return "" + record.raw.sessionVO.beamlineName + ""; - } - } - }, - { - header : 'Session', - dataIndex : 'barCode', - type : 'string', - flex : 1, - renderer : function(comp, val, record) { - if (record.raw.sessionVO != null) { - return "" + moment(record.raw.sessionVO.startDate).format("MMM Do YY") + ""; - } - } - }, - { - header : 'Status', - dataIndex : 'dewarStatus', - name : 'dewarStatus', - type : 'string', - flex : 1, - renderer : function(comp, val, record) { - if (new String(record.raw.dewarStatus).toUpperCase() == 'OPENED') { - return "" + new String(record.raw.dewarStatus).toUpperCase() + ""; - } - return "" + new String(record.raw.dewarStatus).toUpperCase() + ""; - } - }, - { - header : 'Type', - dataIndex : 'type', - name : 'type', - type : 'string', - flex : 1, - hidden : true - }, - { - header : 'Sample Plates', - dataIndex : 'plates', - name : 'plates', - type : 'string', - flex : 1, - hidden : true - }, - { - header : 'Stock Solutions', - dataIndex : 'plates', - name : 'plates', - type : 'string', - width : 100, - renderer : function(comp, val, record) { - var html = "
"; - var stockSolutions = BIOSAXS.proposal.getStockSolutionsByDewarId(record.raw.dewarId); - html = html + "" + stockSolutions.length + " x"; - return html + "
"; - } - }, { - header : 'isStorageDewar', - dataIndex : 'isStorageDewar', - name : 'isStorageDewar', - type : 'string', - flex : 1, - hidden : true - }, { - header : 'Tracking Number From Synchrotron', - dataIndex : 'trackingNumberFromSynchrotron', - name : 'trackingNumberFromSynchrotron', - type : 'string', - flex : 1, - hidden : true - }, { - header : 'Tracking Number To Synchrotron', - dataIndex : 'trackingNumberToSynchrotron', - name : 'trackingNumberToSynchrotron', - type : 'string', - flex : 1, - hidden : true - }, { - header : 'Storage Location', - dataIndex : 'storageLocation', - name : 'storageLocation', - type : 'string', - flex : 1, - hidden : false - }, { - header : 'Comments', - dataIndex : 'comments', - name : 'comments', - type : 'string', - flex : 1, - hidden : false - } - ]; - - if (this.btnEditVisible) { - columns.push({ - id : _this.id + 'buttonEdit', - text : '', - hidden : !_this.btnEditVisible, - width : 85, - sortable : false, - renderer : function(value, metaData, record, rowIndex, colIndex, store) { - return BUI.getGreenButton('EDIT'); - } - }); - } - - if (this.btnRemoveVisible) { - columns.push({ - id : _this.id + 'buttonRemove', - width : 85, - sortable : false, - renderer : function(value, metaData, record, rowIndex, colIndex, store) { - if (_this.btnRemoveVisible) { - return BUI.getRedButton('REMOVE'); - } - } - }); - } - - columns.push({ - dataIndex : 'comments', - type : 'string', - width : 85, - hidden : false, - renderer : function(comp, val, record) { - return BUI.getBlueButton("LABELS", { - href : BUI.getPrintcomponentURL(record.raw.dewarId) - }); - } - }); - - return columns; -}; - -ExampleGrid.prototype._getTopButtons = function() { - var _this = this; - /** Actions buttons **/ - var actions = []; - - /** ADD BUTTON **/ - actions.push(Ext.create('Ext.Action', { - icon : '../images/add.png', - text : 'Add', - disabled : false, - handler : function(widget, event) { - _this.onAddButtonClicked.notify(); - } - })); - - return actions; -}; - -ExampleGrid.prototype.refresh = function(dewars) { - this.features = dewars; - this.store.loadData(this._prepareData(dewars), false); -}; - -ExampleGrid.prototype._sort = function(store) { - store.sort('dewarId', 'DESC'); -}; - -ExampleGrid.prototype._prepareData = function() { - var data = []; - for ( var i = 0; i < this.features.length; i++) { - data.push(this.features[i]); - } - return data; -}; - -ExampleGrid.prototype.getPanel = function() { - this.grid = Ext.create('Ext.grid.Panel', { - style : { - padding : 5 - }, - height : this.height, - store : this.store, - columns : this._getColumns(), - viewConfig : { - stripeRows : true, - listeners : { - itemdblclick : function(dataview, record, item, e) { - _this._edit(record.raw); - }, - cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { - if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonEdit') { - _this._edit(record.raw); - } - - if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonRemove') { - _this.onRemoveButtonClicked.notify(_this.store.getAt(rowIndex).raw.dewarId); - } - } - } - }, - selModel : { - mode : 'SINGLE' - } - }); - - var actions = _this._getTopButtons(); - this.grid.addDocked({ - xtype : 'toolbar', - items : actions - }); - - this.grid.getSelectionModel().on({ - selectionchange : function(sm, selections) { - if (selections.length) { - for ( var i = 0; i < actions.length; i++) { - if (actions[i].enable) { - actions[i].enable(); - } - } - } else { - for ( var i = 0; i < actions.length; i++) { - if (actions[i].alwaysEnabled == false) { - if (actions[i].disable) { - actions[i].disable(); - } - } - } - } - } - }); - return this.grid; -}; - - -ExampleGrid.prototype._getStoreFields = function(data) { - return [ { - name : 'dewarId', - type : 'string' - }, { - name : 'barCode', - type : 'string' - }, { - name : 'code', - type : 'string' - }, { - name : 'comments', - type : 'string' - }, { - name : 'dewarStatus', - type : 'string' - }, { - name : 'isStorageDewar', - type : 'string' - }, { - name : 'plates', - type : 'string' - }, { - name : 'transportValue', - type : 'string' - }, { - name : 'trackingNumberFromSynchrotron', - type : 'string' - }, { - name : 'trackingNumberToSynchrotron', - type : 'string' - }, { - name : 'timeStamp', - type : 'string' - }, { - name : 'storageLocation', - type : 'string' - }, { - name : 'type', - type : 'string' - } - - ]; -}; - -ExampleGrid.prototype._renderGrid = function() { - var _this = this; - - /** Store **/ - var data = this._prepareData(); - this.store = Ext.create('Ext.data.Store', { - fields : this._getStoreFields(data), - autoload : true, - data : data - }); - this._sort(this.store); - - this.store.loadData(data, false); - - this.grid = Ext.create('Ext.grid.Panel', { - style : { - padding : 5 - }, - height : this.height, - store : this.store, - columns : this._getColumns(), - viewConfig : { - stripeRows : true, - listeners : { - itemdblclick : function(dataview, record, item, e) { - _this._edit(record.raw); - }, - cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { - if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonEdit') { - _this._edit(record.raw); - } - - if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonRemove') { - _this.onRemoveButtonClicked.notify(_this.store.getAt(rowIndex).raw.dewarId); - } - } - } - }, - selModel : { - mode : 'SINGLE' - } - }); - - var actions = _this._getTopButtons(); - this.grid.addDocked({ - xtype : 'toolbar', - items : actions - }); - - this.grid.getSelectionModel().on({ - selectionchange : function(sm, selections) { - if (selections.length) { - for ( var i = 0; i < actions.length; i++) { - if (actions[i].enable) { - actions[i].enable(); - } - } - } else { - for ( var i = 0; i < actions.length; i++) { - if (actions[i].alwaysEnabled == false) { - if (actions[i].disable) { - actions[i].disable(); - } - } - } - } - } - }); - return this.grid; -}; - -ExampleGrid.prototype.input = function() { - return { - proposal : DATADOC.getProposal_10(), - dewars : DATADOC.getDewars_10() - - }; -}; - -ExampleGrid.prototype.test = function(targetId) { - var ExampleGrid = new ExampleGrid({ - height : 150 - }); - BIOSAXS.proposal = new Proposal(ExampleGrid.input().proposal); - var panel = ExampleGrid.getPanel(ExampleGrid.input().dewars); - panel.render(targetId); - -}; - - -/** - * Shows a list of experiment AKA data acquisitions - * @height - * @sorters - * @minHeight - * @gridType: Ext.ux.LiveSearchGridPanel or Ext.grid.Panel - * @tbar true or false - * @grouping true or false - * @width - * @title - * #onEditButtonClicked - */ -function ExperimentGrid(args) { - this.width = "100%"; - this.height = 700; - this.minHeight = 500; - - this.id = BUI.id(); - this.gridType = 'Ext.grid.Panel'; //'Ext.ux.LiveSearchGridPanel'; - this.tbar = false; - this.hideHeaders = false; - this.grouping = true; - this.title = null; - - this.filtered = null; - - /** maximum row count **/ - this.limit = 100; - - this.sessionIdFilter = null; - - /** if not null filtered by date **/ - this.date = null; - - this.sorters = [ { - property : 'date', - direction : 'DESC' - }, { - property : 'time', - direction : 'DESC' - } ]; - - this.dates = {}; - if (args != null) { - if (args.height != null) { - this.height = args.height; - } - if (args.limit != null) { - this.limit = args.limit; - } - if (args.sorters != null) { - this.sorters = args.sorters; - } - if (args.sessionId != null){ - this.sessionIdFilter = args.sessionId; - } - if (args.filtered != null) { - this.filtered = args.filtered; - } - if (args.minHeight != null) { - this.minHeight = args.minHeight; - } - if (args.gridType != null) { - this.gridType = args.gridType; - } - if (args.tbar != null) { - this.tbar = args.tbar; - } - if (args.grouping != null) { - this.grouping = args.grouping; - } - if (args.width != null) { - this.width = args.width; - } - if (args.title != null) { - this.title = args.title; - } - - } - /** Events **/ - this.onEditButtonClicked = new Event(this); -} - -ExperimentGrid.prototype._getFilterTypes = function() { - return []; -}; - -ExperimentGrid.prototype._prepareData = function(rows) { - var data = []; - var count = 0; - - rows.sort(function(a,b){return b.experimentId - a.experimentId;}); - for ( var i = 0; i < rows.length; i++) { - var row = rows[i]; - this.dates[moment(row.creationDate).format("YYYYMMDD")] = moment(row.creationDate).format("MMM Do YY"); - if ( - ( this.filtered == null || row.experimentType == this.filtered ) && (count < this.limit || this.limit == null ) && (this.sessionIdFilter == null || this.date != null || (this.date == null && this.sessionIdFilter == row.sessionId)) - ){ - - data.push({ - experimentId : row.experimentId, - status : row.status, - dataAcquisitionFilePath : row.dataAcquisitionFilePath, - type : row.experimentType, - name : row.name, - macromolecules_names : row.macromolecules, - percentageAnalysed : { - value : (row.dataCollectionDoneCount / row.dataCollectionCount) * 100, - text : row.dataCollectionDoneCount + " of " + row.dataCollectionCount - }, - percentageCollected : { - value : (row.measurementDoneCount / row.measurementCount) * 100, - text : row.measurementDoneCount + " of " + row.measurementCount - }, - percentageMerged : { - value : (row.measurementAveragedCount / row.measurementCount) * 100, - text : row.measurementAveragedCount + " of " + row.measurementCount - }, - date : moment(row.creationDate).format("YYYYMMDD"), - time : moment(row.creationDate).format("YYYYMMDDHHmmss"), - creationDate : row.creationDate - }); - count ++; - } - } - return data; -}; - -ExperimentGrid.prototype.getPanel = function(experiments) { - this.features = experiments; - return this._renderGrid(experiments); -}; - -ExperimentGrid.prototype.refresh = function(experiments) { - this.experiments = experiments; - var filtered = []; - for ( var i = 0; i < experiments.length; i++) { - if (experiments[i].experimentType != "TEMPLATE") { - filtered.push(experiments[i]); - } - } - - this.parsedData = this._prepareData(filtered); - - var day = {}; - var dates = []; - for ( var i = 0; i < this.experiments.length; i++) { - var date = moment(this.experiments[i].creationDate).format("MMM Do YYYY"); - if (day[date] == null){ - dates.push({ - date : date, - value : moment(this.experiments[i].creationDate) - }); - day[date] = true; - } - } - - this.storeDate.loadData(dates, false); - this.store.loadData(this.parsedData, false); - - /** If it has already been filtered by date we keep the filter **/ - if (this.date != null){ - this._filterByDate(this.date); - } -}; - -ExperimentGrid.prototype._getTopButtons = function() { - var _this = this; - /** Actions buttons **/ - var actions = []; - - actions.push(Ext.create('Ext.Action', { - icon : '../images/calendar_icon.png', - text : 'Show Calendar', - disabled : false, - handler : function(widget, event) { - var window = Ext.create('Ext.window.Window', { - title : 'Calendar', - width : 600, - height : 600, - modal : true, - closable : true, - layout : { - type : 'vbox', - align : 'stretch' - }, - items : [ { - xtype : 'label', - html : 'Click on a data acquisition to select:', - margin : '5 5 5 5' - }, { - html : '
', - margin : '5 5 5 5' - } - - ] - }).show(); - - var calendarWidget = new CalendarWidget({ - height : 450 - }); - var aux = _this.limit; - /** we remove the limit temporarily **/ - _this.limit = null; - _this.sessionIdFilter = null; - calendarWidget.loadData(_this._prepareData(_this.experiments)); - _this.limit = aux; - calendarWidget.draw('calendar'); - calendarWidget.onClick.attach(function(sender, date) { - date = moment(date, "YYYY-MM-DD"); - _this._filterByDate(date); - window.close(); - - }); - - } - })); - this.storeDate = Ext.create('Ext.data.ArrayStore', { - fields: ['date', 'value'], - data : [] - }); - - this.dateMenu = Ext.create('Ext.form.field.ComboBox', { - hideLabel: true, - store: this.storeDate, - displayField: 'date', - typeAhead: true, - queryMode: 'local', - margin : '0 0 0 30', - triggerAction: 'all', - emptyText:'Select a date...', - selectOnFocus:true, - width:135, - listeners:{ - scope: this, - 'select': function (a,b,c){ - _this.limit = null; - _this._filterByDate(moment(b[0].raw.value, "YYYY-MM-DD")); - } - } - }); - - actions.push(this.dateMenu); - - actions.push("->"); - if (_this.filtered != null){ - actions.push({ - html : "Experiment Type: " + _this.filtered +"" - }); - } - else{ - actions.push({ - html : "Experiment Type: ALL" - }); - } - return actions; -}; - -/** - * Date format: "YYYY-MM-DD" - */ -ExperimentGrid.prototype._filterByDate = function(date) { - var experimentsFiltered = []; - /** Getting all the experiments of date**/ - for ( var i = 0; i < this.experiments.length; i++) { - var experiment = this.experiments[i]; - if (experiment.creationDate != null) { - var experimentDate = moment(experiment.creationDate); - if (experimentDate.year() == date.year()) { - if (experimentDate.month() == date.month()) { - if (experimentDate.date() == date.date()) { - experimentsFiltered.push(experiment); - } - } - } - } - } - var parsedData = this._prepareData(experimentsFiltered); - this.store.loadData(parsedData); - this.date = date; -}; - -/** Only for templates **/ -ExperimentGrid.prototype._removeExperimentById = function(experimentId) { - var _this = this; - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(evt, args) { - _this.grid.setLoading(false); - document.getElementById(BIOSAXS.targetId).innerHTML = ""; - BIOSAXS.start(BIOSAXS.targetId); - }); - this.grid.setLoading("Removing experiment "); - adapter.removeExperimentById(experimentId); -}; - -ExperimentGrid.prototype._renderGrid = function() { - var _this = this; - - /** Store **/ - this.store = Ext.create('Ext.data.Store', { - fields : this._getColumns(), - groupField : 'date', - autoload : true, - data : [], - remoteSort: false, - sorters : this.sorters - }); - - - - - var groupingFeature = Ext.create('Ext.grid.feature.Grouping', { - groupHeaderTpl : Ext.create('Ext.XTemplate', - "
{name:this.formatName}
", { - formatName : function(name) { - return _this.dates[name]; - } - }), - hideGroupedHeader : true, - startCollapsed : false - }); - - this.features = []; - if (this.grouping) { - this.features.push(groupingFeature); - } - - /** Grid **/ - this.grid = Ext.create(this.gridType, { - hideHeaders : this.hideHeaders, - resizable : true, - title : this.title, - width : this.width, - minHeight : this.minHeight, - height : this.height, - features : this.features, - store : this.store, - columns : this._getColumns(), - selModel : { - mode : 'SINGLE' - }, - viewConfig : { - stripeRows : true, - getRowClass : function(record, rowIdx, params, store) { - if (record.raw.type == "TEMPLATE") { - return "template-color-row"; - } - if ((record.raw.type == "CALIBRATION") && (record.raw.status == "FINISHED")) { - return "blue-row"; - } - }, - listeners : { - itemdblclick : function(dataview, record, item, e) { - _this._editExperiment(record.raw.experimentId); - }, - cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { - if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'GO') { - _this._editExperiment(record.raw.experimentId); - } - - if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'REMOVE') { - _this._removeExperimentById(record.raw.experimentId); - } - - } - } - } - }); - - var actions = _this._getTopButtons(); - - if (this.tbar) { - this.grid.addDocked({ - xtype : 'toolbar', - items : actions - }); - this.grid.getSelectionModel().on({ - selectionchange : function(sm, selections) { - if (selections.length) { - for ( var i = 0; i < actions.length; i++) { - if (actions[i].enable) { - actions[i].enable(); - } - } - } else { - for ( var i = 0; i < actions.length; i++) { - if (actions[i].alwaysEnabled == false) { - if (actions[i].disable) { - actions[i].disable(); - } - } - } - } - } - }); - } - - return this.grid; -}; - -ExperimentGrid.prototype._getColumns = function() { - var _this = this; - function actionItemRenderer(value, meta, record, rowIx, ColIx, store) { - if (record.raw.buffer3VOs != null) { - if (record.raw.buffer3VOs.length > 0) { - return 'x-hide-display'; - } - } - - if (record.data.platesCount > 0) { - return 'x-hide-display'; - } - } - - return [ - { - text : 'experimentId', - dataIndex : 'experimentId', - name : 'experimentId', - type : 'string', - hidden : true - }, - { - xtype : 'rownumberer', - width : 40 - }, - { - text : 'Name', - dataIndex : 'name', - name : 'name', - type : 'string', - flex : 1 - }, - - { - text : 'Type', - dataIndex : 'type', - name : 'type', - type : 'string', - flex : 1, - renderer : function(val) { - if (val == "CALIBRATION") { - return "" + val + ""; - } - - return val; - } - }, - { - text : 'Macromolecules', - name : 'macromolecules_names', - dataIndex : 'macromolecules_names', - flex : 1, - renderer : function(val) { - if (val != null) { - return " " + val + ""; - } - return " Information not available"; - } - }, - { - text : 'Buffers', - dataIndex : 'buffer_names', - name : 'buffer_names', - flex : 1, - hidden : true, - renderer : function(val) { - return "Buffer/s: " + val + ""; - } - }, - { - text : 'Status', - dataIndex : 'status', - name : 'status', - type : 'string', - flex : 1, - renderer : function(val, x, sample) { - if (sample.raw.type == "TEMPLATE") { - return "READY"; - } - if (sample.raw.status == "ABORTED") { - return "" + val + ""; - } - return "" + val + ""; - } - }, - { - text : 'Download', - dataIndex : 'creationDate', - name : 'creationDate', - renderer : function(val, x, sample) { - if (sample != null) { - if (sample.raw.type == "HPLC") { - return; - } - return BUI.getZipHTMLByExperimentId(sample.raw.experimentId, sample.raw.name); - } - }, - width : 100 - - }, - { - header : 'Measurements', - dataIndex : 'percentageCollected', - name : 'percentageCollected', - type : 'string', - renderer : function(val, y, sample) { - if ((sample.raw.type == "TEMPLATE") || (sample.raw.type == "HPLC")) { - return; - } - return "
" + BUI.getProgessBar(sample.raw.percentageCollected.value, sample.raw.percentageCollected.text) + "
"; - }, - width : 100 - }, - { - header : 'Averaged', - dataIndex : 'percentageMerged', - name : 'percentageMerged', - type : 'string', - renderer : function(val, y, sample) { - if ((sample.raw.type == "TEMPLATE") || (sample.raw.type == "HPLC")) { - return; - } - return "
" + BUI.getProgessBar(sample.raw.percentageMerged.value, sample.raw.percentageMerged.text) + "
"; - }, - width : 100 - }, - { - header : 'Subtractions', - dataIndex : 'percentageAnalysed', - name : 'percentageAnalysed', - type : 'string', - renderer : function(val, y, sample) { - if ((sample.raw.type == "TEMPLATE") || (sample.raw.type == "HPLC")) { - return; - } - return "
" + BUI.getProgessBar(sample.raw.percentageAnalysed.value, sample.raw.percentageAnalysed.text) + "
"; - }, - width : 100 - }, - - { - text : 'time', - dataIndex : 'time', - name : 'time', - hidden : true, - renderer : function(val) { - return val; - }, - width : 100 - - }, { - text : 'Date', - dataIndex : 'date', - name : 'date', - renderer : function(val) { - return val; - }, - width : 100 - - }, - - { - text : 'Time', - dataIndex : 'creationDate', - name : 'creationDate', - renderer : function(val) { - return moment(val).format(" HH:mm:ss"); - }, - width : 100 - - }, { - id : _this.id + 'GO', - width : 80, - sortable : false, - renderer : function(value, metaData, record, rowIndex, colIndex, store) { - return BUI.getGreenButton('GO'); - } - } ]; -}; - -/** Changes location.href in order to edit the experiment **/ -ExperimentGrid.prototype._editExperiment = function(experimentId) { - if (Ext.urlDecode(window.location.href).sessionId != null) { - location.href = 'viewProjectList.do?reqCode=display&experimentId=' + experimentId + '&sessionId='+ Ext.urlDecode(window.location.href).sessionId; - } else { - location.href = 'viewProjectList.do?reqCode=display&experimentId=' + experimentId; - } -}; - -ExperimentGrid.prototype.input = function() { - var experiments = DATADOC.getExperimentList_10(); - return { - experiments : experiments, - proposal : new MeasurementGrid().input().proposal - - }; -}; - -ExperimentGrid.prototype.test = function(targetId) { - var experimentGrid = new ExperimentGrid({ - height : 350, - minHeight : 350, - width : 1000 - - }); - BIOSAXS.proposal = new Proposal(experimentGrid.input().proposal); - var panel = experimentGrid.getPanel(experimentGrid.input().experiments); - experimentGrid.refresh(experimentGrid.input().experiments); - panel.render(targetId); -}; - -function FitStructureToExperimentDataGrid(args) { - this.height = null; - this.width = null; - this.id = BUI.id(); - - if (args != null) { - if (args.height != null) { - this.height = args.height; - - if (args.width != null) { - this.width = args.width; - } - } - } - - this.onSelected = new Event(this); -} - -FitStructureToExperimentDataGrid.prototype.getStructuresByMacromolecule = function(macromolecule) { - var structures = []; - if (macromolecule.structure3VOs != null) { - if (macromolecule.structure3VOs.length > 0) { - for (var i = 0; i < macromolecule.structure3VOs.length; i++) { - structures.push(macromolecule.structure3VOs[i]); - } - } - } - return structures; -}; - -FitStructureToExperimentDataGrid.prototype._prepareData = function(subtractions) { - var data = []; - for (var i = 0; i < subtractions.length; i++) { - - for (var j = 0; j < subtractions[i].fitStructureToExperimentalData3VOs.length; j++) { - var fit = subtractions[i].fitStructureToExperimentalData3VOs[j]; - data.push({ - fit : fit.fitFilePath, - fitStructureToExperimentalDataId : fit.fitStructureToExperimentalDataId, - mixtureToStructure3VOs : fit.mixtureToStructure3VOs, - subtractedFile : subtractions[i].substractedFilePath - }); - } - } - return data; - -}; - -FitStructureToExperimentDataGrid.prototype.refresh = function(subtractions) { - this.store.loadData(this._prepareData(subtractions), false); -}; - -FitStructureToExperimentDataGrid.prototype.getPanel = function() { - var _this = this; - this.store = Ext.create('Ext.data.Store', { - fields : [ 'fit', 'subtractedFile', 'structureId', 'fitFilePath', 'mixtureToStructure3VOs' ], - data : [] - }); - - this.store.sort([ { - property : 'name', - direction : 'ASC' - } ]); - - this.selModel = Ext.create('Ext.selection.RowModel', { - allowDeselect : true, - mode : 'MULTI', - listeners : { - selectionchange : function(sm, selections) { - var selected = []; - for (var i = 0; i < selections.length; i++) { - selected.push(selections[i].raw); - } - _this.onSelected.notify(selected); - } - } - }); - - this.panel = Ext.create('Ext.grid.Panel', { - store : this.store, - deferredRender : false, - width : this.width, - height : this.height, - margin : 10, - selModel : this.selModel, - columns : [ { - text : 'Name', - dataIndex : 'fit', - flex : 1, - renderer : function(val, b, record) { - return BUI.getFileName(val); - } - }, { - text : 'PDB', - dataIndex : 'mixtureToStructure3VOs', - flex : 1, - renderer : function(values, b, record) { - var html = ""; - for (var i = 0; i < values.length; i++) { - var structure = BIOSAXS.proposal.getStructuresById(values[i].structureId); - html = html + ""; - if (structure != null){ - html = html + ""; - } - html = html + ""; - } - return html + "
" + structure.name + "
"; - } - }, { - text : 'Volume Fraction', - dataIndex : 'mixtureToStructure3VOs', - flex : 1, - renderer : function(values, b, record) { - var html = ""; - for (var i = 0; i < values.length; i++) { - html = html + ""; - html = html + ""; - html = html + ""; - } - - return html + "
" + values[i].volumeFraction + "
"; - } - },{ - text : 'Subtraction', - dataIndex : 'subtractedFile', - flex : 1, - renderer : function(values, b, record) { - return values.split("/")[values.split("/").length - 1]; - } - },], - - viewConfig : { - enableTextSelection : true, - preserveScrollOnRefresh : true - }, - listeners : { - cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { - }, - afterrender : function() { - } - } - }); - return this.panel; -}; - -/** Static method **/ -FitStructureToExperimentDataGrid.prototype.RUNFitScattering = function(subtractionId, structureId) { - var _this = this; - /** Add to Workflow **/ - var adapter = new BiosaxsDataAdapter(); - var workflow = { - 'workflowTitle' : 'FitExperimentalDatatoStructure', - 'comments' : 'FitExperimentalDatatoStructure run from ISPyB for subtractionId: ' + subtractionId + ' and structureId ' + structureId - } - - var inputs = [ { - name : 'subtractionId', - value : subtractionId - }, { - name : 'structureId', - value : structureId - } ]; - - adapter.onSuccess.attach(function(sender, data) { - /** Add to Fit **/ - var adapter2 = new BiosaxsDataAdapter(); - var fit = { - 'workflowId' : data.workflowId, - 'subtractionId' : subtractionId, - 'structureId' : structureId, - 'comments' : 'Sent to workflow engine' - } - adapter2.onSuccess.attach(function(sender, fit) { - _this.refresh(_this.subtractionId, _this.macromolecule); - }); - adapter2.addFitStructureData(fit); - - }); - adapter.addWorkflow(workflow, inputs); - -}; +/** + * Example form + * + * @witdh + * @height + */ +function MolarityForm(args) { + this.id = BUI.id(); + this.width = 700; + this.height = 500; + + if (args != null) { + if (args.width != null) { + this.width = args.width; + } + if (args.height != null) { + this.height = args.height; + } + } + + this.onSave = new Event(this); + this.onClose = new Event(this); +} + -/** - * It shows buffer grid with a top bar with "Add" button - * - * @height - * @searchBar - * @collapsed - * @width - */ -function FrameGrid(args) { - this.height = 500; - this.width = 500; - this.searchBar = false; - this.tbar = false; - this.collapsed = false; - - - if (args != null) { - if (args.height != null) { - this.height = args.height; - } - - if (args.searchBar != null) { - this.searchBar = args.searchBar; - } - - if (args.tbar != null) { - this.tbar = args.tbar; - } - - if (args.collapsed != null) { - this.collapsed = args.collapsed; - } - - if (args.width != null) { - this.width = args.width; - } - } -} - -FrameGrid.prototype._edit = function(bufferId) { - var _this = this; - var window = new BufferWindow(); - window.onSuccess.attach(function(sender, proposal) { - _this.store.loadData(BIOSAXS.proposal.getBuffers()); - }); - window.draw(BIOSAXS.proposal.getBufferById(bufferId)); -}; - -FrameGrid.prototype.refresh = function(buffers, experimentId) { - this.experimentId = experimentId; - this.store.loadData(this._prepareData(buffers), false); -}; - -FrameGrid.prototype._prepareData = function(buffers) { - return buffers; -}; - -FrameGrid.prototype._getTbar = function() { - var _this = this; - var actions = []; - - actions.push(Ext.create('Ext.Action', { - icon : '../images/add.png', - text : 'Add Buffer', - disabled : false, - handler : function(widget, event) { - var window = new BufferWindow(); - window.onSuccess.attach(function(sender) { - _this.store.loadData(BIOSAXS.proposal.getBuffers()); - }); - window.draw({}); - } - })); - return actions; -}; - -FrameGrid.prototype.getPanel = function(buffers) { - var _this = this; - - this.store = Ext.create('Ext.data.Store', { - fields : [ 'frameNumber', 'I0', 'Rg', 'Mass', 'Vc', 'Qr', 'quality'], - data : buffers - }); - - this.store.sort('frameNumber'); - - var type = 'Ext.grid.Panel'; - if (this.searchBar == true) { - type = 'Ext.ux.LiveSearchGridPanel'; - } - - this.grid = Ext.create(type, { - store : this.store, - height : this.height, - width : this.width, - margin : 5, - columns : [{ - text : 'Frame', - dataIndex : 'frameNumber', - flex : 1 - },{ - text : 'I0', - dataIndex : 'I0', - flex : 1, - renderer : function(val, y, sample) { - return BUI.formatValuesUnits(sample.data.I0, "", 12, 3); - } - },{ - text : 'Rg', - dataIndex : 'Rg', - flex : 1, - renderer : function(val, y, sample) { - return BUI.formatValuesUnits(sample.data.Rg, "nm", 12, 3); - } - },{ - text : 'Mass', - dataIndex : 'Mass', - flex : 1, - renderer : function(val, y, sample) { - return BUI.formatValuesUnits(sample.data.Mass, "", 12, 3); - } - },{ - text : 'Vc', - dataIndex : 'Vc', - flex : 1, - renderer : function(val, y, sample) { - return BUI.formatValuesUnits(sample.data.Vc, "", 12, 3); - } - },{ - text : 'Qr', - dataIndex : 'Qr', - flex : 1, - renderer : function(val, y, sample) { - return BUI.formatValuesUnits(sample.data.Qr, "", 12, 3); - } - },{ - text : 'Quality', - dataIndex : 'quality', - flex : 1, - renderer : function(val, y, sample) { - return (Number(sample.data.quality) * 100).toFixed(2) + "%"; - } - }, - { - text : '', - renderer : function(val, x, sample) { - if (sample != null) { - return BUI.getZipHTMLByFrameRangeId(_this.experimentId, sample.raw.frameNumber, sample.raw.frameNumber); - } - }, - width : 100 - }], - flex : 1, - viewConfig : { - stripeRows : true - } - }); - - /** Adding the tbar **/ - if (this.tbar) { - this.grid.addDocked({ - xtype : 'toolbar', - items : this._getTbar() - }); - } - return this.grid; -}; - -FrameGrid.prototype.input = function() { - return []; -}; - -FrameGrid.prototype.test = function(targetId) { - var frameGrid = new FrameGrid({ - width : 800, - height : 350, - collapsed : false, - tbar : true - }); - - var panel = frameGrid.getPanel([]); - panel.render(targetId); -}; - - -function PeakGrid(args) { - this.height = 500; - this.searchBar = false; - this.tbar = false; - this.collapsed = false; - this.width = 500; - this.showExtendedColumns = false; - - if (args != null) { - if (args.showExtendedColumns != null) { - this.showExtendedColumns = args.showExtendedColumns; - } - if (args.height != null) { - this.height = args.height; - } - if (args.searchBar != null) { - this.searchBar = args.searchBar; - } - - if (args.tbar != null) { - this.tbar = args.tbar; - } - - if (args.collapsed != null) { - this.collapsed = args.collapsed; - } - - if (args.width != null) { - this.width = args.width; - } - } -} - - -PeakGrid.prototype.refresh = function(buffers) { - this.store.loadData(this._prepareData(buffers), false); -}; - -PeakGrid.prototype._prepareData = function(buffers) { - -// for ( var i = 0; i < buffers.length; i++) { -// buffers[i].name = "Peak #" + (i+1); +MolarityForm.prototype._getNumericWithHelp = function(type, fieldLabel, fieldName, help) { + return Ext.create('Ext.container.Container', { + margin : "10 0 0 10", + items : [ { + xtype : type, + fieldLabel : fieldLabel, + name : fieldName, + id : this.id + fieldName, + decimalPrecision : 6 + }, { + xtype : 'label', + forId : 'myFieldId', + text : help, + margin : "5 0 0 105", + cls : "inline-help" + } ] + }); +}; + + +MolarityForm.prototype._getItems = function() { + this.macromoleculeCombo = BIOSAXS_COMBOMANAGER.getComboMacromoleculeByMacromolecules(this.getMacromuleculesCandidates(this.macromolecule), { + width : 250, + labelWidth : 100, + margin : 10 + }); + + return [ { + xtype : 'container', + flex : 1, + margin : '10 0 0 10', + border : 0, + layout : 'anchor', + defaultType : 'requiredtext', + items : [ this.macromoleculeCombo, + this._getNumericWithHelp("textfield", "Ratio", "ratio", "Number in assymmetric units") + ] + } ]; +}; + +MolarityForm.prototype._persist = function() { + var _this = this; + var macromoleculeId = this.macromoleculeCombo.getValue(); + var ratio = Ext.getCmp(this.id + "ratio").getValue(); + var comments = "Not used yet"; + var dataAdapter = new BiosaxsDataAdapter(); + this.panel.setLoading("Saving"); + dataAdapter.onSuccess.attach(function(sender, args) { + _this.onSave.notify(); + }); + dataAdapter.saveStoichiometry(this.macromolecule.macromoleculeId, macromoleculeId, ratio, comments); +}; + +MolarityForm.prototype._getButtons = function() { + var _this = this; + + function onClose() { + _this.onClose.notify(); + } + + return [ { + text : 'Save', + handler : function() { + _this._persist(); + } + }, { + text : 'Cancel', + handler : function() { + onClose(); + } + } ]; +}; + +MolarityForm.prototype.getPanel = function() { + this.panel = Ext.create('Ext.form.Panel', { +// width : null, + height : this.height, + margin : 2, + border : 1, + defaultType : 'requiredtext', + items : this._getItems(), + buttons : this._getButtons() + }); + return this.panel; +}; + +/** macromolecules contains all macromolecules except this one **/ +MolarityForm.prototype.getMacromuleculesCandidates = function(macromolecule) { + var macromolecules = []; + if ( BIOSAXS.proposal.macromolecules){ + for (var i = 0; i < BIOSAXS.proposal.macromolecules.length; i++) { + var m = BIOSAXS.proposal.macromolecules[i]; + if (this.macromolecule != null){ + if (m.macromoleculeId != this.macromolecule.macromoleculeId) { + macromolecules.push(m); + } + } + } + } + return macromolecules; +}; + + +/** It populates the form **/ +MolarityForm.prototype.refresh = function(macromolecule) { + this.macromolecule = macromolecule; + +}; + + +MolarityForm.prototype.input = function() { + return {}; +}; + + +/** It populates the form **/ +MolarityForm.prototype.test = function(targetId) { + var macromoleculeForm = new MolarityForm(); + var panel = macromoleculeForm.getPanel(); + panel.render(targetId); +}; + +///** +// * +// * @witdh +// * @height +// */ +//function MacromoleculeForm(args) { +// this.id = BUI.id(); +// this.width = 700; +// this.height = 500; +// +// if (args != null) { +// if (args.width != null) { +// this.width = args.width; +// } +// if (args.height != null) { +// this.height = args.height; +// } +// } +// +// this.onClose = new Event(this); +//} +// +//MacromoleculeForm.prototype.getMacromolecule = function() { +// this.macromolecule.name = Ext.getCmp(this.panel.getItemId()).getValues().name; +// this.macromolecule.acronym = Ext.getCmp(this.panel.getItemId()).getValues().acronym; +// this.macromolecule.comments = Ext.getCmp(this.panel.getItemId()).getValues().comments; +// this.macromolecule.extintionCoefficient = Ext.getCmp(this.panel.getItemId()).getValues().extintionCoefficient; +// this.macromolecule.molecularMass = Ext.getCmp(this.panel.getItemId()).getValues().molecularMass; +// return this.macromolecule; +//}; +// +//MacromoleculeForm.prototype.setMacromolecule = function(macromolecule) { +// this.pdbStore.loadData(macromolecule.structure3VOs); +// +//}; +// +//MacromoleculeForm.prototype.getForm = function(macromolecule) { +// this.panel = Ext.createWidget('form', { +// frame : false, +// border : 0, +// padding : 15, +// width : 550, +// height : 350, +// items : [ { +// xtype : 'container', +// layout : 'hbox', +// items : [ { +// xtype : 'container', +// flex : 1, +// border : false, +// layout : 'anchor', +// defaultType : 'requiredtext', +// items : [ { +// fieldLabel : 'Name', +// name : 'name', +// anchor : '95%', +// tooltip : "Name of the macromolecule", +// value : macromolecule.name +// }, { +// fieldLabel : 'Acronym', +// name : 'acronym', +// anchor : '95%', +// value : macromolecule.acronym +// } ] +// }, { +// xtype : 'container', +// flex : 1, +// layout : 'anchor', +// defaultType : 'textfield', +// items : [ { +// xtype : 'numberfield', +// fieldLabel : 'Mol. Mass (Da)', +// name : 'molecularMass', +// value : macromolecule.molecularMass, +// decimalPrecision : 6 +// }, { +// xtype : 'numberfield', +// fieldLabel : 'Extinction coef.', +// name : 'extintionCoefficient', +// value : macromolecule.extintionCoefficient, +// decimalPrecision : 6 +// } ] +// } ] +// }, { +// xtype : 'textareafield', +// name : 'comments', +// fieldLabel : 'Comments', +// value : macromolecule.comments, +// width : this.width - 10 +// }, +// +// { +// html : BUI.getWarningHTML("The macromolecule is unsaved. Save the macromolecule to get access to other tabs"), +// margin : "150 10 10 10", +// id : this.id + "unsavedWarning", +// hidden : !(!macromolecule.macromoleculeId) +// } +// +// ] +// }); +// return this.panel; +//}; +// +//MacromoleculeForm.prototype.save = function() { +// var _this = this; +// +// var adapter = new BiosaxsDataAdapter(); +// adapter.onSuccess.attach(function(sender, proposal) { +// BIOSAXS.proposal.setItems(proposal); +// _this.panel.setLoading(false); +// +// Ext.getCmp(_this.id + "assembly").enable() +// Ext.getCmp(_this.id + "advanced").enable(); +// Ext.getCmp(_this.id + "unsavedWarning").hide(); +// }); +// +// if (this.getMacromolecule().name == "") { +// BUI.showError("Name field is mandatory"); +// return; +// } +// if (this.getMacromolecule().acronym == "") { +// BUI.showError("Acroynm field is mandatory"); +// return; +// } +// +// /** Check if acronym is unique * */ +// if (this.getMacromolecule().macromoleculeId == null) { +// if (BIOSAXS.proposal.getMacromoleculeByAcronym(this.getMacromolecule().acronym) == null) { +// this.panel.setLoading("ISPyB: Saving Macromolecule") +// adapter.saveMacromolecule(this.getMacromolecule()); +// } else { +// alert("There is already an existing macromolecule with the same acronym"); +// +// } +// } else { +// this.panel.setLoading("ISPyB: Saving Macromolecule") +// adapter.saveMacromolecule(this.getMacromolecule()); +// } +// +//}; +// +//MacromoleculeForm.prototype.getPanel = function(macromolecule) { +// var _this = this; +// this.macromolecule = macromolecule; +// return Ext.createWidget('tabpanel', { +// height : this.height, +// margin : 5, +// plain : true, +// style : { +// padding : 5 +// }, +// items : [ { +// tabConfig : { +// title : "General", +// disabled : false +// }, +// items : [ this.getForm(macromolecule) ], +// bbar : [ "->", { +// text : 'Save', +// cls : 'btn-with-border', +// style : { +// +// border : 1 +// }, +// handler : function() { +// _this.save(); +// } +// }, { +// text : 'Close', +// cls : 'btn-with-border', +// handler : function() { +// _this.onClose.notify(); +// } +// } ] +// }, { +// tabConfig : { +// id : this.id + "assembly", +// title : "Assembly", +// tooltip : 'Description of subunits present in the macromolecule', +// // hidden : (!macromolecule.macromoleculeId), +// disabled : (!macromolecule.macromoleculeId) +// }, +// items : [ this.getMolarityGrid(macromolecule) ] +// }, { +// tabConfig : { +// id : this.id + "advanced", +// title : "Advanced Modeling", +// // hidden : (!macromolecule.macromoleculeId), +// tooltip : 'Definition of the description contacts and symetries', +// disabled : (!macromolecule.macromoleculeId) +// }, +// items : [ this.getRigidBodyForm(macromolecule) ] +// } ] +// }); +//}; +// +//MacromoleculeForm.prototype.getRigidBodyForm = function(macromolecule) { +// var _this = this; +// +// // [P1, P2, P3, P4 ,P5 ,P6 ,32, P42, P52, P62] + P222 +// var symmetry = Ext.create('Ext.data.Store', { +// fields : [ 's' ], +// data : [ { +// "s" : "P1" +// }, { +// "s" : "P2" +// }, { +// "s" : "P3" +// }, { +// "s" : "P4" +// }, { +// "s" : "P5" +// }, { +// "s" : "P6" +// }, { +// "s" : "P32" +// }, { +// "s" : "P42" +// }, { +// "s" : "P52" +// }, { +// "s" : "P62" +// }, { +// "s" : "P222" +// } ] +// }); +// +// if (macromolecule.symmetry == null) { +// macromolecule.symmetry = "P1"; +// } +// var comboBox = Ext.create('Ext.form.ComboBox', { +// fieldLabel : 'Symmetry', +// store : symmetry, +// id : 'comboSym', +// queryMode : 'local', +// displayField : 's', +// valueField : 's', +// value : macromolecule.symmetry, +// margin : "10 0 0 0", +// listeners : { +// change : function(combo, newValue, oldValue, eOpts) { +// macromolecule.symmetry = newValue; +// } +// } +// }); +// +// this.rigidBodyPanel = Ext.createWidget('form', { +// frame : false, +// border : 0, +// padding : 10, +// width : 550, +// height : 400, +// items : [ { +// xtype : 'container', +// layout : 'hbox', +// items : [ { +// xtype : 'container', +// border : false, +// layout : 'hbox', +// items : [ { +// xtype : 'label', +// forId : 'myFieldId', +// text : 'Contact Desc:', +// width : 105, +// margin : '0 0 0 0' +// }, { +// xtype : 'textfield', +// hideLabel : true, +// id : "contactsDescriptionFilePath", +// margin : '0 0 0 0', +// width : 300, +// value : macromolecule.contactsDescriptionFilePath +// }, { +// text : 'Upload', +// xtype : 'button', +// margin : "0 0 0 20", +// width : 100, +// handler : function() { +// _this._openUploadDialog(macromolecule.macromoleculeId, "CONTACTS", "Upload Contact Description File"); +// } +// } ] +// } ] +// }, +// +// comboBox, { +// xtype : 'checkbox', +// margin : '10 0 0 5', +// boxLabel : "I want rigid body modeling run on this stuff", +// checked : true, +// width : 300 +// }, _this.getPDBGrid(macromolecule) ] +// }); +// return this.rigidBodyPanel; +//}; +// +//MacromoleculeForm.prototype.update = function() { +// var _this = this; +// BIOSAXS.proposal.onInitialized.attach(function() { +// if (BIOSAXS.proposal != null) { +// var macromolecules = BIOSAXS.proposal.macromolecules; +// for (var i = 0; i < macromolecules.length; i++) { +// if (macromolecules[i].macromoleculeId == _this.macromolecule.macromoleculeId) { +// _this.macromolecule = macromolecules[i]; +// _this.setMacromolecule(_this.macromolecule); +// _this.molarityStore.loadData(_this.parseMolarityData(_this.macromolecule)); +// _this.pdbGrid.setLoading(false); +// Ext.getCmp("contactsDescriptionFilePath").setValue(_this.macromolecule.contactsDescriptionFilePath) +// _this.molarityGrid.setLoading(false); +// +// } +// } +// } +// }); +// this.molarityGrid.setLoading("Updating"); +// this.pdbGrid.setLoading("Updating"); +// BIOSAXS.proposal.init(); +//}; +// +///******************************************************************************* +// * MOLARITY GRID +// ******************************************************************************/ +// +//MacromoleculeForm.prototype.parseMolarityData = function(macromolecule) { +// var data = []; +// if (macromolecule.stoichiometry != null) { +// for (var i = 0; i < macromolecule.stoichiometry.length; i++) { +// data.push({ +// ratio : macromolecule.stoichiometry[i].ratio, +// acronym : macromolecule.stoichiometry[i].macromolecule3VO.acronym, +// comments : macromolecule.stoichiometry[i].macromolecule3VO.comments, +// stoichiometryId : macromolecule.stoichiometry[i].stoichiometryId, +// name : macromolecule.stoichiometry[i].macromolecule3VO.name +// }); +// } +// } +// return data; +//}; +// +//MacromoleculeForm.prototype.getMolarityGrid = function(macromolecule) { +// var _this = this; +// +// this.molarityStore = Ext.create('Ext.data.Store', { +// fields : [ 'acronym', 'ratio', 'comments', 'stoichiometryId', 'name' ], +// data : this.parseMolarityData(macromolecule), +// sorters : { +// property : 'ratio', +// direction : 'DESC' +// } +// }); +// +// this.molarityGrid = Ext.create('Ext.grid.Panel', { +// store : this.molarityStore, +// height : 350, +// padding : 5, +// columns : [ +// +// { +// text : 'Subunit', +// columns : [ { +// text : "Acronym", +// width : 100, +// hidden : false, +// dataIndex : 'acronym', +// sortable : true +// }, { +// text : "Name", +// width : 100, +// hidden : false, +// dataIndex : 'name', +// sortable : true +// }, { +// text : "Comments", +// width : 100, +// dataIndex : 'comments', +// sortable : true +// } ] +// }, { +// text : "Number
in assymmetric units", +// width : 150, +// dataIndex : 'ratio', +// tooltip : 'Number of times this subunit is present in the macromolecule', +// sortable : true +// }, { +// id : this.id + 'MOLARITY_REMOVE', +// flex : 0.1, +// sortable : false, +// renderer : function(value, metaData, record, rowIndex, colIndex, store) { +// return BUI.getRedButton('REMOVE'); +// } +// } ], +// listeners : { +// cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { +// if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'MOLARITY_REMOVE') { +// var dataAdapter = new BiosaxsDataAdapter(); +// dataAdapter.onSuccess.attach(function() { +// _this.molarityGrid.setLoading(false); +// _this.update(); +// }); +// dataAdapter.removeStoichiometry(record.data.stoichiometryId); +// _this.molarityGrid.setLoading("Removing Structure"); +// } +// } +// }, +// buttons : [ { +// text : 'Add molarity', +// handler : function() { +// +// function onClose() { +// w.destroy(); +// _this.update(); +// } +// var w = Ext.create('Ext.window.Window', { +// title : 'Molarity', +// height : 300, +// width : 500, +// modal : true, +// buttons : [ { +// text : 'Save', +// handler : function() { +// var macromoleculeId = (_this.macromoleculeCombo.getValue()); +// var ratio = Ext.getCmp(_this.id + "ratio").getValue(); +// var comments = ""; +// var dataAdapter = new BiosaxsDataAdapter(); +// dataAdapter.onSuccess.attach(function(sender, args) { +// _this.update(); +// w.destroy(); +// }); +// dataAdapter.saveStoichiometry(_this.macromolecule.macromoleculeId, macromoleculeId, ratio, comments); +// } +// }, { +// text : 'Cancel', +// handler : function() { +// onClose(); +// } +// } ], +// items : [ _this.getMolarityForm(macromolecule) ], +// listeners : { +// onEsc : function() { +// onClose(); +// }, +// close : function() { +// onClose(); +// } +// } +// }).show(); +// } +// } ] +// }); +// return this.molarityGrid; +//}; +// +///******************************************************************************* +// * PDB GRID +// ******************************************************************************/ +// +//MacromoleculeForm.prototype._openUploadDialog = function(macromoleculeId, type, title) { +// var _this = this; +// function onClose() { +// w.destroy(); +// _this.update(); +// } +// +// var w = Ext.create('Ext.window.Window', { +// title : title, +// height : 200, +// width : 400, +// modal : true, +// buttons : [ { +// text : 'Close', +// handler : function() { +// onClose(); +// } +// } ], +// layout : 'fit', +// items : { +// html : "" +// }, +// listeners : { +// onEsc : function() { +// onClose(); +// }, +// close : function() { +// onClose(); +// } +// } +// }).show(); +//}; +// +//MacromoleculeForm.prototype._getPlugins = function() { +// var _this = this; +// var plugins = []; +// // if (this.updateRowEnabled) { +// plugins.push(Ext.create('Ext.grid.plugin.RowEditing', { +// clicksToEdit : 1, +// listeners : { +// validateedit : function(grid, e) { +// /** Comments are always updatable* */ +// e.record.raw.symmetry = e.newValues.symmetry; +// e.record.raw.multiplicity = e.newValues.multiplicity; +// +// var adapter = new BiosaxsDataAdapter(); +// adapter.onSuccess.attach(function(sender, measurement) { +// // _this.grid.setLoading(false); +// }); +// adapter.onError.attach(function() { +// alert("Error"); +// // _this.grid.setLoading(false); +// }); +// +// // _this.grid.setLoading(); +// adapter.saveStructure(e.record.raw); +// } +// } +// })); +// // } +// return plugins; +//}; +// +//MacromoleculeForm.prototype.getPDBGrid = function(macromolecule) { +// var _this = this; +// +// var data = macromolecule.structure3VOs; +// +// // /** Getting PDB from subunits **/ +// // if (macromolecule != null){ +// // if (macromolecule.stoichiometry != null){ +// // for (var i =0; i < macromolecule.stoichiometry.length; i++){ +// // var stoichiometry = macromolecule.stoichiometry[i]; +// // if (stoichiometry.macromolecule3VO != null){ +// // if (stoichiometry.macromolecule3VO.structure3VOs != null){ +// // for (var j = 0; j < stoichiometry.macromolecule3VO.structure3VOs.length; +// // j++) { +// // var structure = stoichiometry.macromolecule3VO.structure3VOs[j]; +// // structure["isSubunit"] = stoichiometry.macromolecule3VO.acronym; +// // data.push(structure) +// // } +// // } +// // } +// // } +// // } +// // } +// +// this.pdbStore = Ext.create('Ext.data.Store', { +// fields : [ 'filePath', 'structureId', 'structureType', 'symmetry', 'structureId', 'name', 'multiplicity' ], +// data : macromolecule.structure3VOs, +// groupField : 'structureType', +// sorters : { +// property : 'structureId', +// direction : 'DESC' +// } +// }); +// +// var groupingFeature = Ext.create('Ext.grid.feature.Grouping', { +// groupHeaderTpl : Ext.create('Ext.XTemplate', +// "
{name:this.formatName}
", { +// formatName : function(name) { +// return name; +// } +// }), +// hideGroupedHeader : true, +// startCollapsed : false +// }); +// +// this.pdbGrid = Ext.create('Ext.grid.Panel', { +// margin : "15 0 0 5", +// height : 250, +// store : this.pdbStore, +// plugins : _this._getPlugins(), +// buttons : [ { +// // text : 'Add PDB file', +// text : 'Add Modeling Option', +// handler : function() { +// _this._openUploadDialog(macromolecule.macromoleculeId, "PDB", 'Upload PDB File'); +// } +// } +// +// ], +// columns : [ +// { +// text : "structureId", +// flex : 0.2, +// hidden : true, +// dataIndex : 'structureId', +// sortable : true +// }, +// { +// text : "File", +// flex : 0.5, +// dataIndex : 'filePath', +// sortable : true, +// hidden : true +// }, +// { +// text : "PDB", +// flex : 0.4, +// dataIndex : 'name', +// sortable : true +// }, +// { +// text : "Symmetry", +// flex : 0.4, +// dataIndex : 'symmetry', +// sortable : true, +// editor : { +// xtype : 'combobox', +// typeAhead : true, +// triggerAction : 'all', +// selectOnTab : true, +// store : [ [ "P1", "P1" ], [ "P2", "P2" ], [ "P3", "P3" ], [ "P4", "P4" ], [ "P5", "P5" ], [ "P6", "P6" ], [ "P32", "P32" ], [ "P42", "P42" ], +// [ "P52", "P52" ], [ "P62", "P62" ], [ "P222", "P222" ] ], +// } +// }, { +// text : "Multiplicity", +// flex : 0.4, +// dataIndex : 'multiplicity', +// sortable : true, +// editor : { +// xtype : 'textfield' +// } +// +// }, { +// text : "Subunit", +// flex : 0.2, +// dataIndex : 'isSubunit', +// sortable : true, +// hidden : true +// }, { +// text : "Type", +// flex : 0.2, +// dataIndex : 'structureType', +// sortable : true, +// hidden : true +// }, +// +// { +// id : this.id + 'REMOVE', +// flex : 0.2, +// hidden : true, +// sortable : false, +// renderer : function(value, metaData, record, rowIndex, colIndex, store) { +// return BUI.getRedButton('REMOVE'); +// } +// }, ], +// +// listeners : { +// itemdblclick : function(dataview, record, item, e) { +// _this._editExperiment(record.raw.experimentId); +// }, +// cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { +// +// if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'REMOVE') { +// var dataAdapter = new BiosaxsDataAdapter(); +// dataAdapter.onSuccess.attach(function() { +// _this.pdbGrid.setLoading(false); +// _this.update(); +// }); +// dataAdapter.removeStructure(record.data.structureId); +// _this.pdbGrid.setLoading("Removing PDB file"); +// } +// +// } +// }, +// viewConfig : { +// getRowClass : function(record, rowIdx, params, store) { +// if (record.raw.isSubunit != null) { +// return "blue-row"; +// } +// } +// } +// }); +// +// return this.pdbGrid; +//}; +// +//MacromoleculeForm.prototype.getMolarityForm = function(macromolecule) { +// var _this = this; +// var data = []; +// for (var i = 0; i < BIOSAXS.proposal.macromolecules.length; i++) { +// var m = BIOSAXS.proposal.macromolecules[i]; +// if (m.macromoleculeId != macromolecule.macromoleculeId) { +// data.push(m); +// } +// // } - /** Adding information of buffer **/ - if (buffers.length > 0){ - buffers.unshift({ - name : "BUFFER", - start : 0, - experimentId : buffers[0].experimentId, - end : buffers[0].start - 1 - }); - } - - - return buffers; -}; - -PeakGrid.prototype._getTbar = function() { - var _this = this; - var actions = []; +// this.macromoleculeCombo = BIOSAXS_COMBOMANAGER.getComboMacromoleculeByMacromolecules(data, { +// width : 250, +// labelWidth : 100, +// margin : 10 +// }); +// +// return Ext.createWidget('form', { +// +// frame : false, +// border : 0, +// // padding : 15, +// width : 550, +// height : 350, +// items : [ { +// xtype : 'container', +// flex : 1, +// border : false, +// layout : 'anchor', +// defaultType : 'requiredtext', +// items : [ this.macromoleculeCombo, { +// xtype : 'numberfield', +// name : 'Ratio', +// id : _this.id + "ratio", +// fieldLabel : 'Number in assymmetric units', +// value : 1, +// decimalPrecision : 6, +// margin : 10 +// }, { +// xtype : 'textareafield', +// name : 'comments', +// fieldLabel : 'Comments', +// margin : 10, +// width : 400, +// value : "" +// +// } ] +// } ] +// }); +// +//}; +// +///******************************************************************************* +// * JAVASCRIPT DOC +// ******************************************************************************/ +//MacromoleculeForm.prototype.input = function() { +// return { +// macromolecule : DATADOC.getMacromolecule_10() +// }; +//}; +// +//MacromoleculeForm.prototype.test = function(targetId) { +// var macromoleculeForm = new MacromoleculeForm(); +// var panel = macromoleculeForm.getPanel(macromoleculeForm.input().macromolecule); +// panel.render(targetId); +//}; + +function ResultSummaryForm() { + this.radiationDamageThreshold = BUI.getRadiationDamageThreshold(); + this.qualityThreshold = BUI.getQualityThreshold(); - actions.push(Ext.create('Ext.Action', { - icon : '../images/add.png', - text : 'Add Buffer', - disabled : false, - handler : function(widget, event) { - var window = new BufferWindow(); - window.onSuccess.attach(function(sender) { - _this.store.loadData(BIOSAXS.proposal.getBuffers()); - }); - window.draw({}); - } - })); - return actions; -}; + /** Data clusters with bufers **/ + this.clusterByBuffers = false; + + /** Visualization are groupeb by concenttrations. Ex: 4.5mg/ml and 4.7mg/ml will be clusterized together **/ + this.collapseConcentrations = true; + + this.summaryHeight = 350; + this.id = BUI.id(); -PeakGrid.prototype.getPanel = function(buffers) { var _this = this; - this.peakCount = 0; - this.store = Ext.create('Ext.data.Store', { - fields : ['name', {name:'start', type : 'int'}, {name:'end', type : 'int'}], - data : buffers - - }); - - this.store.sort('start'); - var type = 'Ext.grid.Panel'; - if (this.searchBar == true) { - type = 'Ext.ux.LiveSearchGridPanel'; - } - this.grid = Ext.create(type, { - store : this.store, - height : this.height, - width : this.width, - margin : 5, - columns : [ - { - text : '', - dataIndex : 'name', - width : 100, - hidden : _this.showExtendedColumns, - renderer : function(val, y, sample) { - if (sample.data.name == "BUFFER") - return "BUFFER"; - _this.peakCount++; - return "Peak #" + _this.peakCount; - } - - - }, - { - text : 'Peaks', - dataIndex : 'start', - sortable : true, - width : 100, - hidden: !_this.showExtendedColumns, - renderer : function(val, y, sample) { - return "From " + Number(sample.raw.start) + " to " + Number(sample.raw.end); - } - }, - { - text : 'Frames', - hidden : _this.showExtendedColumns, - columns : [ - { - text : 'Start', - dataIndex : 'start', - sortable : true, - hidden : _this.showExtendedColumns, - width : 100, - renderer : function(val, y, sample) { - return Number(val); - } - },{ - text : 'End', - dataIndex : 'end', - width : 100, - hidden : _this.showExtendedColumns, - renderer : function(val, y, sample) { - return Number(val); - } - },{ - text : 'Total', - dataIndex : 'end', - width : 100, - hidden : _this.showExtendedColumns, - renderer : function(val, y, sample) { - return "" + (sample.raw.end - sample.raw.start) + " frames"; - } - } - ] - } -// , -// { -// text : '', -// hidden : _this.showExtendedColumns, -// renderer : function(val, x, sample) { -// if (sample != null) { -// return BUI.getZipHTMLByFrameRangeId(sample.raw.experimentId, sample.raw.start, sample.raw.end); -// } -// }, -// width : 100 -// } - ], - flex : 1, - viewConfig : { - stripeRows : true, - getRowClass : function(record, rowIdx, params, store) { - if (record.raw.name == "BUFFER") { - return "blue-row"; - } - } - } + this.qualityControlResultsWidget = new QualityControlResultsWidget({ + qualityThreshold : this.radiationDamageThreshold, + radiationDamageThreshold : this.qualityThreshold + + }); + this.qualityControlResultsWidget.onQualityChanged.attach(function(sender, value) { + _this.qualityThreshold = value; + _this.thresholdsChanged(); }); - /** Adding the tbar **/ - if (this.tbar) { - this.grid.addDocked({ - xtype : 'toolbar', - items : this._getTbar() - }); - } - return this.grid; -}; + this.qualityControlResultsWidget.onRadiationDatamageChanged.attach(function(sender, value) { + _this.radiationDamageThreshold = value; + _this.thresholdsChanged(); -PeakGrid.prototype.input = function() { - return []; -}; + }); + this.concentrationHTMLTableWidget = new ConcentrationHTMLTableWidget({ + height : 250, + showBufferColumns : this.clusterByBuffers + }); -PeakGrid.prototype.test = function(targetId) { - var PeakGrid = new PeakGrid({ - width : 800, + this.plot = new BoxWhiskerGraph({ + targetId : _this.id + '_boxPlot', height : 350, - collapsed : false, - tbar : true + width : 450, + maxBoxWidth : 25 }); - var panel = PeakGrid.getPanel([]); - panel.render(targetId); -}; - -/** - * Macromolecule Grid showing macromolecules and adding anb updating buttons - * - * @height - * @maxHeight - * @width - * @cssFontStyle - * @searchBar makes this grid as Ext.ux.LiveSearchGridPanel - * @tbar top bar containing "Add" and "Update From SMIS" button - * @collapsed - * @collapsible - * @btnEditVisible - * @btnRemoveVisible - * @multiselect makes it multiselect using Ext.selection.CheckboxModel - * - * #onSelected - * #onMacromoleculesChanged - */ -function MacromoleculeGrid(args) { - this.height = 500; - this.width = 500; - this.id = BUI.id(); - this.maxHeight = this.height; + this.rangePlot = new RangeWhiskerGraph({ + targetId : _this.id + '_rangePlot', + height : 350, + width : 450, + maxBoxWidth : 25 + }); +} - this.searchBar = false; - this.tbar = false; +ResultSummaryForm.prototype.thresholdsChanged = function() { + var parsedData = this.prepareData(this.rawData); - this.collapsible = true; - this.collapsed = true; + var filtered = JSON.parse(JSON.stringify(parsedData)); + filtered.concentration.concentrations = []; + for ( var conc in parsedData.concentration.concentrations) { + if (parsedData.concentration.concentrations[conc].calculation.rgGnom.validNumber > 0) { + filtered.concentration.concentrations.push(parsedData.concentration.concentrations[conc]); + } + } - this.btnEditVisible = true; - this.btnRemoveVisible = false; - this.multiselect = false; + this.plotWhisker(filtered); + this.plotRange(filtered); - /** Font style applied to the acronym column **/ - this.cssFontStyle = null; + this.concentrationHTMLTableWidget.refresh(parsedData); +}; - if (args != null) { - if (args.height != null) { - this.height = args.height; - this.maxHeight = this.height; - } - if (args.maxHeight != null) { - this.maxHeight = args.maxHeight; - } - if (args.width != null) { - this.width = args.width; - } - if (args.cssFontStyle != null) { - this.cssFontStyle = args.cssFontStyle; - } +/** Given a frame object (object returned by analyzeFrames()) and depending of the threshold indicates if a datacollection has radiation damage **/ +ResultSummaryForm.prototype.hasRadiationDamage = function(frameObject) { + var bb = frameObject.bufferBeforeFramesMerged / frameObject.framesCount; + var ba = frameObject.bufferAfterFramesMerged / frameObject.framesCount; + var mol = frameObject.framesMerge / frameObject.framesCount; + return !((bb >= this.radiationDamageThreshold) && (ba >= this.radiationDamageThreshold) && (mol >= this.radiationDamageThreshold)); +}; - if (args.searchBar != null) { - this.searchBar = args.searchBar; - } - if (args.tbar != null) { - this.tbar = args.tbar; - } - if (args.collapsible != null) { - this.collapsible = args.collapsible; - } - if (args.collapsed != null) { - this.collapsed = args.collapsed; - } - if (args.btnEditVisible != null) { - this.btnEditVisible = args.btnEditVisible; - } - if (args.btnRemoveVisible != null) { - this.btnRemoveVisible = args.btnRemoveVisible; - } - if (args.multiselect != null) { - this.multiselect = args.multiselect; - } +/** Return (frameObject) an object with the information about the frames for a data collection**/ +ResultSummaryForm.prototype.analyzeFrames = function(dataCollectionRow) { + return { + bufferAfterFramesMerged : dataCollectionRow.bufferAfterFramesMerged, + bufferBeforeFramesMerged : dataCollectionRow.bufferBeforeFramesMerged, + framesCount : dataCollectionRow.framesCount, + framesMerge : dataCollectionRow.framesMerge + }; +}; + +ResultSummaryForm.prototype.detectDataCollectionWarnings = function(dataCollectionRow) { + var frameObject = this.analyzeFrames(dataCollectionRow); + var warnings = { + count : 0, + type : [] + }; + + if (this.hasRadiationDamage(frameObject)) { + warnings.count = warnings.count + 1; + warnings.type.push("RADIATION DAMAGE"); } - this.onSelected = new Event(); - - this.onMacromoleculesChanged = new Event(); -} + if (Number(dataCollectionRow.quality) < this.qualityThreshold) { + warnings.count = 1;//warnings.count + 1; + warnings.type.push("Quality <" + this.qualityThreshold); + } + return warnings; +}; +/** Return array composed by {concentration} objects **/ +ResultSummaryForm.prototype.getConcentrations = function(data) { + var concentrationsId = {}; + for ( var i = 0; i < data.length; i++) { + var warning = this.detectDataCollectionWarnings(data[i]); + var id = data[i].conc;// + "_" + data[i].bufferId; + if (this.clusterByBuffers) { + id = data[i].conc + "_" + data[i].bufferId; + } + if (concentrationsId[id] == null) { + concentrationsId[id] = { + id : id, + concentration : Number(data[i].conc).toFixed(2), + bufferId : data[i].bufferId, + bufferAcronym : data[i].bufferAcronym, + rgGuinier : [], + i0Guinier : [], + rgGnom : [], + dMax : [], + quality : [], + frames : [], + frames_warning : warning.count + }; + } else { + concentrationsId[id].frames_warning = concentrationsId[id].frames_warning + warning.count; + } + concentrationsId[id].frames.push(data[i]); -MacromoleculeGrid.prototype.edit = function(macromolecule) { - var _this = this; - var window = new MacromoleculeWindow(); - window.onSave.attach(function(sender) { - _this.store.loadData(BIOSAXS.proposal.getMacromolecules()); - _this.onMacromoleculesChanged.notify(); - }); - window.draw(macromolecule); -}; + if (warning.count == 0) { + concentrationsId[id].rgGuinier.push(data[i].rgGuinier); + concentrationsId[id].i0Guinier.push(data[i].I0); + concentrationsId[id].quality.push(data[i].quality); + concentrationsId[id].rgGnom.push(data[i].rgGnom); + concentrationsId[id].dMax.push(data[i].dmax); + } -MacromoleculeGrid.prototype.getTbar = function() { - var _this = this; - var actions = []; + } + var concentrations = []; + for ( var item in concentrationsId) { + if (concentrationsId.hasOwnProperty(item)) { + concentrations.push({ + concentration : concentrationsId[item].concentration, + id : item, + bufferId : Number(concentrationsId[item].bufferId).toFixed(2), + bufferAcronym : concentrationsId[item].bufferAcronym, + rgGuinier : concentrationsId[item].rgGuinier, + /** Frames **/ + frames : concentrationsId[item].frames, + frames_warning : concentrationsId[item].frames_warning, + /** Calculation **/ + calculation : { + rgGuinier : BUI.getStandardDeviation(concentrationsId[item].rgGuinier), + i0Guinier : BUI.getStandardDeviation(concentrationsId[item].i0Guinier), + quality : BUI.getStandardDeviation(concentrationsId[item].quality), + rgGnom : BUI.getStandardDeviation(concentrationsId[item].rgGnom), + dMax : BUI.getStandardDeviation(concentrationsId[item].dMax) - actions.push(Ext.create('Ext.Action', { - icon : '../images/add.png', - text : 'Add macromolecule', - disabled : false, - handler : function(widget, event) { - _this.edit(); - } - })); - actions.push("->"); - actions.push(Ext.create('Ext.Action', { - icon : '../images/folder_go.png', - text : 'Update From SMIS', - tooltip : "Retrieve all the macromolecules of your proposal from SMIS database", - disabled : false, - handler : function(widget, event) { - _this.grid.setLoading("Connecting to SMIS"); - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, data) { - BIOSAXS.proposal.setMacromolecules(data.macromolecules); - _this.refresh(BIOSAXS.proposal.macromolecules); - _this.grid.setLoading(false); - }); - adapter.onError.attach(function(sender, data) { - _this.grid.setLoading(false); + } }); - adapter.updateDataBaseFromSMIS(); } - })); - return actions; -}; + } -MacromoleculeGrid.prototype.deselectAll = function() { - this.grid.getSelectionModel().deselectAll(); + return { + concentrations : concentrations + }; }; -MacromoleculeGrid.prototype.selectById = function(macromoleculeId) { - this.grid.getSelectionModel().deselectAll(); - for ( var i = 0; i < this.grid.getStore().data.items.length; i++) { - var item = this.grid.getStore().data.items[i].raw; - if (item.macromoleculeId == macromoleculeId) { - this.grid.getSelectionModel().select(i); +ResultSummaryForm.prototype.prepareData = function(data) { + /** Return array composed by {acronym, bufferId} objects **/ + function getBuffers(data) { + var buffersId = {}; + for ( var i = 0; i < data.length; i++) { + buffersId[data[i].bufferId] = data[i].acronym; + } + var buffers = []; + for ( var id in buffersId) { + if (buffersId.hasOwnProperty(id)) { + buffers.push({ + acronym : buffersId[id], + bufferId : id + }); + } } + return buffers; } -}; -MacromoleculeGrid.prototype.refresh = function(macromolecules) { - this.store.loadData(macromolecules, false); + /** Get a string with all the concentrations **/ + function getConcentrationString(parseConcentrations) { + var concentrations = []; + for ( var i = 0; i < parseConcentrations.concentrations.length; i++) { + concentrations.push(parseConcentrations.concentrations[i].concentration + " mg/ml "); + } + return concentrations.toString(); + } + + var parseConcentrations = this.getConcentrations(data); + + return { + dataCollectionCount : data.length, + buffers : getBuffers(data), + concentration : parseConcentrations, + concentrationLabel : getConcentrationString(parseConcentrations) + }; }; -MacromoleculeGrid.prototype.getColumns = function() { - var _this = this; - var columns = [ - { - text : 'Acronym', - dataIndex : 'acronym', - id : this.id + "acronym", - flex : 1, - renderer : function(value, metaData, record, rowIndex, colIndex, store) { - if (_this.cssFontStyle != null) { - return "" + value + ""; +ResultSummaryForm.prototype.getDataForWhisker = function(data) { + var clusters = []; + + var concentrations = {}; + var i = 0; + var conc = 0; + if (this.collapseConcentrations) { + for (i = 0; i < data.concentration.concentrations.length; i++) { + conc = data.concentration.concentrations[i]; + var concentration = Number(conc.concentration).toFixed(0); + if (concentrations[concentration] == null) { + concentrations[concentration] = {}; + concentrations[concentration].concentration = concentration; + concentrations[concentration].calculation = {}; + concentrations[concentration].calculation.rgGuinier = {}; + concentrations[concentration].calculation.rgGuinier.values = []; + concentrations[concentration].calculation.rgGuinier.values = conc.calculation.rgGuinier.values; + concentrations[concentration].calculation.rgGnom = {}; + concentrations[concentration].calculation.rgGnom.values = []; + concentrations[concentration].calculation.rgGnom.values = conc.calculation.rgGnom.values; + } else { + concentrations[concentration].calculation.rgGuinier.values = concentrations[concentration].calculation.rgGuinier.values + .concat(conc.calculation.rgGuinier.values); + concentrations[concentration].calculation.rgGnom.values = concentrations[concentration].calculation.rgGnom.values + .concat(conc.calculation.rgGnom.values); } - return value; } - }, { - text : 'Name', - dataIndex : 'name', - id : this.id + "name", - flex : 1, - hidden : true - } ]; - if (this.btnEditVisible) { - columns.push({ - id : _this.id + 'buttonEditMacromolecule', - width : 85, - sortable : false, - renderer : function(value, metaData, record, rowIndex, colIndex, store) { - if (_this.btnEditVisible) { - return BUI.getGreenButton('EDIT'); - } - return null; + /** From object to array **/ + var array = []; + for ( var key in concentrations) { + if (concentrations.hasOwnProperty(key)) { + array.push(concentrations[key]); } + } + data.concentration.concentrations = array; + } + + for (i = 0; i < data.concentration.concentrations.length; i++) { + conc = data.concentration.concentrations[i]; + clusters.push({ + name : conc.concentration + "mg/ml", + concentration : Number(conc.concentration), + x : Number(conc.concentration), + classes : [] + }); + clusters[clusters.length - 1].classes.push({ + name : "Guinier", + color : '#9A2EFE', + values : conc.calculation.rgGuinier.values + + }); + clusters[clusters.length - 1].classes.push({ + name : "P(r)", + color : '#2E64FE', + values : conc.calculation.rgGnom.values + }); } - if (this.btnRemoveVisible) { - columns.push({ - id : _this.id + 'buttonRemoveMacromolecule', - width : 85, - sortable : false, - renderer : function(value, metaData, record, rowIndex, colIndex, store) { - if (_this.btnRemoveVisible) { - return BUI.getRedButton('REMOVE'); - } - return null; - } + return { + clusters : clusters.sort(function(a, b) { + return a.concentration - b.concentration; + }) + }; +}; + +ResultSummaryForm.prototype.getDataForWhiskerQuality = function(data) { + var clusters = []; + + for ( var i = 0; i < data.concentration.concentrations.length; i++) { + var conc = data.concentration.concentrations[i]; + clusters.push({ + name : conc.concentration + "mg/ml", + classes : [] + }); + clusters[clusters.length - 1].classes.push({ + name : "Quality", + values : conc.calculation.quality.values + }); } - return columns; + return { + clusters : clusters + }; +}; + +ResultSummaryForm.prototype.plotQualityWhisker = function(parsedData) { + this.qualityPlot.refresh(this.getDataForWhiskerQuality(parsedData)); }; +ResultSummaryForm.prototype.plotWhisker = function(parsedData) { + this.plot.refresh(this.getDataForWhisker(parsedData)); +}; -/** Returns the grid **/ -MacromoleculeGrid.prototype.getPanel = function() { +ResultSummaryForm.prototype.plotRange = function(parsedData) { + this.rangePlot.refresh(this.getDataForWhisker(parsedData)); +}; + +ResultSummaryForm.prototype.getPanel = function(data) { var _this = this; + _this.rawData = data; + var parsedData = this.prepareData(data); - this.store = Ext.create('Ext.data.Store', { - fields : [ 'macromoleculeId', 'name', 'acronym' ] - }); + this.panel = Ext + .createWidget( + 'form', + { + bodyPadding : 20, + frame : false, + border : 0, + width : this.width, + height : this.summaryHeight + 1000, + items : [ { + xtype : 'container', + layout : 'hbox', + items : [ { + xtype : 'container', + flex : 1, + border : false, + layout : 'anchor', + defaultType : 'textfield', + items : [ + _this.qualityControlResultsWidget.getPanel(), + { + xtype : 'fieldset', + width : 950, + margin : "20, 0 0 0", + height : this.summaryHeight + 500, + items : [ + { + html : "
Concentration Analysis
", + border : 0, + width : 900 - var type = 'Ext.grid.Panel'; - if (this.searchBar == true) { - type = 'Ext.ux.LiveSearchGridPanel'; - } + }, + { + html : this.concentrationHTMLTableWidget.getPanel(parsedData), + border : 0, + width : 900, + margin : "10, 0 0 10" - this.selModel = Ext.create('Ext.selection.RowModel', { -// allowDeselect : true, -// mode : 'MULTI', - listeners : { - selectionchange : function(sm, selections) { - var selected = []; - for (var i = 0; i < selections.length; i++) { - selected.push(selections[i].raw); - } - _this.onSelected.notify(selected); - } - } - }); + }, + { + xtype : 'container', + layout : 'vbox', + border : 5, + items : [ + { + html : "
Rg based on Guinier and Gnom
", + border : 8, + width : 900, + margin : "20 0 0 10" - this.grid = Ext.create(type, { - id : this.id, - title : 'Macromolecules', - collapsible : this.collapsible, - collapsed : this.collapsed, - store : this.store, - height : this.height, - maxHeight : this.maxHeight, - selModel : this.selModel, - columns : this.getColumns(), - width : this.width, - viewConfig : { - stripeRows : true, - listeners : { - 'celldblclick' : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { - _this.edit(BIOSAXS.proposal.getMacromoleculeById(record.data.macromoleculeId)); - }, - 'cellclick' : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { - if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonEditMacromolecule') { - _this.edit(BIOSAXS.proposal.getMacromoleculeById(record.data.macromoleculeId)); - } - if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonRemoveMacromolecule') { - BUI.showBetaWarning(); - } - } + }, { + xtype : 'container', + layout : 'hbox', + items : [ { + xtype : 'container', + layout : 'vbox', + items : [ { + html : "
", + border : 0, + width : this.plot.width, + padding : "10 0 0 20", + listeners : { + afterrender : function() { + _this.plotWhisker(parsedData); - } - } - }); + } + } + }, { + html : "
Concentration (mg/ml)
", + width : 450, + border : 0, + margin : "-50 0 0 0" + + } ] + }, { + xtype : 'container', + layout : 'vbox', + items : [ { + html : "
", + border : 0, + width : this.rangePlot.width, + padding : "10 0 0 10", + listeners : { + afterrender : function() { + _this.plotRange(parsedData); + } + } + }, { + html : "
Concentration (mg/ml)
", + width : 450, + border : 0, + margin : "-50 0 0 0" + + } ] + } ] + } ] + } ] + } ] + } + + ] + } ] + }); + return this.panel; - /** Adding the tbar **/ - if (this.tbar) { - this.grid.addDocked({ - xtype : 'toolbar', - items : this.getTbar() - }); - } - return this.grid; }; -MacromoleculeGrid.prototype.input = function() { +ResultSummaryForm.prototype.input = function() { return { - proposal : DATADOC.getProposal_10() + data : DATADOC.getData_3367() }; }; -MacromoleculeGrid.prototype.test = function(targetId) { - var macromoleculeGrid = new MacromoleculeGrid({ - width : 800, - height : 350, - collapsed : false, - tbar : true - }); - - BIOSAXS.proposal = new Proposal(macromoleculeGrid.input().proposal); - var panel = macromoleculeGrid.getPanel(BIOSAXS.proposal.macromolecules); +ResultSummaryForm.prototype.test = function(targetId) { + var resultSummaryForm = new ResultSummaryForm(); + var panel = resultSummaryForm.getPanel(resultSummaryForm.input().data); panel.render(targetId); + }; -/** - * Shows measurements with their attributes as specimen, exposure temperature, - * volume to load, transmission etc... - * - * @addBtnMultipleEdit: if true add a button for changing measurements' - * parameters by choosing multiple measurements. It opens - * MultipleEditMeasurementGridWindow - * @collapsed: if true it doesn't show buffer's measurements - * @editor: hashmap containing columns to be edited. It is an array of a json - * like editor of Ext - * @selModel - * @collapseBtnEnable - * @removeBtnEnabled Ext.selection.RowModel - * @addBtnEnable - * @updateRowEnabled true/false if update plugin is set to the grid - * @showTitle - * @title - * @isStatusColumnHidden - * @isPriorityColumnHidden - * @isTimeColumnHidden - * @estimateTime - * @margin - * @tbar - * @height - * @maxHeight - * @minHeight - * @width - * @maxWidth - * @resizable - * @experimentColorBased when colors for buffers and macromolecules are not - * selected by the proposal but by experiment, so the - * number of colors is smaller +/** + * Example form + * + * @witdh + * @height + */ +function RigibBodyModelingForm(args) { + this.id = BUI.id(); + this.width = 700; + this.height = 500; + + if (args != null) { + if (args.width != null) { + this.width = args.width; + } + if (args.height != null) { + this.height = args.height; + } + } + + var _this = this; + this.rigidBodyGrid = new AprioriRigidBodyGrid(); + + this.rigidBodyGrid.onUploadFile.attach(function(sender, type, title){ + _this._openUploadDialog(_this.macromolecule.macromoleculeId, type, title); + }); + + this.rigidBodyGrid.onRemove.attach(function(sender, type, title){ + _this._update(); + }); + + this.onSave = new Event(this); + +} + +RigibBodyModelingForm.prototype._getItems = function() { + var _this = this; + + + return [ { + xtype : 'label', + forId : 'myFieldId', + text : 'Information for model fit, mixture analysis and rigid body modeling', + margin : '15 0 20 10', + cls : "inline-help" + }, + this.rigidBodyGrid.getPanel(), + { + xtype : 'label', + forId : 'myFieldId', + text : 'Distance restraints may be imposed on the model using contacts conditions file (OPTIONAL)', + margin : '25 0 5 10', + cls : "inline-help" + }, + { + xtype : 'container', + layout : 'hbox', + items : [ { + xtype : 'container', + border : false, + layout : 'hbox', + items : [ { + xtype : 'label', + forId : 'myFieldId', + text : 'Contact Description File: (Optional)', + width : 150, + margin : '10 0 0 10' + }, { + id : this.id + "contactsDescriptionFilePath", + xtype : 'textfield', + hideLabel : true, + margin : '10 0 0 0', + width : 200, + disabled: true + }, { + text : 'Upload', + xtype : 'button', + margin : "10 0 0 10", + width : 80, + handler : function() { + + _this._openUploadDialog(_this.macromolecule.macromoleculeId, "CONTACTS", "Upload Contact Description File"); + } + }, { + text : 'Remove', + id : _this.id + "_remove", + xtype : 'button', + margin : "10 0 0 10", + width : 80, + handler : function() { + _this.macromolecule.contactsDescriptionFilePath = null; + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, proposal) { + /** Updating the proposal global object **/ + BIOSAXS.proposal.setItems(proposal); + _this.panel.setLoading(false); + + var saved = BIOSAXS.proposal.getMacromoleculeByAcronym(_this.macromolecule.acronym); + /** Refreshing to check that everything is ok **/ + _this.refresh(saved); + _this.onSave.notify(saved); + }); + + _this.panel.setLoading("Saving Macromolecule") + adapter.saveMacromolecule(_this.macromolecule); + } + } ] + } ] + }, { + xtype : 'panel', + html : "Go to SASREF manual for further information", + margin : "10 0 0 160", + border : 0 + + }, + { + xtype : 'checkbox', + margin : '10 0 0 5', + boxLabel : "I want rigid body modeling run on this stuff", + checked : true, + width : 300 + } ] + +}; + +/** Because update is a jsp page we don't know if the user has uploaded a file or not then we need to refresh **/ +RigibBodyModelingForm.prototype._update = function(macromoleculeId, type, title) { + var _this = this; + BIOSAXS.proposal.onInitialized.attach(function() { + if (BIOSAXS.proposal != null) { + _this.refresh(BIOSAXS.proposal.getMacromoleculeById(_this.macromolecule.macromoleculeId)); + _this.panel.setLoading(false); + } + }); + this.panel.setLoading(); + BIOSAXS.proposal.init(); +}; + +RigibBodyModelingForm.prototype._openUploadDialog = function(macromoleculeId, type, title) { + var _this = this; + function onClose() { + w.destroy(); + _this._update(); + + } + + var w = Ext.create('Ext.window.Window', { + title : title, + height : 200, + width : 400, + modal : true, + buttons : [ { + text : 'Close', + handler : function() { + onClose(); + } + } ], + layout : 'fit', + items : { + html : "" + }, + listeners : { + onEsc : function() { + onClose(); + }, + close : function() { + onClose(); + } + } + }).show(); +}; + +RigibBodyModelingForm.prototype._getButtons = function() { + return []; +}; + +RigibBodyModelingForm.prototype.getPanel = function() { + var _this = this; + this.panel = Ext.create('Ext.form.Panel', { + width : this.width, + height : this.height, + margin : 10, + border : 1, + defaultType : 'textfield', + items : this._getItems(), + buttons : this._getButtons(), + listeners : { + afterrender : function(){ + _this._populate(); + + } + } + }); + return this.panel; +}; + +/** Populates could be call when the DOM is not filled yet **/ +RigibBodyModelingForm.prototype._populate = function() { + if (this.macromolecule != null){ + if (Ext.getCmp(this.id + "contactsDescriptionFilePath") != null){ + if (this.macromolecule.contactsDescriptionFilePath != null){ + Ext.getCmp(this.id + "contactsDescriptionFilePath").setValue(this.macromolecule.contactsDescriptionFilePath); + Ext.getCmp(this.id + "_remove").enable(); + } + else{ + Ext.getCmp(this.id + "_remove").disable(); + Ext.getCmp(this.id + "contactsDescriptionFilePath").setValue(""); + + } + } + } +}; + +/** It populates the form * */ +RigibBodyModelingForm.prototype.refresh = function(macromolecule) { + this.macromolecule = macromolecule; + this.rigidBodyGrid.refresh(macromolecule); + this._populate(); +}; + +RigibBodyModelingForm.prototype.input = function() { + return {}; +}; + + +/** It populates the form **/ +RigibBodyModelingForm.prototype.test = function(targetId) { + var macromoleculeForm = new RigibBodyModelingForm(); + var panel = macromoleculeForm.getPanel(); + panel.render(targetId); +}; + + +/** + * Same form as MX part * - * #onClick #onSelected #onRemoved #onUpdateTime #onMeasurementChanged - * #onExperimentChanged + * @creationMode if true a create button appears instead of save + * @showTitle true or false */ -function MeasurementGrid(args) { +function ShipmentForm(args) { this.id = BUI.id(); - this.height = 500; - this.width = 900; - - this.maxWidth = 1200; - this.maxHeight = 600; - this.minHeight = 500; - - this.unitsFontSize = 9; - this.title = "Measurements"; - this.estimateTime = false; - this.collapsed = true; - this.tbar = true; - + this.creationMode = false; this.showTitle = true; - this.resizable = true; - this.updateRowEnabled = true; - - /** - * Hash map containing the keys of the editable columns. Ex: - * 'exposureTemperature' * - */ - this.editor = { - comments : { - xtype : 'textfield', - allowBlank : true + if (args != null) { + if (args.creationMode != null) { + this.creationMode = args.creationMode; } - }; + if (args.showTitle != null) { + this.showTitle = args.showTitle; + } + } - this.isTimeColumnHidden = false; - this.isStatusColumnHidden = false; - this.isPriorityColumnHidden = true; - this.margin = "0 0 0 0"; + this.onSaved = new Event(this); +} - this.addBtnEnable = true; - this.sorter = [ { - property : 'priority', - direction : 'ASC' - } ]; +ShipmentForm.prototype.fillStores = function() { + this.panel.setLoading("Loading Labcontacts from database"); + this.labContactForSendingStore.loadData(BIOSAXS.proposal.getLabcontacts(), false); + this.labContactForReturnStore.loadData(BIOSAXS.proposal.getLabcontacts(), false); + this.panel.setLoading(false); + if (this.shipment != null) { + this.setShipment(this.shipment); + } +}; - this.removeBtnEnabled = false; - this.collapseBtnEnable = true; - this.addBtnMultipleEdit = false; - this.sortingBtnEnable = false; +ShipmentForm.prototype.draw = function(targetId) { + this.getPanel().render(targetId); +}; +ShipmentForm.prototype.setShipment = function(shipment) { + this.shipment = shipment; var _this = this; - this.selModel = Ext.create('Ext.selection.RowModel', { - allowDeselect : true, - mode : 'MULTI', - listeners : { - selectionchange : function(sm, selections) { - var selected = []; - for (var i = 0; i < selections.length; i++) { - selected.push(selections[i].raw); - } - _this.onSelected.notify(selected); - } - } - }); - - if (args != null) { - - if (args.selModel != null) { - this.selModel = args.selModel; - } + Ext.getCmp(_this.id + "shippingName").setValue(shipment.json.shippingName); + Ext.getCmp(_this.id + "shippingStatus").setValue(shipment.json.shippingStatus); + Ext.getCmp(_this.id + "comments").setValue(shipment.json.comments); + if (shipment.json.sendingLabContactVO != null) { + this.labContactsSendingCombo.setValue(shipment.json.sendingLabContactVO.labContactId); + } + if (shipment.json.returnLabContactVO != null) { + this.labContactsReturnCombo.setValue(shipment.json.returnLabContactVO.labContactId); + } - if (args.removeBtnEnabled != null) { - this.removeBtnEnabled = args.removeBtnEnabled; - } +}; - if (args.addBtnMultipleEdit != null) { - this.addBtnMultipleEdit = args.addBtnMultipleEdit; - } +ShipmentForm.prototype._saveShipment = function() { + var _this = this; + var shippingId = null; + if (this.shipment != null) { + shippingId = this.shipment.json.shippingId; + } + var json = { + shippingId : shippingId, + name : Ext.getCmp(_this.id + "shippingName").getValue(), + status : Ext.getCmp(_this.id + "shippingStatus").getValue(), + sendingLabContactId : Ext.getCmp(_this.id + "shipmentform_sendingLabContactId").getValue(), + returnLabContactId : Ext.getCmp(_this.id + "returnLabContactId").getValue(), + returnCourier : Ext.getCmp(_this.id + "returnCourier").getValue(), + courierAccount : Ext.getCmp(_this.id + "courierAccount").getValue(), + BillingReference : Ext.getCmp(_this.id + "BillingReference").getValue(), + dewarAvgCustomsValue : Ext.getCmp(_this.id + "dewarAvgCustomsValue").getValue(), + dewarAvgTransportValue : Ext.getCmp(_this.id + "dewarAvgTransportValue").getValue(), + comments : Ext.getCmp(_this.id + "comments").getValue() + }; - // if (args.experimentColorBased != null) { - // this.experimentColorBased = args.experimentColorBased; - // } + var dataAdapter = new BiosaxsDataAdapter(); + dataAdapter.onSuccess.attach(function(sender, shipment) { + window.location = BUI.getShippingURL(shipment.shippingId); + }); - if (args.collapsed != null) { - this.collapsed = args.collapsed; - } - if (args.resizable != null) { - this.resizable = args.resizable; - } + dataAdapter.onError.attach(function(sender, error) { + _this.onError.notify(error); + }); - if (args.editor != null) { - this.editor = args.editor; - } + /** Cheking params **/ + if (json.name == "") { + BUI.showError("Name field is mandatory"); + return; + } - if (args.collapseBtnEnable != null) { - this.collapseBtnEnable = args.collapseBtnEnable; - } + if (json.sendingLabContactId == null) { + BUI.showError("Lab contact for sending field is mandatory"); + return; + } - if (args.addBtnEnable != null) { - this.addBtnEnable = args.addBtnEnable; - } - if (args.sortingBtnEnable != null) { - this.sortingBtnEnable = args.sortingBtnEnable; - } + if (json.returnLabContactId == null) { + BUI.showError("Lab contact for return field is mandatory"); + return; + } - if (args.isPriorityColumnHidden != null) { - this.isPriorityColumnHidden = args.isPriorityColumnHidden; - } + dataAdapter.createShipment(json.shippingId, json.name, json.status, json.comments, json.sendingLabContactId, json.returnLabContactId, + json.returnCourier, json.courierAccount, json.BillingReference, json.dewarAvgCustomsValue, json.dewarAvgTransportValue); - if (args.width != null) { - this.width = args.width; - } +}; - if (args.updateRowEnabled != null) { - this.updateRowEnabled = args.updateRowEnabled; - } +ShipmentForm.prototype.getPanel = function(shipment) { + var _this = this; + this.shipment = shipment; + var required = '*'; + var buttons = []; - if (args.showTitle != null) { - this.showTitle = args.showTitle; - if (this.showTitle == false) { - this.title = null; + if (_this.creationMode) { + buttons.push({ + text : 'Create', + scope : this, + handler : function() { + _this._saveShipment(); } - } - - if (args.height != null) { - this.height = args.height; - } - - if (args.maxHeight != null) { - this.maxHeight = args.maxHeight; - } - - if (args.minHeight != null) { - this.minHeight = args.minHeight; - } + }); + } else { + buttons.push({ + text : 'Save', + scope : this, + handler : function() { + _this._saveShipment(); + } + }); - if (args.maxWidth != null) { - this.maxWidth = args.maxWidth; - } + } - if (args.isStatusColumnHidden != null) { - this.isStatusColumnHidden = args.isStatusColumnHidden; - } - if (args.isTimeColumnHidden != null) { - this.isTimeColumnHidden = args.isTimeColumnHidden; - } + this.labContactForSendingStore = Ext.create('Ext.data.Store', { + fields : [ 'cardName', 'labContactId' ] + }); - if (args.title != null) { - this.title = args.title; - } - if (args.estimateTime != null) { - this.estimateTime = args.estimateTime; - } + this.labContactForReturnStore = Ext.create('Ext.data.Store', { + fields : [ 'cardName', 'labContactId' ] + }); - if (args.margin != null) { - this.margin = args.margin; - } + // Create the combo box, attached to the states data store + this.labContactsSendingCombo = Ext.create('Ext.form.ComboBox', { + id : _this.id + "shipmentform_sendingLabContactId", + fieldLabel : 'Lab contact for sending', + afterLabelTextTpl : required, + store : this.labContactForSendingStore, + queryMode : 'local', + labelWidth : 200, + displayField : 'cardName', + valueField : 'labContactId' + }); - if (args.tbar != null) { - this.tbar = args.tbar; + this.labContactsReturnCombo = Ext.create('Ext.form.ComboBox', { + id : _this.id + "returnLabContactId", + fieldLabel : 'If No, Lab-Contact for Return', + afterLabelTextTpl : required, + store : this.labContactForReturnStore, + queryMode : 'local', + labelWidth : 200, + displayField : 'cardName', + valueField : 'labContactId', + listeners : { + change : function(x, newValue) { + for ( var i = 0; i < x.getStore().data.items.length; i++) { + if (x.getStore().data.items[i].raw.labContactId == newValue) { + Ext.getCmp(_this.id + "returnCourier").setValue(x.getStore().data.items[i].raw.defaultCourrierCompany); + Ext.getCmp(_this.id + "courierAccount").setValue(x.getStore().data.items[i].raw.courierAccount); + Ext.getCmp(_this.id + "BillingReference").setValue(x.getStore().data.items[i].raw.billingReference); + Ext.getCmp(_this.id + "dewarAvgCustomsValue").setValue(x.getStore().data.items[i].raw.dewarAvgCustomsValue); + Ext.getCmp(_this.id + "dewarAvgTransportValue").setValue(x.getStore().data.items[i].raw.dewarAvgTransportValue); + } + } + } } + }); - if (args.sorter != null) { - this.sorter = args.sorter; - } + if (this.panel == null) { + this.panel = Ext.create('Ext.form.Panel', { + bodyPadding : 5, + width : 600, + border : 1, + items : [ { + xtype : 'fieldset', + title : 'Details', + collapsible : false, + defaultType : 'textfield', + layout : 'anchor', + defaults : { + anchor : '100%' + }, + items : [ { + xtype : 'requiredtext', + fieldLabel : 'Shipment Label', + allowBlank : false, + name : 'shippingName', + id : _this.id + 'shippingName', + value : '', + anchor : '50%' + }, { + xtype : 'textareafield', + name : 'comments', + id : _this.id + 'comments', + fieldLabel : 'Comments', + value : '' + }, { + fieldLabel : 'Status', + readOnly : true, + id : _this.id + 'shippingStatus', + value : 'Opened', + anchor : '50%' + } ] + }, { + xtype : 'fieldset', + title : 'Lab-Contacts', + collapsible : false, + defaultType : 'textfield', + layout : 'anchor', + defaults : { + anchor : '100%' + }, + items : [ this.labContactsSendingCombo, this.labContactsReturnCombo ] + }, { + border : 0, + html : BUI.getWarningHTML("These informations are relevant for all shipments") + }, { + xtype : 'fieldset', + title : 'Courier accounts details for return', + collapsible : false, + layout : 'anchor', + defaults : { + anchor : '100%' + }, + items : [ { + xtype : 'textfield', + labelWidth : 400, + fieldLabel : 'Courier company for return (if ESRF sends a dewar back)', + id : _this.id + 'returnCourier', + value : '' + }, { + xtype : 'textfield', + labelWidth : 400, + fieldLabel : 'Courier account', + id : _this.id + 'courierAccount', + value : '' + }, { + xtype : 'textfield', + labelWidth : 400, + fieldLabel : 'Billing reference', + id : _this.id + 'BillingReference', + value : '' + }, { + xtype : 'numberfield', + labelWidth : 400, + fieldLabel : 'Average Customs value of a dewar (Euro)', + id : _this.id + 'dewarAvgCustomsValue', + value : '' + }, { + xtype : 'numberfield', + labelWidth : 400, + fieldLabel : 'Average Transport value of a dewar (Euro)', + id : _this.id + 'dewarAvgTransportValue', + value : '' + } ] + } ], + buttons : buttons + }); } - this.onClick = new Event(this); - this.onSelected = new Event(this); - this.onRemoved = new Event(this); - this.onUpdateTime = new Event(this); - this.onMeasurementChanged = new Event(this); - this.onExperimentChanged = new Event(this); -} - -MeasurementGrid.prototype._sortBy = function(sort) { - var _this = this; - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, data) { - _this.onExperimentChanged.notify(data); - _this.grid.setLoading(false); - // wizardWidget.window.close(); - }); - adapter.onError.attach(function(sender, data) { - _this.grid.setLoading(false); - alert("Oops, there was a problem"); - }); - _this.grid.setLoading("Sorting"); - adapter.sortMeasurements(this.experiments.experiments[0].experimentId, sort); + this.fillStores(); + if (this.showTitle) { + this.panel.setTitle('Create a new Shipment'); + } + return this.panel; }; -MeasurementGrid.prototype._getMenu = function() { - var _this = this; - if (this.tbar) { +ShipmentForm.prototype.input = function() { + return { + proposal : DATADOC.getProposal_10() - var items = []; - if (_this.addBtnEnable) { - items.push({ - icon : '../images/add.png', - text : 'Add measurements', - handler : function() { - _this._openAddMeasurementWindow(); - } - }); - } + }; +}; - if (_this.addBtnMultipleEdit) { - items.push({ - icon : '../images/Edit_16x16_01.png', - text : 'Multiple Edit', - handler : function() { - var multipleEditMeasurementGridWindow = new MultipleEditMeasurementGridWindow(); - multipleEditMeasurementGridWindow.onExperimentChanged.attach(function(sender, data) { - _this.onExperimentChanged.notify(data); - }); +ShipmentForm.prototype.test = function(targetId) { + var shipmentForm = new ShipmentForm({ + creationMode : true - multipleEditMeasurementGridWindow.draw(_this.measurements, _this.experiments); + }); + BIOSAXS.proposal = new Proposal(shipmentForm.input().proposal); + shipmentForm.getPanel().render(targetId); +}; + +/** + * #onSaved + */ +function StockSolutionForm(args) { + this.id = BUI.id(); + this.actions = []; - } - }); + if (args != null) { + if (args.actions != null) { + this.actions = args.actions; } + } + this.onSaved = new Event(this); +} - items.push("->"); +StockSolutionForm.prototype.getStockSolution = function() { + if (this.stockSolution != null) { + this.stockSolution.concentration = Ext.getCmp(this.id + "stockSolution_concentration").getValue(); + this.stockSolution.storageTemperature = Ext.getCmp(this.id + "stockSolution_storageTemperature").getValue(); + this.stockSolution.volume = Ext.getCmp(this.id + "stockSolution_volume").getValue(); + this.stockSolution.comments = Ext.getCmp(this.id + "stockSolution_comments").getValue(); + this.stockSolution.name = Ext.getCmp(this.id + "stockSolution_name").getValue(); + this.stockSolution.bufferId = this.bufferCombo.getValue(); - if (_this.sortingBtnEnable) { - var split = Ext.create('Ext.button.Split', { - text : 'Sort by', - // handle a click on the button itself - handler : function() { - // alert("The button was clicked"); - }, - menu : new Ext.menu.Menu({ - items : [ - { - text : 'First Created First Measured', - handler : function() { - _this._sortBy("FIFO"); - } - }, "-", { - text : 'Default', - handler : function() { - _this._sortBy("DEFAULT"); - } - } ] - }) - }); - items.push(split); + if (this.macromoleculeCombo.getValue() != null) { + this.stockSolution.macromoleculeId = this.macromoleculeCombo.getValue(); + } else { + this.stockSolution.macromolecule3VO = null; } - if (_this.collapseBtnEnable) { - items.push({ - text : 'Collapse buffers', - enableToggle : true, - scope : this, - toggleHandler : function(item, pressed) { - this.collapsed = pressed; - this.grid.getStore().loadData(this._prepareData(this.measurements, this.experiments), false); - }, - pressed : this.collapsed - }); + } else { + return { + concentration : Ext.getCmp(this.id + "stockSolution_concentration").getValue(), + storageTemperature : Ext.getCmp(this.id + "stockSolution_storageTemperature").getValue(), + volume : Ext.getCmp(this.id + "stockSolution_volume").getValue(), + comments : Ext.getCmp(this.id + "stockSolution_comments").getValue(), + name : Ext.getCmp(this.id + "stockSolution_name").getValue(), + bufferId : this.bufferCombo.getValue(), + macromoleculeId : this.macromoleculeCombo.getValue() + }; + } + return this.stockSolution; +}; + +StockSolutionForm.prototype.setStockSolution = function(stockSolution) { + if (stockSolution != null) { + if (stockSolution.macromoleculeId != null) { + this.macromoleculeCombo.setValue(stockSolution.macromoleculeId); } - var tb = Ext.create('Ext.toolbar.Toolbar', { - items : items - }); - return tb; + this.bufferCombo.setValue(stockSolution.bufferId); + Ext.getCmp(this.id + "stockSolution_concentration").setValue(this.stockSolution.concentration); + Ext.getCmp(this.id + "stockSolution_storageTemperature").setValue(this.stockSolution.storageTemperature); + Ext.getCmp(this.id + "stockSolution_volume").setValue(this.stockSolution.volume); + Ext.getCmp(this.id + "stockSolution_name").setValue(this.stockSolution.name); + Ext.getCmp(this.id + "stockSolution_comments").setValue(this.stockSolution.comments); } - return null; }; -/** Opens WizardWidget for adding new measurements * */ -MeasurementGrid.prototype._openAddMeasurementWindow = function(measurements, experiments) { - var _this = this; - var wizardWidget = new WizardWidget(); - wizardWidget.onFinished.attach(function(sender, result) { - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, data) { - _this.onExperimentChanged.notify(data); - _this.grid.setLoading(false); - wizardWidget.window.close(); - }); - wizardWidget.current.setLoading("ISPyB: Adding measurements"); - adapter.addMeasurements(result.name, "comments", result.data, _this.experiments.experiments[0].experimentId); +StockSolutionForm.prototype.getBufferCombo = function() { + this.bufferCombo = BIOSAXS_COMBOMANAGER.getComboBuffers(BIOSAXS.proposal.getBuffers(), { + labelWidth : 120, + margin : '0 0 10 0', + width : 220 + }); + return this.bufferCombo; +}; - wizardWidget.draw(null, new MeasurementCreatorStepWizardForm(BIOSAXS.proposal.getMacromolecules(), { - noNext : true - })); +StockSolutionForm.prototype.getMacromoleculeCombo = function() { + this.macromoleculeCombo = BIOSAXS_COMBOMANAGER.getComboMacromoleculeByMacromolecules(BIOSAXS.proposal.getMacromolecules(), { + labelWidth : 120, + margin : '0 0 10 0', + width : 220 + + }); + return this.macromoleculeCombo; }; -/******************************************************************************* - * Opens WizardWidget for adding new measurements - * - * @Measurements - * @Experiments experimentList Object - ******************************************************************************/ -MeasurementGrid.prototype._prepareData = function(measurements, experiments) { - var data = []; - for (var i = 0; i < measurements.length; i++) { - var measurement = measurements[i]; - var specimen = experiments.getSampleById(measurement.specimenId); - var buffer = experiments.getBufferById(specimen.bufferId); - measurement.buffer_acronym = buffer.acronym; - measurement.bufferId = buffer.bufferId; - measurement.volume = specimen.volume; - if (specimen.macromolecule3VO != null) { - measurement.acronym = specimen.macromolecule3VO.acronym; - measurement.macromoleculeId = specimen.macromolecule3VO.macromoleculeId; - } - measurement.concentration = specimen.concentration; - if (measurement.run3VO != null) { - measurement.energy = measurement.run3VO.energy; - measurement.expExposureTemperature = measurement.run3VO.exposureTemperature; - measurement.storageTemperature = measurement.run3VO.storageTemperature; - measurement.timePerFrame = measurement.run3VO.timePerFrame; - measurement.radiationAbsolute = measurement.run3VO.radiationAbsolute; - measurement.radiationRelative = measurement.run3VO.radiationRelative; - measurement.status = "DONE"; +StockSolutionForm.prototype.refresh = function() { +}; - try { - if (measurement.run3VO.timeStart != null) { - if (measurement.run3VO.timeStart != "") { - measurement.miliseconds = moment(measurement.run3VO.timeStart).format("X"); - } - } - } catch (E) { - console.log(E); - } - } +StockSolutionForm.prototype._getTopPanel = function() { + return { + xtype : 'container', + layout : 'hbox', + border : 0, + items : [ { + xtype : 'container', + layout : 'hbox', + items : [ { + xtype : 'container', + flex : 1, + border : false, + layout : 'anchor', + defaultType : 'textfield', + items : [ - if (experiments.getDataCollectionByMeasurementId(measurement.measurementId).length > 0) { - var measurementtodatacollection3VOs = experiments.getDataCollectionByMeasurementId(measurement.measurementId)[0].measurementtodatacollection3VOs; - for (var k = 0; k < measurementtodatacollection3VOs.length; k++) { - if (measurementtodatacollection3VOs[k].dataCollectionOrder == 1) { - var specimenBuffer = experiments.getSampleById(experiments.getMeasurementById(measurementtodatacollection3VOs[k].measurementId).specimenId); - if (specimenBuffer.sampleplateposition3VO != null) { - measurement.bufferSampleplateposition3VO = specimenBuffer.sampleplateposition3VO; - measurement.bufferSampleplate = (experiments.getSamplePlateById(specimenBuffer.sampleplateposition3VO.samplePlateId)); - } - } - } - } + this.getMacromoleculeCombo(), { + xtype : 'requiredtext', + id : this.id + 'stockSolution_name', + fieldLabel : 'Acronym', + labelWidth : 120, + width : 250 + }, { + xtype : 'requiredtext', + id : this.id + 'stockSolution_concentration', + fieldLabel : 'Conc. (mg/ml)', + labelWidth : 120, + width : 250 + }, - if (this.collapsed) { - /** If collapsed only the samples * */ - if (specimen.macromolecule3VO != null) { - data.push(measurement); - } - } else { - data.push(measurement); - } + { + id : this.id + 'stockSolution_storageTemperature', + fieldLabel : 'Storage Temp.(C)', + labelWidth : 120, + width : 250 + }, { + xtype : 'requiredtext', + id : this.id + 'stockSolution_volume', + fieldLabel : 'Volume in Well (µl)', + labelWidth : 120, + width : 250 + } ] + } ] + }, { + xtype : 'container', + flex : 1, + layout : 'anchor', + defaultType : 'textfield', + margin : '0 0 0 10', + items : [ this.getBufferCombo() ] + } ] + }; - } - return data; }; -/** - * Refresh data grid with the measurements and the experiments - * - * @measurements array with measurement3VO objects - * @experiments array with experiments objects - */ -MeasurementGrid.prototype.refresh = function(measurements, experiments) { - this.experiments = experiments; - this.measurements = measurements; - this.store.loadData(this._prepareData(measurements, experiments), false); +StockSolutionForm.prototype.getPanel = function(stockSolution) { + this.stockSolution = stockSolution; + this.panel = Ext.createWidget({ + xtype : 'container', + layout : 'vbox', + border : 0, + style : { + padding : '10px' + }, + fieldDefaults : { + labelAlign : 'left', + labelWidth : 50 + }, + items : [ this._getTopPanel(stockSolution), { + id : this.id + 'stockSolution_comments', + xtype : 'textareafield', + name : 'comments', + fieldLabel : 'Comments', + labelWidth : 120, + width : '100%' + } ] + }); + + this.setStockSolution(stockSolution); + return this.panel; +}; + +StockSolutionForm.prototype.input = function() { + return { + stock : { + "stockSolutionId" : 6, + "proposalId" : 3124, + "macromoleculeId" : 5933, + "bufferId" : 811, + "instructionSet3VO" : null, + "boxId" : 305861, + "storageTemperature" : "20", + "volume" : "300", + "concentration" : "1.2", + "comments" : "Buffer EDTA with A", + "name" : "A_EDTA_1.2", + "samples" : [], + "buffer" : "EDTA", + "macromolecule" : "A" + }, + proposal : new MeasurementGrid().input().proposal + }; +}; + +StockSolutionForm.prototype.test = function(targetId) { + var stockSolutionForm = new StockSolutionForm(); + BIOSAXS.proposal = new Proposal(stockSolutionForm.input().proposal); + var panel = stockSolutionForm.getPanel(new Shipment(stockSolutionForm.input().stock)); + panel.render(targetId); }; + + +BoxWhiskerGraph.prototype.cleanArray = GenericGraph.prototype.cleanArray; +BoxWhiskerGraph.prototype.plotAxes = GenericGraph.prototype.plotAxes; +BoxWhiskerGraph.prototype.plotRuler = GenericGraph.prototype.plotRuler; +BoxWhiskerGraph.prototype.drawSVGVerticalText = GenericGraph.prototype.drawSVGVerticalText; +BoxWhiskerGraph.prototype.getClassColor = GenericGraph.prototype.getClassColor; +BoxWhiskerGraph.prototype.getDimensions = GenericGraph.prototype.getDimensions; +BoxWhiskerGraph.prototype.calculate = GenericGraph.prototype.calculate; +BoxWhiskerGraph.prototype.getMedian = GenericGraph.prototype.getMedian; +BoxWhiskerGraph.prototype.pointToPixel = GenericGraph.prototype.pointToPixel; +BoxWhiskerGraph.prototype.maxValueWhisker = GenericGraph.prototype.maxValueWhisker; +BoxWhiskerGraph.prototype.refresh = GenericGraph.prototype.refresh; /** - * Set status bar to busy (refreshing icon) + * Subclass of GenericGraph * - * @msg message to be displayed on the bar + * @maxBoxWidth */ -MeasurementGrid.prototype._showStatusBarBusy = function(msg) { - var statusBar = Ext.getCmp(this.id + 'basic-statusbar'); - statusBar.setStatus({ - text : msg, - iconCls : 'x-status-busy', - clear : false - }); +function BoxWhiskerGraph(args){ + this.maxBoxWidth = 25; + + if (args == null){ + args = new Object(); + } + args["plotHorizontalByCluster"] = true; + + GenericGraph.call(this, args); + + if (args.maxBoxWidth != null){ + this.maxBoxWidth = args.maxBoxWidth; + } }; + + /** - * Set status bar to ready (ok icon) - * - * @msg message to be displayed on the bar - */ -MeasurementGrid.prototype._showStatusBarReady = function(msg) { - var statusBar = Ext.getCmp(this.id + 'basic-statusbar'); - statusBar.setStatus({ - text : msg, - iconCls : 'x-status-valid', - clear : false - }); +There are several different methods for calculating quartiles.[1] This calculator uses a method described by Moore and McCabe to find quartile values. +The same method also used by The TI-83 to calculate quartile values. +With this method, the first quartile is the median of the numbers below the median, the third quartile is the median of the numbers above the median. + +http://www.miniwebtool.com/quartile-calculator/ +http://www.alcula.com/calculators/statistics/box-plot/ +**/ +BoxWhiskerGraph.prototype.getQ1 = function(array){ + array = array.slice(0, array.length/2); + return this.getMedian(array); }; -/** - * If updateRowEnabled returns an array with Ext.grid.plugin.RowEditing - */ -MeasurementGrid.prototype._getPlugins = function() { - var _this = this; - var plugins = []; - if (this.updateRowEnabled) { - plugins.push(Ext.create('Ext.grid.plugin.RowEditing', { - clicksToEdit : 1, - listeners : { - validateedit : function(grid, e) { - /** Setting values * */ - for ( var key in _this.editor) { - e.record.raw[key] = e.newValues[key]; - } - /** Comments are always updatable* */ - e.record.raw.comments = e.newValues.comments; +BoxWhiskerGraph.prototype.getQ3 = function(array){ + array = array.slice((array.length + 1)/2); + return this.getMedian(array); + +}; - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, measurement) { - _this.onMeasurementChanged.notify(measurement); - _this.grid.setLoading(false); - }); - adapter.onError.attach(function() { - alert("Error"); - _this.grid.setLoading(false); - }); +BoxWhiskerGraph.prototype.getQ2 = function(array){ + return this.getMedian(array); +}; - _this.grid.setLoading(); - adapter.saveMeasurement(e.record.raw, _this.experiments.experiments[0]); - } - } - })); +BoxWhiskerGraph.prototype.getBelowOutliers = function(belowLimit, array){ + var points = new Array(); + for (var i = 0; i < array.length; i++){ + if (array[i] <= belowLimit){ + points.push(array[i]); + } } - return plugins; + return points; }; -/** - * @key name of the columns mathing the this.editor[key] - */ -MeasurementGrid.prototype._getEditor = function(key) { - if (this.editor[key] != null) { - return this.editor[key]; +BoxWhiskerGraph.prototype.getAboveOutliers = function(aboveLimit, array){ + var points = new Array(); + for (var i = 0; i < array.length; i++){ + if (array[i] >= aboveLimit){ + points.push(array[i]); + } } - return null; + return points; }; -MeasurementGrid.prototype.getPanel = function(measurements, experiments) { - this.experiments = experiments; - this.measurements = measurements; - var _this = this; - this.store = Ext.create('Ext.data.Store', { - fields : [ { - name : 'miliseconds', - type : 'int' - }, 'priority', 'bufferId', { - name : 'exposureTemperature', - type : 'numeric' - }, 'volumeToLoad','code', 'transmission', 'viscosity', 'waitTime', 'flow', 'buffer_acronym', 'macromoleculeId', 'acronym', 'concentration', 'extraFlowTime', 'volume', 'energy', - 'expExposureTemperature', 'storageTemperature', 'timePerFrame', 'radiationAbsolute', 'radiationRelative', 'status', 'comments' ], - data : this._prepareData(measurements, experiments) - }); - this.store.sort(this.sorter); - var bbar = {}; - try { - bbar = Ext.create('Ext.ux.StatusBar', { - id : _this.id + 'basic-statusbar', - defaultText : 'Ready', - text : 'Ready', - iconCls : 'x-status-valid', - items : [] - }); - } catch (exp) { - console.log("bbar error"); + +BoxWhiskerGraph.prototype.minValueWhisker = function(belowLimit, q1, array){ + var points = new Array(); + + for (var i = 0; i < array.length; i++){ + if ((array[i] < q1) && (array[i]) > belowLimit){ + points.push(array[i]); + } + } + + if (points.length > 0){ + points.sort(function(a, b){return a - b;}); + return points[0]; } + return null; +}; - this.grid = Ext - .create( - 'Ext.grid.Panel', - { - id : this.id, - title : this.title, - store : this.store, - selModel : this.selModel, - plugins : this._getPlugins(), - resizable : this.resizable, - margin : this.margin, - maxHeight : this.maxHeight, - minHeight : this.minHeight, - maxWidth : this.maxWidth, - width : this.width, - tbar : this._getMenu(), - columns : [ - { - text : 'Order', - dataIndex : 'priority', - width : 50, - hidden : _this.isPriorityColumnHidden, - sortable : true +BoxWhiskerGraph.prototype.isNumber = function(value){ + if (value=="") return false; - }, - { - text : 'Run Number', - dataIndex : 'code', - width : 50, - hidden : true, - sortable : true + var d = parseInt(value); + if (!isNaN(d)) return true; else return false; - }, +}; - { - text : 'Specimen', - columns : [ +BoxWhiskerGraph.prototype.plotBoxWhisker = function(boxProperties){ + if (this.maxBoxWidth != null){ + if (boxProperties.width > this.maxBoxWidth){ + boxProperties.x = boxProperties.x + (boxProperties.width/2) - (this.maxBoxWidth/2); + boxProperties.width = this.maxBoxWidth; + } + + } - { - text : '', - dataIndex : 'macromoleculeId', - width : 30, - renderer : function(val, y, sample) { - if (val != null) { - if (_this.experiments == null) { - return BUI.getRectangleColorDIV(BIOSAXS.proposal.macromoleculeColors[val], 10, 10); - } else { - return BUI.getRectangleColorDIV(_this.experiments.macromoleculeColors[val], 10, 10); - } - } - }, - sortable : true - }, - { - text : 'Macromo.', - dataIndex : 'acronym', - width : 80, - renderer : function(val, y, sample) { - return val; - }, - sortable : true - }, - { - text : 'Conc. ', - dataIndex : 'concentration', - width : 80, - renderer : function(val, y, sample) { - if (sample.raw.macromoleculeId == null) { - return ""; - } - if (isNaN(val)) - return val; + if (this.plotPoints){ + for(var i = 0; i < boxProperties.values.length; i++){ + var value = boxProperties.values[i]; + var toPixel = this.pointToPixel(value, boxProperties); + SVG.drawCircle(boxProperties.x + (boxProperties.width/2) + 2, toPixel, this.pointRadius, this.svg, [["fill", "green"], ["fill-opacity", this.fillOpacityPoint],['stroke-opacity', this.strokeOpacityPoint], ["stroke", "black"]]); + } + } + - if (val != 0) { - return BUI.formatValuesUnits(val, '', { - fontSize : 16, - decimals : 3, - unitsFontSize : this.unitsFontSize - }); - } else { - return; - } + var boxColor = this.getClassColor(boxProperties.name); + var result = this.calculate(boxProperties.values); + /** Q1 **/ + + if (this.isNumber(result.Q1)){ + SVG.drawLine(boxProperties.x, this.pointToPixel(result.Q1, boxProperties), boxProperties.x + boxProperties.width, this.pointToPixel(result.Q1, boxProperties), this.svg, [["stroke", boxColor]]); + } + /** Q2 **/ + if (this.isNumber(result.Q2)){ + SVG.drawLine(boxProperties.x, this.pointToPixel(result.Q2, boxProperties), boxProperties.x + boxProperties.width, this.pointToPixel(result.Q2, boxProperties), this.svg, [["stroke", "blue"], ["stroke-width", "2"]]); + } + + /** Q3 **/ + if (this.isNumber(result.Q3)){ + SVG.drawLine(boxProperties.x, this.pointToPixel(result.Q3, boxProperties), boxProperties.x + boxProperties.width, this.pointToPixel(result.Q3, boxProperties), this.svg, [["stroke", boxColor]]); + } + + /** Concenting Q1 and Q3 **/ + if (this.isNumber(result.Q1)&&this.isNumber(result.Q3)){ + SVG.drawLine(boxProperties.x, this.pointToPixel(result.Q1, boxProperties), boxProperties.x, this.pointToPixel(result.Q3, boxProperties), this.svg, [["stroke", boxColor]]); + SVG.drawLine(boxProperties.x + boxProperties.width, this.pointToPixel(result.Q1, boxProperties), boxProperties.x + boxProperties.width, this.pointToPixel(result.Q3, boxProperties), this.svg, [["stroke", boxColor]]); + } + + /** min-whisker **/ + if (result["min-whisker"] != null){ + if (this.isNumber(result.Q1)){ + SVG.drawLine(boxProperties.x + (boxProperties.width/2) + 2, this.pointToPixel(result.Q1, boxProperties), boxProperties.x + (boxProperties.width/2) + 2, this.pointToPixel(result["min-whisker"], boxProperties), this.svg, [["stroke", boxColor]]); + } + SVG.drawLine(boxProperties.x,this.pointToPixel(result["min-whisker"], boxProperties), boxProperties.x + boxProperties.width, this.pointToPixel(result["min-whisker"], boxProperties) , this.svg, [["stroke", boxColor]]); + + } + + /** max-whisker **/ + if (result["max-whisker"] != null){ + if (this.isNumber(result.Q3)){ + SVG.drawLine(boxProperties.x + (boxProperties.width/2) + 2, this.pointToPixel(result.Q3, boxProperties), boxProperties.x + (boxProperties.width/2) + 2, this.pointToPixel(result["max-whisker"], boxProperties), this.svg, [["stroke", boxColor]]); + } + SVG.drawLine(boxProperties.x , this.pointToPixel(result["max-whisker"], boxProperties), boxProperties.x + boxProperties.width, this.pointToPixel(result["max-whisker"], boxProperties), this.svg, [["stroke", boxColor]]); + + } + + /** outliners **/ + if (result["above-outliers"] != null){ + for (var point in result["above-outliers"]){ + SVG.drawCircle(boxProperties.x + (boxProperties.width/2) + 2, this.pointToPixel(result["above-outliers"][point], boxProperties), this.pointRadius, this.svg, [["fill", "red"], ["fill-opacity", "1"]]); + //SVG.drawText(boxProperties.x + (boxProperties.width/2) - 2, this.pointToPixel(result["above-outliers"][point], boxProperties) + 2, "x",this.svg, [["fill", "black"], ["font-size", "x-small"]]); + } + } + if (result["below-outliers"] != null){ + for (var point in result["below-outliers"]){ + SVG.drawCircle(boxProperties.x + (boxProperties.width/2) + 2, this.pointToPixel(result["below-outliers"][point], boxProperties), this.pointRadius, this.svg, [["fill", "red"], ["fill-opacity", "1"]]); + //SVG.drawText(boxProperties.x + (boxProperties.width/2) - 2, this.pointToPixel(result["below-outliers"][point], boxProperties) + 2, "x",this.svg, [["fill", "black"], ["font-size", "x-small"]]); + } + } +}; - }, - sortable : true - }, - { - text : '', - dataIndex : 'bufferId', - width : 30, - hidden : false, - renderer : function(val, y, sample) { - if (val != null) { - var color = '#FFCCFF'; - if (_this.experiments != null) { - var dc = _this.experiments.getDataCollectionByMeasurementId(sample.raw.measurementId); - if (dc != null) { - if (dc.length > 0) { - color = _this.experiments.getSpecimenColorByBufferId(_this.experiments - .getMeasurementById(dc[0].measurementtodatacollection3VOs[0].measurementId).specimenId); - } - } - } else { - color = BIOSAXS.proposal.bufferColors[val]; - } - return BUI.getRectangleColorDIV(color, 10, 10); - } - }, - sortable : true - }, - { - text : 'Buffer', - dataIndex : 'buffer_acronym', - width : 120, - renderer : function(val, y, sample) { - if (sample.raw.bufferSampleplateposition3VO != null) { - return BIOSAXS.proposal.getBufferById(sample.raw.bufferId).acronym + " Plate: [" - + sample.raw.bufferSampleplate.slotPositionColumn + ", " - + BUI.getSamplePlateLetters()[sample.raw.bufferSampleplateposition3VO.rowNumber - 1] + "-" - + sample.raw.bufferSampleplateposition3VO.columnNumber + "]"; - } - return val; - }, - sortable : true - }, { - text : 'Position', - width : 100, - hidden : true, - renderer : function(val, y, sample) { - if (_this.experiments != null) { - return BUI.getSamplePositionHTML(_this.experiments.getSampleById(sample.raw.specimenId), _this.experiments.experiments[0]); - } - } - } ] - }, - { - text : 'Parameters', - columns : [ - { - text : 'Ex. Flow. time (s)', - dataIndex : 'extraFlowTime', - width : 100, - hidden : true, - renderer : function(val, y, sample) { - return BUI.formatValuesUnits(val, 's', { - fontSize : 12, - decimals : 0, - unitsFontSize : this.unitsFontSize - }); - } - }, - { - text : 'Exp. Temp.', - dataIndex : 'exposureTemperature', - width : 70, - renderer : function(val, y, sample) { - if (Number(val)) { - return BUI.formatValuesUnits(val, 'C', { - fontSize : 12, - decimals : 2, - unitsFontSize : this.unitsFontSize - }); - } - return null; - }, - sortable : true, - editor : this._getEditor("exposureTemperature") - }, - { - text : 'Vol. Load', - dataIndex : 'volumeToLoad', - width : 60, - hidden : false, - editor : this._getEditor("volumeToLoad"), - renderer : function(val, y, sample) { - // return val+ " µl"; - return BUI.formatValuesUnits(val, 'µl', { - fontSize : 12, - decimals : 2, - unitsFontSize : this.unitsFontSize - }); - } - }, - { - text : 'Volume in Well', - dataIndex : 'volume', - hidden : true, - editor : this._getEditor("volume"), - width : 80, - renderer : function(val, y, sample) { - // return val + "(µl)"; - return BUI.formatValuesUnits(val, 'µl', { - fontSize : 12, - decimals : 2, - unitsFontSize : this.unitsFontSize - }); - } - }, - { - text : 'Trans.', - dataIndex : 'transmission', - width : 60, - editor : this._getEditor("transmission"), - renderer : function(val, y, sample) { - return BUI.formatValuesUnits(val, '%', { - fontSize : 12, - decimals : 0, - unitsFontSize : this.unitsFontSize - }); - } - }, - { - text : 'Wait T.', - dataIndex : 'waitTime', - editor : this._getEditor("waitTime"), - width : 50, - renderer : function(val, y, sample) { - // if (val != 0) { - return BUI.formatValuesUnits(val, 's', { - fontSize : 12, - decimals : 0, - unitsFontSize : this.unitsFontSize - }); - // } - } - }, - { - text : 'Flow', - dataIndex : 'flow', - editor : this._getEditor("flow"), - width : 50, - renderer : function(val, y, sample) { - if (val == true) { - return "yes"; - } - return null; - } - }, - { - text : 'Viscosity', - dataIndex : 'viscosity', - tooltip : 'The viscosity of a fluid is a measure of its resistance to gradual deformation by shear stress or tensile stress. For liquids, it corresponds to the informal notion of "thickness"', - editor : this._getEditor("viscosity"), - width : 50, - renderer : function(val, y, sample) { - return val; - } - } ] - }, { - text : 'Status', - dataIndex : 'status', - width : 50, - hidden : _this.isStatusColumnHidden, - renderer : function(val, y, sample) { - if (val != null) { - if (val == 'DONE') { - return "" + val + " "; - } - } - } - }, { - text : 'Time', - dataIndex : 'time', - width : 80, - hidden : _this.isTimeColumnHidden, - renderer : function(val, y, sample) { - if (sample.raw.run3VO != null) { - if (sample.raw.run3VO.timeStart != null) { - if (sample.raw.run3VO.timeStart != "") { - var m = moment(sample.raw.run3VO.timeStart); - return m.format("hh:mm:ss a"); - } - } - } - } - }, { - text : 'Energy', - dataIndex : 'energy', - width : 100, - hidden : true - }, { - text : 'Real Exp. Temp.(C)', - width : 100, - dataIndex : 'expExposureTemperature', - hidden : true - }, { - text : 'Storage Temp.(C)', - width : 100, - dataIndex : 'storageTemperature', - hidden : true - }, { - text : 'Time/Frame (s)', - width : 100, - dataIndex : 'timePerFrame', - hidden : true - }, { - text : 'Radiation Relative', - dataIndex : 'radiationRelative', - width : 100, - hidden : true - }, { - text : 'Radiation Absolute', - dataIndex : 'radiationAbsolute', - width : 100, - hidden : true - }, { - text : 'Comments', - dataIndex : 'comments', - flex : 1, - hidden : false, - editor : this._getEditor("comments") - }, { - id : _this.id + 'buttonRemoveSample', - text : '', - hidden : !_this.removeBtnEnabled, - width : 100, - sortable : false, - renderer : function(value, metaData, record, rowIndex, colIndex, store) { - if (record.raw.macromoleculeId != null) { - if (_this.removeBtnEnabled) { - return BUI.getRedButton('REMOVE'); - } - } - } - } ], - bbar : bbar, - viewConfig : { - stripeRows : true, - getRowClass : function(record, index, rowParams, store) { - if (record.data.status == "DONE") { - return 'green-row'; - } - }, - listeners : { - 'itemclick' : function(grid, record, item, index, e, eOpts) { - _this.onClick.notify({ - specimen : record.raw - }); - }, - 'cellclick' : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { - if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonRemoveSample') { - grid.getStore().removeAt(rowIndex); +BoxWhiskerGraph.prototype.plotClusterTitles = function(properties){ + /** Cluster Titles **/ + var posX = this.left + this.rulerWidth; + var data = this.data; + for(var i = 0; i < data.clusters.length; i++ ){ + /** title for the clusters **/ + posX = posX + this.interClustersSpace; + + /** Drawing title of classes **/ + var cluster = data.clusters[i]; + var posXClasses = posX; + for(var j = 0; j < cluster.classes.length; j++){ + //SVG.drawRectangle(posXClasses, this.height - (this.bottom + this.clusterTitleHeight + this.rulerHeight), properties.classWidth, this.rulerHeight, this.svg, [["fill", "green"]]); +// SVG.drawText(posXClasses + 1/6*properties.classWidth, this.height - (this.bottom + this.clusterTitleHeight + (this.rulerHeight*1/4)), cluster.classes[j].name, this.svg, [["style", "font-size:xx-small"]]); + var color = cluster.classes[j].color; + this.drawSVGVerticalText(posXClasses + 1/2*(properties.classWidth), this.height - (this.bottom - this.clusterTitleHeight + (this.rulerHeight)), cluster.classes[j].name , [["style", "font-size:x-small;"], ["fill", color]]); + + // SVG.drawRectangle(posXClasses, this.height - (this.bottom + this.clusterTitleHeight), properties.classWidth, 2, this.svg, [["fill", "black"]]); + posXClasses = posXClasses + properties.classWidth + this.interClassesSpace; + } + + var clusterTitleSpace = (data.clusters[i].classes.length * properties.classWidth) + ((data.clusters[i].classes.length - 1) * this.interClassesSpace); + //SVG.drawRectangle(posX, this.height - (this.bottom + this.clusterTitleHeight), clusterTitleSpace, this.clusterTitleHeight, this.svg, [["fill", "pink"]]); + SVG.drawRectangle(posX, this.height - (this.clusterTitleHeight+this.bottom), clusterTitleSpace, 1, this.svg, []); + +// var transform = ["translate", "(" + posX + 1/3*clusterTitleSpace +", " + this.height - (this.bottom + (this.clusterTitleHeight*1/4)) +")"], ["transform", "rotate(180)"]; +// var transform = ["transform", "translate(" + (posX + 1/3*clusterTitleSpace) +", " + (this.height - (this.bottom + (this.clusterTitleHeight*1/4))) + "), rotate(-90) "]; +// this.drawSVGVerticalText(posX + 1/3*clusterTitleSpace, this.height - (this.bottom + (this.clusterTitleHeight*1/4)),data.clusters[i].name, [["style", "font-size:x-small"]]); + SVG.drawText(posX , this.height - (this.bottom + (this.clusterTitleHeight*1/4)),data.clusters[i].name ,this.svg, [["style", "font-size:x-small"]]); + posX = posX + clusterTitleSpace; + } +}; + +BoxWhiskerGraph.prototype.plotWhisters = function(data, properties){ + var colors = ["yellow", "orange", "green"]; + var posX = this.left + this.rulerWidth; + for(var i = 0; i < data.clusters.length; i++ ){ + var cluster = data.clusters[i]; + /** inter cluster space **/ + //SVG.drawRectangle(posX, this.top, this.interClustersSpace, this.height - this.top - this.bottom - this.clusterTitleHeight - this.rulerHeight, this.svg, [["fill", "black"]]); + posX = posX + this.interClustersSpace; + + for (var j = 0; j < cluster.classes.length; j ++){ + + //SVG.drawRectangle(posX, this.top, properties.classWidth, this.height - this.top - this.bottom - this.clusterTitleHeight - this.rulerHeight, this.svg, [["fill", colors[j]]]); + this.plotBoxWhisker( + { + name : cluster.classes[j].name, + values : cluster.classes[j].values, + minPoint : properties.minPoint, + maxPoint : properties.maxPoint, + x : posX, + y : this.top, + width : properties.classWidth, + height : this.height - this.top - this.bottom - this.clusterTitleHeight - this.rulerHeight + + + } + ); + posX = posX + properties.classWidth; + //SVG.drawRectangle(posX, this.top, this.interClassesSpace, this.height - this.top - this.bottom - this.clusterTitleHeight - this.rulerHeight, this.svg, [["fill", "black"]]); + if (j < cluster.classes.length - 1){ + posX = posX + this.interClassesSpace; + } + } + } +}; + +BoxWhiskerGraph.prototype.draw = function(targetId, data){ + this.targetId = targetId; + var properties = (this.getDimensions(data)); + this.svg = SVG.createSVGCanvas(document.getElementById(this.targetId), [["width", this.width], ["height", this.height]]); + this.plotAxes(properties); + this.plotClusterTitles(properties); + this.plotWhisters(data,properties); +}; + +BoxWhiskerGraph.prototype.input = function(){ + return DATADOC.getBoxWhikerData(); +}; + +BoxWhiskerGraph.prototype.test = function(targetId){ + var plot = new BoxWhiskerGraph( + { + targetId : targetId, + height : 350, + width : 450, + maxBoxWidth : 25 + } + ); + plot.refresh(this.input()); +}; + + + + + + + +function DataSet(){ + this.json = null; + +}; + + +DataSet.prototype.loadFromJSON = function(json){ + if(json != null) { + if(this.validate(json)) { + this.json = json; + } + } +}; + + +DataSet.prototype.toJSON = function(json){ + return this.json; +}; + + +/** Abstract method to be override on childs classes **/ +DataSet.prototype.validate = function(json){ + if (true){ + return true; + } + /*else{ + throw "Data validation failed"; + }*/ +}; + + + + +Edge.prototype.getName = GraphItem.prototype.getName; +Edge.prototype.setName = GraphItem.prototype.setName; +Edge.prototype.getId = GraphItem.prototype.getId; + +function Edge(id, name, nodeSource, nodeTarget, args){ + GraphItem.prototype.constructor.call(this, id, name, args); + + this.sourceNode = nodeSource; + this.targetNode = nodeTarget; + +}; + +Edge.prototype.toJSON = function(){ + return {"index": this.id,"sourceIndex":this.sourceNode.getId(),"targetIndex":this.targetNode.getId(),"args":this.args}; +}; + +Edge.prototype.getNodeSource = function(){ + return this.sourceNode; +}; + +Edge.prototype.getNodeTarget = function(){ + return this.targetNode; +}; + +Edge.prototype.remove = function(){ + //Remove edge object in the nodes + this.getNodeSource().removeEdge(this); + this.getNodeTarget().removeEdge(this); + + this.deleted.notify(this); +}; + + +EdgeGraphFormatter.prototype.setProperties = ItemGraphFormatter.prototype.setProperties; +EdgeGraphFormatter.prototype.getDefault = ItemGraphFormatter.prototype.getDefault; +EdgeGraphFormatter.prototype.getSelected = ItemGraphFormatter.prototype.getSelected; +EdgeGraphFormatter.prototype.getOver = ItemGraphFormatter.prototype.getOver; +EdgeGraphFormatter.prototype.getDragging = ItemGraphFormatter.prototype.getDragging; +EdgeGraphFormatter.prototype.getId = ItemGraphFormatter.prototype.getId; +EdgeGraphFormatter.prototype.toJSON = ItemGraphFormatter.prototype.toJSON; +EdgeGraphFormatter.prototype.loadFromJSON = ItemGraphFormatter.prototype.loadFromJSON; +EdgeGraphFormatter.prototype._setEvents = ItemGraphFormatter.prototype._setEvents; + +function EdgeGraphFormatter(id, defaultFormat, selectedFormat, overFormat, dragginFormat){ + ItemGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, dragginFormat); + + this.getDefault().args.type = "EdgeGraphFormatter"; +}; + + + + +LineEdgeGraphFormatter.prototype.setProperties = EdgeGraphFormatter.prototype.setProperties; +LineEdgeGraphFormatter.prototype.getDefault = EdgeGraphFormatter.prototype.getDefault; +LineEdgeGraphFormatter.prototype.getSelected = EdgeGraphFormatter.prototype.getSelected; +LineEdgeGraphFormatter.prototype.getOver = EdgeGraphFormatter.prototype.getOver; +LineEdgeGraphFormatter.prototype.getDragging = EdgeGraphFormatter.prototype.getDragging; +LineEdgeGraphFormatter.prototype.getId = EdgeGraphFormatter.prototype.getId; +LineEdgeGraphFormatter.prototype.toJSON = EdgeGraphFormatter.prototype.toJSON; +LineEdgeGraphFormatter.prototype.loadFromJSON = EdgeGraphFormatter.prototype.loadFromJSON; +LineEdgeGraphFormatter.prototype._setEvents = EdgeGraphFormatter.prototype._setEvents; + +function LineEdgeGraphFormatter(id, defaultFormat, selectedFormat, overFormat, dragginFormat){ + EdgeGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, dragginFormat); + this.args.type = "LineEdgeGraphFormatter"; +}; + +/** DIRECTED **/ +DirectedLineEdgeGraphFormatter.prototype.setProperties = EdgeGraphFormatter.prototype.setProperties; +DirectedLineEdgeGraphFormatter.prototype.getDefault = EdgeGraphFormatter.prototype.getDefault; +DirectedLineEdgeGraphFormatter.prototype.getSelected = EdgeGraphFormatter.prototype.getSelected; +DirectedLineEdgeGraphFormatter.prototype.getOver = EdgeGraphFormatter.prototype.getOver; +DirectedLineEdgeGraphFormatter.prototype.getDragging = EdgeGraphFormatter.prototype.getDragging; +DirectedLineEdgeGraphFormatter.prototype.getId = EdgeGraphFormatter.prototype.getId; +DirectedLineEdgeGraphFormatter.prototype.toJSON = EdgeGraphFormatter.prototype.toJSON; +DirectedLineEdgeGraphFormatter.prototype.loadFromJSON = EdgeGraphFormatter.prototype.loadFromJSON; +DirectedLineEdgeGraphFormatter.prototype._setEvents = EdgeGraphFormatter.prototype._setEvents; + +function DirectedLineEdgeGraphFormatter(id, defaultFormat, selectedFormat, overFormat, dragginFormat){ + EdgeGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, dragginFormat); + this.args.type = "DirectedLineEdgeGraphFormatter"; + this.args.arrowSize = "3"; +}; +DirectedLineEdgeGraphFormatter.prototype.getArrowSize = function(){ + return this.args.arrowSize; +}; + +OdirectedLineEdgeGraphFormatter.prototype.setProperties = EdgeGraphFormatter.prototype.setProperties; +OdirectedLineEdgeGraphFormatter.prototype.getDefault = EdgeGraphFormatter.prototype.getDefault; +OdirectedLineEdgeGraphFormatter.prototype.getSelected = EdgeGraphFormatter.prototype.getSelected; +OdirectedLineEdgeGraphFormatter.prototype.getOver = EdgeGraphFormatter.prototype.getOver; +OdirectedLineEdgeGraphFormatter.prototype.getDragging = EdgeGraphFormatter.prototype.getDragging; +OdirectedLineEdgeGraphFormatter.prototype.getId = EdgeGraphFormatter.prototype.getId; +OdirectedLineEdgeGraphFormatter.prototype.toJSON = EdgeGraphFormatter.prototype.toJSON; +OdirectedLineEdgeGraphFormatter.prototype.loadFromJSON = EdgeGraphFormatter.prototype.loadFromJSON; +OdirectedLineEdgeGraphFormatter.prototype._setEvents = EdgeGraphFormatter.prototype._setEvents; + +function OdirectedLineEdgeGraphFormatter(id, defaultFormat, selectedFormat, overFormat, dragginFormat){ + EdgeGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, dragginFormat); + this.args.type = "DirectedLineEdgeGraphFormatter"; + this.args.arrowSize = "3"; +}; + +OdirectedLineEdgeGraphFormatter.prototype.getArrowSize = function(){ + return this.args.arrowSize; +}; + + +/** CUT (inhibition) **/ +CutDirectedLineEdgeGraphFormatter.prototype.setProperties = EdgeGraphFormatter.prototype.setProperties; +CutDirectedLineEdgeGraphFormatter.prototype.getDefault = EdgeGraphFormatter.prototype.getDefault; +CutDirectedLineEdgeGraphFormatter.prototype.getSelected = EdgeGraphFormatter.prototype.getSelected; +CutDirectedLineEdgeGraphFormatter.prototype.getOver = EdgeGraphFormatter.prototype.getOver; +CutDirectedLineEdgeGraphFormatter.prototype.getDragging = EdgeGraphFormatter.prototype.getDragging; +CutDirectedLineEdgeGraphFormatter.prototype.getId = EdgeGraphFormatter.prototype.getId; +CutDirectedLineEdgeGraphFormatter.prototype.toJSON = EdgeGraphFormatter.prototype.toJSON; +CutDirectedLineEdgeGraphFormatter.prototype.loadFromJSON = EdgeGraphFormatter.prototype.loadFromJSON; +CutDirectedLineEdgeGraphFormatter.prototype._setEvents = EdgeGraphFormatter.prototype._setEvents; + +function CutDirectedLineEdgeGraphFormatter(id, defaultFormat, selectedFormat, overFormat, dragginFormat){ + EdgeGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, dragginFormat); + this.args.type = "CutDirectedLineEdgeGraphFormatter"; +}; + + + + +BezierEdgeGraphFormatter.prototype.setProperties = EdgeGraphFormatter.prototype.setProperties; +BezierEdgeGraphFormatter.prototype.setProperties = EdgeGraphFormatter.prototype.setProperties; +BezierEdgeGraphFormatter.prototype.getDefault = EdgeGraphFormatter.prototype.getDefault; +BezierEdgeGraphFormatter.prototype.getSelected = EdgeGraphFormatter.prototype.getSelected; +BezierEdgeGraphFormatter.prototype.getOver = EdgeGraphFormatter.prototype.getOver; +BezierEdgeGraphFormatter.prototype.getDragging = EdgeGraphFormatter.prototype.getDragging; +BezierEdgeGraphFormatter.prototype.getId = EdgeGraphFormatter.prototype.getId; +BezierEdgeGraphFormatter.prototype.toJSON = EdgeGraphFormatter.prototype.toJSON; +BezierEdgeGraphFormatter.prototype.loadFromJSON = EdgeGraphFormatter.prototype.loadFromJSON; +BezierEdgeGraphFormatter.prototype._setEvents = EdgeGraphFormatter.prototype._setEvents; + +function BezierEdgeGraphFormatter(id, defaultFormat, selectedFormat, overFormat, dragginFormat){ + EdgeGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, dragginFormat); + this.args.type = "BezierEdgeGraphFormatter"; +}; + + + +DotDirectedLineEdgeGraphFormatter.prototype.setProperties = EdgeGraphFormatter.prototype.setProperties; +DotDirectedLineEdgeGraphFormatter.prototype.getDefault = EdgeGraphFormatter.prototype.getDefault; +DotDirectedLineEdgeGraphFormatter.prototype.getSelected = EdgeGraphFormatter.prototype.getSelected; +DotDirectedLineEdgeGraphFormatter.prototype.getOver = EdgeGraphFormatter.prototype.getOver; +DotDirectedLineEdgeGraphFormatter.prototype.getDragging = EdgeGraphFormatter.prototype.getDragging; +DotDirectedLineEdgeGraphFormatter.prototype.getId = EdgeGraphFormatter.prototype.getId; +DotDirectedLineEdgeGraphFormatter.prototype.toJSON = EdgeGraphFormatter.prototype.toJSON; +DotDirectedLineEdgeGraphFormatter.prototype.loadFromJSON = EdgeGraphFormatter.prototype.loadFromJSON; +DotDirectedLineEdgeGraphFormatter.prototype._setEvents = EdgeGraphFormatter.prototype._setEvents; + +function DotDirectedLineEdgeGraphFormatter(id, defaultFormat, selectedFormat, overFormat, dragginFormat){ + EdgeGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, dragginFormat); + this.args.type = "DotDirectedLineEdgeGraphFormatter"; +}; + + +OdotDirectedLineEdgeGraphFormatter.prototype.setProperties = EdgeGraphFormatter.prototype.setProperties; +OdotDirectedLineEdgeGraphFormatter.prototype.getDefault = EdgeGraphFormatter.prototype.getDefault; +OdotDirectedLineEdgeGraphFormatter.prototype.getSelected = EdgeGraphFormatter.prototype.getSelected; +OdotDirectedLineEdgeGraphFormatter.prototype.getOver = EdgeGraphFormatter.prototype.getOver; +OdotDirectedLineEdgeGraphFormatter.prototype.getDragging = EdgeGraphFormatter.prototype.getDragging; +OdotDirectedLineEdgeGraphFormatter.prototype.getId = EdgeGraphFormatter.prototype.getId; +OdotDirectedLineEdgeGraphFormatter.prototype.toJSON = EdgeGraphFormatter.prototype.toJSON; +OdotDirectedLineEdgeGraphFormatter.prototype.loadFromJSON = EdgeGraphFormatter.prototype.loadFromJSON; +OdotDirectedLineEdgeGraphFormatter.prototype._setEvents = EdgeGraphFormatter.prototype._setEvents; + +function OdotDirectedLineEdgeGraphFormatter(id, defaultFormat, selectedFormat, overFormat, dragginFormat){ + EdgeGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, dragginFormat); + this.args.type = "OdotDirectedLineEdgeGraphFormatter"; +}; + + + +function GraphDataset(){ + DataSet.prototype.constructor.call(this); + this.edges = new Object(); + this.vertices = new Object(); + this.verticesIndex = new Object(); + + //Events + this.newVertex = new Event(this); + this.vertexNameChanged = new Event(this); + this.vertexDeleted = new Event(this); + + this.newEdge = new Event(this); + this.edgeNameChanged = new Event(this); + this.edgeDeleted = new Event(this); + + this.json = new Object(); + this.json.vertices = new Array(); + this.json.edges = new Array(); + this.json.relations = new Array(); +}; + +GraphDataset.prototype.loadFromJSON = DataSet.prototype.loadFromJSON; +GraphDataset.prototype.toJSON = DataSet.prototype.toJSON; +GraphDataset.prototype.validate = DataSet.prototype.validate; + +/** Devuelve el numero de edges incidentes sobre el nodo con mas edges **/ +GraphDataset.prototype.getMaxClass = function(){ + var maxClassNode = 0; + for ( var node in this.vertices) { + if (this.vertices[node].getEdgesCount() > maxClassNode){ + maxClassNode = this.vertices[node].getEdgesCount(); + } + } + return maxClassNode; +}; + +/** Devuelve el numero de edges incidentes sobre el nodo con mas edges **/ +GraphDataset.prototype.getMinClass = function(){ + var minClassNode = Math.min(); + for ( var node in this.vertices) { + if (this.vertices[node].getEdgesCount() < minClassNode){ + minClassNode = this.vertices[node].getEdgesCount(); + } + } + return minClassNode; +}; + +GraphDataset.prototype.getVertexByName = function(nodeName){ + var results = new Array(); + + for (var vertexId in this.verticesIndex[nodeName]){ + var vertexByid = this.getVertexById(this.verticesIndex[nodeName][vertexId]); + results.push(vertexByid); + //* añadido nuevo porque fallaba el anterior codigo + return vertexByid + } + + if (results <= 1){ + return this.getVertexById(this.verticesIndex[nodeName]); + } + else{ + return results; + } +}; + +GraphDataset.prototype.getVertexById = function(id){ + return this.vertices[id]; +}; + +GraphDataset.prototype.toSIF = function(){ + var sifDataAdapter = new SifFileDataAdapter(); + return sifDataAdapter.toSIF(this); +}; + +GraphDataset.prototype.toSIFID = function(){ + var sifDataAdapter = new SifFileDataAdapter(); + return sifDataAdapter.toSIFID(this); +}; + +GraphDataset.prototype.toDOT = function(){ + var dotFileDataAdapter = new DotFileDataAdapter(); + return dotFileDataAdapter.toDOT(this); +}; + +GraphDataset.prototype.toDOTID = function(){ + var dotFileDataAdapter = new DotFileDataAdapter(); + return dotFileDataAdapter.toDOTID(this); +}; + +GraphDataset.prototype._addNode = function(nodeName, args){ + return new Vertex(this._getVerticesCount()-1, nodeName, args); +}; + +GraphDataset.prototype.addNode = function(nodeName, args){ + this.json.vertices.push(nodeName); + this._addVerticesIndex(nodeName, this._getVerticesCount() - 1); + var vertex = this._addNode(nodeName, args); + this.vertices[this._getVerticesCount()-1] = vertex; + this._setNodeEvents(vertex); + this.newVertex.notify(vertex); +}; + +GraphDataset.prototype._addVerticesIndex = function(nodeName, id){ + if (this.verticesIndex[nodeName] == null){ + this.verticesIndex[nodeName] = new Array(); + } + this.verticesIndex[nodeName].push(id); +}; + +GraphDataset.prototype.addEdge = function(edgeName, nodeSourceId, nodeTargetId, args){ + this.json.edges.push(edgeName); + var nodeSource = this.getVertexById(nodeSourceId); + var nodeTarget = this.getVertexById(nodeTargetId); + var index = this.getEdgesCount() - 1; + this.edges[index] = new Edge(index, edgeName, nodeSource, nodeTarget, args); + this.json.relations.push({"index": index, "sourceIndex": nodeSourceId, "targetIndex": nodeTargetId, "args": args }); + + nodeSource.addEdge(this.edges[index]); + nodeTarget.addEdge(this.edges[index]); + this._setEdgeEvents(this.edges[index]); + this.newEdge.notify(this.edges[index]); +}; + +GraphDataset.prototype.getVertices = function(){ + return this.vertices; +}; + +GraphDataset.prototype.getEdges = function(){ + return this.edges; +}; + +GraphDataset.prototype.getEdgeById = function(edgeId){ + return this.edges[edgeId]; +}; + +GraphDataset.prototype._getVerticesCount = function(){ + return this.json.vertices.length; +}; + + +GraphDataset.prototype.getVerticesCount = function(){ + var count = 0; + for ( var vertex in this.getVertices()) { + count ++; + } + return count; +}; + + +GraphDataset.prototype.getEdgesCount = function(){ + return this.json.edges.length; +}; + +GraphDataset.prototype.init = function(){ + this.edges = new Object(); + this.vertices = new Object(); +}; + +GraphDataset.prototype._setNodeEvents = function(node){ + var _this = this; + //NODE EVENTS + node.deleted.attach(function (sender, node){ + _this._removeNode(node); + }); + + node.nameChanged.attach(function (sender, args){ + var item = args.item; + var newName = item.name; + var indexes = _this.verticesIndex[args.previousName]; + for(var i = 0; i < indexes.length; i++){ + if(indexes[i] == item.id) + indexes.splice(i,1); + } + if(indexes.length == 0){ + delete _this.verticesIndex[args.previousName]; + } + _this._addVerticesIndex(newName, item.id); + _this.json.vertices[item.id] = newName; + _this.vertexNameChanged.notify(args); + }); +}; + +GraphDataset.prototype._setEdgeEvents = function(edge){ + var _this = this; + //EDGE EVENTS + edge.nameChanged.attach(function (sender, edge){ + _this.edgeNameChanged.notify(edge); + + }); + + edge.deleted.attach(function (sender, edge){ + _this._removeEdge(edge); + }); +}; + +GraphDataset.prototype._connectVerticesByName = function(nodeNameSource, nodeNameTarget){ + var source = this.getVertexByName(nodeNameSource); + var target = this.getVertexByName(nodeNameTarget); + + if ((source != null)&&(target!=null)){ + this.addEdge(source.getName() +"_" + target.getName(), source.getId(), target.getId(), {}); + } + else{ + if (source == null){ + console.log("No encontrado: " + nodeNameSource) + } + if (target == null){ + console.log("No encontrado: " + nodeNameTarget) + } + } +}; + +GraphDataset.prototype.loadFromJSON = function(json){ + var json = json; + this.init(); + this.json = new Object(); + this.json.vertices = new Array(); + this.json.edges = new Array(); + this.json.relations = new Array(); + + for ( var i = 0; i < json.nodes.length; i++) { + if (json.nodes[i] != null){ + var name = json.nodes[i]; + this.addNode(name); + } + else{ + this.json.vertices.push(null); + } + } + + for ( var i = 0; i < json.edges.length; i++) { + if (json.edges[i] != null){ + if (json.relations[i] != null){ + var name = json.edges[i]; + this.addEdge(name, json.relations[i].sourceIndex, json.relations[i].targetIndex, json.relations[i].args); + } + } + else{ + this.json.edges.push(null); + this.json.relations.push(null); + } + } +}; + +GraphDataset.prototype.prettyPrint = function(){ + for ( var node in this.vertices) { + console.log(this.vertices[node].getName() ); + for ( var j = 0; j < this.vertices[node].getEdgesIn().length; j++) { + console.log(" --> " + this.vertices[node].getEdgesIn()[j].getNodeTarget().getName() ); + } + } +}; + +GraphDataset.prototype._removeEdge = function(edge){ + this.json.edges[edge.getId()] = null; + this.json.relations[edge.getId()] = null; + + delete this.edges[edge.getId()]; + this.edgeDeleted.notify(edge); + + +}; + +GraphDataset.prototype._removeNode = function(node){ + this.json.vertices[node.getId()] = null; + delete this.vertices[node.getId()]; + this.vertexDeleted.notify(node); +}; + +GraphDataset.prototype.toJSON = function(){ + var json = new Object(); + var nodes = new Array(); + json.nodes = this.json.vertices; //nodes; + json.edges = this.json.edges; //edges; + json.relations = this.json.relations; + return json; +}; + +GraphDataset.prototype.clone = function(){ + var dsDataset = new GraphDataset(); + dsDataset.loadFromJSON(this.toJSON()); + return dsDataset; +}; +//GraphDataset.prototype.test = function(){ +// this.loadFromJSON(this.toJSON()); +//}; + +function labels(){ + var names = new Array(); + + var dataset = interactomeViewer.graphEditorWidget.dataset; + var layout = interactomeViewer.graphEditorWidget.layout; + + for ( var vertexId in interactomeViewer.graphEditorWidget.dataset.getVertices()) { + names.push(interactomeViewer.graphEditorWidget.dataset.getVertexById(vertexId).getName()); + } + + var sorted = (names.sort()); + console.log(sorted) + var distance = 0.01; + var altura = 0.6; + for ( var i = 0; i < names.length; i++) { + var id =dataset.getVertexByName(names[i]).getId(); + + layout.getNodeById(id).setCoordenates(distance, altura); + + + distance = parseFloat(distance) + parseFloat(0.03); + + altura = parseFloat(altura) + parseFloat(0.02); + + if (parseFloat(altura) == 0.9800000000000003){ + + altura = 0.6; + distance = distance - 0.51; + } + + } + + +}; + +function GraphItem(id, name, args){ + this.id = id; + this.name = name; + this.type = "NONE"; + + this.args = new Object(); + + + if (args!=null){ + this.args = args; + if (args.type !=null){ + this.type = args.type; + } + } + + //Events + this.nameChanged = new Event(this); + this.deleted = new Event(this); +} + +GraphItem.prototype.getName = function(){ + return this.name; +}; + +GraphItem.prototype.getId = function(){ + return this.id; +}; + +GraphItem.prototype.setName = function(name){ + var oldName = this.getName(); + this.name = name; + this.nameChanged.notify({"item": this, "previousName" : oldName}); +}; + + + + + +function LayoutDataset(){ + this.dataset = null; + this.vertices = new Object(); + this.changed = new Event(this); + + + this.args = new Object(); + + //RANDOM, CIRCLE + this.args.type = "CIRCLE"; +}; + +LayoutDataset.prototype.loadFromJSON = function(dataset, json){ + var _this = this; + this.vertices = new Object(); + this.dataset = dataset; //new GraphDataset(); + for ( var vertex in json) { + this.vertices[vertex] = new NodeLayout(vertex, json[vertex].x, json[vertex].y); + this.vertices[vertex].changed.attach(function (sender, item){ + _this.changed.notify(item); + }); + } + this._attachDatasetEvents(); +}; + + +LayoutDataset.prototype.toJSON = function(){ + var serialize = new Object(); + for ( var vertex in this.vertices) { + serialize[vertex] = new Object(); + serialize[vertex].x = this.vertices[vertex].x; + serialize[vertex].y = this.vertices[vertex].y; + } + serialize.dataset = new Object(); + serialize.dataset =this.dataset.toJSON(); + return serialize; +}; + +LayoutDataset.prototype.dataBind = function(graphDataset){ + this.dataset = graphDataset; + this._attachDatasetEvents(); + this._calculateLayout(); +}; + +LayoutDataset.prototype._removeVertex = function(vertexId){ + delete this.vertices[vertexId]; +}; + +LayoutDataset.prototype._attachDatasetEvents = function(){ + var _this = this; + + this.dataset.vertexDeleted.attach(function (sender, item){ + _this._removeVertex(item.getId()); + }); + + this.dataset.newVertex.attach(function (sender, item){ + _this.vertices[item.getId()] = new NodeLayout(item.getId(), 0.5, 0.5); + _this.vertices[item.getId()].changed.attach(function (sender, item){ + _this.changed.notify(item); + }); + }); +}; + +LayoutDataset.prototype.getType = function(){ + return this.args.type; +}; + +LayoutDataset.prototype._calculateLayoutVertices = function(type, count){ + + if (type == "CIRCLE"){ + var radius = 0.4; + var centerX = 0.5; + var centerY = 0.5; + var verticesCoordinates = new Array(); + for(var i = 0; i < count; i++){ + x = centerX + radius * Math.sin(i * 2 * Math.PI/count); + y = centerY + radius * Math.cos(i * 2 * Math.PI/count); + verticesCoordinates.push({'x':x,'y':y}); + } + return verticesCoordinates; + } +}; + + +LayoutDataset.prototype._calculateLayout = function(){ + var _this = this; + if (this.getType() == "RANDOM"){ + for ( var vertex in this.dataset.getVertices()) { + if (this.vertices[vertex] == null){ + this.vertices[vertex] = new NodeLayout(vertex, 0, 0); + } + this.vertices[vertex].setCoordinates(Math.random(), Math.random()); + this.vertices[vertex].changed.attach(function (sender, item){ + _this.changed.notify(item); + }); + } + } + + if ( this.getType() == "CIRCLE"){ + + var count = this.dataset._getVerticesCount(); + var verticesCoordinates = this._calculateLayoutVertices(this.getType(), count); + + var aux = 0; + for ( var vertex in this.dataset.getVertices()) { + if (this.vertices[vertex] == null){ + this.vertices[vertex] = new NodeLayout(vertex, 0, 0); + } + this.vertices[vertex].setCoordinates(verticesCoordinates[aux].x, verticesCoordinates[aux].y);//{"x":, "y":}; + aux++; + this.vertices[vertex].changed.attach(function (sender, item){ + _this.changed.notify(item); + }); + } + } + + + if (this.getType() == "SQUARE"){ + + var count = this.dataset._getVerticesCount(); + var xMin = 0.1; + var xMax = 0.9; + var yMin = 0.1; + var yMax = 0.9; + + var rows = Math.sqrt(count); + var step = (xMax - xMin) / rows; + + var verticesCoordinates = new Array(); + for(var i = 0; i < rows; i ++){ + for ( var j = 0; j < rows; j++) { + x = i * step + xMin; + y = j * step + yMin; + verticesCoordinates.push({'x':x,'y':y}); + } + } + + var aux = 0; + for ( var vertex in this.dataset.getVertices()) { + if (this.vertices[vertex] == null){ + this.vertices[vertex] = new NodeLayout(vertex, 0, 0); + } + this.vertices[vertex].setCoordinates(verticesCoordinates[aux].x, verticesCoordinates[aux].y);//{"x":, "y":}; + aux++; + this.vertices[vertex].changed.attach(function (sender, item){ + _this.changed.notify(item); + }); + } + } + +}; + +LayoutDataset.prototype.getNodeById = function(id){ + return this.vertices[id]; +}; + +LayoutDataset.prototype.getVerticesByArea = function(x1, y1, x2, y2){ + var vertices = new Array(); + for ( var vertex in this.dataset.getVertices()) { + if ((this.vertices[vertex].x >= x1)&&(this.vertices[vertex].x <= x2)){ + if ((this.vertices[vertex].y >= y1)&&(this.vertices[vertex].y <= y2)){ + vertices.push(this.vertices[vertex]); + } + } + } + return vertices; +}; + + + + +LayoutDataset.prototype.getLayout = function(type){ + + if (type == "CIRCLE"){ + this.args.type = "CIRCLE"; + this._calculateLayout(); + return; + } + + if (type == "SQUARE"){ + this.args.type = "SQUARE"; + this._calculateLayout(); + return; + } + + if (type == "RANDOM"){ + this.args.type = "RANDOM"; + this._calculateLayout(); + return; + } + + + var dotText = this.dataset.toDOTID(); + var url = "http://bioinfo.cipf.es/utils/ws/rest/network/layout/"+type+".coords"; + var _this = this; + + $.ajax({ + async: true, + type: "POST", + url: url, + dataType: "text", + data: { + dot :dotText + }, + cache: false, + success: function(data){ + var response = JSON.parse(data); + for ( var vertexId in response) { + _this.vertices[vertexId].setCoordinates(response[vertexId].x, response[vertexId].y); + } + } + }); + +// $.ajax({ +// async: true, +// type: "POST", +// url: url, +// dataType: "script", +// data: { +// dot :dotText +// }, +// cache: false, +// success: function(data){ +// var response = JSON.parse(data); +// for ( var vertexId in response) { +// _this.vertices[vertexId].setCoordinates(response[vertexId].x, response[vertexId].y); +// } +// } +// }); + +}; + +function NodeLayout(id, x, y, args){ + this.id = id; + this.x = x; + this.y = y; + this.changed = new Event(this); +}; + +NodeLayout.prototype.getId = function(id){ + return this.id; +}; + +NodeLayout.prototype.setCoordinates = function(x, y){ + this.x = x; + this.y = y; + this.changed.notify(this); +}; + + + +function NetworkDataSetFormatter(vertexFormatProperties, defaultEdgeProperties, args){ + DataSet.prototype.constructor.call(this); + + this.args = new Object(); + + this.vertices = new Object(); + this.edges = new Object(); + this.dataset = null; + this.maxClass = 0; + this.minClass = 0; + + /** Coordenates with default Setting */ + this.args.width = 1100; + this.args.height = 500; + + /** Optional parameters */ + this.args.backgroundColor = "#FFFFFF"; + this.args.backgroundImage = null; + this.args.backgroundImageHeight = null; + this.args.backgroundImageWidth = null; + this.args.backgroundImageX = null; + this.args.backgroundImageY = null; + + + this.args.balanceNodes = false; + this.args.nodesMaxSize = 3; + this.args.nodesMinSize = 0.5; + + + /** Zoom **/ + this.args.zoomScale = 1; + this.args.zoomScaleStepFactor = 0.4; + + if (args != null){ + if (args.backgroundImage != null){ + this.args.backgroundImage = args.backgroundImage; + } + + if (args.width != null){ + this.args.width = args.width; + } + + if (args.height != null){ + this.args.height = args.height; + this.args.svgHeight = args.height; + } + + if (args.svgHeight != null){ + this.args.svgHeight = args.svgHeight; + } + + if (args.backgroundColor != null){ + this.args.backgroundColor = args.backgroundColor; + } + + if(args.balanceNodes != null){ + this.args.balanceNodes = args.balanceNodes; + } + + if(args.nodesMaxSize != null){ + this.args.nodesMaxSize = args.nodesMaxSize; + } + if(args.nodesMinSize != null){ + this.args.nodesMinSize = args.nodesMinSize; + } + } + + this.args.defaultEdgeProperties = {"fill":"#000000", "radius":"1", "stroke":"#000000", "size":1, "title":{"fontSize":10, "fontColor":"#000000"}}; + this.args.vertexFormatProperties = { "fill":"#000000", "stroke":"#000000", "stroke-opacity":"#000000"}; + + if (vertexFormatProperties!= null){ + this.args.vertexFormatProperties = vertexFormatProperties; + } + if (defaultEdgeProperties!= null){ + this.args.defaultEdgeProperties = defaultEdgeProperties; + } + + /** Events **/ + this.changed = new Event(this); + this.edgeChanged = new Event(this); + this.resized = new Event(this); + this.backgroundImageChanged= new Event(this); + this.backgroundColorChanged= new Event(this); +}; + +NetworkDataSetFormatter.prototype.loadFromJSON = function(dataset, json){ + this.args = new Object(); + this.vertices = new Object(); + this.args = json; + this._setDataset(dataset); + var _this = this; + + for ( var vertex in json.vertices) { + this.addVertex(vertex, json.vertices[vertex]); + } + + for ( var edgeId in json.edges) { + this.addEdge(edgeId, json.edges[edgeId]); + } +}; + + +NetworkDataSetFormatter.prototype.toJSON = function(){ + + +// this.args.vertices = new Object(); +// this.args.edges = new Object(); +// +// for ( var vertex in this.vertices) { +// this.args.vertices[vertex] = this.getVertexById(vertex).toJSON(); +// } +// for ( var edge in this.edges) { +// this.args.edges[edge] = this.getEdgeById(edge).toJSON(); +// } +// +// return (this.args); + + + var serialize = new Object(); + serialize = JSON.parse(JSON.stringify(this.args)); + serialize.vertices = new Object(); + serialize.edges = new Object(); + + for ( var vertex in this.vertices) { + serialize.vertices[vertex] = this.getVertexById(vertex).toJSON(); + } + for ( var edge in this.edges) { + serialize.edges[edge] = this.getEdgeById(edge).toJSON(); + } + + return (serialize); +}; + + + +NetworkDataSetFormatter.prototype._getNodeSize = function(nodeId){ + if (this.isVerticesBalanced()){ + var total = this.maxClass - this.minClass; + if (total == 0){ total = 1;} + var nodeConnection = this.dataset.getVertexById(nodeId).getEdges().length; + return ((nodeConnection*this.args.nodesMaxSize)/total) + this.args.nodesMinSize; + } + return this.getVertexById(nodeId).getDefault().getSize(); +}; + +NetworkDataSetFormatter.prototype._recalculateSize = function(){ + if (this.isVerticesBalanced()){ + this.maxClass = this.dataset.getMaxClass(); + this.minClass = this.dataset.getMinClass(); + for ( var vertexIdAll in this.vertices) { + var size = this._getNodeSize(vertexIdAll); + this.vertices[vertexIdAll].getDefault().setSize(size); + this.vertices[vertexIdAll].getSelected().setSize(size); + this.vertices[vertexIdAll].getOver().setSize(size); + } + } +}; + + +NetworkDataSetFormatter.prototype.addVertex = function(vertexId, json){ + + + if (json == null){ + this.vertices[vertexId] = new CircleVertexGraphFormatter(vertexId, this.args.vertexFormatProperties.defaultFormat, this.args.vertexFormatProperties.selected, this.args.vertexFormatProperties.over, this.args.vertexFormatProperties.dragging); + } + else{ + + /** Cargo los attributos desde el json **/ + if (json.type == null){ + this.vertices[vertexId] = new CircleVertexGraphFormatter(vertexId, this.args.vertexFormatProperties.defaultFormat, this.args.vertexFormatProperties.selected, this.args.vertexFormatProperties.over, this.args.vertexFormatProperties.dragging); + } + + if ((json.type == "SquareVertexGraphFormatter")||(json.type == "SquareVertexNetworkFormatter")){ + this.vertices[vertexId] = new SquareVertexGraphFormatter(vertexId); + this.vertices[vertexId].loadFromJSON(json); + } + + if ((json.type == "CircleVertexGraphFormatter")||(json.type == "CircleVertexNetworkFormatter")){ + this.vertices[vertexId] = new CircleVertexGraphFormatter(vertexId); + this.vertices[vertexId].loadFromJSON(json); + } + + if ((json.type == "EllipseVertexGraphFormatter")||(json.type == "EllipseVertexNetworkFormatter")){ + this.vertices[vertexId] = new EllipseVertexGraphFormatter(vertexId); + this.vertices[vertexId].loadFromJSON(json); + } + + if ((json.type == "RectangleVertexGraphFormatter")||(json.type == "RectangleVertexNetworkFormatter")){ + this.vertices[vertexId] = new RectangleVertexGraphFormatter(vertexId); + this.vertices[vertexId].loadFromJSON(json); + } + + if ((json.type == "RoundedVertexGraphFormatter")||(json.type == "RoundedVertexNetworkFormatter")){ + this.vertices[vertexId] = new RoundedVertexGraphFormatter(vertexId); + this.vertices[vertexId].loadFromJSON(json); + } + + } + + + var _this = this; + this.vertices[vertexId].stateChanged.attach(function (sender, item){ + _this.changed.notify(item); + }); + + var size = this._getNodeSize(vertexId); + this.vertices[vertexId].defaultFormat.args.size = size; + this.vertices[vertexId].selected.args.size = size; + this.vertices[vertexId].over.args.size = size; + +}; + +NetworkDataSetFormatter.prototype.addEdge = function(edgeId, json){ + + /** Es un edge nuevo que le doy los atributos por defecto **/ + if (json == null){ + if (this.dataset.getEdgeById(edgeId).getNodeSource().getId() == this.dataset.getEdgeById(edgeId).getNodeTarget().getId()){ + this.edges[edgeId] = new BezierEdgeGraphFormatter(edgeId, this.args.defaultEdgeProperties.defaultFormat, this.args.defaultEdgeProperties.selected, this.args.defaultEdgeProperties.over, this.args.defaultEdgeProperties.dragging); + }else{ + this.edges[edgeId] = new DirectedLineEdgeGraphFormatter(edgeId, this.args.defaultEdgeProperties.defaultFormat, this.args.defaultEdgeProperties.selected, this.args.defaultEdgeProperties.over, this.args.defaultEdgeProperties.dragging); + } + } + else{ + /** Cargo los attributos desde el json **/ + if (json.type == null){ + this.edges[edgeId] = new LineEdgeGraphFormatter(edgeId, this.args.defaultEdgeProperties.defaultFormat, this.args.defaultEdgeProperties.selected, this.args.defaultEdgeProperties.over, this.args.defaultEdgeProperties.dragging); + } + + if ((json.type == "LineEdgeGraphFormatter")||(json.type == "LineEdgeNetworkFormatter")){ + this.edges[edgeId] = new LineEdgeGraphFormatter(edgeId); + this.edges[edgeId].loadFromJSON(json); + } + if ((json.type == "DirectedLineEdgeGraphFormatter")||(json.type == "DirectedLineEdgeNetworkFormatter")){ + this.edges[edgeId] = new DirectedLineEdgeGraphFormatter(edgeId); + this.edges[edgeId].loadFromJSON(json); + } + + if ((json.type == "BezierEdgeGraphFormatter")||(json.type == "BezierEdgeNetworkFormatter")){ + this.edges[edgeId] = new BezierEdgeGraphFormatter(edgeId); + this.edges[edgeId].loadFromJSON(json); + } + + + if ((json.type == "CutDirectedLineEdgeGraphFormatter")||(json.type == "CutDirectedLineEdgeNetworkFormatter")){ + this.edges[edgeId] = new CutDirectedLineEdgeGraphFormatter(edgeId); + this.edges[edgeId].loadFromJSON(json); + } + + if ((json.type == "DotDirectedLineEdgeGraphFormatter")||(json.type == "DotDirectedLineEdgeNetworkFormatter")){ + this.edges[edgeId] = new DotDirectedLineEdgeGraphFormatter(edgeId); + this.edges[edgeId].loadFromJSON(json); + } + if ((json.type == "OdotDirectedLineEdgeGraphFormatter")||(json.type == "OdotDirectedLineEdgeNetworkFormatter")){ + this.edges[edgeId] = new OdotDirectedLineEdgeGraphFormatter(edgeId); + this.edges[edgeId].loadFromJSON(json); + } + + if ((json.type == "OdirectedLineEdgeGraphFormatter")||(json.type == "OdirectedLineEdgeNetworkFormatter")){ + this.edges[edgeId] = new OdirectedLineEdgeGraphFormatter(edgeId); + this.edges[edgeId].loadFromJSON(json); + } + } + + var _this = this; + this.edges[edgeId].stateChanged.attach(function (sender, item){ + _this.edgeChanged.notify(item); + }); + + this._recalculateSize(); +}; + +NetworkDataSetFormatter.prototype._setDataset = function(dataset){ + this.dataset = dataset; + this.maxClass = dataset.getMaxClass(); + this.minClass = dataset.getMinClass(); + this._attachDatasetEvents(); +}; + +NetworkDataSetFormatter.prototype.changeEdgeType = function(edgeId, type){ + if ((type == "LineEdgeGraphFormatter")||(type == "LineEdgeNetworkFormatter")){ + this.edges[edgeId] = new LineEdgeGraphFormatter(edgeId, this.args.defaultEdgeProperties.defaultFormat, this.args.defaultEdgeProperties.selected, this.args.defaultEdgeProperties.over, this.args.defaultEdgeProperties.dragging); + + } + if ((type == "DirectedLineEdgeGraphFormatter")||(type == "DirectedLineEdgeNetworkFormatter")){ + this.edges[edgeId] = new DirectedLineEdgeGraphFormatter(edgeId, this.args.defaultEdgeProperties.defaultFormat, this.args.defaultEdgeProperties.selected, this.args.defaultEdgeProperties.over, this.args.defaultEdgeProperties.dragging); + } + + if ((type == "CutDirectedLineEdgeGraphFormatter")||(type == "CutDirectedLineEdgeNetworkFormatter")){ + this.edges[edgeId] = new CutDirectedLineEdgeGraphFormatter(edgeId, this.args.defaultEdgeProperties.defaultFormat, this.args.defaultEdgeProperties.selected, this.args.defaultEdgeProperties.over, this.args.defaultEdgeProperties.dragging); + } + + if ((type == "DotDirectedLineEdgeGraphFormatter")||(type == "DotDirectedLineEdgeNetworkFormatter")){ + this.edges[edgeId] = new DotDirectedLineEdgeGraphFormatter(edgeId, this.args.defaultEdgeProperties.defaultFormat, this.args.defaultEdgeProperties.selected, this.args.defaultEdgeProperties.over, this.args.defaultEdgeProperties.dragging); + } + + if ((type == "OdotDirectedLineEdgeGraphFormatter")||(type == "OdotDirectedLineEdgeNetworkFormatter")){ + this.edges[edgeId] = new OdotDirectedLineEdgeGraphFormatter(edgeId, this.args.defaultEdgeProperties.defaultFormat, this.args.defaultEdgeProperties.selected, this.args.defaultEdgeProperties.over, this.args.defaultEdgeProperties.dragging); + } + + if ((type == "OdirectedLineEdgeGraphFormatter")||(type == "OdirectedLineEdgeNetworkFormatter")){ + this.edges[edgeId] = new OdirectedLineEdgeGraphFormatter(edgeId, this.args.defaultEdgeProperties.defaultFormat, this.args.defaultEdgeProperties.selected, this.args.defaultEdgeProperties.over, this.args.defaultEdgeProperties.dragging); + } + + + + var _this = this; + this.edges[edgeId].stateChanged.attach(function (sender, item){ + _this.edgeChanged.notify(item); + }); + _this.edgeChanged.notify(this.edges[edgeId]); +}; +/* +classe = "SquareNetworkNodeFormatter"; +} +if (value == "circle"){ + classe = "CircleNetworkNodeFormatter"; + **/ +NetworkDataSetFormatter.prototype.changeNodeType = function(vertexId, type){ + var defaultFormat = JSON.parse(JSON.stringify(this.vertices[vertexId].getDefault())); + var selectedFormat = JSON.parse(JSON.stringify(this.vertices[vertexId].getSelected())); + var defaultFormat = JSON.parse(JSON.stringify(this.vertices[vertexId].getDefault())); + var defaultFormat = JSON.parse(JSON.stringify(this.vertices[vertexId].getDefault())); + + if ((type == "SquareVertexGraphFormatter")||(type == "SquareVertexNetworkFormatter")){ + this.vertices[vertexId] = new SquareVertexGraphFormatter(vertexId,defaultFormat, this.args.vertexFormatProperties.selected, this.args.vertexFormatProperties.over, this.args.vertexFormatProperties.dragging); + } + + if ((type == "CircleVertexGraphFormatter")||(type == "CircleVertexNetworkFormatter")){ + this.vertices[vertexId] = new CircleVertexGraphFormatter(vertexId, defaultFormat, this.args.vertexFormatProperties.selected, this.args.vertexFormatProperties.over, this.args.vertexFormatProperties.dragging); + } + + if ((type == "EllipseVertexGraphFormatter")||(type == "EllipseVertexNetworkFormatter")){ + this.vertices[vertexId] = new EllipseVertexGraphFormatter(vertexId, defaultFormat, this.args.vertexFormatProperties.selected, this.args.vertexFormatProperties.over, this.args.vertexFormatProperties.dragging); + } + + if ((type == "RectangleVertexGraphFormatter")||(type == "RectangleVertexNetworkFormatter")){ + this.vertices[vertexId] = new RectangleVertexGraphFormatter(vertexId, defaultFormat, this.args.vertexFormatProperties.selected, this.args.vertexFormatProperties.over, this.args.vertexFormatProperties.dragging); + } + + if ((type == "RoundedVertexGraphFormatter")||(type == "RoundedVertexNetworkFormatter")){ + this.vertices[vertexId] = new RoundedVertexGraphFormatter(vertexId, defaultFormat, this.args.vertexFormatProperties.selected, this.args.vertexFormatProperties.over, this.args.vertexFormatProperties.dragging); + } + + if ((type == "OctagonVertexGraphFormatter")||(type == "OctagonVertexNetworkhFormatter")){ + this.vertices[vertexId] = new OctagonVertexGraphFormatter(vertexId, defaultFormat, this.args.vertexFormatProperties.selected, this.args.vertexFormatProperties.over, this.args.vertexFormatProperties.dragging); + } + + + var _this = this; + this.vertices[vertexId].stateChanged.attach(function (sender, item){ + _this.changed.notify(item); + }); + _this.changed.notify(this.vertices[vertexId]); +}; + + + +NetworkDataSetFormatter.prototype.dataBind = function(networkDataSet){ + var _this = this; + this._setDataset(networkDataSet); + + for ( var vertex in this.dataset.getVertices()) { + this.addVertex(vertex); + } + + for ( var edge in this.dataset.getEdges()) { + this.addEdge(edge); + } +}; + +NetworkDataSetFormatter.prototype._removeEdge = function(edgeId){ + delete this.edges[edgeId]; +}; + +NetworkDataSetFormatter.prototype._removeVertex = function(vertexId){ + delete this.vertices[vertexId]; +}; + +NetworkDataSetFormatter.prototype._attachDatasetEvents = function(id){ + var _this = this; + this.dataset.vertexDeleted.attach(function (sender, item){ + _this._removeVertex(item.getId()); + }); + + this.dataset.edgeDeleted.attach(function (sender, item){ + _this._removeEdge(item.getId()); + }); + + this.dataset.newVertex.attach(function (sender, item){ + _this.addVertex(item.getId()); + }); + + this.dataset.newEdge.attach(function (sender, item){ + _this.addEdge(item.getId()); + }); +}; + + +NetworkDataSetFormatter.prototype.getVertexById = function(id){ + return this.vertices[id]; +}; + +NetworkDataSetFormatter.prototype.getEdgeById = function(id){ + return this.edges[id]; +}; + +NetworkDataSetFormatter.prototype.makeLabelsBigger = function(){ +for ( var vertexId in this.vertices) { + var fontSize = this.vertices[vertexId].getDefault().getFontSize() + 2; + this.vertices[vertexId].getDefault().setFontSize(fontSize); + } +}; + +NetworkDataSetFormatter.prototype.makeLabelsSmaller = function(){ + for ( var vertexId in this.vertices) { + var fontSize = this.vertices[vertexId].getDefault().getFontSize() - 2; + this.vertices[vertexId].getDefault().setFontSize(fontSize); + } +}; + + +NetworkDataSetFormatter.prototype.resize = function(width, height){ + this.args.width = width; + this.args.height = height; + this.resized.notify(); +}; + +/** ZOOM GETTERS **/ +NetworkDataSetFormatter.prototype.getZoomScaleStepFactor = function(){return this.args.zoomScaleStepFactor;}; +NetworkDataSetFormatter.prototype.setZoomScaleStepFactor = function(scaleFactor){this.args.zoomScaleStepFactor = scaleFactor;}; +NetworkDataSetFormatter.prototype.getZoomScale = function(){return this.args.zoomScale;}; +NetworkDataSetFormatter.prototype.setZoomScale = function(scale){this.args.zoomScale = scale;}; + +NetworkDataSetFormatter.prototype.getNodesMaxSize = function(){return this.args.nodesMaxSize;}; +NetworkDataSetFormatter.prototype.getNodesMinSize = function(){return this.args.nodesMinSize;}; + +/** SIZE SETTERS AND GETTERS **/ +NetworkDataSetFormatter.prototype.isVerticesBalanced = function(){return this.args.balanceNodes;}; +NetworkDataSetFormatter.prototype.getWidth = function(){return this.args.width;}; +NetworkDataSetFormatter.prototype.getHeight = function(){return this.args.height;}; + +/** OPTIONAL PARAMETERS GETTERS AND SETTERS **/ +NetworkDataSetFormatter.prototype.getBackgroundImage = function(){return this.args.backgroundImage;}; +NetworkDataSetFormatter.prototype.setBackgroundImage = function(value){this.args.backgroundImage = value; this.backgroundImageChanged.notify(this);}; + + +NetworkDataSetFormatter.prototype.getBackgroundImageWidth = function(){return this.args.backgroundImageWidth;}; +NetworkDataSetFormatter.prototype.setBackgroundImageWidth = function(value){this.args.backgroundImageWidth = value; this.backgroundImageChanged.notify(this);}; + +NetworkDataSetFormatter.prototype.getBackgroundImageHeight = function(){return this.args.backgroundImageHeight;}; +NetworkDataSetFormatter.prototype.setBackgroundImageHeight = function(value){this.args.backgroundImageHeight = value; this.backgroundImageChanged.notify(this);}; + +NetworkDataSetFormatter.prototype.getBackgroundImageX = function(){return this.args.backgroundImageX;}; +NetworkDataSetFormatter.prototype.setBackgroundImageX = function(value){this.args.backgroundImageX = value; this.backgroundImageChanged.notify(this);}; + +NetworkDataSetFormatter.prototype.getBackgroundImageY = function(){return this.args.backgroundImageX;}; +NetworkDataSetFormatter.prototype.setBackgroundImageY = function(value){this.args.backgroundImageY = value; this.backgroundImageChanged.notify(this);}; + + + +NetworkDataSetFormatter.prototype.getBackgroundColor = function(){return this.args.backgroundColor;}; +NetworkDataSetFormatter.prototype.setBackgroundColor = function(value){this.args.backgroundColor = value; this.backgroundColorChanged.notify(this);}; + +NetworkDataSetFormatter.prototype.getWidth = function(){return this.args.width;}; +NetworkDataSetFormatter.prototype.setWidth = function(value){this.args.width = value;}; + +NetworkDataSetFormatter.prototype.getHeight = function(){return this.args.height;}; +NetworkDataSetFormatter.prototype.setHeight = function(value){this.args.height = value;}; + + + + +Vertex.prototype.getName = GraphItem.prototype.getName; +Vertex.prototype.setName = GraphItem.prototype.setName; +Vertex.prototype.getId = GraphItem.prototype.getId; + +function Vertex(id, name, args){ + GraphItem.prototype.constructor.call(this, id, name, args); + this.edgesIn= new Array(); + this.edgesOut= new Array(); +}; + +Vertex.prototype.getEdges = function(){ + return this.edgesIn.concat(this.edgesOut); +}; + +Vertex.prototype.getEdgesCount = function(){ + return this.getEdges().length; +}; + +Vertex.prototype.getEdgesIn = function(){ + return this.edgesIn; +}; + +Vertex.prototype.getEdgesOut = function(){ + return this.edgesOut; +}; + +Vertex.prototype.addEdge = function(edge){ + if (edge.getNodeSource().getId() == this.id){ + this.edgesIn.push(edge); + } + else{ + this.edgesOut.push(edge); + } +}; + +Vertex.prototype.removeEdge = function(edge){ + for ( var i = 0; i < this.getEdgesIn().length; i++) { + var edgeIn = this.edgesIn[i]; + if (edgeIn.getId() == edge.getId()){ + this.edgesIn.splice(i, 1); + } + } + for ( var i = 0; i < this.getEdgesOut().length; i++) { + var edgeIn = this.edgesOut[i]; + if (edgeIn.getId() == edge.getId()){ + this.edgesOut.splice(i, 1); + } + } +}; + +Vertex.prototype.remove = function(){ + var edges = this.getEdges(); + for ( var i = 0; i < edges.length; i++) { + var edge = edges[i]; + edge.remove(); + } + this.deleted.notify(this); +}; + + + + + +VertexGraphFormatter.prototype.getDefault = ItemGraphFormatter.prototype.getDefault; +VertexGraphFormatter.prototype.getSelected = ItemGraphFormatter.prototype.getSelected; +VertexGraphFormatter.prototype.getOver = ItemGraphFormatter.prototype.getOver; +VertexGraphFormatter.prototype.getDragging = ItemGraphFormatter.prototype.getDragging; +VertexGraphFormatter.prototype.getId = ItemGraphFormatter.prototype.getId; +VertexGraphFormatter.prototype.toJSON = ItemGraphFormatter.prototype.toJSON; +VertexGraphFormatter.prototype.loadFromJSON = ItemGraphFormatter.prototype.loadFromJSON; +VertexGraphFormatter.prototype._setEvents = ItemGraphFormatter.prototype._setEvents; + + +function VertexGraphFormatter(defaultFormat, selectedFormat, overFormat, draggingFormat){ + ItemGraphFormatter.prototype.constructor.call(this, defaultFormat, selectedFormat, overFormat, draggingFormat); +}; + + +CircleVertexGraphFormatter.prototype.getDefault = VertexGraphFormatter.prototype.getDefault; +CircleVertexGraphFormatter.prototype.getSelected = VertexGraphFormatter.prototype.getSelected; +CircleVertexGraphFormatter.prototype.getOver = VertexGraphFormatter.prototype.getOver; +CircleVertexGraphFormatter.prototype.getDragging = VertexGraphFormatter.prototype.getDragging; +CircleVertexGraphFormatter.prototype.getId = VertexGraphFormatter.prototype.getId; +CircleVertexGraphFormatter.prototype.toJSON = VertexGraphFormatter.prototype.toJSON; +CircleVertexGraphFormatter.prototype.loadFromJSON = VertexGraphFormatter.prototype.loadFromJSON; +CircleVertexGraphFormatter.prototype._setEvents = VertexGraphFormatter.prototype._setEvents; + +function CircleVertexGraphFormatter(id, defaultFormat, selectedFormat, overFormat, draggingFormat){ + VertexGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, draggingFormat); + this.args.type = "CircleVertexGraphFormatter"; + if (defaultFormat != null){ + if (defaultFormat.radius != null){ + this.defaultFormat.args.radius = defaultFormat.radius; + } + } + + if (selectedFormat != null){ + if (selectedFormat.radius != null){ + this.selected.args.radius = selectedFormat.radius; + } + } + + if (overFormat != null){ + if (overFormat.radius != null){ + this.over.args.radius = overFormat.radius; + } + } + + if (draggingFormat != null){ + if (draggingFormat.radius != null){ + this.dragging.args.draggingFormat = draggingFormat.radius; + } + } +}; + + + +SquareVertexGraphFormatter.prototype.getDefault = VertexGraphFormatter.prototype.getDefault; +SquareVertexGraphFormatter.prototype.getSelected = VertexGraphFormatter.prototype.getSelected; +SquareVertexGraphFormatter.prototype.getOver = VertexGraphFormatter.prototype.getOver; +SquareVertexGraphFormatter.prototype.getDragging = VertexGraphFormatter.prototype.getDragging; +SquareVertexGraphFormatter.prototype.getId = VertexGraphFormatter.prototype.getId; +SquareVertexGraphFormatter.prototype.toJSON = VertexGraphFormatter.prototype.toJSON; +SquareVertexGraphFormatter.prototype.loadFromJSON = VertexGraphFormatter.prototype.loadFromJSON; +SquareVertexGraphFormatter.prototype._setEvents = VertexGraphFormatter.prototype._setEvents; + +function SquareVertexGraphFormatter(id, defaultFormat, selectedFormat, overFormat, draggingFormat){ + VertexGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, draggingFormat); + this.args.type = "SquareVertexGraphFormatter"; + if (defaultFormat != null){ + if (defaultFormat.radius != null){ + this.defaultFormat.args.radius = defaultFormat.radius; + } + } + + if (selectedFormat != null){ + if (selectedFormat.radius != null){ + this.selected.args.radius = selectedFormat.radius; + } + } + + if (overFormat != null){ + if (overFormat.radius != null){ + this.over.args.radius = overFormat.radius; + } + } + + if (draggingFormat != null){ + if (draggingFormat.radius != null){ + this.dragging.args.draggingFormat = draggingFormat.radius; + } + } +}; + + +EllipseVertexGraphFormatter.prototype.getDefault = VertexGraphFormatter.prototype.getDefault; +EllipseVertexGraphFormatter.prototype.getSelected = VertexGraphFormatter.prototype.getSelected; +EllipseVertexGraphFormatter.prototype.getOver = VertexGraphFormatter.prototype.getOver; +EllipseVertexGraphFormatter.prototype.getDragging = VertexGraphFormatter.prototype.getDragging; +EllipseVertexGraphFormatter.prototype.getId = VertexGraphFormatter.prototype.getId; +EllipseVertexGraphFormatter.prototype.toJSON = VertexGraphFormatter.prototype.toJSON; +EllipseVertexGraphFormatter.prototype.loadFromJSON = VertexGraphFormatter.prototype.loadFromJSON; +EllipseVertexGraphFormatter.prototype._setEvents = VertexGraphFormatter.prototype._setEvents; + +function EllipseVertexGraphFormatter(id, defaultFormat, selectedFormat, overFormat, draggingFormat){ + VertexGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, draggingFormat); + this.args.type = "EllipseVertexGraphFormatter"; + if (defaultFormat != null){ + if (defaultFormat.radius != null){ + this.defaultFormat.args.radius = defaultFormat.radius; + } + } + + if (selectedFormat != null){ + if (selectedFormat.radius != null){ + this.selected.args.radius = selectedFormat.radius; + } + } + + if (overFormat != null){ + if (overFormat.radius != null){ + this.over.args.radius = overFormat.radius; + } + } + + if (draggingFormat != null){ + if (draggingFormat.radius != null){ + this.dragging.args.draggingFormat = draggingFormat.radius; + } + } +}; + + + +RectangleVertexGraphFormatter.prototype.getDefault = ItemGraphFormatter.prototype.getDefault; +RectangleVertexGraphFormatter.prototype.getSelected = ItemGraphFormatter.prototype.getSelected; +RectangleVertexGraphFormatter.prototype.getOver = ItemGraphFormatter.prototype.getOver; +RectangleVertexGraphFormatter.prototype.getDragging = ItemGraphFormatter.prototype.getDragging; +RectangleVertexGraphFormatter.prototype.getId = ItemGraphFormatter.prototype.getId; +RectangleVertexGraphFormatter.prototype.toJSON = ItemGraphFormatter.prototype.toJSON; +RectangleVertexGraphFormatter.prototype.loadFromJSON = ItemGraphFormatter.prototype.loadFromJSON; +RectangleVertexGraphFormatter.prototype._setEvents = ItemGraphFormatter.prototype._setEvents; + +function RectangleVertexGraphFormatter(id, defaultFormat, selectedFormat, overFormat, draggingFormat){ + VertexGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, draggingFormat); + this.args.type = "RectangleVertexGraphFormatter"; + if (defaultFormat != null){ + if (defaultFormat.radius != null){ + this.defaultFormat.args.radius = defaultFormat.radius; + } + } + + if (selectedFormat != null){ + if (selectedFormat.radius != null){ + this.selected.args.radius = selectedFormat.radius; + } + } + + if (overFormat != null){ + if (overFormat.radius != null){ + this.over.args.radius = overFormat.radius; + } + } + + if (draggingFormat != null){ + if (draggingFormat.radius != null){ + this.dragging.args.draggingFormat = draggingFormat.radius; + } + } +}; + + + +RoundedVertexGraphFormatter.prototype.getDefault = ItemGraphFormatter.prototype.getDefault; +RoundedVertexGraphFormatter.prototype.getSelected = ItemGraphFormatter.prototype.getSelected; +RoundedVertexGraphFormatter.prototype.getOver = ItemGraphFormatter.prototype.getOver; +RoundedVertexGraphFormatter.prototype.getDragging = ItemGraphFormatter.prototype.getDragging; +RoundedVertexGraphFormatter.prototype.getId = ItemGraphFormatter.prototype.getId; +RoundedVertexGraphFormatter.prototype.toJSON = ItemGraphFormatter.prototype.toJSON; +RoundedVertexGraphFormatter.prototype.loadFromJSON = ItemGraphFormatter.prototype.loadFromJSON; +RoundedVertexGraphFormatter.prototype._setEvents = ItemGraphFormatter.prototype._setEvents; + +function RoundedVertexGraphFormatter(id, defaultFormat, selectedFormat, overFormat, draggingFormat){ + VertexGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, draggingFormat); + this.args.type = "RoundedVertexGraphFormatter"; + if (defaultFormat != null){ + if (defaultFormat.radius != null){ + this.defaultFormat.args.radius = defaultFormat.radius; + } + } + + if (selectedFormat != null){ + if (selectedFormat.radius != null){ + this.selected.args.radius = selectedFormat.radius; + } + } + + if (overFormat != null){ + if (overFormat.radius != null){ + this.over.args.radius = overFormat.radius; + } + } + + if (draggingFormat != null){ + if (draggingFormat.radius != null){ + this.dragging.args.draggingFormat = draggingFormat.radius; + } + } +}; + + + +OctagonVertexGraphFormatter.prototype.getDefault = ItemGraphFormatter.prototype.getDefault; +OctagonVertexGraphFormatter.prototype.getSelected = ItemGraphFormatter.prototype.getSelected; +OctagonVertexGraphFormatter.prototype.getOver = ItemGraphFormatter.prototype.getOver; +OctagonVertexGraphFormatter.prototype.getDragging = ItemGraphFormatter.prototype.getDragging; +OctagonVertexGraphFormatter.prototype.getId = ItemGraphFormatter.prototype.getId; +OctagonVertexGraphFormatter.prototype.toJSON = ItemGraphFormatter.prototype.toJSON; +OctagonVertexGraphFormatter.prototype.loadFromJSON = ItemGraphFormatter.prototype.loadFromJSON; +OctagonVertexGraphFormatter.prototype._setEvents = ItemGraphFormatter.prototype._setEvents; + +function OctagonVertexGraphFormatter(id, defaultFormat, selectedFormat, overFormat, draggingFormat){ + VertexGraphFormatter.prototype.constructor.call(this, id, defaultFormat, selectedFormat, overFormat, draggingFormat); + this.args.type = "OctagonVertexGraphFormatter"; + if (defaultFormat != null){ + if (defaultFormat.radius != null){ + this.defaultFormat.args.radius = defaultFormat.radius; + } + } + + if (selectedFormat != null){ + if (selectedFormat.radius != null){ + this.selected.args.radius = selectedFormat.radius; + } + } + + if (overFormat != null){ + if (overFormat.radius != null){ + this.over.args.radius = overFormat.radius; + } + } + + if (draggingFormat != null){ + if (draggingFormat.radius != null){ + this.dragging.args.draggingFormat = draggingFormat.radius; + } + } +}; + + + +var Colors = new function() +{ + this.hashColor = []; + this.getColorByScoreArrayValue = function (arrayScore) + { + var array = new Array(); + + for (var i = 0; i< arrayScore.length; i++) + { + + var color = this.getColorByScoreValue(arrayScore[i]) + array.push( color); + + } + return array; + }; + + this.getHexStringByScoreArrayValue = function (arrayScore) + { + var arrayColor = this.getColorByScoreArrayValue(arrayScore); + var arrayHex = new Array(); + for (var i = 0; i< arrayColor.length; i++) + { + arrayHex.push( arrayColor[i].HexString()); + } + return arrayHex; + }; + + this.getColorByScoreValue = function (score) + { + + var truncate = score.toString().substr(0,4); + if (this.hashColor[truncate]!=null) + { + return this.hashColor[truncate]; + } + + + if(isNaN(score)) { + return Colors.ColorFromRGB(0,0,0); + } + var value; + + var from, to; + if(score < 0.5) { + from = Colors.ColorFromRGB(0,0,255); + to = Colors.ColorFromRGB(255,255,255); + value = (score * 2); + } else { + from = Colors.ColorFromRGB(255,255,255); + to = Colors.ColorFromRGB(255,0,0); + value = (score * 2) - 1; + } + + var x = value; + var y = 1.0 - value; + var color = Colors.ColorFromRGB(y * from.Red() + x * to.Red(), y * from.Green() + x * to.Green(), y * from.Blue() + x * to.Blue()); + + this.hashColor[truncate] = color; + + return color; + }; + + this.ColorFromHSV = function(hue, sat, val) + { + var color = new Color(); + color.SetHSV(hue,sat,val); + return color; + }; + + this.ColorFromRGB = function(r, g, b) + { + var color = new Color(); + color.SetRGB(r,g,b); + return color; + }; + + this.ColorFromHex = function(hexStr) + { + var color = new Color(); + color.SetHexString(hexStr); + return color; + }; + + function Color() { + //Stored as values between 0 and 1 + var red = 0; + var green = 0; + var blue = 0; + + //Stored as values between 0 and 360 + var hue = 0; + + //Strored as values between 0 and 1 + var saturation = 0; + var value = 0; + + this.SetRGB = function(r, g, b) + { + red = r/255.0; + green = g/255.0; + blue = b/255.0; + calculateHSV(); + }; + + this.Red = function() { return Math.round(red*255); }; + + this.Green = function() { return Math.round(green*255); }; + + this.Blue = function() { return Math.round(blue*255); }; + + this.SetHSV = function(h, s, v) + { + hue = h; + saturation = s; + value = v; + calculateRGB(); + }; + + this.Hue = function() + { return hue; }; + + this.Saturation = function() + { return saturation; }; + + this.Value = function() + { return value; }; + + this.SetHexString = function(hexString) + { + if(hexString == null || typeof(hexString) != "string") + { + this.SetRGB(0,0,0); + return; + } + + if (hexString.substr(0, 1) == '#') + hexString = hexString.substr(1); + + if(hexString.length != 6) + { + this.SetRGB(0,0,0); + return; + } + + var r = parseInt(hexString.substr(0, 2), 16); + var g = parseInt(hexString.substr(2, 2), 16); + var b = parseInt(hexString.substr(4, 2), 16); + if (isNaN(r) || isNaN(g) || isNaN(b)) + { + this.SetRGB(0,0,0); + return; + } + + this.SetRGB(r,g,b); + }; + + this.HexString = function() + { + + var rStr = this.Red().toString(16); + if (rStr.length == 1) + rStr = '0' + rStr; + var gStr = this.Green().toString(16); + if (gStr.length == 1) + gStr = '0' + gStr; + var bStr = this.Blue().toString(16); + if (bStr.length == 1) + bStr = '0' + bStr; + return ('#' + rStr + gStr + bStr).toUpperCase(); + }; + + this.Complement = function() + { + var newHue = (hue >= 180) ? hue - 180 : hue + 180; + var newVal = (value * (saturation - 1) + 1); + var newSat = (value*saturation) / newVal; + var newColor = new Color(); + newColor.SetHSV(newHue, newSat, newVal); + return newColor; + } ; + + function calculateHSV() + { + var max = Math.max(Math.max(red, green), blue); + var min = Math.min(Math.min(red, green), blue); + + value = max; + + saturation = 0; + if(max != 0) + saturation = 1 - min/max; + + hue = 0; + if(min == max) + return; + + var delta = (max - min); + if (red == max) + hue = (green - blue) / delta; + else if (green == max) + hue = 2 + ((blue - red) / delta); + else + hue = 4 + ((red - green) / delta); + hue = hue * 60; + if(hue < 0) + hue += 360; + } + + function calculateRGB() + { + red = value; + green = value; + blue = value; + + if(value == 0 || saturation == 0) + return; + + var tHue = (hue / 60); + var i = Math.floor(tHue); + var f = tHue - i; + var p = value * (1 - saturation); + var q = value * (1 - saturation * f); + var t = value * (1 - saturation * (1 - f)); + switch(i) + { + case 0: + red = value; green = t; blue = p; + break; + case 1: + red = q; green = value; blue = p; + break; + case 2: + red = p; green = value; blue = t; + break; + case 3: + red = p; green = q; blue = value; + break; + case 4: + red = t; green = p; blue = value; + break; + default: + red = value; green = p; blue = q; + break; + } + } + } +} +(); +/* + * Clase gestiona la insercción, eliminación de los elementos en el DOM + * + * Vital hacerla SIEMPRE compatible hacia atrás + * + * Last update: 28-10-2010 + * + */ + + +var DOM = {}; + +DOM.createNewElement = function(type, nodeParent, attributes) +{ + + var node = document.createElement(type); + for (var i=0; i0) + { + parent.removeChild(parent.childNodes[0]); + + } +}; + +DOM.select = function(targetID) +{ + return document.getElementById(targetID); +// return $("#"+targetID); +}; +var Geometry = +{ + + /** From tow points obtains the angles formed with the cartesian side **/ + getAngleBetweenTwoPoints : function(x1, y1, x2, y2){ + //var m = (y1 - y2)/ (x1 - x2); + return Math.atan2(y2-y1, x2-x1); + }, + + getAdjacentSideOfRectangleRight : function(angle, hypotenuse){ + return Math.abs(Math.cos(angle)*hypotenuse); + }, + + getOppositeSideOfRectangleRight : function(angle, hypotenuse){ + return Math.abs(Math.sin(angle)*hypotenuse); + }, + + toDegree: function(radian){ + return radian*180/Math.PI; + + } + + +}; + +var SVG = +{ + svgns : 'http://www.w3.org/2000/svg', + xlinkns : "http://www.w3.org/1999/xlink", + + createSVGCanvas: function(parentNode, attributes) + { + + attributes.push( ['xmlns', SVG.svgns], ['xmlns:xlink', 'http://www.w3.org/1999/xlink']); + var svg = document.createElementNS("http://www.w3.org/2000/svg", "svg"); + this._setProperties(svg, attributes); + parentNode.appendChild( svg); + return svg; + + }, + + createRectangle : function (x, y, width, height, attributes){ + var rect = document.createElementNS(this.svgns, "rect"); + rect.setAttribute("x",x); + rect.setAttribute("y",y); + rect.setAttribute("width",width); + rect.setAttribute("height",height); + SVG._setProperties(rect, attributes); + return rect; + }, + + drawImage64 : function (x, y, width, height, base64, svgNode, attributes) { + var node = SVG.createImage64(x, y, width, height, base64, attributes); + svgNode.appendChild(node); + return node; + }, + + createImage64 : function (x, y, width, height, base64, attributes) { + var img = document.createElementNS(this.svgns, "image"); + img.setAttribute("x",x); + img.setAttribute("y",y); + img.setAttribute("width",width); + img.setAttribute("height",height); + img.setAttribute("xlink:href",base64); + SVG._setProperties(img, attributes); + return img; + }, + + createLine: function (x1, y1, x2, y2, attributes){ + var line = document.createElementNS(this.svgns,"line"); + line.setAttribute("x1",x1); + line.setAttribute("y1",y1); + line.setAttribute("x2", x2); + line.setAttribute("y2", y2); + SVG._setProperties(line, attributes); + return line; + }, + + createClip: function (id, nodeToClip, attributes){ + var clip = document.createElementNS(this.svgns,"clipPath"); + clip.setAttribute("id",id); + clip.appendChild(nodeToClip); + return clip; + }, + + drawClip : function (id, nodeToClip, svgNode) { + var node = SVG.createClip(id, nodeToClip); + svgNode.appendChild(node); + return node; + }, + + drawRectangle : function (cx, cy, width, height, svgNode, attributes) { + try{ + var node = SVG.createRectangle(cx, cy, width, height, attributes); + svgNode.appendChild(node); + } + catch(e){ + + console.log("-------------------- "); + console.log("Error on drawRectangle " + e); + console.log(attributes); + console.log("-------------------- "); + } + return node; + }, + + createEllipse : function (x, y, rx, ry, attributes){ + var rect = document.createElementNS(this.svgns, "ellipse"); + rect.setAttribute("cx",x); + rect.setAttribute("cy",y); + rect.setAttribute("rx",rx); + rect.setAttribute("ry",ry); + SVG._setProperties(rect, attributes); + return rect; + }, + + drawEllipse : function (cx, cy, rx, ry, svgNode, attributes) { + var node = SVG.createEllipse(cx, cy, rx, ry, attributes); + svgNode.appendChild(node); + return node; + }, + + drawImage : function (x, y, canvasSVG, attributes) { + var image = document.createElementNS(this.svgns, "image"); + image.setAttribute("x",x); + image.setAttribute("y",y); + canvasSVG.appendChild(image); + SVG._setProperties(image, attributes); + }, + + drawLine : function (x1, y1, x2, y2, nodeSVG, attributes) { + try{ + var line = SVG.createLine(x1, y1, x2, y2, attributes); + nodeSVG.appendChild(line); + }catch(e){ + } + return line; + }, + + + drawPath: function (d, nodeSVG, attributes) { + var path = SVG.createPath(d, attributes); + nodeSVG.appendChild(path); + return path; + }, + + createPoligon : function (points, attributes){ + var poligon = document.createElementNS(this.svgns, "polygon"); + poligon.setAttribute("points",points); + SVG._setProperties(poligon, attributes); + return poligon; + }, + + drawPoligon : function (points, canvasSVG, attributes){ + var poligon = SVG.createPoligon(points, attributes); + canvasSVG.appendChild(poligon); + return poligon; + }, + // + + createPath : function (d, attributes){ + var path = document.createElementNS(this.svgns, "path"); + path.setAttribute("d",d); + SVG._setProperties(path, attributes); + return path; + }, + + drawCircle : function (x, y, radio, canvasSVG, attributes) { + + var newText = document.createElementNS(this.svgns,"circle"); + newText.setAttribute("cx",x); + newText.setAttribute("cy",y); + newText.setAttribute("r",radio); + + canvasSVG.appendChild(newText); + attributes["cursor"] = "pointer"; + this._setProperties(newText, attributes); + return newText; + }, + + + _setProperties: function(node, attributes) + { + if (attributes instanceof Array){ + for (var i=0; i< attributes.length; i++) + { + node.setAttribute(attributes[i][0], attributes[i][1]); + } + } + else{ + for ( var key in attributes){ + node.setAttribute(key, attributes[key]); + } + } + }, + +/* drawPath: function(pointsArray, canvasSVG, attributes){ + var path = document.createElementNS(this.svgns,"polyline"); + path.setAttribute ('id', id); + + var d= pointsArray[0].x+ " "+ pointsArray[0].y; + for (var i=1; i< pointsArray.length; i++) + { + d=d+" "+pointsArray[i].x+" "+pointsArray[i].y; + } + path.setAttribute ('points', d); + canvasSVG.appendChild(path); + },*/ + + createText : function (x, y, text, attributes) { + var node = document.createElementNS(this.svgns,"text"); + node.setAttributeNS(null , "x",x); + node.setAttributeNS(null, "y",y); + + var textNode = document.createTextNode(text); + node.appendChild(textNode); + + this._setProperties(node, attributes); + return node; + }, + + drawText : function (x, y, text, canvasSVG, attributes) { + var text = SVG.createText(x, y, text, attributes); + canvasSVG.appendChild(text); + return text; + }, + + + + drawGroup: function(svgNode, attributes) + { + var group = SVG.createGroup(attributes); + svgNode.appendChild(group); + return group; + }, + + createGroup: function(attributes){ + var group = document.createElementNS(this.svgns,"g"); + this._setProperties(group, attributes); + return group; + } + +}; + + + +var CanvasToSVG = { + + convert: function(sourceCanvas, targetSVG, x, y, id, attributes) { + + var img = this._convert(sourceCanvas, targetSVG, x, y, id); + + for (var i=0; i< attributes.length; i++) + { + img.setAttribute(attributes[i][0], attributes[i][1]); + } + }, + + _convert: function(sourceCanvas, targetSVG, x, y, id) { + var svgNS = "http://www.w3.org/2000/svg"; + var xlinkNS = "http://www.w3.org/1999/xlink"; + // get base64 encoded png from Canvas + var image = sourceCanvas.toDataURL(); + + // must be careful with the namespaces + var svgimg = document.createElementNS(svgNS, "image"); + + svgimg.setAttribute('id', id); + + //svgimg.setAttribute('class', class); + //svgimg.setAttribute('xlink:href', image); + svgimg.setAttributeNS(xlinkNS, 'xlink:href', image); + + + + + svgimg.setAttribute('x', x ? x : 0); + svgimg.setAttribute('y', y ? y : 0); + svgimg.setAttribute('width', sourceCanvas.width); + svgimg.setAttribute('height', sourceCanvas.height); + //svgimg.setAttribute('cursor', 'pointer'); + svgimg.imageData = image; + + targetSVG.appendChild(svgimg); + return svgimg; + }, + + importSVG: function(sourceSVG, targetCanvas) { + svg_xml = sourceSVG;//(new XMLSerializer()).serializeToString(sourceSVG); + var ctx = targetCanvas.getContext('2d'); + + var img = new Image(); + img.src = "data:image/svg+xml;base64," + btoa(svg_xml); +// img.onload = function() { + ctx.drawImage(img, 0, 0); +// }; + } + +}; +/* +Graph.prototype.importSVG = function(sourceSVG, targetCanvas) { + sourceSVG = this._svg; + targetCanvas = document.createElementNS('canvas'); + // https://developer.mozilla.org/en/XMLSerializer + svg_xml = (new XMLSerializer()).serializeToString(sourceSVG); + var ctx = targetCanvas.getContext('2d'); + // this is just a JavaScript (HTML) image + var img = new Image(); + // http://en.wikipedia.org/wiki/SVG#Native_support + // https://developer.mozilla.org/en/DOM/window.btoa + img.src = "data:image/svg+xml;base64," + btoa(svg_xml); + img.onload = function() { + // after this, Canvas’ origin-clean is DIRTY + ctx.drawImage(img, 0, 0); + } +}; +*/ + +/* + +Normalizacion de datos para dibujar colores +Issues: + No sé como debería llamarse esta libreria + No sé si ya existe una funciçon en javascript que lo haga + + +*/ + + +var Normalizer = new function() +{ + this.normalizeArray = function (arrayData) + { + + return this.standardizeArray(this.normal(arrayData)); + +// var result = this._getMaxAndMin(arrayData); +// var min =result[0]; +// var max = result[1]; +// +// +// //los hacemos todos positivos +// for (var i = 0; i< arrayData.length; i++) +// { +// arrayData[i]= Math.abs(min) + parseFloat(arrayData[i]); +// } +// +// var result = this._getMaxAndMin(arrayData); +// var min =result[0]; +// var max = result[1]; +// +// +// var resultArray = new Array(); +// for (var i = 0; i< arrayData.length; i++) +// { +// resultArray.push(arrayData[i]*1/max); +// } +// return resultArray; + }; + + this.normal = function(arrayData){ + var mean = this._getMean(arrayData); + var deviation = this._getStdDeviation(arrayData); + var result = this._getMaxAndMin(arrayData); + var min = result[0]; + var max = result[1]; + + var resultArray = new Array(); + for (var i = 0; i< arrayData.length; i++) { + if (deviation!=0){ + resultArray.push((arrayData[i]-mean)/deviation); + }else{ + resultArray.push(arrayData[i]); + } + } + return resultArray; + }; + + this.standardizeArray = function(arrayData) + { + var result = this._getMaxAndMin(arrayData); + var min = result[0]; + var max = result[1]; + + var offset = ( min <= 0 ) ? Math.abs(min) : (-1 * min); + var resultArray = new Array(); + for (var i = 0; i< arrayData.length; i++) { + if(max + offset!=0){ + resultArray.push((arrayData[i] + offset) / (max + offset)); + }else{ + resultArray.push(arrayData[i]+offset); + } + } + return resultArray; + }; + + + this._getMean = function(arrayData) { + var sum = 0; + for (var i = 0; i< arrayData.length; i++) { + sum = sum + parseFloat(arrayData[i]); + } + return sum/arrayData.length; + }; + + this._getStdDeviation = function(arrayData) { + var mean = this._getMean(arrayData); + var acum = 0.0; + for(var i=0; i max) max = parseFloat(arrayData[i]); + } + + return [min, max]; + }; +}; +function GraphCanvas(componentID, targetNode, args) { + this.args = {}; + /** target */ + this.targetID = targetNode.id; + + /** id manage */ + this.id = componentID; + this.args.idGraph = this.id + "main"; + this.args.idBackgroundNode = this.id + "background"; + + this.args.idEdgesGraph = this.id + "edges"; + this.args.idNodesGraph = this.id + "vertices"; + this.args.idLabelGraph = this.id + "label"; + this.args.idBackground = this.id + "background"; + + /** Objects Graph **/ + this.dataset = null; + this.formatter = null; + this.layout = null; + + /** Drawing **/ + this.circleDefaultRadius = 2; + this.squareDefaultSide = this.circleDefaultRadius * 1.5; + + /** Directed Arrow **/ + this.arrowDefaultSize = this.circleDefaultRadius; + + /** Groups **/ + this.GraphGroup = null; + this.GraphNodeGroup = null; + this.GraphLabelGroup = null; + this.GraphBackground = null; + + /** SETTINGS FLAGS **/ + this.args.draggingCanvasEnabled = false; //Flag to set if the canvas can be dragged + this.args.multipleSelectionEnabled = false; + this.args.interactive = false; + this.args.labeled = false; + this.args.linkEnabled = false; + + /** If numberEdge > maxNumberEdgesMoving then only it will move edges when mouse up **/ + this.args.maxNumberEdgesMoving = 3; + this.args.maxNumberEdgesFiringEvents = 50; + + /** Linking edges **/ + this.args.linking = false; + this.linkStartX = 0; + this.linkStartY = 0; + this.linkSVGNode = null; + this.linkNodeSource = null; + this.linkNodeTarget = null; + + /** Dragging Control **/ + this.draggingElement = null; + this.dragging = false; + this.nMouseOffsetX = 0; + this.nMouseOffsetY = 0; + this.dragStartX = 0; + this.dragStartY = 0; + this.desplazamientoX = 0; + this.desplazamientoY = 0; + + /** Selection Control **/ + this.selecting = false; + this.selectorX = null; + this.selectorY = null; + this.selectorSVGNode = null; + + /** Node status **/ + this.args.isVertexSelected = {}; + this.args.selectedVertices = []; + + /** Edges status **/ + this.args.isEdgeSelected = {}; + //this.args.selectedEdges = []; + + if (args != null) { + if (args.multipleSelectionEnabled != null) { + this.args.multipleSelectionEnabled = args.multipleSelectionEnabled; + this.args.draggingCanvasEnabled = !(this.args.multipleSelectionEnabled); + } + if (args.draggingCanvasEnabled != null) { + this.args.draggingCanvasEnabled = args.draggingCanvasEnabled; + this.args.multipleSelectionEnabled = !(this.args.draggingCanvasEnabled); + } + if (args.interactive != null) { + this.args.interactive = args.interactive; + } + if (args.labeled != null) { + this.args.labeled = args.labeled; + } + + } + + /** Hashmap with the svg node labels **/ + this.svgLabels = {}; + + /** EVENTS **/ + this.onVertexOut = new Event(this); + this.onVertexOver = new Event(this); + this.onVertexSelect = new Event(this); + this.onEdgeSelect = new Event(this); + this.onCanvasClicked = new Event(this); + this.onVertexUp = new Event(this); +} + +GraphCanvas.prototype.showLabels = function(value) { + this.args.labeled = value; + this.removeLabels(); + if (value) { + this.renderLabels(); + } +}; + +GraphCanvas.prototype.getSelectedVertices = function() { + return this.args.selectedVertices; +}; + +GraphCanvas.prototype.getSelectedEdges = function() { + var selected = []; + for ( var selectedEdge in this.args.isEdgeSelected) { + selected.push(selectedEdge); + } + return selected; +}; + +GraphCanvas.prototype.createSVGDom = function(targetID, id, width, height, backgroundColor) { + var container = document.getElementById(targetID); + this._svg = SVG.createSVGCanvas(container, [ + [ "style", "background-color:" + backgroundColor + ";" ], [ "id", id ], [ "dragx", 0 ], [ "dragy", 0 ], + [ "height", this.getFormatter().getHeight() ], [ "width", this.getFormatter().getWidth() ] ]); + return this._svg; +}; + +/** MULTIPLE SELECTION **/ +GraphCanvas.prototype.isMultipleSelectionEnabled = function() { + return this.args.multipleSelectionEnabled; +}; + +GraphCanvas.prototype.setMultipleSelection = function(value) { + this.args.multipleSelectionEnabled = value; + this.args.draggingCanvasEnabled = (!value); +}; + +GraphCanvas.prototype.setSelecting = function(value) { + this.selecting = value; +}; + +/** linking **/ +GraphCanvas.prototype.setLinking = function(value) { + this.args.linkEnabled = value; + this.selecting = !value; + this.dragging = !value; +}; + +/** CANVAS MOVING **/ +GraphCanvas.prototype.setDraggingCanvas = function(value) { + this.args.draggingCanvasEnabled = value; + this.args.multipleSelectionEnabled = !value; +}; + +GraphCanvas.prototype.isDraggingCanvasEnabled = function() { + return this.args.draggingCanvasEnabled; +}; +/** ZOOM **/ +GraphCanvas.prototype.getScale = function() { + return this.getFormatter().getZoomScale(); +}; + +GraphCanvas.prototype.setScale = function(scale) { + var graphNode = document.getElementById(this.args.idGraph); + graphNode.setAttribute("transform", graphNode.getAttribute("transform").replace("scale(" + this.getScale() + ")", "scale(" + scale + ")")); + this.getFormatter().setZoomScale(scale); +}; + +GraphCanvas.prototype.zoomIn = function() { + this.setScale(this.getScale() + this.getFormatter().getZoomScaleStepFactor()); +}; + +GraphCanvas.prototype.zoomOut = function() { + this.setScale(this.getScale() - this.getFormatter().getZoomScaleStepFactor()); + +}; + +/** SVG COORDENATES **/ +GraphCanvas.prototype.getSVGCoordenates = function(evt) { + var p = this._svg.createSVGPoint(); + p.x = evt.clientX; + p.y = evt.clientY; + + var m = this._svg.getScreenCTM(document.documentElement); + p = p.matrixTransform(m.inverse()); + return p; +}; + +/** SVG EVENTS **/ +GraphCanvas.prototype.mouseClick = function(event) { + if (event.button == 0) { + if (!this.args.interactive) { + return; + } + + if (this.isVertex(event.target)) { + this.clickNode(this.getVertexIdFromSVGId(event.target.id)); + } + /** Como el evento mouseClick viene despues del mouse up es aqui donde manejo el tema de deseccionar los elementos que estoy dragging **/ + if (this.dragging) { + this.dragging = false; + } + } +}; + +GraphCanvas.prototype.mouseMove = function(evt) { + if (this.selecting) { + this.clearLabels(); + + var width = (this.getSVGCoordenates(evt).x - this.selectorX); + var height = (this.getSVGCoordenates(evt).y - this.selectorY); + if ((width > 0) && (height > 0)) { + this.displaySelection(this.selectorX, this.selectorY, width, height); + } + if ((width > 0) && (height < 0)) { + this.displaySelection(this.selectorX, this.getSVGCoordenates(evt).y, width, Math.abs(height)); + } + if ((width < 0) && (height < 0)) { + this.displaySelection(this.getSVGCoordenates(evt).x, this.getSVGCoordenates(evt).y, Math.abs(width), Math.abs(height)); + } + if ((width < 0) && (height > 0)) { + this.displaySelection(this.selectorX + width, this.selectorY, Math.abs(width), Math.abs(height)); + } + + var x1 = (parseFloat(this.selectorSVGNode.getAttribute("x")) - DOM.select(this.id).getAttribute("dragx")) / this.getFormatter().getWidth(); + var y1 = (parseFloat(this.selectorSVGNode.getAttribute("y")) - DOM.select(this.id).getAttribute("dragy")) / this.getFormatter().getHeight(); + var x2 = (x1 + parseFloat(this.selectorSVGNode.getAttribute("width") / this.getFormatter().getWidth())); + var y2 = (y1 + parseFloat(this.selectorSVGNode.getAttribute("height") / this.getFormatter().getHeight())); + + this.deselectNodes(this.getLayout()); + var verticesSelected = this.getLayout().getVerticesByArea(x1 / this.getFormatter().getZoomScale(), y1 / this.getFormatter().getZoomScale(), + x2 / this.getFormatter().getZoomScale(), y2 / this.getFormatter().getZoomScale()); + for ( var i = 0; i < verticesSelected.length; i++) { + this.selectNode(verticesSelected[i].getId()); + this.renderLabel(verticesSelected[i].getId()); + } + + } + var p = null; + if (this.args.linking) { + p = this.getSVGCoordenates(evt); + if (this.linkSVGNode != null) { + this.linkSVGNode.setAttribute("x2", p.x - 2); + this.linkSVGNode.setAttribute("y2", p.y - 2); + } + } + + if (this.dragging) { + p = this.getSVGCoordenates(evt); + p.x -= this.nMouseOffsetX; + p.y -= this.nMouseOffsetY; + this.desplazamientoX = (this.getSVGCoordenates(evt).x - this.dragStartX);// + parseFloat(DOM.select(this.id).getAttribute("dragx")); + this.desplazamientoY = (this.getSVGCoordenates(evt).y - this.dragStartY);// + parseFloat(DOM.select(this.id).getAttribute("dragy")); + + if (this.draggingElement != null) { + /** Click sobre el recct del banground que provoca que mueva todo el canvas **/ + if (this.isNodeCanvas(this.draggingElement)) { + + p = this.getSVGCoordenates(evt); + p.x = this.desplazamientoX; + p.y = this.desplazamientoY; + + this.draggingElement.setAttribute("dragx", p.x); + this.draggingElement.setAttribute("dragy", p.y); + this.draggingElement = document.getElementById(this.args.idGraph); + this.draggingElement.setAttribute("transform", "translate(" + p.x + "," + p.y + "), scale(" + this.getScale() + ")"); + + DOM.select(this.id).setAttribute("dragx", p.x); + DOM.select(this.id).setAttribute("dragy", p.y); + + if (this.NodeSVGbackgroundImage != null) { + this.NodeSVGbackgroundImage.setAttribute("dragx", p.x); + this.NodeSVGbackgroundImage.setAttribute("dragy", p.y); + } + } else { + if (this.isVertex(this.draggingElement)) { + this.selectNode(this.getVertexIdFromSVGId(this.draggingElement.id)); + this.desplazamientoX = this.desplazamientoX / this.getFormatter().getZoomScale(); + this.desplazamientoY = this.desplazamientoY / this.getFormatter().getZoomScale(); + this.moveSelectedNodes(this.desplazamientoX, this.desplazamientoY); + + this.dragStartX = this.getSVGCoordenates(evt).x; + this.dragStartY = this.getSVGCoordenates(evt).y; + } else { + if (this.isNodeBackground(this.draggingElement)) { + + this.draggingElement.setAttribute("dragx", p.x); + this.draggingElement.setAttribute("dragy", p.y); + this.draggingElement = document.getElementById(this.args.idGraph); + this.draggingElement.setAttribute("transform", "translate(" + p.x + "," + p.y + "), scale(" + this.getScale() + ")"); + } else { + this.draggingElement.setAttribute("transform", "translate(" + p.x + "," + p.y + ")"); + } + } + } + } + } +}; + +GraphCanvas.prototype.moveSelectedNodes = function(offsetX, offsetY) { + for ( var i = 0; i < this.getSelectedVertices().length; i++) { + + var nodeId = this.getSelectedVertices()[i]; + var svgNodeId = this.getSVGNodeId(nodeId); + + var x = parseFloat(DOM.select(svgNodeId).getAttribute("dragx")) + parseFloat(offsetX);// - parseFloat(DOM.select(this.id).getAttribute("dragx")); + var y = parseFloat(DOM.select(svgNodeId).getAttribute("dragy")) + parseFloat(offsetY);// + parseFloat(DOM.select(this.id).getAttribute("dragy")); + + this._movingNode(DOM.select(svgNodeId), x, y); + } +}; + +GraphCanvas.prototype.mouseDown = function(evt) { + if (event.button == 0) { + + /** if !no interactive mouse events do anything **/ + if (!this.args.interactive) { + return; + } + + var p = this.getSVGCoordenates(evt); + + /** When click on canvas or background deselect all **/ + if (this.isNodeCanvas(evt.target) || this.isNodeBackground(evt.target)) { + this.deselectNodes(); + this.deselectEdges(); + this.onCanvasClicked.notify(); + } + + /** if I am linking vertices **/ + if (this.args.linkEnabled) { + + if (!this.args.linking) { + this.args.linking = true; + if (this.isVertex(evt.target)) { + this.linkStartX = p.x; + this.linkStartY = p.y; + this.linkSVGNode = SVG.drawLine(p.x, p.y, p.x, p.y, this._svg, { + "stroke" : "#FF0000" + }); + this.linkNodeSource = this.getVertexIdFromSVGId(evt.target.id); + } + } else { + this.linkNodeTarget = this.getVertexIdFromSVGId(evt.target.id); + this.args.linking = false; + this.args.linkEnabled = false; + if (this.isVertex(evt.target)) { + this.getDataset().addEdge(this.linkNodeSource + "_" + this.linkNodeTarget, this.linkNodeSource, this.linkNodeTarget, {}); + } + this.linkSVGNode.parentNode.removeChild(this.linkSVGNode); + } + return; + } + + /** Id is a vertex or the canvas **/ + if (this.isVertex(evt.target) || this.isNodeCanvas(evt.target) || this.isNodeBackground(evt.target)) { + this._startDragging(evt); + } + /** if i is edge **/ + if (this.isEdge(evt.target)) { + this.selectEdge(this.getEdgeIdFromSVGId(evt.target.getAttribute("id"))); + } + + if (this.args.multipleSelectionEnabled) { + if (!this.dragging) { + this.setSelecting(true); + this.selectorX = p.x; + this.selectorY = p.y; + this.displaySelection(p.x, p.y, 1, 1); + } + } + + } + if (event.button == 1) { + this.setLinking(false); + this.setMultipleSelection(false); + this.selecting = false; + + /** Id is a vertex or the canvas **/ + if (this.isVertex(evt.target) || this.isNodeCanvas(evt.target) || this.isNodeBackground(evt.target)) { + this._startDragging(evt); + } + } +}; + +GraphCanvas.prototype.mouseUp = function(event) { + if (!this.args.interactive) { + return; + } + + if (this.dragging) { + this._stopDragging(event); + if (this.isVertex(event.target)) { + var vertexId = this.getVertexIdFromSVGId(event.target.id); + if (this.getDataset().getVertexById(vertexId).getEdges().length >= this.args.maxNumberEdgesMoving) { + this.moveEdge(vertexId); + } + } + } + + if (this.selecting) { + this.setSelecting(false); + + var x1 = (parseFloat(this.selectorSVGNode.getAttribute("x")) - DOM.select(this.id).getAttribute("dragx")) / this.getFormatter().getWidth(); + var y1 = (parseFloat(this.selectorSVGNode.getAttribute("y")) - DOM.select(this.id).getAttribute("dragy")) / this.getFormatter().getHeight(); + var x2 = (x1 + parseFloat(this.selectorSVGNode.getAttribute("width") / this.formatter.getWidth())); + var y2 = (y1 + parseFloat(this.selectorSVGNode.getAttribute("height") / this.formatter.getHeight())); + + var verticesSelected = this.getLayout().getVerticesByArea(x1 / this.getFormatter().getZoomScale, y1 / this.getFormatter().getZoomScale, + x2 / this.getFormatter().getZoomScale, y2 / this.getFormatter().getZoomScale); + + for ( var i = 0; i < verticesSelected.length; i++) { + this.selectNode(verticesSelected[i].getId()); + } + + if (this.selectorSVGNode != null) { + this._svg.removeChild(this.selectorSVGNode); + } + + if (this.args.labeled) { + this.clearLabels(); + this.renderLabels(); + } + + this.selectorSVGNode = null; + // this.renderLabels(); + } +}; + +/** SELECTION **/ +GraphCanvas.prototype.displaySelection = function(x, y, width, height) { + if (this.selectorSVGNode != null) { + this.selectorSVGNode.setAttribute("x", x); + this.selectorSVGNode.setAttribute("y", y); + this.selectorSVGNode.setAttribute("width", width); + this.selectorSVGNode.setAttribute("height", height); + } else { + this.selectorSVGNode = SVG.drawRectangle(x, y, width, height, this._svg, { + "fill" : "red", + "stroke" : "black", + "opacity" : "0.2", + "stroke-opacity" : "1" + }); + } +}; + +/** DRAGGING **/ +GraphCanvas.prototype._startDragging = function(evt) { + if (!this.isDraggingCanvasEnabled()) { + if (this.isNodeCanvas(evt.target)) { + this.draggingElement = null; + } + } + + if (this.isVertex(evt.target) || (this.isNodeBackground(evt.target) && (this.isDraggingCanvasEnabled()))|| (this.isNodeCanvas(evt.target) && (this.isDraggingCanvasEnabled()))) { + this.clearLabels(); + this.draggingElement = evt.target; + this.dragging = true; + var p = this.getSVGCoordenates(evt); + + this.nMouseOffsetX = p.x - parseInt(evt.target.getAttribute("dragx")); + this.nMouseOffsetY = p.y - parseInt(evt.target.getAttribute("dragy")); + + if (this.isVertex(evt.target)) { + this.dragStartX = parseInt(this.draggingElement.getAttribute("dragx")) * this.getFormatter().getZoomScale() + + parseFloat(DOM.select(this.id).getAttribute("dragx")); + this.dragStartY = parseInt(this.draggingElement.getAttribute("dragy")) * this.getFormatter().getZoomScale() + + parseFloat(DOM.select(this.id).getAttribute("dragy")); + } else { + this.dragStartX = p.x - parseInt(this.draggingElement.getAttribute("dragx"));// + parseFloat(DOM.select(this.id).getAttribute("dragx")); + this.dragStartY = p.y - parseInt(this.draggingElement.getAttribute("dragy"));// + parseFloat(DOM.select(this.id).getAttribute("dragy")); + } + } +}; + +GraphCanvas.prototype._stopDragging = function(event) { + this.nMouseOffsetX = 0; + this.nMouseOffsetX = 0; + /** despues del evento up viene el evento click entonces acabo el dragging en el mouseclick **/ + this.dragging = false; + this.draggingElement = null; + this.renderLabels(); + + this.setLinking(false); + this.setMultipleSelection(true); + this.selecting = false; + +}; + +/** Move the edges of the vertex with the vertexId indicado **/ +GraphCanvas.prototype.moveEdge = function(vertexId) { + var x = this.getLayout().getNodeById(vertexId).x * this.getFormatter().getWidth(); + var y = this.getLayout().getNodeById(vertexId).y * this.getFormatter().getHeight(); + + /** Moving edges out **/ + for ( var i = 0; i < this.getDataset().getVertexById(vertexId).getEdgesOut().length; i++) { + var edgeId = this.getDataset().getVertexById(vertexId).getEdgesOut()[i].getId(); + var svgEdgeId = this.getSVGEdgeId(edgeId); + var edgeFormatter = this.getFormatter().getEdgeById(edgeId); + if (edgeFormatter instanceof LineEdgeGraphFormatter) { + DOM.select(svgEdgeId + "_shadow").setAttribute("x2", x); + DOM.select(svgEdgeId + "_shadow").setAttribute("y2", y); + DOM.select(svgEdgeId).setAttribute("x2", x); + DOM.select(svgEdgeId).setAttribute("y2", y); + } + + if ((edgeFormatter instanceof DirectedLineEdgeGraphFormatter) || (edgeFormatter instanceof OdirectedLineEdgeGraphFormatter) + || (edgeFormatter instanceof OdotDirectedLineEdgeGraphFormatter) || (edgeFormatter instanceof DotDirectedLineEdgeGraphFormatter) + || (edgeFormatter instanceof CutDirectedLineEdgeGraphFormatter)) { + this.removeEdge(edgeId); + this.renderEdge(edgeId); + } + } + + /** Moving edges in **/ + for ( var i = 0; i < this.getDataset().getVertexById(vertexId).getEdgesIn().length; i++) { + var edgeId = this.getDataset().getVertexById(vertexId).getEdgesIn()[i].getId(); + var svgEdgeId = this.getSVGEdgeId(edgeId); + var edgeFormatter = this.getFormatter().getEdgeById(edgeId); + if (edgeFormatter instanceof LineEdgeGraphFormatter) { + DOM.select(svgEdgeId).setAttribute("x1", x); + DOM.select(svgEdgeId).setAttribute("y1", y); + DOM.select(svgEdgeId + "_shadow").setAttribute("x1", x); + DOM.select(svgEdgeId + "_shadow").setAttribute("y1", y); + } + + if ((edgeFormatter instanceof DirectedLineEdgeGraphFormatter) || (edgeFormatter instanceof OdirectedLineEdgeGraphFormatter) + || (edgeFormatter instanceof OdotDirectedLineEdgeGraphFormatter) || (edgeFormatter instanceof DotDirectedLineEdgeGraphFormatter) + || (edgeFormatter instanceof CutDirectedLineEdgeGraphFormatter)) { + this.removeEdge(edgeId); + this.renderEdge(edgeId); + } + + if (edgeFormatter instanceof BezierEdgeGraphFormatter) { + var radius = this.getFormatter().getVertexById(vertexId).getDefault().getSize() * this.getFormatter().getNodesMaxSize(); + var d = this.calculateCoordenatesBezier(radius, x, y); + DOM.select(svgEdgeId).setAttribute("d", d); + } + } +}; + +GraphCanvas.prototype.moveNode = function(vertexId) { + var x = this.getLayout().getNodeById(vertexId).x * this.getFormatter().getWidth(); + var y = this.getLayout().getNodeById(vertexId).y * this.getFormatter().getHeight(); + var svgNodeElement = DOM.select(this.getSVGNodeId(vertexId)); + + svgNodeElement.setAttribute("dragx", x); + svgNodeElement.setAttribute("dragy", y); + svgNodeElement.setAttribute("transform", "translate(" + x + "," + y + ")"); + + if (this.getDataset().getVertexById(vertexId).getEdges().length < this.args.maxNumberEdgesMoving) { + this.moveEdge(vertexId); + } +}; + +GraphCanvas.prototype._movingNode = function(svgNodeElement, x, y) { + var vertexId = this.getVertexIdFromSVGId(svgNodeElement.getAttribute("id")); + this.getLayout().getNodeById(vertexId).setCoordinates(x / this.getFormatter().getWidth(), y / this.getFormatter().getHeight()); + this.desplazamientoX = 0; + this.desplazamientoY = 0; + this.removeLabel(vertexId); + this.renderLabel(vertexId); +}; + +/** INIT **/ +GraphCanvas.prototype.init = function() { + + this._svg = this.createSVGDom(this.targetID, this.id, this.getFormatter().getWidth(), this.getFormatter().getHeight(), this.getFormatter() + .getBackgroundColor()); + this.GraphGroup = SVG.drawGroup(this._svg, [ [ "id", this.args.idGraph ], [ "transform", "translate(0,0), scale(1)" ] ]); + this.GraphBackground = SVG.drawGroup(this.GraphGroup, [ [ "id", this.args.idBackground ] ]); + this.GraphEdgeGroup = SVG.drawGroup(this.GraphGroup, [ [ "id", this.args.idEdgesGraph ] ]); + this.GraphNodeGroup = SVG.drawGroup(this.GraphGroup, [ [ "id", this.args.idNodesGraph ] ]); + this.GraphLabelGroup = SVG.drawGroup(this.GraphGroup, [ [ "id", this.args.idLabelGraph ] ]); + + if ((this.getFormatter().getBackgroundImage() != null) && (this.getFormatter().getBackgroundImage() != "")) { + this.setBackgroundImage(this.getFormatter().getBackgroundImage()); + } + /** SVG Events listener */ + var _this = this; + this._svg.addEventListener("click", function(event) { + _this.mouseClick(event); + }, false); + this._svg.addEventListener("mousemove", function(event) { + _this.mouseMove(event, _this); + }, false); + this._svg.addEventListener("mousedown", function(event) { + _this.mouseDown(event, _this); + }, false); + this._svg.addEventListener("mouseup", function(event) { + _this.mouseUp(event, _this); + }, false); +}; + +/* + GraphCanvas.prototype.backgroungToSVG = function(){ + var _this = this; + var canvas = document.createElement('canvas'); + canvas.setAttribute("id", "canvas"); + canvas.width = this.formatter.getWidth(); + canvas.height = this.formatter.getHeight(); + + this._svg.parentNode.parentNode.appendChild(canvas); + var ctx = document.getElementById('canvas').getContext('2d'); + var img = new Image(); + + img.src = this.formatter.getBackgroundImage(); + ctx.drawImage(img,0,0 ,_this.formatter.getWidth(), _this.formatter.getHeight()); + + + img.onload = function() { + canvas.parentNode.removeChild(canvas); + } + + this.NodeSVGbackgroundImage.setAttribute("xlink:href", document.getElementById("canvas").toDataURL()); + this.NodeSVGbackgroundImage.removeAttribute("href"); + + // + + };*/ + +GraphCanvas.prototype.setBackgroundImage = function() { + if (this.NodeSVGbackgroundImage != null) { + this.NodeSVGbackgroundImage.parentNode.removeChild(this.NodeSVGbackgroundImage); + } + $('#' + this.targetID).svg(); + $('#' + this.targetID).svg("get"); + + $('#' + this.targetID).svg("get")._svg = document.getElementById(this.id); + + var svg = $('#' + this.targetID).svg("get"); + this.NodeSVGbackgroundImage = svg.image(0, 0, this.getFormatter().getWidth(), this.getFormatter().getHeight(), this.getFormatter() + .getBackgroundImage()); + this.NodeSVGbackgroundImage.setAttribute("id", this.args.idBackgroundNode); + + this.NodeSVGbackgroundImage.setAttribute("x", 0); + this.NodeSVGbackgroundImage.setAttribute("y", 0); + + this.NodeSVGbackgroundImage.setAttribute("dragx", 0); + this.NodeSVGbackgroundImage.setAttribute("dragy", 0); + + if (this.getFormatter().args.backgroundImageHeight != null) { + this.NodeSVGbackgroundImage.setAttribute("height", this.getFormatter().args.backgroundImageHeight); + } + if (this.getFormatter().args.backgroundImageWidth != null) { + this.NodeSVGbackgroundImage.setAttribute("width", this.getFormatter().args.backgroundImageWidth); + } + + if (this.getFormatter().args.backgroundImageX != null) { + this.NodeSVGbackgroundImage.setAttribute("x", this.getFormatter().args.backgroundImageX); + } + if (this.getFormatter().args.backgroundImageY != null) { + this.NodeSVGbackgroundImage.setAttribute("y", this.getFormatter().args.backgroundImageY); + } + + this.GraphBackground.appendChild(this.NodeSVGbackgroundImage); + this.NodeSVGbackgroundImage.removeAttribute("href"); + this.NodeSVGbackgroundImage.setAttribute("xlink:href", this.getFormatter().getBackgroundImage()); +}; + +GraphCanvas.prototype.removeBackgroundImage = function() { + if (this.NodeSVGbackgroundImage != null) { + this.NodeSVGbackgroundImage.parentNode.removeChild(this.NodeSVGbackgroundImage); + } +}; + +GraphCanvas.prototype._setBackgroundColor = function(color) { + var attributes = [ [ "fill", color ] ]; + SVG.drawRectangle(0, 0, this.getFormatter().getWidth(), this.getFormatter().getHeight(), this.GraphBackground, attributes); +}; + +/** Serialize **/ +GraphCanvas.prototype.toJSON = function() { + var json = {}; + json.dataset = {}; + json.formatter = {}; + json.layout = {}; + json.dataset = this.getDataset().toJSON(); + json.formatter = this.getFormatter().toJSON(); + json.layout = this.getLayout().toJSON(); + return json; +}; + +GraphCanvas.prototype.toHTML = function() { + //this.backgroungToSVG(); + var html = this._svg.parentElement.innerHTML; + + var start = html.indexOf("") + 6; + + return html.substr(start, end); +}; + +/** DRAW **/ +GraphCanvas.prototype.draw = function(graphdataset, graphformatter, graphlayout) { + this.setDataset(graphdataset); + this.setFormatter(graphformatter); + this.setLayout(graphlayout); + + var _this = this; + this.getFormatter().changed.attach(function(sender, item) { + _this.removeNode(item.getId()); + _this.renderNode(item.getId()); + if (_this.args.labeled) { + _this.removeLabel(item.getId()); + _this.renderLabel(item.getId()); + } + + }); + //TODO + this.getFormatter().edgeChanged.attach(function(sender, item) { + _this.removeEdge(item.getId()); + _this.renderEdge(item.getId()); + }); + + this.getFormatter().resized.attach(function(sender, item) { + _this.resize(_this.getFormatter().getWidth(), _this.getFormatter().getHeight()); + }); + + this.getFormatter().backgroundImageChanged.attach(function(sender, item) { + _this.setBackgroundImage(_this.getFormatter().getBackgroundImage()); + }); + + this.getFormatter().backgroundColorChanged.attach(function(sender, item) { + _this._setBackgroundColor(_this.getFormatter().getBackgroundColor()); + }); + + this.getLayout().changed.attach(function(sender, item) { + _this.moveNode(item.getId()); + _this.moveEdge(item.getId()); + if (_this.args.labeled) { + _this.removeLabel(item.getId()); + _this.renderLabel(item.getId()); + } + }); + + this.getDataset().newVertex.attach(function(sender, item) { + + _this.renderNode(item.getId()); + if (_this.args.labeled) { + _this.renderLabel(item.getId()); + } + }); + + this.getDataset().newEdge.attach(function(sender, item) { + _this.renderEdge(item.getId()); + }); + + this.getDataset().vertexDeleted.attach(function(sender, item) { + _this.removeNode(item.getId()); + if (_this.args.labeled) { + _this.removeLabel(item.getId()); + } + }); + + this.getDataset().edgeDeleted.attach(function(sender, item) { + _this.removeEdge(item.getId()); + }); + + this.getDataset().vertexNameChanged.attach(function(sender, args) { + if (_this.args.labeled) { + _this.removeLabel(args.item.getId()); + _this.removeLabel(args.item.getId()); + _this.renderLabel(args.item.getId()); + } + }); + this.init(); + this.render(); +}; + +GraphCanvas.prototype.render = function() { + for ( var id in this.getDataset().getVertices()) { + this.renderNode(id); + } + this.renderLabels(); + this.renderEdges(); +}; + +GraphCanvas.prototype.renderLabels = function() { + if (this.args.labeled) { + for ( var id in this.getDataset().getVertices()) { + this.renderLabel(id); + } + } +}; + +GraphCanvas.prototype.removeLabels = function() { + for ( var id in this.getDataset().getVertices()) { + this.removeLabel(id); + } +}; + +/** Utilities method for nodes **/ +GraphCanvas.prototype.isNodeCanvas = function(node) { + return ((node.id == this.args.idGraph) || (node.id == this.id)); +}; + +GraphCanvas.prototype.isNodeBackground = function(node) { + return ((node.id == this.args.idBackgroundNode)); +}; + +GraphCanvas.prototype.isVertex = function(node) { + if (node.getAttribute("id") != null) { + if (node.getAttribute("id").indexOf("_v_") != -1) { + return true; + } + } + return false; +}; + +GraphCanvas.prototype.isLabel = function(node) { + if (node.getAttribute("id") != null) { + if (node.getAttribute("id").indexOf("_l_") != -1) { + return true; + } + } + return false; +}; + +GraphCanvas.prototype.isEdge = function(node) { + if (node.getAttribute("id") != null) { + if (node.getAttribute("id").indexOf("_e_") != -1) { + return true; + } + } + return false; +}; + +/** Resize **/ +GraphCanvas.prototype.resize = function(width, height) { + // this._svg.setAttribute("width", width); + // this._svg.setAttribute("height", height); + if (this.NodeSVGbackgroundImage != null) { + this.NodeSVGbackgroundImage.setAttribute("width", width); + this.NodeSVGbackgroundImage.setAttribute("height", height); + } + + this._svg.setAttribute("width", width); + this._svg.setAttribute("height", height); + + this.clearCanvas(); + this.render(); +}; + +GraphCanvas.prototype.clearCanvas = function() { + DOM.removeChilds(this.GraphEdgeGroup.getAttribute("id")); + DOM.removeChilds(this.GraphNodeGroup.getAttribute("id")); + this.clearLabels(); +}; + +GraphCanvas.prototype.clearLabels = function() { + DOM.removeChilds(this.GraphLabelGroup.getAttribute("id")); +}; + +/** ID'S converter **/ +GraphCanvas.prototype.getSVGNodeId = function(nodeId) { + return this.id + "_v_" + nodeId; +}; + +GraphCanvas.prototype.getSVGEdgeId = function(edgeId) { + return this.id + "_e_" + edgeId; +}; + +GraphCanvas.prototype.getSVGArrowEdgeId = function(edgeId) { + return this.id + "_arrow_" + edgeId; +}; + +GraphCanvas.prototype.getSVGLabelId = function(edgeId) { + return this.id + "_l_" + edgeId; +}; + +GraphCanvas.prototype.blinkVertexById = function(vertexId) { + $("#" + this.getSVGNodeId(vertexId)).fadeIn().fadeOut().fadeIn().fadeOut().fadeIn().fadeOut(); +}; + +GraphCanvas.prototype.getVertexIdFromSVGId = function(svgVertexId) { + return svgVertexId.replace(this.id, "").replace("_v_", ""); +}; + +GraphCanvas.prototype.getEdgeIdFromSVGId = function(svgEdgeId) { + return svgEdgeId.replace(this.id, "").replace("_e_", ""); +}; + +/** VERTEX **/ +GraphCanvas.prototype.getVertexById = function(id) { + return document.getElementById(this.getSVGNodeId(id)); +}; + +GraphCanvas.prototype.renderNodes = function() { + for ( var id in this.getDataset().getVertices()) { + this.renderNode(id); + } +}; + +GraphCanvas.prototype.overNode = function(nodeId) { + if (!this.args.interactive) { + return; + } + /** If selected we don't change the format **/ + if (this.args.isVertexSelected[nodeId] == null) { + var args = this.getFormatter().getVertexById(nodeId).getOver(); + args.args["cursor"] = 'pointer'; + this.changeVertexFormat(nodeId, args); + } +}; + +GraphCanvas.prototype.outNode = function(nodeId) { + if (!this.args.interactive) { + return; + } + + /** If selected we don't change the format **/ + if (this.args.isVertexSelected[nodeId] == null) { + this.changeVertexFormat(nodeId, this.getFormatter().getVertexById(nodeId).getDefault()); + } +}; + +GraphCanvas.prototype.overLabel = function(nodeId) { + this.overNode(nodeId); + // this.svgLabels[nodeId].setAttribute("cursor", "pointer"); +}; + +GraphCanvas.prototype.outLabel = function(nodeId) { + this.outNode(nodeId); + // this.svgLabels[nodeId].setAttribute("cursor", ""); +}; + +GraphCanvas.prototype.clickNode = function(nodeId) { + if (!this.args.interactive) { + return; + } + + /** si el evento se dispara oprque estaba dragging entonces no activo nada **/ + if (this.args.isVertexSelected[nodeId] == null) { + this.selectNode(nodeId); + } else { + this.deselectNode(nodeId); + } +}; + +GraphCanvas.prototype.selectNode = function(nodeId) { + for ( var i = 0; i < this.args.selectedVertices.length; i++) { + var format = this.getFormatter().getVertexById(nodeId).getSelected(); + format.opacity = 0.2; + this.changeVertexFormat(nodeId, this.getFormatter().getVertexById(nodeId).getSelected()); + } + + if (this.args.isVertexSelected[nodeId] == null) { + var format = this.getFormatter().getVertexById(nodeId).getSelected(); + format.opacity = 1; + this.changeVertexFormat(nodeId, this.getFormatter().getVertexById(nodeId).getSelected()); + this.args.selectedVertices.push(nodeId); + this.args.isVertexSelected[nodeId] = this.args.selectedVertices.length - 1; + this.onVertexSelect.notify(nodeId); + } +}; + +GraphCanvas.prototype.selectAllEdges = function() { + this.deselectNodes(); + this.deselectEdges(); + + for ( var edgesId in this.getDataset().edges) { + this.selectEdge(edgesId); + } +}; + +GraphCanvas.prototype.selectAllNodes = function() { + this.deselectNodes(); + this.deselectEdges(); + + for ( var vertexId in this.getDataset().vertices) { + this.selectNode(vertexId); + } +}; + +GraphCanvas.prototype.selectAll = function() { + this.deselectNodes(); + this.deselectEdges(); + + for ( var vertexId in this.getDataset().vertices) { + this.selectNode(vertexId); + } + + for ( var edgesId in this.getDataset().edges) { + this.selectEdge(edgesId); + } +}; + +GraphCanvas.prototype.selectEdge = function(edgeId) { + if (this.args.isEdgeSelected[edgeId] == null) { + this.changeEdgeFormat(edgeId, this.getFormatter().getEdgeById(edgeId).getSelected()); + //this.args.selectedEdges.push(edgeId); + this.args.isEdgeSelected[edgeId] = true; //this.args.selectedEdges.length - 1; + this.onEdgeSelect.notify(edgeId); + } +}; + +GraphCanvas.prototype.selectEdges = function(edges) { + + for ( var i = 0; i < edges.length; i++) { + this.selectEdge(edges[i]); + } +}; + +GraphCanvas.prototype.deselectNode = function(nodeId) { + if (this.args.isVertexSelected[nodeId] != null) { + this.changeVertexFormat(nodeId, this.getFormatter().getVertexById(nodeId).getDefault()); + this.args.selectedVertices.splice(this.args.isVertexSelected[nodeId], 1); + var index = this.args.isVertexSelected[nodeId]; + delete this.args.isVertexSelected[nodeId]; + + for ( var vertex in this.args.isVertexSelected) { + if (this.args.isVertexSelected[vertex] > index) { + this.args.isVertexSelected[vertex] = this.args.isVertexSelected[vertex] - 1; + } + } + } +}; + +GraphCanvas.prototype.deselectNodes = function() { + var selected = JSON.parse(JSON.stringify(this.getSelectedVertices())); + for ( var i = 0; i < selected.length; i++) { + this.deselectNode(selected[i]); + } +}; +GraphCanvas.prototype.selectNodes = function(idNodes) { + + for ( var i = 0; i < idNodes.length; i++) { + this.selectNode(idNodes[i]); + } + + // for ( var vertex in this.args.isVertexSelected) { + // if (this.args.isVertexSelected[vertex] > index){ + // this.args.isVertexSelected[vertex] = this.args.isVertexSelected[vertex] - 1; + // } + // } + +}; + +GraphCanvas.prototype.changeVertexFormat = function(nodeId, format) { + var svgNode = DOM.select(this.getSVGNodeId(nodeId)); + if (svgNode != null) { + var properties = format.toJSON(); + for ( var item in properties) { + svgNode.setAttribute(item, properties[item]); + } + + if (this.getFormatter().getVertexById(nodeId) instanceof CircleVertexGraphFormatter) { + var transform = "translate(" + svgNode.getAttribute("dragx") + "," + svgNode.getAttribute("dragy") + "), scale(" + format.getSize() + ")"; + svgNode.setAttribute("transform", transform); + } + } +}; + +GraphCanvas.prototype.renderLabel = function(nodeId) { + var x = Math.ceil(this.getLayout().getNodeById(nodeId).x * this.getFormatter().getWidth()); + var y = Math.ceil(this.getLayout().getNodeById(nodeId).y * this.getFormatter().getHeight()); + + var svgAttributesNode = JSON.parse(JSON.stringify(this.getFormatter().getVertexById(nodeId).getDefault().toJSON().title)); + svgAttributesNode.id = this.getSVGLabelId(this.getDataset().getVertexById(nodeId).getId()); + svgAttributesNode.dx = (-1) * (this.getDataset().getVertexById(nodeId).getName().length * svgAttributesNode["font-size"]) / 4 - 4; + + svgAttributesNode.dy = parseFloat((this.getFormatter().getVertexById(nodeId).getDefault().getSize())) + + parseFloat(svgAttributesNode["font-size"]) + parseFloat(this.getFormatter().getVertexById(nodeId).getDefault().getStrokeWidth()) - 4; + svgAttributesNode.dragx = Math.ceil(this.getLayout().getNodeById(nodeId).x * this.getFormatter().getWidth()); + + var gragy = parseFloat(this.getFormatter().getVertexById(nodeId).getDefault().getSize()) + + Math.ceil(this.getLayout().getNodeById(nodeId).y * this.getFormatter().getHeight()); + svgAttributesNode.dragy = gragy; + svgAttributesNode.transform = "translate(" + svgAttributesNode.dragx + "," + svgAttributesNode.dragy + ")";//, scale("+this.formatter.getVertexById(nodeId).getDefault().getSize()+")"; + + var nodeSVG = SVG.drawText(0, 0, this.getDataset().getVertexById(nodeId).getName(), this.GraphLabelGroup, svgAttributesNode); + + this.svgLabels[nodeId] = nodeSVG; + + /** Events for the SVG node **/ + var _this = this; + if (nodeSVG != null) { + nodeSVG.addEventListener("mouseover", function() { + _this.overLabel(nodeId); + }, false); + nodeSVG.addEventListener("mouseout", function() { + _this.outLabel(nodeId); + }, false); + } + +}; + +GraphCanvas.prototype.removeLabel = function(labelId) { + if (DOM.select(this.getSVGLabelId(labelId)) != null) { + DOM.select(this.getSVGLabelId(labelId)).parentNode.removeChild(DOM.select(this.getSVGLabelId(labelId))); + } +}; + +GraphCanvas.prototype.renderNode = function(nodeId) { + var svgAttributesNode = JSON.parse(JSON.stringify(this.getFormatter().getVertexById(nodeId).getDefault().toJSON())); + svgAttributesNode.dragx = Math.ceil(this.getLayout().getNodeById(nodeId).x * this.getFormatter().getWidth()); + svgAttributesNode.dragy = Math.ceil(this.getLayout().getNodeById(nodeId).y * this.getFormatter().getHeight()); + svgAttributesNode.transform = "translate(" + svgAttributesNode.dragx + "," + svgAttributesNode.dragy + ")"; + svgAttributesNode.id = this.getSVGNodeId(nodeId); + /*svgAttributesNode["stroke-width"] = 3 ; + svgAttributesNode["stroke-opacity"] = 1 ; + svgAttributesNode["fill-opacity"] = svgAttributesNode["opacity"] ; + svgAttributesNode["opacity"] = 1 ;*/ + this.circleDefaultRadius = this.getFormatter().getVertexById(nodeId).getDefault().getSize(); + var nodeSVG; + + if (this.getFormatter().getVertexById(nodeId) instanceof CircleVertexGraphFormatter) { + nodeSVG = SVG.drawCircle(0, 0, this.circleDefaultRadius, this.GraphNodeGroup, svgAttributesNode); + } + + if (this.getFormatter().getVertexById(nodeId) instanceof SquareVertexGraphFormatter) { + //nodeSVG = SVG.drawRectangle(0 - (this.circleDefaultRadius) ,0 - (this.formatter.getVertexById(nodeId).getDefault().getSize()) , (this.getFormatter().getVertexById(nodeId).getDefault().getSize()*2), (this.getFormatter().getVertexById(nodeId).getDefault().getSize()*2), this.GraphNodeGroup, svgAttributesNode); + nodeSVG = SVG.drawRectangle(0 - (this.circleDefaultRadius), 0 - (this.circleDefaultRadius), (this.circleDefaultRadius * 2), + (this.circleDefaultRadius * 2), this.GraphNodeGroup, svgAttributesNode); + } + + if (this.getFormatter().getVertexById(nodeId) instanceof EllipseVertexGraphFormatter) { + nodeSVG = SVG.drawEllipse(0, 0, this.circleDefaultRadius * 1.5, this.circleDefaultRadius, this.GraphNodeGroup, svgAttributesNode); + } + + if (this.getFormatter().getVertexById(nodeId) instanceof RectangleVertexGraphFormatter) { + //nodeSVG = SVG.drawRectangle(0 - (this.circleDefaultRadius) ,0 - ((this.circleDefaultRadius*2)/2) , (this.circleDefaultRadius*2), (this.circleDefaultRadius), this.GraphNodeGroup, svgAttributesNode); + nodeSVG = SVG.drawRectangle(0 - (this.circleDefaultRadius * 1.5), 0 - (this.circleDefaultRadius), (this.circleDefaultRadius * 2 * 1.5), + (this.circleDefaultRadius * 2), this.GraphNodeGroup, svgAttributesNode); + + } + + if (this.getFormatter().getVertexById(nodeId) instanceof RoundedVertexGraphFormatter) { + svgAttributesNode.ry = 2;// this.formatter.getVertexById(nodeId).getDefault().getSize()/4; + svgAttributesNode.rx = 2;// this.formatter.getVertexById(nodeId).getDefault().getSize()/4; + nodeSVG = SVG.drawRectangle(0 - (this.circleDefaultRadius * 1.5), 0 - (this.circleDefaultRadius), (this.circleDefaultRadius * 2 * 1.5), + (this.circleDefaultRadius * 2), this.GraphNodeGroup, svgAttributesNode); + } + + // + + if (this.getFormatter().getVertexById(nodeId) instanceof OctagonVertexGraphFormatter) { + svgAttributesNode.ry = 2; + svgAttributesNode.rx = 2; + nodeSVG = SVG.drawRectangle(0 - (this.circleDefaultRadius * 1.5), 0 - (this.circleDefaultRadius), (this.circleDefaultRadius * 2 * 1.5), + (this.circleDefaultRadius * 2), this.GraphNodeGroup, svgAttributesNode); + } + + nodeSVG.internalId = nodeId; + // + var _this = this; + + /** Events for the SVG node **/ + if (nodeSVG != null) { + nodeSVG.addEventListener("mouseover", function() { + _this.onVertexOver.notify(nodeId); + _this.overNode(nodeId); + }, false); + nodeSVG.addEventListener("mouseout", function() { + _this.onVertexOut.notify(nodeId); + _this.outNode(nodeId); + }, false); + //nodeSVG.addEventListener("click", function(){_this.clickNode(nodeId);}, false); + // + nodeSVG.addEventListener("mouseup", function() { + _this.onVertexUp.notify(nodeId); + }, false); + } +}; + +GraphCanvas.prototype.removeNode = function(nodeId) { + DOM.select(this.getSVGNodeId(nodeId)).parentNode.removeChild(DOM.select(this.getSVGNodeId(nodeId))); + if (this.args.labeled) { + this.removeLabel(nodeId); + } +}; + +/** REMOVING **/ +GraphCanvas.prototype.removeSelected = function() { + /** El orden importa **/ + this.removeSelectedEdges(); + this.removeSelectedNode(); + +}; + +GraphCanvas.prototype.removeSelectedNode = function() { + var selected = JSON.parse(JSON.stringify(this.getSelectedVertices())); + this.deselectNodes(); + var sorted = selected.sort(function(a, b) { + return a - b + }); + for ( var i = 0; i < sorted.length; i++) { + if (this.getDataset().getVertexById(sorted[i]) != null) { + this.getDataset().getVertexById(sorted[i]).remove(); + } + } +}; + +/** EDGES **/ +GraphCanvas.prototype.removeEdge = function(edgeId) { + if (DOM.select(this.getSVGEdgeId(edgeId)) != null) { + DOM.select(this.getSVGEdgeId(edgeId)).parentNode.removeChild(DOM.select(this.getSVGEdgeId(edgeId))); + } + + if (DOM.select(this.getSVGEdgeId(edgeId) + "_shadow") != null) { + DOM.select(this.getSVGEdgeId(edgeId) + "_shadow").parentNode.removeChild(DOM.select(this.getSVGEdgeId(edgeId) + "_shadow")); + } + + if (DOM.select(this.getSVGArrowEdgeId(edgeId)) != null) { + DOM.select(this.getSVGArrowEdgeId(edgeId)).parentNode.removeChild(DOM.select(this.getSVGArrowEdgeId(edgeId))); + } +}; + +GraphCanvas.prototype.overEdge = function(edgeId) { + if ((!this.args.interactive) || this.dragging || this.selecting) { + return; + } + + /** If selected we don't change the format **/ + if (this.args.isEdgeSelected[edgeId] == null) { + var format = this.getFormatter().getEdgeById(edgeId).getOver(); + format.args["cursor"] = "pointer"; + this.changeEdgeFormat(edgeId, format); + } +}; + +GraphCanvas.prototype.outEdge = function(edgeId) { + if (!this.args.interactive) { + return; + } + + /** If selected we don't change the format **/ + if (this.args.isEdgeSelected[edgeId] == null) { + this.changeEdgeFormat(edgeId, this.getFormatter().getEdgeById(edgeId).getDefault()); + } +}; + +GraphCanvas.prototype.changeEdgeFormat = function(edgeId, format) { + var svgEdge = DOM.select(this.getSVGEdgeId(edgeId) + "_shadow"); + if (svgEdge != null) { + var properties = format.toJSON(); + for ( var item in properties) { + svgEdge.setAttribute(item, properties[item]); + } + } +}; + +GraphCanvas.prototype.deselectEdge = function(edgeID) { + if (this.args.isEdgeSelected[edgeID] != null) { + this.changeEdgeFormat(edgeID, this.getFormatter().getEdgeById(edgeID).getDefault()); + var index = this.args.isEdgeSelected[edgeID]; + delete this.args.isEdgeSelected[edgeID]; + } +}; + +GraphCanvas.prototype.deselectEdges = function() { + var selected = JSON.parse(JSON.stringify(this.getSelectedEdges())); + for ( var i = 0; i < selected.length; i++) { + this.deselectEdge(selected[i]); + } +}; + +GraphCanvas.prototype.removeSelectedEdges = function() { + var selected = JSON.parse(JSON.stringify(this.getSelectedEdges())); + this.deselectEdges(); + for ( var i = 0; i < selected.length; i++) { + if (this.getDataset().getEdgeById(selected[i]) != null) { + this.getDataset().getEdgeById(selected[i]).remove(); + } + } +}; + +GraphCanvas.prototype.renderEdge = function(edgeId) { + var svgAttributesEdge = this.getFormatter().getEdgeById(edgeId).getDefault().toJSON(); + var edge = this.getDataset().getEdgeById(edgeId); + + var svgNodeTarget = this.getVertexById(edge.getNodeTarget().getId()); + var svgNodeSource = this.getVertexById(edge.getNodeSource().getId()); + svgAttributesEdge.id = this.getSVGEdgeId(edge.getId()) + "_shadow"; + + var svgEdge = null; + + if (this.getFormatter().getEdgeById(edgeId) instanceof LineEdgeGraphFormatter) { + var coordenateSourceX = svgNodeSource.getAttribute("dragx"); + var coordenateSourceY = svgNodeSource.getAttribute("dragy"); + var coordenateTargetX = svgNodeTarget.getAttribute("dragx"); + var coordenateTargetY = svgNodeTarget.getAttribute("dragy"); + + SVG.drawLine(coordenateSourceX, coordenateSourceY, coordenateTargetX, coordenateTargetY, this.GraphEdgeGroup, svgAttributesEdge); + var attributesShadow = {}; + attributesShadow.id = this.getSVGEdgeId(edge.getId()); + attributesShadow["stroke-opacity"] = 0; + attributesShadow["stroke-width"] = 4; + attributesShadow["stroke"] = "black"; + svgEdge = SVG.drawLine(svgNodeSource.getAttribute("dragx"), svgNodeSource.getAttribute("dragy"), svgNodeTarget.getAttribute("dragx"), + svgNodeTarget.getAttribute("dragy"), this.GraphEdgeGroup, attributesShadow); + } + + if (this.getFormatter().getEdgeById(edgeId) instanceof BezierEdgeGraphFormatter) { + var nodeId = edge.getNodeTarget().getId(); + var nodeSize = this.formatter.getVertexById(nodeId).getDefault().getSize() * this.getFormatter().getNodesMaxSize(); + svgAttributesEdge.fill = "none"; + svgAttributesEdge.id = this.getSVGEdgeId(edgeId); + var d = this.calculateCoordenatesBezier(nodeSize, svgNodeSource.getAttribute("dragx"), svgNodeSource.getAttribute("dragy")); + svgEdge = SVG.drawPath(d, this.GraphEdgeGroup, svgAttributesEdge); + } + ; + + if ((this.getFormatter().getEdgeById(edgeId) instanceof DirectedLineEdgeGraphFormatter) + || (this.getFormatter().getEdgeById(edgeId) instanceof CutDirectedLineEdgeGraphFormatter) + || (this.getFormatter().getEdgeById(edgeId) instanceof DotDirectedLineEdgeGraphFormatter) + || (this.getFormatter().getEdgeById(edgeId) instanceof OdotDirectedLineEdgeGraphFormatter) + || (this.getFormatter().getEdgeById(edgeId) instanceof OdirectedLineEdgeGraphFormatter)) { + var coordenateSourceX = svgNodeSource.getAttribute("dragx"); + var coordenateSourceY = svgNodeSource.getAttribute("dragy"); + var coordenateTargetX = svgNodeTarget.getAttribute("dragx"); + var coordenateTargetY = svgNodeTarget.getAttribute("dragy"); + + var offset = parseFloat(this.getFormatter().getVertexById(this.getDataset().getEdgeById(edgeId).getNodeTarget().getId()).getDefault() + .getSize() + * this.circleDefaultRadius); + var point = this._calculateEdgePointerPosition(coordenateSourceX, coordenateSourceY, coordenateTargetX, coordenateTargetY, offset); + coordenateTargetX = point.x; + coordenateTargetY = point.y; + SVG.drawLine(coordenateSourceX, coordenateSourceY, coordenateTargetX, coordenateTargetY, this.GraphEdgeGroup, svgAttributesEdge); + + var attributesShadow = {}; + attributesShadow.id = this.getSVGEdgeId(edge.getId()); + attributesShadow["stroke-opacity"] = 0; + attributesShadow["stroke-width"] = 4; + attributesShadow["stroke"] = "black"; + svgEdge = SVG.drawLine(coordenateSourceX, coordenateSourceY, coordenateTargetX, coordenateTargetY, this.GraphEdgeGroup, attributesShadow); + } + + if (this.getFormatter().getEdgeById(edgeId) instanceof DirectedLineEdgeGraphFormatter + || (this.getFormatter().getEdgeById(edgeId) instanceof OdirectedLineEdgeGraphFormatter)) { + var coordenateSourceX = svgNodeSource.getAttribute("dragx"); + var coordenateSourceY = svgNodeSource.getAttribute("dragy"); + var coordenateTargetX = svgNodeTarget.getAttribute("dragx"); + var coordenateTargetY = svgNodeTarget.getAttribute("dragy"); + + var point = this._calculateEdgePointerPosition(coordenateSourceX, coordenateSourceY, coordenateTargetX, coordenateTargetY, offset); + coordenateTargetX = point.x; + coordenateTargetY = point.y; + + var angle = Geometry.toDegree(point.angle) + 90; + this.arrowDefaultSize = this.getFormatter().getEdgeById(edgeId).getArrowSize(); //getDefault().getArrowSize(); + var d = "-" + this.arrowDefaultSize + ",0 0,-" + parseFloat(this.arrowDefaultSize) * 2 + " " + this.arrowDefaultSize + ",0"; + + var attributes; + + if (this.getFormatter().getEdgeById(edgeId) instanceof DirectedLineEdgeGraphFormatter) { + attributes = [ + [ "fill", this.getFormatter().getEdgeById(edgeId).getDefault().getStroke() ], + [ "stroke", this.getFormatter().getEdgeById(edgeId).getDefault().getStroke() ], [ "id", this.getSVGArrowEdgeId(edgeId) ] ]; + } else { + attributes = [ + [ "fill", "#FFFFFF" ], [ "stroke", this.getFormatter().getEdgeById(edgeId).getDefault().getStroke() ], + [ "id", this.getSVGArrowEdgeId(edgeId) ] ]; + } + + var flechaSVGNode = SVG.drawPoligon(d, this.GraphEdgeGroup, attributes);//, ["transform", "rotate("+angle+"), translate(0,0)"]]); + flechaSVGNode.setAttribute("transform", " translate(" + coordenateTargetX + ", " + coordenateTargetY + "), rotate(" + angle + ")"); + } + ; + + if (this.getFormatter().getEdgeById(edgeId) instanceof CutDirectedLineEdgeGraphFormatter) { + var coordenateSourceX = svgNodeSource.getAttribute("dragx"); + var coordenateSourceY = svgNodeSource.getAttribute("dragy"); + var coordenateTargetX = svgNodeTarget.getAttribute("dragx"); + var coordenateTargetY = svgNodeTarget.getAttribute("dragy"); + + var point = this._calculateEdgePointerPosition(coordenateSourceX, coordenateSourceY, coordenateTargetX, coordenateTargetY, offset); + coordenateTargetX = point.x; + coordenateTargetY = point.y; + + var angle = Geometry.toDegree(point.angle) + 90; + + //this.arrowDefaultSize = 2; //getDefault().getArrowSize(); + var d = "-4,0 4,0 4,-2 -4,-2"; + + var flechaSVGNode = SVG.drawPoligon(d, this.GraphEdgeGroup, [ + [ "fill", this.getFormatter().getEdgeById(edgeId).getDefault().getStroke() ], + [ "stroke", this.getFormatter().getEdgeById(edgeId).getDefault().getStroke() ], [ "id", this.getSVGArrowEdgeId(edgeId) ] ]);//, ["transform", "rotate("+angle+"), translate(0,0)"]]); + flechaSVGNode.setAttribute("transform", " translate(" + coordenateTargetX + ", " + coordenateTargetY + "), rotate(" + angle + ")"); + } + ; + + if ((this.getFormatter().getEdgeById(edgeId) instanceof DotDirectedLineEdgeGraphFormatter) + || (this.getFormatter().getEdgeById(edgeId) instanceof OdotDirectedLineEdgeGraphFormatter)) { + var coordenateSourceX = svgNodeSource.getAttribute("dragx"); + var coordenateSourceY = svgNodeSource.getAttribute("dragy"); + var coordenateTargetX = svgNodeTarget.getAttribute("dragx"); + var coordenateTargetY = svgNodeTarget.getAttribute("dragy"); + var point = this._calculateEdgePointerPosition(coordenateSourceX, coordenateSourceY, coordenateTargetX, coordenateTargetY, offset); + coordenateTargetX = point.x; + coordenateTargetY = point.y; + var angle = Geometry.toDegree(point.angle) + 90; + // this.arrowDefaultSize = this.formatter.getEdgeById(edgeId).getArrowSize(); //getDefault().getArrowSize(); + var attributes = []; + if (this.getFormatter().getEdgeById(edgeId) instanceof OdotDirectedLineEdgeGraphFormatter) { + attributes = [ + [ "fill", "#FFFFFF" ], [ "stroke", this.getFormatter().getEdgeById(edgeId).getDefault().getStroke() ], + [ "id", this.getSVGArrowEdgeId(edgeId) ] ]; + } else { + attributes = [ + [ "fill", this.getFormatter().getEdgeById(edgeId).getDefault().getStroke() ], + [ "stroke", this.getFormatter().getEdgeById(edgeId).getDefault().getStroke() ], [ "id", this.getSVGArrowEdgeId(edgeId) ] ]; + } + var flechaSVGNode = SVG.drawCircle(0, 0, 4, this.GraphEdgeGroup, attributes); + flechaSVGNode.setAttribute("transform", " translate(" + coordenateTargetX + ", " + coordenateTargetY + "), rotate(" + angle + ")"); + } + ; + + var _this = this; + /** Events for the SVG edge **/ + if (svgEdge != null) { + if (this.getDataset().getEdgesCount() < this.args.maxNumberEdgesFiringEvents) { + svgEdge.addEventListener("mouseover", function() { + _this.overEdge(edgeId); + }, false); + svgEdge.addEventListener("mouseout", function() { + _this.outEdge(edgeId); + }, false); + } + } +}; + +GraphCanvas.prototype._calculateEdgePointerPosition = function(sourceX, sourceY, targetX, targetY, radius) { + var angle = Geometry.getAngleBetweenTwoPoints(sourceX, sourceY, targetX, targetY); + + /** Suponiendo el node source que este a la derecha **/ + if ((targetX - sourceX) < 0) { + var b = Geometry.getAdjacentSideOfRectangleRight(angle, radius); + targetX = parseFloat(targetX) + parseFloat(b); + arrowX = parseFloat(targetX) + parseFloat(b) + this.arrowDefaultSize / 2; + } else { + var b = Geometry.getAdjacentSideOfRectangleRight(angle, radius); + targetX = parseFloat(targetX) - parseFloat(b); + arrowX = parseFloat(targetX) - parseFloat(b) - this.arrowDefaultSize / 2; + } + + /** Suponiendo el node source que este a la arriba **/ + if ((targetY - sourceY) > 0) { + var a = Geometry.getOppositeSideOfRectangleRight(angle, radius); + targetY = parseFloat(targetY) - parseFloat(a); + arrowY = parseFloat(targetY) - parseFloat(a) - this.arrowDefaultSize / 2; + } else { + var a = Geometry.getOppositeSideOfRectangleRight(angle, radius); + targetY = parseFloat(targetY) + parseFloat(a); + arrowY = parseFloat(targetY) + parseFloat(a) - this.arrowDefaultSize / 2; + + } + + return { + "x" : arrowX, + "y" : arrowY, + "angle" : angle + }; +}; + +GraphCanvas.prototype.calculateCoordenatesBezier = function(nodeSize, x1, y1) { + var x11 = x1 - (nodeSize / 2); + var y11 = y1 - (nodeSize / 2); + + var x12 = parseFloat(x1) + parseFloat(nodeSize / 2); + var y12 = y1 - (nodeSize / 2); + + var curvePointX = (x12 - x11) / 2 + x11; + var curvePointY = y1 - (nodeSize * 2); + var d = "M" + x11 + "," + y11 + " T" + curvePointX + "," + curvePointY + " " + x12 + "," + y12; + return d; + +}; + +GraphCanvas.prototype.renderEdges = function() { + for ( var edge in this.getDataset().getEdges()) { + this.renderEdge(this.getDataset().getEdgeById(edge).getId()); + + } +}; + +GraphCanvas.prototype.getLastSelectedNode = function() { + var node = null; + if (this.getSelectedVertices().length > 0) { + var nodeId = this.getSelectedVertices()[this.getSelectedVertices().length - 1]; + node = this.getDataset().getVertexById(nodeId); + } + return node; +}; +/* + GraphCanvas.prototype.getNodeByNameAndIndex = function(node, index){ + var nodeId = this.getDataset().verticesIndex[node][index]; + var nodeItem = this.getDataset().getVertexById(nodeId); + return nodeItem; + }; + */ + +GraphCanvas.prototype.setDataset = function(dataset) { + this.dataset = dataset; +}; + +GraphCanvas.prototype.setFormatter = function(formatter) { + this.formatter = formatter; +}; + +GraphCanvas.prototype.setLayout = function(layout) { + this.layout = layout; +}; + +/** API **/ +GraphCanvas.prototype.getDataset = function() { + return this.dataset; +}; + +GraphCanvas.prototype.getFormatter = function() { + return this.formatter; +}; + +GraphCanvas.prototype.getLayout = function() { + return this.layout; +}; + +/** API DATASET **/ +GraphCanvas.prototype.addVertex = function(name, args) { + this.getDataset().addNode(name, args); +}; + +GraphCanvas.prototype.removeVertex = function(vertexId) { + this.getDataset().getVertexById(vertexId).remove(); +}; + +GraphCanvas.prototype.addEdge = function(edgeName, nodeSourceId, nodeTargetId, args) { + this.getDataset().addEdge(edgeName, nodeSourceId, nodeTargetId, args); +}; +/* + GraphCanvas.prototype.removeEdge = function(edgeId){ + this.getDataset().getEdgeById(edgeId).remove(); + }; + */ + +/** API FORMATTER **/ +GraphCanvas.prototype.getWidth = function() { + return this.getFormatter().getWidth(); +}; + +GraphCanvas.prototype.getHeight = function() { + return this.getFormatter().getHeight(); +}; + +GraphCanvas.prototype.getBackgroundImage = function() { + return this.getFormatter().getBackgroundImage(); +}; + +//GraphCanvas.prototype.setBackgroundImage = function(value){ +// this.getFormatter().setBackgroundImage(value); +//}; + +GraphCanvas.prototype.getBackgroundColor = function() { + return this.getFormatter().getBackgroundColor(); +}; + +GraphCanvas.prototype.setBackgroundColor = function() { + this.getFormatter().setBackgroundColor(value); +}; + +//GraphCanvas.prototype.setEdgeFill = function(edgeId, value){ +// this.getFormatter().getEdgeById(edgeId).getDefault().setFill(value); +//}; +// +//GraphCanvas.prototype.getEdgeFill = function(edgeId){ +// return this.getFormatter().getEdgeById(edgeId).getDefault().getFill(); +//}; + +/** VERTICES FORMATTER **/ +GraphCanvas.prototype.setVertexSize = function(vertexId, value) { + this.getFormatter().getVertexById(vertexId).getDefault().setSize(value); +}; + +GraphCanvas.prototype.getVertexSize = function(vertexId) { + return this.getFormatter().getVertexById(vertexId).getDefault().getSize(); +}; + +GraphCanvas.prototype.setVertexStroke = function(vertexId, value) { + this.getFormatter().getVertexById(vertexId).getDefault().setStroke(value); +}; + +GraphCanvas.prototype.getVertexStroke = function(vertexId) { + return this.getFormatter().getVertexById(vertexId).getDefault().getStroke(); +}; + +GraphCanvas.prototype.setVertexStrokeOpacity = function(vertexId, value) { + this.getFormatter().getVertexById(vertexId).getDefault().setStrokeOpacity(value); +}; + +GraphCanvas.prototype.getVertexStrokeOpacity = function(vertexId) { + return this.getFormatter().getVertexById(vertexId).getDefault().getStrokeOpacity(); +}; + +GraphCanvas.prototype.setVertexOpacity = function(vertexId, value) { + this.getFormatter().getVertexById(vertexId).getDefault().setOpacity(value); +}; + +GraphCanvas.prototype.getVertexOpacity = function(vertexId) { + return this.getFormatter().getVertexById(vertexId).getDefault().getOpacity(); +}; + +GraphCanvas.prototype.setVertexFill = function(vertexId, color) { + this.getFormatter().getVertexById(vertexId).getDefault().setFill(color); +}; + +GraphCanvas.prototype.getVertexFill = function(vertexId) { + return this.getFormatter().getVertexById(vertexId).getDefault().getFill(); +}; + +/** EDGES FORMATTER **/ +GraphCanvas.prototype.setEdgeSize = function(edgeId, value) { + this.getFormatter().getEdgeById(edgeId).getDefault().setSize(value); +}; + +GraphCanvas.prototype.getEdgeSize = function(edgeId) { + return this.getFormatter().getEdgeById(edgeId).getDefault().getSize(); +}; + +GraphCanvas.prototype.setEdgeStroke = function(edgeId, value) { + this.getFormatter().getEdgeById(edgeId).getDefault().setStroke(value); +}; + +GraphCanvas.prototype.getEdgeStroke = function(edgeId) { + return this.getFormatter().getEdgeById(edgeId).getDefault().getStroke(); +}; + +GraphCanvas.prototype.setEdgeStrokeOpacity = function(edgeId, value) { + this.getFormatter().getEdgeById(edgeId).getDefault().setStrokeOpacity(value); +}; + +GraphCanvas.prototype.getEdgeStrokeOpacity = function(edgeId) { + return this.getFormatter().getEdgeById(edgeId).getDefault().getStrokeOpacity(); +}; + +GraphCanvas.prototype.setEdgeFill = function(edgeId, color) { + this.getFormatter().getEdgeById(edgeId).getDefault().setFill(color); +}; + +GraphCanvas.prototype.getEdgeFill = function(edgeId) { + return this.getFormatter().getEdgeById(edgeId).getDefault().getFill(); +}; + +/** API LAYOUT **/ +GraphCanvas.prototype.setCoordinates = function(vertexId, x, y) { + return this.getLayout().getEdgeById(vertexId).setCoordinates(x, y); +}; + + + +function HPLCGraph(args) { + this.width = 600; + this.height = 600; + this.title = ''; + this.bbar = false; + this.plotInnerPanelPadding = 10; + this.plotPanelPadding = 5; + this.id = BUI.id(); - if (record.raw.measurementId != null) { - /** For testing * */ - grid.setLoading("ISPyB: Removing measurement"); - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, data) { - grid.setLoading(false); - /** - * We get and refresh experiment - * because specimens has changed * - */ - var adapter2 = new BiosaxsDataAdapter(); - adapter2.onSuccess.attach(function(sender, experiment) { - _this.onRemoved.notify(experiment); - _this._showStatusBarReady('Ready'); - }); - if (_this.experiments.experiments[0].experimentId != null) { - adapter2.getExperimentById(_this.experiments.experiments[0].experimentId, "MEDIUM"); - _this._showStatusBarBusy("ISPyB: Removing Unused Specimens"); - } - }); + this.hidePlots = null; + this.xlabel = ""; + this.scaled = false; + this.xParam = null; + this.showRangeSelector = true; + this.interactionModel = null; + + /** for each stat the max and minimum value when it is scaled in order to show correctly in the legend **/ + this.ranges = {}; + if (args != null) { + if (args.interactionModel != null) { + this.interactionModel = args.interactionModel; + } + if (args.width != null) { + this.width = args.width; + } + if (args.height != null) { + this.height = args.height; + } + if (args.bbar != null) { + this.bbar = args.bbar; + } + if (args.title != null) { + this.title = args.title; + } + if (args.plots != null) { + this.plots = args.plots; + } + + if (args.scaled != null) { + this.scaled = args.scaled; + } + if (args.xlabel != null) { + this.xlabel = args.xlabel; + } + if (args.xParam != null) { + this.xParam = args.xParam; + } + if (args.showRangeSelector != null) { + this.showRangeSelector = args.showRangeSelector; + } + } + + this.onZoomX = new Event(this); + this.onResetZoom = new Event(this); + this.dblclick = new Event(this); +} + +HPLCGraph.prototype.getMenu = function () { + var _this = this; + /** Actions buttons **/ + var actions = []; + + function toggle(item, pressed) { + if (pressed) { + _this.plots[item.param] = true; + } else { + delete _this.plots[item.param]; + } + _this.reloadData(this.hplcData); + } + + for (var i = 0; i < this.hplcData.length; i++) { + if (this.hplcData[i].showOnMenu != false) { + var param = this.hplcData[i].param; + var style = "style='padding:0 0px 0 5px;'"; + actions.push({ + text : "
" + BUI.getRectangleColorDIV(this.hplcData[i].color, 10, 10) + " " + this.hplcData[i].label + "
", + id : _this.id + param, + param : param, + enableToggle : true, + scope : this, + toggleHandler : toggle, + pressed : (_this.plots[param] != null) + }); + } + } + actions.push("-"); + + actions.push({ + text : "Scale", + enableToggle : true, + scope : this, + pressed : this.scaled, + icon : '../images/icon_graph.png', + toggleHandler : function (item, pressed) { + _this.scaled = pressed; + _this.reloadData(this.hplcData); + } + }); + + actions.push("->"); + actions.push({ + text : "Save", + scope : this, + icon : '../images/save.gif', + handler : function (item, pressed) { + var largeImage = document.createElement("img"); + largeImage.style.display = 'block'; + largeImage.style.width = 200 + "px"; + largeImage.style.height = 200 + "px"; + largeImage.setAttribute('src', Dygraph.Export.asCanvas(this.dygraphObject.dygraph).toDataURL()); + window.open(Dygraph.Export.asCanvas(this.dygraphObject.dygraph).toDataURL(), 'Image', ''); + } + }); + + return actions; +}; - adapter.onError.attach(function(sender, data) { - alert("Error: " + data); - grid.setLoading(false); - }); +/** Looks for the maximum value and then divide everything but that value **/ +HPLCGraph.prototype.scaledData = function (data) { + for (var i = 0; i < data.length; i++) { + var values = this.getMaxAndMinValue(data[i]); + data[i] = this.divideValuesByMax(data[i], values.max); + this.ranges[data[i].label] = values; + } + return data; +}; - adapter.removeMeasurement(record.raw); - } - } - } +/** Given a stat float[] and a max number it will divide each value by max **/ +HPLCGraph.prototype.divideValuesByMax = function (stat, max) { + for (var j = 0; j < stat.data.length; j++) { + if (max != 0) { + stat.data[j] = Number(stat.data[j]) / max; + stat.std[j] = Number(stat.std[j]) / max; + } + } + return stat; +}; - } - } - }); +/** returns max value of a stat **/ +HPLCGraph.prototype.getMaxAndMinValue = function (stat) { + var max = 0; + var min = stat.data[0]; + for (var j = 0; j < stat.data.length; j++) { + if (Number(stat.data[j]) > max) { + max = Number(stat.data[j]); + } + if (Number(stat.std[j]) > max) { + max = Number(stat.std[j]); + } + if (Number(stat.data[j]) < min) { + min = Number(stat.data[j]); + } + } + return { + max : Number(max), + min : Number(min) + }; +}; - this.grid.on("afterrender", function() { +HPLCGraph.prototype.getPoint = function (data, i) { + var point = [ 10, 10, 10 ]; + var y = parseFloat(data.data[i]); + var error = parseFloat(data.std[i]); + if (data.fdata == null) { + return [ y - error, y, y + error ]; + } else { + if (data.fstd != null) { + return [ data.fstd(y - error), data.fdata(y), data.fstd(y + error) ]; + } + return [ data.fdata(y) - error, data.fdata(y), data.fdata(y) + error ]; + } + return point; +}; - function updateTime() { - try { - _this.estimatedTime = _this.estimatedTime - 1; +HPLCGraph.prototype.reloadData = function(hplcData) { + this.panel.setLoading(false); + this.hplcData = hplcData; - _this.onUpdateTime.notify({ - hours : (Number(_this.estimatedTime / 3600).toFixed()), - minutes : (Number((_this.estimatedTime / 60) % 60).toFixed()), - seconds : (Number(_this.estimatedTime % 60).toFixed()) + var data = hplcData; + - }); + /** In case of having peaks **/ + if (this.peaks != null) { + for (var peak in this.peaks) { + var values = []; + var std = []; + for (var i = 0; i < this.peaks[peak].length; i++) { + values.push(this.peaks[peak][i][1]); + std.push(this.peaks[peak][i][2]); + } + data.push({ + param : peak, + data : values, + showOnMenu : false, + fdata : function (a) { + var value = (Math.log(parseFloat(a))); + if (isNumber(value)) { + return value; + } + }, + fstd : function (a) { + var value = Math.log(Math.abs(parseFloat(a))); + if (isNumber(value)) + return value; + }, + std : std, + color : this.colorPeak[peak], + label : peak + }); + + } + } + + + if (this.scaled) { + data = this.scaledData(JSON.parse(JSON.stringify(hplcData))); + } - if (Number(_this.estimatedTime) < 0) { - _this.timer = null; - grid.setTitle(_this.title); + var paramIndex = {}; + var parsed = []; + var j = 0; + for (var i = 0; i < data[0].data.length - 1; i++) { + var aux = []; + for (j = 0; j < data.length; j++) { + if (this.plots != null) { + if (this.plots[data[j].param] != null) { + aux.push(this.getPoint(data[j], i)); + paramIndex[data[j].param] = aux.length - 1; } + } else { + aux.push([ data[j].data[i] - data[j].std[i], data[j].data[i], data[j].data[i] + data[j].std[i] ]); + } + } + parsed.push([]); - } catch (e) { - console.log(e); - _this.timer = null; + var index = i; + if (this.xParam != null) { + index = parseFloat(data[this.xParam].data[i]); + } + + parsed[parsed.length - 1].push(index); + + for (j = 0; j < data.length; j++) { + if (this.plots != null) { + if (this.plots[data[j].param] != null) { + parsed[parsed.length - 1].push(aux[paramIndex[data[j].param]]); + } + } else { + parsed[parsed.length - 1].push(aux[j]); } } + } - if (_this.estimateTime) { - var experimentList = _this.experiments; - var collected = experimentList.getMeasurementsCollected(); - if (collected.length > 0) { - if (collected[0].run3VO != null) { - try { - var end = collected[0].run3VO.timeEnd; - var start = collected[0].run3VO.timeStart; - var dstart = moment(start); - var dend = moment(end); - var seconds = Number(dend.diff(dstart) / 1000).toFixed(); + var colors = []; + var labels = [ "" ]; + for (j = 0; j < data.length; j++) { + if (this.plots != null) { + if (this.plots[data[j].param] != null) { + colors.push(data[j].color); + labels.push(data[j].label); + } + } else { + parsed[parsed.length - 1].push(aux[j]); + } + } - _this.estimatedTime = (seconds * experimentList.getMeasurementsNotCollected().length); + this._renderDygraph(parsed, colors, labels); +}; - if (_this.estimatedTime > 0) { - updateTime(); - _this.timer = setInterval(function() { - updateTime(); - }, 1000); +HPLCGraph.prototype._renderDygraph = function (parsed, colors, labels) { + this.dygraphObject = new StdDevDyGraph(this.id, { + width : this.width, + height : this.height - 10, + xlabel : this.xlabel, + showRangeSelector : this.showRangeSelector, + interactionModel : this.interactionModel, + scaled : this.scaled, + ranges : this.ranges + }); + this.dygraphObject.draw(parsed, colors, labels); + + var _this = this; + this.dygraphObject.onZoomX.attach(function (sender, args) { + try { + _this.onZoomX.notify(args); + } catch (e) { + } + }); + + this.dygraphObject.onResetZoom.attach(function (sender, args) { + try { + _this.onResetZoom.notify(args); + } catch (e) { + } + }); + + this.dygraphObject.dblclick.attach(function (sender, args) { + try { + _this.dblclick.notify(args); + } catch (e) { + } + }); + +}; + +HPLCGraph.prototype.loadData = function (data) { + var _this = this; + this.reloadData(data); + this.panel.addDocked({ + xtype : 'toolbar', + items : this.getMenu() + }); + + + if (this.bbar == true){ + this.panel.addDocked({ + xtype : 'toolbar', + dock: 'bottom', + items : [ + { + xtype: 'numberfield', + id: 'main_field_start', + fieldLabel: 'Range from', + width: 170, + labelWidth : 70, + value: 0, + minValue: 0 + }, + { + xtype: 'numberfield', + id: 'main_field_end', + fieldLabel: 'to', + width: 130, + labelWidth : 30, + value: 0, + minValue: 0 + }, + { + xtype: 'button', + text: 'Go', + handler: function () { + var start = parseFloat(Ext.getCmp("main_field_start").getValue()); + var end = parseFloat(Ext.getCmp("main_field_end").getValue()); + + if (start < 0) { + start = 0; + } + if (end < 0) { + end = 0; + } + if (start > end) { + var aux = end; + end = start; + start = aux; + } + + _this.dygraphObject.dygraph.updateOptions({ isZoomedIgnoreProgrammaticZoom: true, dateWindow: [start, end] }); + } } + ] + }); + } +}; - } catch (e) { - } - } - } - } +HPLCGraph.prototype.getPanel = function () { + this.panel = Ext.create('Ext.panel.Panel', { + padding : this.plotPanelPadding, + width : this.width + 4 * this.plotInnerPanelPadding, + height : this.height + 4 * this.plotInnerPanelPadding, + items : [ { + html : "", + id : this.id, + width : this.width, + height : this.height + } ] }); - return this.grid; + return this.panel; }; -/** Method for testing * */ -MeasurementGrid.prototype.input = function() { - var experiment = DATADOC.getExperiment_10(); - var measurements = DATADOC.getMeasurements_10(); - var proposal = DATADOC.getProposal_10(); - return { - experiment : experiment, - measurements : measurements, - proposal : proposal - }; +HPLCGraph.prototype.input = function () { + return DATADOC.getHPLCData(); }; -MeasurementGrid.prototype.test = function(targetId) { - var measurementGrid = new MeasurementGrid({ - tbar : true - - }); - BIOSAXS.proposal = new Proposal(measurementGrid.input().proposal); - var panel = measurementGrid.getPanel(measurementGrid.input().measurements, new ExperimentList([ new Experiment(measurementGrid.input().experiment) ])); - panel.render(targetId); +HPLCGraph.prototype.getDataByFrameNumber = function (frameNumber) { + var data = {}; + data.frameNumber = frameNumber; + for (var key in this.hplcData){ + data[this.hplcData[key].label] = this.hplcData[key].data[frameNumber]; + } + console.log(data); + return data; }; - -/** - * A shipment may contains one or more cases where stock solutions and sample - * plates are stored - * - * @height - * @btnEditVisible - * @btnRemoveVisible - * - * #onEditButtonClicked - */ -function MolarityGrid(args) { - this.height = 100; - this.width = 100; - if (args != null) { - if (args.height != null) { - this.height = args.height; - } - if (args.width != null) { - this.width = args.width; + +HPLCGraph.prototype.test = function (targetId) { + var mainPlotPanel = new HPLCGraph({ + title : 'I0', + width : 800, + height : 400, + plots : { + "I0" : true, + "Rg" : true, + "Mass" : true + }, + xlabel : "HPLC Frames", + scaled : this.scaled, + interactionModel : { + 'dblclick' : function (event, g, context) { + } } - } + }); + mainPlotPanel.getPanel().render(targetId); + mainPlotPanel.loadData(mainPlotPanel.input()); - var _this = this; - - this.molarityForm = new MolarityForm({height : 180, width : 455}); +}; - this.molarityForm.onSave.attach(function(sender){ - _this.molarityWindow.destroy(); - _this.updateProposal(); - - }); - - this.molarityForm.onClose.attach(function(sender){ - _this.molarityWindow.destroy(); - - }); + +function MergesHPLCGraph(args) { + HPLCGraph.prototype.constructor.call(this, args); - /** Events * */ - this.onEditButtonClicked = new Event(this); +// this.peakColors = ["#00FB42", "#00BA31", "#007C21", "#003E10"]; + this.peakColors = ["#DEBD00", "#6D9100", "#872900", "#0092CC"]; } -MolarityGrid.prototype._getColumns = function() { - return [ { - text : 'Subunit', - columns : [ { - text : "Acronym", - width : 100, - hidden : false, - dataIndex : 'acronym', - sortable : true - }, { - text : "Name", - width : 125, - hidden : false, - dataIndex : 'name', - sortable : true - }, { - text : "MM Est.", - width : 100, - dataIndex : 'molecularMass', - sortable : true, - renderer : function(grid, cls, record){ - return BUI.formatValuesUnits(record.data.molecularMass , "Da", 10, 2); - - } - } ] - }, { -// text : "Number
in assymmetric units", - text : "Ratio", - width : 100, - dataIndex : 'ratio', - tooltip : 'Number of times the subunit is present in the macromolecule', - sortable : true - }, { - id : this.id + 'MOLARITY_REMOVE', - flex : 0.1, - sortable : false, - renderer : function(value, metaData, record, rowIndex, colIndex, store) { - return BUI.getRedButton('REMOVE'); - } - } ]; -}; -MolarityGrid.prototype._openMolarityWindow = function() { - this.molarityWindow = Ext.create('Ext.window.Window', { - title : 'Molarity', - height : 220, - width : 500, - modal : true, - items : [this.molarityForm.getPanel() ] - }).show(); -}; +MergesHPLCGraph.prototype.scaledData = HPLCGraph.prototype.scaledData; +MergesHPLCGraph.prototype.divideValuesByMax = HPLCGraph.prototype.divideValuesByMax; +MergesHPLCGraph.prototype.getMaxAndMinValue = HPLCGraph.prototype.getMaxAndMinValue; +MergesHPLCGraph.prototype.getPoint = HPLCGraph.prototype.getPoint; +MergesHPLCGraph.prototype.reloadData = HPLCGraph.prototype.reloadData; +MergesHPLCGraph.prototype._renderDygraph = HPLCGraph.prototype._renderDygraph; +MergesHPLCGraph.prototype.loadData = HPLCGraph.prototype.loadData; +MergesHPLCGraph.prototype.getPanel = HPLCGraph.prototype.getPanel; +MergesHPLCGraph.prototype.getDataByFrameNumber = HPLCGraph.prototype.getDataByFrameNumber; -MolarityGrid.prototype._getButtons = function() { - var _this = this; - return [ { - text : 'Add subunit', - icon : '../images/add.png', - handler : function() { - _this._openMolarityWindow(); + +MergesHPLCGraph.prototype.setPeaks = function (data) { + this.peaks = data; + /** get size of peaks **/ + this.peakKeys = []; + this.colorPeak = {}; + var colorCount = 1; + for (var key in this.peaks) { + if (this.peaks.hasOwnProperty(key)) { + var color = this.peakColors[colorCount % this.peakColors.length]; + colorCount = colorCount + 1; + this.peakKeys.push(key); + this.colorPeak[key] = color; } - }]; + } + this.peakKeys.sort(); }; -MolarityGrid.prototype.updateProposal = function() { +MergesHPLCGraph.prototype.getMenu = function () { var _this = this; - this.panel.setLoading(); - BIOSAXS.proposal.onInitialized.attach(function() { - if (BIOSAXS.proposal != null) { - var macromolecules = BIOSAXS.proposal.macromolecules; - for (var i = 0; i < macromolecules.length; i++) { - - if (macromolecules[i].macromoleculeId == _this.macromolecule.macromoleculeId) { - _this.refresh(macromolecules[i]); - _this.panel.setLoading(false); + /** Actions buttons **/ + var actions = []; + + function toggle(item, pressed) { + if (pressed) { + _this.plots[item.param] = true; + } else { + delete _this.plots[item.param]; + } + _this.reloadData(_this.hplcData); + } + + + /** Toolbar for peaks Average **/ + if (this.peaks != null) { + var items = []; + for (var i = 0; i < this.peakKeys.length; i++) { + var color = this.colorPeak[this.peakKeys[i]]; + items.push({ + text: "Peak #" + i + " " + this.peakKeys[i].replace("- ", " to #").replace(".0", "").replace(".0", "") + "", + peakid : this.peakKeys[i], + checked: false, + checkHandler: function (sender, pressed) { + var item = new Object(); + item.param = sender.peakid; + toggle(item, pressed); } - } + }); } - }); - BIOSAXS.proposal.init(); -}; - + + var menu = Ext.create('Ext.menu.Menu', { + id: 'mainMenu', + style: { + overflow: 'visible' + }, + items: items + }); + var tb = Ext.create('Ext.toolbar.Toolbar'); + tb.add({ + text: 'Peaks Avg.', + menu: menu + }); + actions.push(tb); + } -MolarityGrid.prototype.getPanel = function() { - var _this = this; + - this.molarityStore = Ext.create('Ext.data.Store', { - fields : [ 'acronym', 'ratio', 'comments', 'stoichiometryId', 'name', 'molecularMass' ], - sorters : { - property : 'ratio', - direction : 'DESC' + for (var i = 0; i < this.hplcData.length; i++) { + if (this.hplcData[i].showOnMenu != false) { + var param = this.hplcData[i].param; + var style = "style='padding:0 0px 0 5px;'"; + actions.push({ + text : "
" + BUI.getRectangleColorDIV(this.hplcData[i].color, 10, 10) + " " + this.hplcData[i].label + "
", + id : _this.id + param, + param : param, + enableToggle : true, + scope : this, + margin : 5, + toggleHandler : toggle, + pressed : (_this.plots[param] != null) + }); } - }); - - this.panel = Ext.create('Ext.grid.Panel', { - store : this.molarityStore, - height : this.height, - padding : 5, - columns : this._getColumns(), - listeners : { - cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { - - /** Remove entry * */ - if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'MOLARITY_REMOVE') { - var dataAdapter = new BiosaxsDataAdapter(); - dataAdapter.onSuccess.attach(function(sender) { - _this.updateProposal(); - - }); - dataAdapter.removeStoichiometry(record.data.stoichiometryId); - _this.panel.setLoading("Removing Structure"); - } - } - }, - tbar : this._getButtons() - }); - return this.panel; -}; + } -MolarityGrid.prototype._prepareData = function(macromolecule) { - /** Return an array of [{ratio,acronym, stoichiometryId, name}] **/ - var data = []; - if (macromolecule.stoichiometry != null) { - for (var i = 0; i < macromolecule.stoichiometry.length; i++) { - var hostMacromolecule = BIOSAXS.proposal.getMacromoleculeById(macromolecule.stoichiometry[i].macromoleculeId); - data.push({ - ratio : macromolecule.stoichiometry[i].ratio, - acronym : hostMacromolecule.acronym, - comments : hostMacromolecule.comments, - molecularMass : hostMacromolecule.molecularMass, - stoichiometryId : macromolecule.stoichiometry[i].stoichiometryId, - name : hostMacromolecule.name - }); - } + actions.push("->"); + actions.push({ + text : "Save", + scope : this, + icon : '../images/save.gif', + handler : function (item, pressed) { + var largeImage = document.createElement("img"); + largeImage.style.display = 'block'; + largeImage.style.width = 200 + "px"; + largeImage.style.height = 200 + "px"; + largeImage.setAttribute('src', Dygraph.Export.asCanvas(this.dygraphObject.dygraph).toDataURL()); + window.open(Dygraph.Export.asCanvas(this.dygraphObject.dygraph).toDataURL(), 'Image', ''); } - return data; -}; + }); -MolarityGrid.prototype.refresh = function(macromolecule) { - if (macromolecule != null){ - this.molarityStore.loadData(this._prepareData(macromolecule)); - this.molarityForm.refresh(macromolecule); - this.macromolecule = macromolecule; - } + return actions; }; -MolarityGrid.prototype.input = function() { - return { - proposal : DATADOC.getProposal_10(), - dewars : DATADOC.getDewars_10() - }; +MergesHPLCGraph.prototype.input = function () { + return DATADOC.getScatteringHPLCFrameData(); }; -MolarityGrid.prototype.test = function(targetId) { - var MolarityGrid = new MolarityGrid({ - height : 150 +MergesHPLCGraph.prototype.test = function (targetId) { + var mainPlotPanel = new MergesHPLCGraph({ + title : 'Scattering', + width : this.plotWidth, + height : 500, + showRangeSelector : false, + xParam : 0, + xlabel : "scattering_I", + plots : { + "scattering_I" : true, + "subtracted_I" : true, + "buffer_I" : true + } }); - BIOSAXS.proposal = new Proposal(MolarityGrid.input().proposal); - var panel = MolarityGrid.getPanel(MolarityGrid.input().dewars); - panel.render(targetId); - + mainPlotPanel.getPanel().render(targetId); + mainPlotPanel.loadData(mainPlotPanel.input()); }; + + +function NetworkWidget(args) { + this.id = "NetworkViewer_" + Math.random().toString().replace(".", "_"); + + this.label = true; + if (args != null) { + if (args.targetId != null) { + this.targetId = args.targetId; + } + if (args.label != null) { + this.label = args.label; + } + } + + this.onVertexOver = new Event(this); + this.onVertexOut = new Event(this); +} + +NetworkWidget.prototype.draw = function(dataset, formatter, layout) { + + this.graphCanvas = new GraphCanvas(this.id, document.getElementById(this.targetId), { + "labeled" : this.label, + "multipleSelectionEnabled" : false, + "draggingCanvasEnabled" : false + }); + this.graphCanvas.draw(dataset, formatter, layout); + + var _this = this; + this.graphCanvas.onVertexOver.attach(function(sender, nodeId) { + _this.onVertexOver.notify(nodeId); + }); + + this.graphCanvas.onVertexOut.attach(function(sender, nodeId) { + _this.onVertexOut.notify(nodeId); + }); +}; + +/** SELECT VERTICES BY NAME * */ +NetworkWidget.prototype.selectVertexByName = function(vertexName) { + var vertices = this.getDataset().getVertexByName(vertexName); + if (vertices != null) { + for ( var nodeId in vertices) { + if (vertices.hasOwnProperty(nodeId)) { + var vertexId = vertices[nodeId].getId(); + this.selectVertexById(vertexId); + } + } + } +}; + +NetworkWidget.prototype.selectVerticesByName = function(verticesName) { + for ( var i = 0; i < verticesName.length; i++) { + this.selectVertexByName(verticesName[i]); + } +}; + +/** SELECT VERTICES BY ID * */ +NetworkWidget.prototype.selectVertexById = function(vertexId) { + this.graphCanvas.selectNode(vertexId); + this.blinkVertexById(vertexId); +}; + +NetworkWidget.prototype.selectVerticesById = function(verticesId) { + for ( var i = 0; i < verticesId.length; i++) { + this.selectVertexById(verticesId[i]); + } +}; + +/** VECINDARIO * */ +NetworkWidget.prototype.selectNeighbourhood = function() { + this.selectEdgesFromVertices(); + this.selectAdjacent(); +}; +/** DESELECT * */ +NetworkWidget.prototype.deselectNodes = function() { + this.graphCanvas.deselectNodes(); +}; + +/** SELECT ALL NODES * */ +NetworkWidget.prototype.selectAllNodes = function() { + this.getGraphCanvas().selectAllNodes(); +}; + +/** SELECT EVERYTHING * */ +NetworkWidget.prototype.selectAll = function() { + this.getGraphCanvas().selectAll(); +}; + +/** SELECT ALL EDGES * */ +NetworkWidget.prototype.selectAllEdges = function() { + this.getGraphCanvas().selectAllEdges(); +}; + +/** ZOOM * */ +NetworkWidget.prototype.setScale = function(value) { + this.graphCanvas.setScale(value); +}; + +NetworkWidget.prototype.getScale = function() { + return this.graphCanvas.getScale(value); +}; + +/** SELECT ADJACENT VERTICES FROM SELECTED VERTICES * */ +NetworkWidget.prototype.selectAdjacent = function() { + var selectedVertices = this.getGraphCanvas().getSelectedVertices(); + var edges = []; + for ( var i = 0; i < selectedVertices.length; i++) { + edges = edges.concat(this.getGraphCanvas().getDataset().getVertexById(selectedVertices[i]).getEdges()); + } + var vertices = []; + for ( i = 0; i < edges.length; i++) { + vertices.push(edges[i].getNodeSource().getId()); + vertices.push(edges[i].getNodeTarget().getId()); + } + + this.selectVerticesById(vertices); +}; + +/** SELECT EDGES FROM SELECTED VERTICES * */ +NetworkWidget.prototype.selectEdgesFromVertices = function() { + var selectedVertices = this.getGraphCanvas().getSelectedVertices(); + var edges = []; + for ( var i = 0; i < selectedVertices.length; i++) { + edges = edges.concat(this.getGraphCanvas().getDataset().getVertexById(selectedVertices[i]).getEdges()); + } + var edgesId = []; + for ( i = 0; i < edges.length; i++) { + edgesId.push(edges[i].getId()); + } + this.getGraphCanvas().selectEdges(edgesId); +}; + +/** BLINKING * */ +NetworkWidget.prototype.blinkVertexById = function(vertexId) { + this.graphCanvas.blinkVertexById(vertexId); +}; + +/** COMPONENTE CONEXA DE LOS NODOS SELECCIONADOS * */ +NetworkWidget.prototype.selectConnectedComponent = function() { + var elements = this.getConnectedComponent(); + this.selectVerticesById(elements.nodes); + this.graphCanvas.selectEdges(elements.edges); +}; + +NetworkWidget.prototype.getConnectedComponent = function() { + var nodosVisitados = {}; + var aristasVisitadas = {}; + var arrNodos = []; + var arrAristas = []; + var selectedGraphNodesId = this.getGraphCanvas().getSelectedVertices(); + for ( var i = 0; i < selectedGraphNodesId.length; i++) { + this.visitNode(selectedGraphNodesId[i], nodosVisitados, aristasVisitadas, arrNodos, arrAristas); + } + return { + nodes : arrNodos, + edges : arrAristas + }; +}; + +NetworkWidget.prototype.visitNode = function(nodeId, nodosVisitados, aristasVisitadas, arrNodos, arrAristas) { + nodosVisitados[nodeId] = true; + arrNodos.push(nodeId); + var nodeEdges = this.getDataset().getVertexById(nodeId).getEdges(); + for ( var j = 0; j < nodeEdges.length; j++) { + var edge = nodeEdges[j]; + var edgeId = edge.getId(); + if (aristasVisitadas[edgeId] == null) { + aristasVisitadas[edgeId] = true; + arrAristas.push(edgeId); + var nodeTargetId = edge.getNodeTarget().getId(); + if (nodosVisitados[nodeTargetId] == null) { + this.visitNode(nodeTargetId, nodosVisitados, aristasVisitadas, arrNodos, arrAristas); + } + var nodeSourceId = edge.getNodeSource().getId(); + if (nodosVisitados[nodeSourceId] == null) { + this.visitNode(nodeSourceId, nodosVisitados, aristasVisitadas, arrNodos, arrAristas); + } + } + } +}; + +/** COLLAPSE SELECTED VERTICES * */ +NetworkWidget.prototype.collapse = function() { + var selectedVertices = this.getGraphCanvas().getSelectedVertices(); + var xMin = -Infinity; + var xMax = Infinity; + var yMin = -Infinity; + var yMax = Infinity; + + for ( var i = 0; i < selectedVertices.length; i++) { + var vertex = this.getGraphCanvas().getLayout().getNodeById(selectedVertices[i]); + if (xMin < vertex.x) { + xMin = vertex.x; + } + if (xMax > vertex.x) { + xMax = vertex.x; + } + if (yMin < vertex.y) { + yMin = vertex.y; + } + if (yMax > vertex.y) { + yMax = vertex.y; + } + } + + var centerX = xMin - xMax; + var centerY = yMin - yMax; + var radius = (xMax - xMin) / 4; + + var count = selectedVertices.length; + var verticesCoordinates = []; + + for ( i = 0; i < selectedVertices.length; i++) { + x = centerX + radius * Math.sin(i * 2 * Math.PI / count); + y = centerY + radius * Math.cos(i * 2 * Math.PI / count); + verticesCoordinates.push({ + 'x' : x, + 'y' : y + }); + } + + for ( i = 0; i < selectedVertices.length; i++) { + this.getGraphCanvas().getLayout().getNodeById(selectedVertices[i]).setCoordinates(verticesCoordinates[i].x, verticesCoordinates[i].y); + } +}; +/** SETTER FONT SIZE * */ +NetworkWidget.prototype.setVerticesFontSize = function(value) { + for ( var nodeId in this.getDataset().getVertices()) { + if (this.getDataset().getVertices().hasOwnProperty(nodeId)) { + this.getFormatter().getVertexById(nodeId).getDefault().setFontSize(value); + } + } +}; + +/** GETTERS * */ +NetworkWidget.prototype.getFormatter = function() { + return this.getGraphCanvas().getFormatter(); +}; +NetworkWidget.prototype.getLayout = function() { + return this.getGraphCanvas().getLayout(); +}; + +NetworkWidget.prototype.getDataset = function() { + return this.getGraphCanvas().getDataset(); +}; + +NetworkWidget.prototype.getGraphCanvas = function() { + return this.graphCanvas; +}; + +RangeWhiskerGraph.prototype.cleanArray = GenericGraph.prototype.cleanArray; +RangeWhiskerGraph.prototype.plotAxes = GenericGraph.prototype.plotAxes; +RangeWhiskerGraph.prototype.plotRuler = GenericGraph.prototype.plotRuler; +RangeWhiskerGraph.prototype.drawSVGVerticalText = GenericGraph.prototype.drawSVGVerticalText; +RangeWhiskerGraph.prototype.getClassColor = GenericGraph.prototype.getClassColor; +RangeWhiskerGraph.prototype.getDimensions = GenericGraph.prototype.getDimensions; +RangeWhiskerGraph.prototype.calculate = GenericGraph.prototype.calculate; +RangeWhiskerGraph.prototype.getMedian = GenericGraph.prototype.getMedian; +RangeWhiskerGraph.prototype.pointToPixel = GenericGraph.prototype.pointToPixel; +RangeWhiskerGraph.prototype.maxValueWhisker = GenericGraph.prototype.maxValueWhisker; +RangeWhiskerGraph.prototype.refresh = GenericGraph.prototype.refresh; + /** - * Given data analysis parsed with ResultsAssemblyWidget makes a selector grid with buffer/protein - **/ -function SpecimenSelectorResultGrid() { - this.id = BUI.id(); -} + * Subclass of GenericGraph + * + * @plotHorizontalByCluster + */ +function RangeWhiskerGraph(args) { + this.maxBoxWidth = 25; -SpecimenSelectorResultGrid.prototype._prepareData = function(data) { - var parsed = []; - for ( var i = 0; i < data.length; i++) { - var row = data[i]; - for ( var j = 0; j < row.conditions.length; j++) { - parsed.push({ - bufferId : row.conditions[j].bufferId, - macromoleculeId : row.macromoleculeId, - macromoleculeAcronym : BIOSAXS.proposal.getMacromoleculeById(row.macromoleculeId).acronym, - bufferAcronym : BIOSAXS.proposal.getBufferById(row.conditions[j].bufferId).acronym - }); - } + if (args == null) { + args = {}; } - return parsed; -}; - -SpecimenSelectorResultGrid.prototype.refresh = function(data) { - this.store.loadData(this._prepareData(data)); -}; - -SpecimenSelectorResultGrid.prototype.getPanel = function(data) { - var _this = this; - this.store = Ext.create('Ext.data.Store', { - fields : [ 'macromoleculeId', 'bufferId', 'macromoleculeAcronym', 'bufferAcronym', 'concentration' ], - data : this._prepareData(data), - groupField : 'macromoleculeAcronym' - }); + args.plotHorizontalByCluster = false; - this.store.sort('concentration'); - this.grid = Ext.create('Ext.grid.Panel', { - id : this.id, - store : this.store, - width : this.width, - height : this.height, - maxHeight : this.maxHeight, - border : 1, - features : [ { - ftype : 'grouping', - groupHeaderTpl : '{name}', - hideGroupedHeader : true, - startCollapsed : false - } ], - selModel : Ext.create('Ext.selection.CheckboxModel', { - allowDeselect : true, - mode : 'MULTI', - listeners : { - selectionchange : function(sm, selections) { - _this.selected = []; - for ( var i = 0; i < selections.length; i++) { - _this.selected.push(selections[i].raw); - } - } - } - }), - margin : 10, - sortableColumns : true, - columns : [ { - text : 'Macromolecule', - dataIndex : 'macromoleculeAcronym', - flex : 1 - }, { - text : '', - dataIndex : 'bufferId', - width : 20, - hidden : false, - renderer : function(val, y, sample) { - return BUI.getRectangleColorDIV(BIOSAXS.proposal.bufferColors[val], 10, 10); - } - }, { - text : 'Buffer', - dataIndex : 'bufferAcronym', - flex : 1 - }, { - text : 'Concentration', - dataIndex : 'concentration', - flex : 1, - renderer : function(val, y, sample) { - return val + " mg/ml"; - } - } ] - }); + GenericGraph.call(this, args); - /** Adding the tbar **/ - if (this.tbar) { - this.grid.addDocked({ - xtype : 'toolbar', - items : this.getTbar() - }); + if (args.maxBoxWidth != null) { + this.maxBoxWidth = args.maxBoxWidth; } - return this.grid; -}; +} -SpecimenSelectorResultGrid.prototype.input = function() { - return { - data : new ResultsAssemblyGrid()._prepareData(new ResultsAssemblyGrid().input().data), - proposal : new ResultsAssemblyGrid().input().proposal - }; +RangeWhiskerGraph.prototype.refresh = function(data) { + document.getElementById(this.targetId).innerHTML = ""; + this.draw(this.targetId, data); }; -SpecimenSelectorResultGrid.prototype.test = function(targetId) { - var specimenSelectorResultGrid = new SpecimenSelectorResultGrid(); - BIOSAXS.proposal = new Proposal(specimenSelectorResultGrid.input().proposal); - var panel = specimenSelectorResultGrid.getPanel(specimenSelectorResultGrid.input().data); - panel.render(targetId); -}; +RangeWhiskerGraph.prototype.isNumber = function(value) { + if (value == "") + return false; -/** - * Show all buffer conditions for each macromolecules pointing out the measurements quality - * - * @height - * @maxHeight - * @width - * @searchBar - * @tbar - * @btnResultVisible - * - * #onClick - */ -function ResultsAssemblyGrid(args) { - this.height = 500; - this.id = BUI.id(); - this.maxHeight = this.height; + var d = parseInt(value); + if (!isNaN(d)) + return true; + else + return false; - this.width = 900; - this.searchBar = false; - this.tbar = false; - this.btnResultVisible = false; +}; - /** For processing **/ - this.processed = {}; - this.renderedPlotIndex = 0; +/** + There are several different methods for calculating quartiles.[1] This calculator uses a method described by Moore and McCabe to find quartile values. + The same method also used by The TI-83 to calculate quartile values. + With this method, the first quartile is the median of the numbers below the median, the third quartile is the median of the numbers above the median. - this.plotWidth = 210; - this.plotHeight = 80; + http://www.miniwebtool.com/quartile-calculator/ + http://www.alcula.com/calculators/statistics/box-plot/ - /** Colors **/ - this.validColor = BUI.getValidColor(); - this.warningColor = BUI.getWarningColor(); - this.notValidColor = BUI.getErrorColor(); + **/ +RangeWhiskerGraph.prototype.getQ1 = function(array) { + array = array.slice(0, array.length / 2); + return this.getMedian(array); +}; - /** Show warning if guinier quality less than **/ - this.guinierQuality = BUI.getQualityThreshold(); - this.framePercentageThreshold = BUI.getRadiationDamageThreshold(); +RangeWhiskerGraph.prototype.getQ3 = function(array) { + array = array.slice((array.length + 1) / 2); + return this.getMedian(array); - if (args != null) { - if (args.height != null) { - this.height = args.height; - this.maxHeight = this.height; - } - if (args.maxHeight != null) { - this.maxHeight = args.maxHeight; - } - if (args.width != null) { - this.width = args.width; - } - if (args.searchBar != null) { - this.searchBar = args.searchBar; +}; + +RangeWhiskerGraph.prototype.getQ2 = function(array) { + return this.getMedian(array); +}; + +RangeWhiskerGraph.prototype.getBelowOutliers = function(belowLimit, array) { + var points = []; + for ( var i = 0; i < array.length; i++) { + if (array[i] <= belowLimit) { + points.push(array[i]); } + } + return points; +}; - if (args.tbar != null) { - this.tbar = args.tbar; +RangeWhiskerGraph.prototype.getAboveOutliers = function(aboveLimit, array) { + var points = []; + for ( var i = 0; i < array.length; i++) { + if (array[i] >= aboveLimit) { + points.push(array[i]); } - if (args.btnResultVisible != null) { - this.btnResultVisible = args.btnResultVisible; + } + return points; +}; + +RangeWhiskerGraph.prototype.minValueWhisker = function(belowLimit, q1, array) { + var points = []; + + for ( var i = 0; i < array.length; i++) { + if ((array[i] < q1) && (array[i]) > belowLimit) { + points.push(array[i]); } + } + if (points.length > 0) { + points.sort(function(a, b) { + return a - b; + }); + return points[0]; } + return null; +}; - this.onClick = new Event(); -} +//RangeWhiskerGraph.prototype.maxValueWhisker = function(aboveLimit, q3, array){ +// var points = []; +// for (var i = 0; i < array.length; i++){ +// if ((array[i] > q3) && (array[i]) <= aboveLimit){ +// points.push(array[i]); +// } +// } +// if (points.length > 0){ +// points.sort(function(a, b){return a - b;}); +// return points[points.length - 1]; +// } +// return null; +//}; -ResultsAssemblyGrid.prototype.edit = function(macromoleculeId) { - var _this = this; - var window = new MacromoleculeWindow(); - window.onSuccess.attach(function(sender, proposal) { - _this.store.loadData(_this._prepareData(BIOSAXS.proposal.getMacromolecules())); - }); - window.draw(BIOSAXS.proposal.getMacromoleculeById(macromoleculeId)); +RangeWhiskerGraph.prototype.drawPoints = function(boxProperties) { + if (this.plotPoints) { + for ( var i = 0; i < boxProperties.values.length; i++) { + var value = boxProperties.values[i]; + var toPixel = this.pointToPixel(value, boxProperties); + SVG.drawCircle(boxProperties.x, toPixel, this.pointRadius, this.svg, + [ + [ "fill", "green" ], [ "fill-opacity", this.fillOpacityPoint ], [ 'stroke-opacity', this.strokeOpacityPoint ], + [ "stroke", "black" ] ]); + } + } }; -ResultsAssemblyGrid.prototype.getTbar = function() { - var _this = this; - var actions = []; +RangeWhiskerGraph.prototype.plotRangeQ1Q3 = function(data, properties) { + var posX = this.left + this.rulerWidth; - actions.push(Ext.create('Ext.Action', { - icon : '../images/add.png', - text : 'Add Macromolecule', - disabled : false, - handler : function(widget, event) { - var window = new MacromoleculeWindow(); - window.onSuccess.attach(function(sender) { - _this.refresh(); - }); - window.draw({}); - } - })); + var boxBordersPointsQ1 = []; + var boxBordersPointsQ3 = []; + var Q2 = []; - actions.push(Ext.create('Ext.Action', { - icon : '../images/add.png', - text : 'Define an Assembly', - disabled : false, - handler : function(widget, event) { - var createAssemblywindow = new CreateAssemblyWindow(); - createAssemblywindow.onSaved.attach(function(evt, args) { - if (args.macromoleculeIds.length > 0) { - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, proposal) { - _this.refresh(); - }); - adapter.saveAssembly(args.assemblyId, args.macromoleculeIds); + for ( var i = 0; i < data.clusters.length; i++) { + var cluster = data.clusters[i]; + /** inter cluster space **/ + posX = posX + this.interClustersSpace; - } + for ( var j = 0; j < cluster.classes.length; j++) { + var ratio = properties.width / (properties.xValues.range); + var coorX = (cluster.x - properties.xValues.min) * ratio + this.rulerWidth; + var boxProperties = { + name : cluster.classes[j].name, + values : cluster.classes[j].values, + minPoint : properties.minPoint, + maxPoint : properties.maxPoint, + x : coorX + this.left, + y : this.top, + width : properties.classWidth, + height : this.height - this.top - this.bottom - this.clusterTitleHeight - this.rulerHeight + }; - }); - createAssemblywindow.draw(_this.experiment); - } - })); + var result = this.calculate(boxProperties.values); + var boxColor = this.getClassColor(boxProperties.name); - return actions; -}; + if (this.isNumber(result.Q1) && this.isNumber(result.Q3)) { + var x = boxProperties.x; + var y = this.pointToPixel(result.Q1, boxProperties); + if (boxBordersPointsQ1[cluster.classes[j].name] == null) { + boxBordersPointsQ1[cluster.classes[j].name] = []; + } + boxBordersPointsQ1[cluster.classes[j].name].push({ + x : x, + y : y + }); -ResultsAssemblyGrid.prototype.refresh = function() { - this.store.loadData(this._prepareData()); -}; + x = boxProperties.x; + y = this.pointToPixel(result.Q3, boxProperties); + if (boxBordersPointsQ3[cluster.classes[j].name] == null) { + boxBordersPointsQ3[cluster.classes[j].name] = []; + } + boxBordersPointsQ3[cluster.classes[j].name].push({ + x : x, + y : y + }); + } else { -ResultsAssemblyGrid.prototype.addCondition = function(record, data_parsed, i) { - function getCondition(record) { - return { - concentration : record.conc, - quality : record.quality, - bufferBeforeFramesMerged : record.bufferBeforeFramesMerged, - bufferAfterFramesMerged : record.bufferAfterFramesMerged, - framesCount : record.framesCount, - framesMerge : record.framesMerge - }; - } + if (this.isNumber(result.Q2)) { - if (data_parsed[i].conditions != null) { - var bufferFound = false; - for ( var index in data_parsed[i].conditions) { - condition = data_parsed[i].conditions[index]; + if (boxBordersPointsQ1[cluster.classes[j].name] == null) { + boxBordersPointsQ1[cluster.classes[j].name] = []; + } - if ((condition.macromoleculeId == record.macromoleculeId) && (condition.bufferId == record.bufferId)) { - data_parsed[i].conditions[index].concentrations.push(getCondition(record)); - bufferFound = true; + if (boxBordersPointsQ3[cluster.classes[j].name] == null) { + boxBordersPointsQ3[cluster.classes[j].name] = []; + } + + boxBordersPointsQ1[cluster.classes[j].name].push({ + x : boxProperties.x, + y : this.pointToPixel(result.Q2, boxProperties) + }); + boxBordersPointsQ3[cluster.classes[j].name].push({ + x : boxProperties.x, + y : this.pointToPixel(result.Q2, boxProperties) + }); + } } } - if (!bufferFound) { - data_parsed[i].conditions.push({ - macromoleculeId : record.macromoleculeId, - bufferId : record.bufferId, - concentrations : [ getCondition(record) ] - }); + } + for ( var classe in boxBordersPointsQ1) { + var points = boxBordersPointsQ1[classe]; + var pointsSVG = ""; + for (var k = 0; k < points.length; k++) { + pointsSVG = Number(points[k].x).toFixed(1) + "," + Number(points[k].y).toFixed(1) + " " + pointsSVG; + } + + points = boxBordersPointsQ3[classe]; + for (var z = points.length - 1; z >= 0; z--) { + pointsSVG = Number(points[z].x).toFixed(1) + "," + Number(points[z].y).toFixed(1) + " " + pointsSVG; } + + SVG.drawPoligon(pointsSVG, this.svg, [ + [ "fill", this.getClassColor(classe) ], [ "opacity", "0.3" ], [ "stroke", "black" ], [ "stroke-width", 1 ] ]); } }; -ResultsAssemblyGrid.prototype.process = function(record, data_parsed) { - if (this.processed[record.macromoleculeId] == null) { - this.processed[record.macromoleculeId] = true; - record.measurementCount = 1; - record.subtractionCount = 1; - record.averageCount = 1; - record.conditions = []; - data_parsed.push(record); - } +RangeWhiskerGraph.prototype.plotWhisters = function(data, properties) { + var colors = [ "yellow", "orange", "green" ]; - for ( var i = 0; i < data_parsed.length; i++) { - if (data_parsed[i].macromoleculeId == record.macromoleculeId) { - data_parsed[i].measurementCount = data_parsed[i].measurementCount + 1; + this.plotRangeQ1Q3(data, properties); + var Q2 = {}; - if (record.subtractionId != null) { - data_parsed[i].subtractionCount = data_parsed[i].subtractionCount + 1; - this.addCondition(record, data_parsed, i); - } + for ( var i = 0; i < data.clusters.length; i++) { + var cluster = data.clusters[i]; + for ( var j = 0; j < cluster.classes.length; j++) { + var ratio = properties.width / (properties.xValues.range); + var coorX = (cluster.x - properties.xValues.min) * ratio + this.left + this.rulerWidth - this.rulerStroke; + var boxProperties = { + name : cluster.classes[j].name, + values : cluster.classes[j].values, + minPoint : properties.minPoint, + maxPoint : properties.maxPoint, + x : coorX, + y : this.top, + width : properties.classWidth, + height : this.height - this.top - this.bottom - this.clusterTitleHeight - this.rulerHeight + }; - if (record.framesMerge != null) { - data_parsed[i].averageCount = data_parsed[i].averageCount + 1; - } + this.drawPoints(boxProperties); - if (record.timeStart != null) { - if (data_parsed[i].timeStart != null) { - if (moment(data_parsed[i].timeStart).format("X") > moment(record.timeStart).format("X")) { - data_parsed[i].timeStart = record.timeStart; - } + /** PLOTTING Q2 **/ + var result = this.calculate(boxProperties.values); + var boxColor = this.getClassColor(boxProperties.name); + if (this.isNumber(result.Q2)) { + if (Q2[boxProperties.name] != null) { + SVG.drawLine(boxProperties.x, this.pointToPixel(result.Q2, boxProperties), Q2[boxProperties.name].x, Q2[boxProperties.name].y, + this.svg, [ [ "stroke", boxColor ], [ "stroke-width", "2" ] ]); + } + Q2[boxProperties.name] = { + x : boxProperties.x, + y : this.pointToPixel(result.Q2, boxProperties) } } } } - return data_parsed; - }; -ResultsAssemblyGrid.prototype._prepareData = function(data) { - var data_parsed = []; - for ( var i = 0; i < data.length; i++) { - data_parsed = this.process(data[i], data_parsed); - } - this.data = data_parsed; - return data_parsed; +RangeWhiskerGraph.prototype.draw = function(targetId, data) { + //this.calculate(data); + this.targetId = targetId; + var properties = (this.getDimensions(data)); + this.svg = SVG.createSVGCanvas(document.getElementById(this.targetId), [ [ "width", this.width ], [ "height", this.height ] ]); + this.plotAxes(properties); + this.plotWhisters(data, properties); }; -/** Given an array of conditions it returns distinct(concentrations) order by concentration and hash map with number of ocurrences**/ -ResultsAssemblyGrid.prototype.parseConcentrations = function(val, conditions, differentConcentration, quality) { - var conditions = []; - var differentConcentration = {}; - var quality = []; - for ( var i = 0; i < val.length; i++) { - var conc = Number(val[i].concentration).toFixed(1); - var quality = Number(val[i].quality).toFixed(2); - if (differentConcentration[conc] == null) { - differentConcentration[conc] = 0; - conditions.push({ - concentration : conc, - quality : [ quality ], - frames : [ { - bufferAfterFramesMerged : val[i].bufferAfterFramesMerged, - bufferBeforeFramesMerged : val[i].bufferBeforeFramesMerged, - framesMerge : val[i].framesMerge, - framesCount : val[i].framesCount - } ] - }); - } else { - /** Add quality **/ - for ( var j = 0; j < conditions.length; j++) { - if (conditions[j].concentration == conc) { - conditions[j].quality.push(quality); - conditions[j].frames.push({ - bufferAfterFramesMerged : val[i].bufferAfterFramesMerged, - bufferBeforeFramesMerged : val[i].bufferBeforeFramesMerged, - framesMerge : val[i].framesMerge, - framesCount : val[i].framesCount - }); - } - } - } +RangeWhiskerGraph.prototype.input = function() { + return DATADOC.getBoxWhikerData(); +}; - differentConcentration[conc] = differentConcentration[conc] + 1; - } - /** sorting concentrations **/ - conditions.sort(function(a, b) { - return a.concentration - b.concentration; +RangeWhiskerGraph.prototype.test = function(targetId) { + var plot = new RangeWhiskerGraph({ + targetId : targetId, + height : 350, + width : 450, + maxBoxWidth : 25 }); - return { - concentrations : conditions, - differentConcentration : differentConcentration - }; + plot.refresh(this.input()); }; + +StdDevDyGraph.prototype.dblclick = DygraphWidget.prototype.dblclick; +StdDevDyGraph.prototype._createHTLMWrapper = DygraphWidget.prototype._createHTLMWrapper; +StdDevDyGraph.prototype.draw = DygraphWidget.prototype.draw; -ResultsAssemblyGrid.prototype.getConditionWarnings = function(condition) { - - var withWarnings = 0; - - for ( var i = 0; i < condition.frames.length; i++) { - if (condition.quality[i] == null) { - withWarnings = withWarnings + 1; - continue; - } else { - if (Number(condition.quality[i]) < this.guinierQuality) { - withWarnings = withWarnings + 1; - continue; - } - } - - if (condition.frames[i].bufferBeforeFramesMerged / condition.frames[i].framesCount < this.framePercentageThreshold|| condition.frames[i].framesMerged / condition.frames[i].framesCount < this.framePercentageThreshold|| condition.frames[i].bufferAfterFramesMerged / condition.frames[i].framesCount < this.framePercentageThreshold) { - withWarnings = withWarnings + 1; - continue; - } +function StdDevDyGraph(targetId, args) { + this.scaled = false; + if (args == null) { + args = {}; } + args.customBars = true; + DygraphWidget.prototype.constructor.call(this, targetId, args); +} +StdDevDyGraph.prototype.input = function () { return { - withWarnings : withWarnings, - withoutWarnings : condition.frames.length - withWarnings + data : [ [ 1, [ 2, 3, 3.5 ], [ 4, 4.2, 5 ] ], [ 2, [ 5, 5.5, 5.7 ], [ 4, 4.2, 5 ] ] ], + colors : [ "blue", "red" ], + labels : [ "", 'data1', 'data2' ] }; }; -ResultsAssemblyGrid.prototype.getFrameHTMLTable = function(warnings) { - var html = ""; - if (warnings.withWarnings > 0) { - html = html + "
" + warnings.withWarnings + - "x
"; - } - - if (warnings.withoutWarnings > 0) { - html = html + "
" + warnings.withoutWarnings + - "x
"; - } - return html; -}; +StdDevDyGraph.prototype.test = function (targetId) { + var dygraphObject = new StdDevDyGraph(targetId, { + width : 500, + height : 400, + xlabel : "xLabel", + showRangeSelector : false + }); -ResultsAssemblyGrid.prototype.createConcentrationRow = function(numberOcu, condition, warnings){ - var html = ""; - if (numberOcu > 1){ - html = html + "" +numberOcu + "x " + BUI.formatConcentration(condition.concentration); - } - else{ - html = html + BUI.formatConcentration(condition.concentration); - } - html = html + ""; - html = html + this.getFrameHTMLTable(warnings); + ""; - html = html + ""; - return html; + dygraphObject.draw(dygraphObject.input().data, dygraphObject.input().colors, dygraphObject.input().labels); }; + + + +function AbinitioGrid(args) { + this.height = null; + this.width = null; + this.id = BUI.id(); + + if (args != null) { + if (args.height != null) { + this.height = args.height; + + if (args.width != null) { + this.width = args.width; + } + } + } + + this.onSelected = new Event(this); +}; + + +AbinitioGrid.prototype.refresh = function(subtractions){ + this.store.loadData(this._prepareData(subtractions)); +}; + +AbinitioGrid.prototype._prepareData = function(subtractions){ + /** Parsing data * */ + var models = []; + for (var l = 0; l < subtractions.length; l++) { + var subtraction = subtractions[l]; + for (var k = 0; k < subtraction.substractionToAbInitioModel3VOs.length; k++) { + var data = subtraction.substractionToAbInitioModel3VOs[k].abinitiomodel3VO; + if (data.averagedModel != null) { + models.push(data.averagedModel); + models[models.length - 1].type = "Reference"; + } + + if (data.shapeDeterminationModel != null) { + models.push(data.shapeDeterminationModel); + models[models.length - 1].type = "Refined"; + } + + if (data.modelList3VO != null) { + if (data.modelList3VO.modeltolist3VOs != null) { + for (var i = 0; i < data.modelList3VO.modeltolist3VOs.length; i++) { + models.push(data.modelList3VO.modeltolist3VOs[i].model3VO); + models[models.length - 1].type = "Model"; + } + } + } + } + } + return models; +}; + +AbinitioGrid.prototype.getPanel = function(){ + var _this = this; + + + var modelFields = [ "modelId", "type", "chiSqrt", "dmax", "firFile", "logFile", "fitFile", "pdbFile", "rfactor", "rg", "volume" ]; + Ext.define('AbinitioModel', { + extend : 'Ext.data.Model', + fields : modelFields + + }); + + /** + * Store in Memory + */ + this.store = Ext.create('Ext.data.Store', { + model : 'AbinitioModel', + autoload : true, + groupField : 'type' + }); + + + var groupingFeature = Ext.create('Ext.grid.feature.Grouping',{ + groupHeaderTpl: '{name} ({rows.length} model{[values.rows.length > 1 ? "s" : ""]})', + startCollapsed: true, + collapsible : true + }); + + this.grid = Ext.create('Ext.grid.Panel', { + collapsible : false, + resizable : true, + features: [groupingFeature], + autoscroll : true, + multiSelect : true, + store : this.store, + height : this.height, + width : this.width, + margin : 10, + columns : [ { + text : "Type", + dataindex : "type", + hidden : true, + renderer : function(a, b, record) { + return record.data.type; + }, + flex : 1 + }, + { + text : "ModelId", + dataindex : "modelId", + hidden : true, + renderer : function(a, b, record) { + return record.data.modelId; + + }, + flex : 1 + }, + + { + text : "chiSqrt", + dataindex : "chiSqrt", + renderer : function(a, b, record) { + if (record.data.dmax != null) { + return BUI.formatValuesUnits(record.data.chiSqrt, "", 12, this.decimals); + } + + }, + flex : 1 + }, + { + text : "Dmax", + dataindex : "dmax", + renderer : function(a, b, record) { + if (record.data.dmax != null) { + return BUI.formatValuesUnits(record.data.dmax, "nm", 12, this.decimals); + } + + }, + flex : 1 + }, { + text : "rFactor", + dataindex : "rfactor", + hidden : true, + renderer : function(a, b, record) { + if (record.data.rfactor != null) { + return record.data.rfactor; + } + }, + flex : 1 + }, { + text : "Rg", + dataindex : "rg", + renderer : function(a, b, record) { + if (record.data.rg != null) { + return BUI.formatValuesUnits(record.data.rg, "nm", 12, this.decimals); + } + + }, + flex : 1 + }, + { + text : "Volume", + dataindex : "volume", + renderer : function(a, b, record) { + if (record.raw.volume != null){ + return BUI.formatValuesUnits(record.raw.volume, '') + " nm3"; + } + }, + flex : 1 + }, + { + text : "PDB", + dataindex : "pdbFile", + renderer : function(a, b, record) { + if (record.data.pdbFile != null){ + return record.data.pdbFile.split("/")[record.data.pdbFile.split("/").length - 1]; + } + }, + flex : 1 + }, { + text : "Fir", + dataindex : "firFile", + renderer : function(a, b, record) { + if (record.data.firFile != null){ + return record.data.firFile.split("/")[record.data.firFile.split("/").length - 1]; + } + }, + flex : 1 + }, { + text : "LOG", + dataindex : "logFile", + hidden : true, + renderer : function(a, b, record) { + if (record.data.logFile != null){ + return record.data.logFile.split("/")[record.data.logFile.split("/").length - 1]; + } + }, + flex : 1 + } + ], + viewConfig : { + enableTextSelection : true, + preserveScrollOnRefresh : true, + stripeRows : true, + listeners : { + 'celldblclick' : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + }, + 'cellclick' : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + var models = []; + for (var i = 0; i < grid.getSelectionModel().selected.items.length; i++) { + models.push(grid.getSelectionModel().selected.items[i].raw); + } + _this.onSelected.notify(models); + } + } + } + }); + return this.grid; + +}; + +function AdditiveGrid(args) { + this.onRemoveButtonClicked = new Event(this); +} -ResultsAssemblyGrid.prototype.getConditionHTMLTable = function(val, style, record) { - var maxNumberColumns = 2; - var html = "
"; - var nColumns = 0; - for ( var r = 0; r < val.length; r++) { - if (nColumns == maxNumberColumns) { - nColumns = 0; - html = html + ""; - } - html = html + ""; - nColumns = nColumns + 1; - } - html = html + "
"; - var value = val[r]; - - var bufferAcronym = (BIOSAXS.proposal.getBufferById(value.bufferId).acronym); - - var parsed = (this.parseConcentrations(value.concentrations)); - var conditions = parsed.concentrations; - var differentConcentration = parsed.differentConcentration; - - /** Checking warnings **/ - var warnings = []; - var concentrationValidPerconcentration = 0; - var measurements = 0; - for ( var i = 0; i < conditions.length; i++) { - measurements = measurements + conditions[i].frames.length; - var warning = this.getConditionWarnings(conditions[i]); - warnings.push(warning); - if (warning.withoutWarnings > 0) { - concentrationValidPerconcentration = concentrationValidPerconcentration + 1; - } - } - - this.validColor = '#E0F8E0'; - this.warningColor = '#F5DA81'; - this.notValidColor = '#F6CED8'; - - var color = this.warningColor; - if (concentrationValidPerconcentration > 2) { - color = this.validColor; - } - /** More measurement need to be done **/ - if (measurements < 3) { - color = this.notValidColor; - } - - html = html + ""; - html = html + ""; - for ( var i = 0; i < conditions.length; i++) { - html = html + this.createConcentrationRow(differentConcentration[conditions[i].concentration], conditions[i], warnings[i]); - } - - html = html + ""; - html = html + "
" + bufferAcronym.toUpperCase() + - "
"; - - html = html + "
"; - return html; +AdditiveGrid.prototype.getBuffer = function() { + return this.buffer; }; -ResultsAssemblyGrid.prototype._getTbar = function() { - function goTo(url) { - window.location = url; - } - +AdditiveGrid.prototype._getActions = function() { var _this = this; - return [ { - icon : '../images/application_view_list.png', - text : 'Multiple Select', - handler : function() { - var specimenSelectorResultGrid = new SpecimenSelectorResultGrid(); - var window = Ext.create('Ext.window.Window', { - title : 'Multiple select', - height : 600, - width : 600, - layout : 'fit', - items : [ specimenSelectorResultGrid.getPanel(_this.data) ], - buttons : [ { - text : 'Go', - handler : function() { - var array = []; - for ( var i = 0; i < specimenSelectorResultGrid.selected.length; i++) { - var row = specimenSelectorResultGrid.selected[i]; - array.push({ - macromoleculeId : row.macromoleculeId, - bufferId : row.bufferId - }); - } - goTo(BUI.getMacromoleculeResultsURLByMultipleSearch(array)); + /** Actions buttons **/ + var actions = []; - } - }, { - text : 'Cancel', - handler : function() { - window.close(); - } - } ] + actions.push(Ext.create('Ext.Action', { + icon : '../images/add.png', + text : 'Add', + disabled : true, + alwaysEnabled : true, + handler : function(widget, event) { + _this.buffer.bufferhasadditive3VOs.push(BIOSAXS_BEANS.getBufferhasAdditive3VO()); + _this.refresh(_this.buffer, _this.experiment); + } + })); - }).show(); + return actions; +}; +AdditiveGrid.prototype.refresh = function(buffer, experiment) { + this.buffer = buffer; + this.experiment = experiment; + if (buffer) { + if (buffer.bufferhasadditive3VOs) { + this.features = buffer.bufferhasadditive3VOs; } - } ]; + } + this.experiment = experiment; + this.store.loadData(this._prepareData(), false); }; -ResultsAssemblyGrid.prototype.getLegendPanel = function() { - return { - html : '
' + BUI.getRectangleColorDIV(this.validColor, 10, 10) + - 'Good quality measurements' + - BUI.getRectangleColorDIV(this.warningColor, 10, 10) + - 'Probably valid with manual processing' + - BUI.getRectangleColorDIV(this.notValidColor, 10, 10) + - 'More measurements need to be done
' - }; -}; -/** Returns the grid **/ -ResultsAssemblyGrid.prototype.getPanel = function(macromolecules) { - var _this = this; +AdditiveGrid.prototype.getAdditives = function() { + var additives = []; + for ( var i = 0; i < this.store.getCount(); i++) { + var bufferHasAdditive = BIOSAXS_BEANS.getBufferhasAdditive3VO(); + var data = this.store.getAt(i).getData(); - this.store = Ext.create('Ext.data.Store', { - fields : [ - 'macromoleculeId', 'macromoleculeAcronym', 'measurementCount', 'subtractionCount', 'averageCount', 'timeStart', 'concentrationArray', - 'bufferConditions', 'conditions' ], - data : this._prepareData(macromolecules) - }); + bufferHasAdditive.additive3VO.name = data.name; + bufferHasAdditive.additive3VO.comments = data.comments; + bufferHasAdditive.additive3VO.additiveType = data.additiveType; - this.store.sort('macromoleculeAcronym'); + bufferHasAdditive.bufferId = this.buffer.bufferId; + bufferHasAdditive.bufferHasAdditiveId = data.bufferHasAdditiveId; + bufferHasAdditive.quantity = data.quantity; + additives.push(bufferHasAdditive); + } - this.grid = Ext.create('Ext.grid.Panel', { - id : this.id, - title : 'Macromolecules', - store : this.store, - tbar : this._getTbar(), - bbar : [ this.getLegendPanel() ], - width : this.width, - height : this.height, - maxHeight : this.maxHeight, - sortableColumns : true, - columns : [ - { - text : '', - dataIndex : 'macromoleculeId', - width : 20, - hidden : false, - renderer : function(val, y, sample) { - return BUI.getRectangleColorDIV(BIOSAXS.proposal.macromoleculeColors[val], 10, 10); - } - }, - { - text : 'Macromolecule', - dataIndex : 'macromoleculeAcronym', - width : 200 + return additives; +}; + +AdditiveGrid.prototype._getFields = function(buffers) { + var columns = [ + + { + header : 'Name', + dataIndex : 'name', + type : 'string', + editor : { + allowBlank : true }, - { - text : 'Buffer Conditions', - dataIndex : 'conditions', - flex : 1, - renderer : function(val, style, record) { - return _this.getConditionHTMLTable(val, style, record); - } + flex : 1 + }, + { + header : 'Type', + name : 'additiveType', + dataIndex : 'additiveType', + editor : { + xtype : 'combobox', + typeAhead : true, + triggerAction : 'all', + selectOnTab : true, + store : BIOSAXS.proposal.getAdditiveTypes(), + lazyRender : true, + listClass : 'x-combo-list-small' }, - { - header : 'Average', - dataIndex : 'percentageCollected', - name : 'percentageCollected', - type : 'string', - hidden : true, - renderer : function(val, y, sample) { - return "
" + - BUI.getProgessBar((sample.raw.averageCount / sample.raw.measurementCount) * 100, sample.raw.averageCount + "/" + - sample.raw.measurementCount) + "
"; - }, - width : 100, - sorter : false + flex : 0.6 + }, + { + header : 'Quantity', + dataIndex : 'quantity', + name : 'quantity', + editor : { + allowBlank : true }, - { - header : 'Subtractions', - dataIndex : 'percentageCollected', - name : 'percentageCollected', - type : 'string', - hidden : true, - renderer : function(val, y, sample) { - return "
"+ BUI.getProgessBar((sample.raw.subtractionCount / sample.raw.measurementCount) * 100, sample.raw.subtractionCount + "/" + - sample.raw.measurementCount) + "
"; - }, - width : 100 - }, { - header : 'Date', - dataIndex : 'timeStart', - name : 'timeStart', - type : 'string', - renderer : function(value, metaData, record, rowIndex, colIndex, store) { - if (record.raw.timeStart != null) { - return moment(record.raw.timeStart).format("MMM Do YY"); + type : 'string', + flex : 1 + }, + { + xtype : 'actioncolumn', + items : [ { + icon : '../images/cancel.png', + tooltip : 'Delete additive', + scope : this, + handler : function(grid, rowIndex, colIndex) { + if ((grid.getStore().getAt(rowIndex).data.bufferHasAdditiveId == null)|| (grid.getStore().getAt(rowIndex).data.bufferHasAdditiveId == "")) { + grid.getStore().removeAt(rowIndex); + } else { + this.onRemoveButtonClicked.notify({ + 'bufferId' : this.buffer.bufferId, + 'bufferHasAdditiveId' : this.store.getAt(rowIndex).data.bufferHasAdditiveId + }); } } - }, { - header : 'Download', - dataIndex : 'percentageCollected', - name : 'percentageCollected', - type : 'string', - renderer : function(val, y, sample) { - return BUI.getZipHTMLByMacromoleculeId(sample.raw.macromoleculeId); - }, - width : 100 - }, + } ] + } ]; + + return columns; +}; + +AdditiveGrid.prototype._prepareData = function() { + var data = []; + if (this.features == null) { + this.features = []; + } + for (var i = 0; i < this.features.length; i++) { + var object = this.features[i]; + object.name = this.features[i].additive3VO.name; + object.additiveType = this.features[i].additive3VO.additiveType; + object.comments = this.features[i].additive3VO.comments; + object.additiveId = this.features[i].additive3VO.additiveId; + data.push(object); + } + return data; +}; - { - id : 'btnResultVisible', - width : 85, - sortable : false, - renderer : function(value, metaData, record, rowIndex, colIndex, store) { - return BUI.getGreenButton('GO'); - } - } ], +AdditiveGrid.prototype.getPanel = function(buffer, experiment) { + this.buffer = buffer; + this.features = buffer.bufferhasadditive3VOs; + this.experiment = experiment; + return this._renderGrid(); +}; + +AdditiveGrid.prototype.getStore = function() { + var _this = this; + var store = Ext.create('Ext.data.Store', { + fields : [ "name", "additiveType", "comments", "additiveId", "bufferHasAdditiveId", "quantity" ], + autoload : false, + data : this._prepareData(), + listeners : { + update : function(store, record) { + record.index = _this.grid.getSelectionModel().getCurrentPosition().row; + _this.buffer.bufferhasadditive3VOs[record.index].additive3VO.name = record.data.name; + _this.buffer.bufferhasadditive3VOs[record.index].additive3VO.additiveType = record.data.additiveType; + _this.buffer.bufferhasadditive3VOs[record.index].additive3VO.comments = record.data.comments; + _this.buffer.bufferhasadditive3VOs[record.index].quantity = record.data.quantity; + } + } + }); + + // store.sort('bufferHasAdditiveId', 'ASC'); + store.loadData(this._prepareData(), false); + return store; +}; + +AdditiveGrid.prototype._renderGrid = function() { + var _this = this; + this.store = this.getStore(); + + var cellEditing = Ext.create('Ext.grid.plugin.CellEditing', { + clicksToEdit : 1 + }); + + this.grid = Ext.create('Ext.grid.Panel', { + dockedItems : [ { + xtype : 'toolbar', + items : this._getActions() + } ], + store : this.store, + height : 230, + title : "Additives", + width : "100%", + columns : _this._getFields(), + plugins : [ cellEditing ], viewConfig : { stripeRows : true, listeners : { - afterrender : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { - }, - celldblclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { - _this.edit(record.data.macromoleculeId); - }, - cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { - if (grid.getGridColumns()[cellIndex].getId() == 'buttonEditMacromolecule') { - _this.edit(record.data.macromoleculeId); - } - if (grid.getGridColumns()[cellIndex].getId() == 'buttonRemoveMacromolecule') { - BUI.showBetaWarning(); - } - if (grid.getGridColumns()[cellIndex].getId() == 'btnResultVisible') { - window.location = BUI.getMacromoleculeResultsURL(record.data.macromoleculeId); - } + itemcontextmenu : function(view, rec, node, index, e) { + e.stopEvent(); + contextMenu.showAt(e.getXY()); + return false; } } + }, + selModel : { + mode : 'SINGLE' } }); - /** Adding the tbar **/ - if (this.tbar) { - this.grid.addDocked({ - xtype : 'toolbar', - items : this.getTbar() - }); - } return this.grid; }; -ResultsAssemblyGrid.prototype.input = function(targetId) { - return { - data : DATADOC.getData_3367(), - proposal : DATADOC.getProposal_3367() - }; +AdditiveGrid.prototype.input = function() { }; -ResultsAssemblyGrid.prototype.test = function(targetId) { - var grid = new ResultsAssemblyGrid({ - height : 600, - searchBar : false, - tbar : false, - btnResultVisible : true, - width : 1000 - }); - BIOSAXS.proposal = new Proposal(grid.input().proposal); - var panel = grid.getPanel(grid.input().data); +AdditiveGrid.prototype.test = function(targetId) { + var grid = new AdditiveGrid(); + var panel = grid.getPanel([]); panel.render(targetId); }; +/** AnalysisGrid **/ +function AnalysisGrid(args) { + var _this = this; -function RigidModelGrid(args) { - this.height = null; - this.width = null; this.id = BUI.id(); + if (Ext.get("mainPanel")){ + this.width = Ext.get("mainPanel").getWidth(); + } + else{ + this.width = this.maxWidth; + } + this.maxWidth = 1500; + + /** Visibles **/ + this.isGuinierTabVisible = true; + this.isGnomTabVisible = true; + this.isPorodTabVisible = true; + this.isScatteringPlotVisible = true; + this.isAbinitioTabVisible = true; + this.showButtonsVisible = true; + this.isI0Visible = true; + + this.margin = null; + this.grouped = true; + + this.hideNulls = false; + this.decimals = 4; + this.height = null; + this.sorters = [ { + property : 'conc', + direction : 'ASC' + } ]; if (args != null) { + if (args.grouped != null) { + this.grouped = args.grouped; + } + + if (args.showButtonsVisible != null) { + this.showButtonsVisible = args.showButtonsVisible; + } + if (args.isI0Visible != null) { + this.isI0Visible = args.isI0Visible; + } + if (args.sorters != null) { + this.sorters = args.sorters; + } + if (args.isScatteringPlotVisible != null) { + this.isScatteringPlotVisible = args.isScatteringPlotVisible; + } + if (args.isGuinierTabVisible != null) { + this.isGuinierTabVisible = args.isGuinierTabVisible; + } + if (args.isGnomTabVisible != null) { + this.isGnomTabVisible = args.isGnomTabVisible; + } + if (args.isPorodTabVisible != null) { + this.isPorodTabVisible = args.isPorodTabVisible; + } + if (args.hideNulls != null) { + this.hideNulls = args.hideNulls; + } if (args.height != null) { this.height = args.height; + } + if (args.width != null) { + this.width = args.width; + } + if (args.maxWidth != null) { + this.maxWidth = args.maxWidth; + } + } +} - if (args.width != null) { - this.width = args.width; +AnalysisGrid.prototype.refresh = function(data, args) { + this.store.loadData(this._prepareData(data), false); + if (args != null){ + if (args.experiment != null){ + this.experiment = args.experiment; + } + } +}; + +AnalysisGrid.prototype._getPorod = function() { + return { + text : 'Porod', + name : 'Porod', + columns : [ + { + text : 'Volume', + dataIndex : 'volumeEdna', + width : 75, + sortable : true, + hidden : !this.isPorodTabVisible, + renderer : function(val, y, sample) { + if (sample.raw.volume != null) + return BUI.formatValuesUnits(sample.raw.volume, '') + " nm3"; + } + }, + { + text : 'MM Vol. est.', + dataIndex : 'volumeEdna', + tooltip : '[Volume/2 - Volume/1.5] (Guinier)', + sortable : true, + width : 95, + hidden : !this.isPorodTabVisible, + renderer : function(val, y, sample) { + if (sample.raw.volume != null) + return Number(sample.raw.volume / 2).toFixed(1) + " - " + Number(sample.raw.volume / 1.5).toFixed(1)+ "kD"; + } + } ] + }; +}; + + +AnalysisGrid.prototype._getFramesColumn = function() { + var _this = this; + return { + text : 'Frames (Averaged/Total)', + dataIndex : 'datacollection', + name : 'datacollection', + sortable : true, + width : 150, + renderer : function(dataCollections, y, data) { + /** Bug of Webservices: frames count were swapped with frames averages **/ + if (data.raw.bufferAfterFramesCount){ + if (data.raw.bufferAfterFramesMerged){ + if (parseInt(data.raw.bufferAfterFramesMerged) > parseInt(data.raw.bufferAfterFramesCount)){ + var aux = parseInt(data.raw.bufferAfterFramesCount); + data.raw.bufferAfterFramesCount= data.raw.bufferAfterFramesMerged; + data.raw.bufferAfterFramesMerged = aux; + } + } + } + + if (data.raw.bufferBeforeFramesCount){ + if (data.raw.bufferBeforeFramesMerged){ + if (parseInt(data.raw.bufferBeforeFramesMerged) > parseInt(data.raw.bufferBeforeFramesCount)){ + var aux = parseInt(data.raw.bufferBeforeFramesCount); + data.raw.bufferBeforeFramesCount= data.raw.bufferBeforeFramesMerged; + data.raw.bufferBeforeFramesMerged = aux; + } + } + + } + + var bufferAcronym = data.raw.bufferAcronym; + var macromoleculeAcronym = data.raw.macromoleculeAcronym; + var bbmerges = data.raw.bufferBeforeFramesMerged; + var molmerges = data.raw.framesMerge; + var bamerges = data.raw.bufferAfterFramesMerged; + var totalframes = data.raw.framesCount; + var bufferId = data.raw.bufferId; + var macromoleculeId = data.raw.macromoleculeId; + var macromoleculeColor = null; + if (_this.experiment != null){ + macromoleculeColor = _this.experiment.macromoleculeColors[data.raw.macromoleculeId]; } - } - } - - this.onSelected = new Event(this); -} - - -RigidModelGrid.prototype.refresh = function(subtractions) { - var data = []; - if (subtractions != null){ - if (subtractions.length > 0){ - for (j in subtractions){ - var subtraction = subtractions[j]; - if (subtraction.rigidBodyModeling3VOs != null){ - for (i in subtraction.rigidBodyModeling3VOs){ - data.push(subtraction.rigidBodyModeling3VOs[i]); + + /** BUG in the database to be fixed **/ + try{ + if (totalframes != null){ + if(molmerges != null){ + if (parseFloat(totalframes) < parseFloat(molmerges)){ + var aux = totalframes; + totalframes = molmerges; + molmerges = aux; + } } } } + catch(e){ + + } + + return BUI.getHTMLTableForFrameAveraged(bufferAcronym, + macromoleculeAcronym, + bbmerges, + molmerges, + bamerges, + totalframes, + bufferId, + macromoleculeId, + macromoleculeColor); } - } - this.store.loadData(data); + }; }; -RigidModelGrid.prototype.getPanel = function() { +AnalysisGrid.prototype._getColumns = function() { var _this = this; - this.store = Ext.create('Ext.data.Store', { - fields : [ 'symmetry', 'subtractionId', 'subUnitConfigFilePath', 'rigidBodyModelingId', 'rigidBodyModelFilePath', 'logFilePath', 'fitFilePath', 'curveConfigFilePath', - 'crossCorrConfigFilePath', 'contactDescriptionFilePath' ], - data : [] - }); - - this.store.sort([ { - property : 'name', - direction : 'ASC' - } ]); + return [ + { + name : 'groupeField', + dataIndex : 'groupeField', + hidden : true - this.panel = Ext.create('Ext.grid.Panel', { - store : this.store, - width : this.width, - height : this.height, - margin : 10, - deferredRender : false, - columns : [ { - text : 'RBM', - dataIndex : 'rigidBodyModelFilePath', - hidden : false, - flex : 1, - renderer : function(val){ - return BUI.getFileName(val); - } - }, { - text : 'Sub Unit Conf.', - dataIndex : 'subUnitConfigFilePath', + }, + { + "header" : "subtractionId", hidden : true, - flex : 1, - renderer : function(val){ - return BUI.getFileName(val); + "dataIndex" : "subtractionId", + "name" : "subtractionId" + }, + { + header : "Macromolecule", + dataIndex : "macromoleculeAcronym", + name : "macromoleculeAcronym", + renderer : function(val, y, sample) { + return '
' + val + '
' + + BUI.formatValuesUnits(sample.raw.conc, "mg/ml", 10, this.decimals) + '
' + + BUI.formatValuesUnits(sample.raw.exposureTemperature, "C", 10, this.decimals) + '
' } - }, { - text : 'Log', - dataIndex : 'logFilePath', + }, + { + header : "Order", + dataIndex : "priorityLevelId", + name : "priorityLevelId", + hidden : true + }, + { + header : "Code", + dataIndex : "code", + name : "code", + hidden : true + }, + { + header : "Conc.", + dataIndex : "conc", + width : 80, + name : "conc", + type : "number", hidden : true, - flex : 1, - renderer : function(val){ - return BUI.getFileName(val); - } - }, { - text : 'Fit', - dataIndex : 'fitFilePath', - hidden : false, - flex : 1, - renderer : function(val){ - return BUI.getFileName(val); - } - }, { - text : 'Curve Conf.', - dataIndex : 'curveConfigFilePath', - hidden : false, - flex : 1, - renderer : function(val){ - return BUI.getFileName(val); + renderer : function(val, y, sample) { + return BUI.formatValuesUnits(val, "mg/ml", 10, this.decimals); } - }, { - text : 'Cross Corr.', - dataIndex : 'crossCorrConfigFilePath', - hidden : false, - flex : 1, - renderer : function(val){ - return BUI.getFileName(val); + }, + { + text : 'Scattering', + width : 100, + dataIndex : 'subtractionId', + name : 'subtractionId', + hidden : !this.isScatteringPlotVisible, + renderer : function(val, y, sample) { + var url = BUI.getURL() + '&type=scattering&subtractionId=' + sample.raw.subtractionId; + var event = "OnClick= window.open('" + url + "')"; + return ''; } - }, { - text : 'Contact Desc.', - dataIndex : 'contactDescriptionFilePath', + }, + { + text : 'Kratky', + width : 100, + dataIndex : 'subtractionId', hidden : true, - flex : 1, - renderer : function(val){ - return BUI.getFileName(val); + name : 'subtractionId', + renderer : function(val, y, sample) { + var url = BUI.getURL() + '&type=kratky&subtractionId=' + sample.raw.subtractionId; + var event = "OnClick= window.open('" + url + "')"; + return ''; } - } ], - - viewConfig : { - enableTextSelection : true, - preserveScrollOnRefresh : true }, - listeners : { - cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { - - }, - afterrender : function() { + this._getFramesColumn(), + { + text : 'File Name', + dataIndex : 'averageFilePath', + name : 'averageFilePath', + sortable : true, + width : 150, + hidden : true, + renderer : function(dataCollections, y, data) { + return BUI.getHTMLTableForPrefixes(data.raw.bufferBeforeAverageFilePath, data.raw.averageFilePath, + data.raw.bufferAfterAverageFilePath); } - } - }); - return this.panel; -}; + }, - -/** - * shows shipments - * - * @height - * @width - * @minHeight - * @btnEditVisible - */ -function ShipmentGrid(args) { - this.id = BUI.id(); - this.height = 100; - this.width = null; - this.minHeight = null; - this.btnEditVisible = true; + { + text : 'Guinier', + name : 'Guinier', + columns : [ + { + text : 'Rg', + dataIndex : 'rgGuinier', + name : 'rgGuinier', + hidden : !this.isGuinierTabVisible, + width : 75, + tooltip : 'In polymer physics, the radius of gyration is used to describe the dimensions of a polymer chain.', + sortable : true, + renderer : function(val, y, sample) { + if (sample.raw.rgGuinier != null) { + /** Show warning if rgGuinier and rgGnom differ more than 10% **/ + if (Math.abs(sample.raw.rgGuinier - sample.raw.rgGnom) > (sample.raw.rgGuinier * 0.1)) { + return "" + BUI.formatValuesUnits(sample.raw.rgGuinier, "") + ""; - if (args != null) { - if (args.height != null) { - this.height = args.height; - } - if (args.width != null) { - this.width = args.width; - } - if (args.minHeight != null) { - this.minHeight = args.minHeight; - } - if (args.btnEditVisible != null) { - this.btnEditVisible = args.btnEditVisible; - } - } -} + } + return BUI.formatValuesUnits(sample.raw.rgGuinier, "nm", 12, this.decimals); + } + } + }, + { + text : 'Points', + dataIndex : 'points', + sortable : true, + width : 100, + type : 'string', + hidden : !this.isGuinierTabVisible, + renderer : function(val, y, sample) { + if ((sample.raw.firstPointUsed == "") || (sample.raw.firstPointUsed == null)) + return; + return "" + sample.raw.firstPointUsed + " - " + sample.raw.lastPointUsed + " (" + + (sample.raw.lastPointUsed - sample.raw.firstPointUsed) + " )"; + } + }, + { + text : 'Quality', + dataIndex : 'quality', + hidden : !this.isGuinierTabVisible, + tooltip : 'Estimated data quality. 1.0 - means ideal quality, 0.0 - unusable data. In table format it is given in percent (100% - ideal quality, 0% - unusable data). Please note that this estimation is based only on the Guinier interval (very low angles).', + width : 60, + sortable : true, + renderer : function(val, y, sample) { + if (sample.raw.quality != null) { + val = sample.raw.quality; + if ((val != null) && (val != "")) { + return "" + (Number(val) * 100).toFixed(2) + " %"; + } + } + } + }, { + text : 'I(0)', + dataIndex : 'I0', + sortable : true, + hidden : !this.isI0Visible, + tooltip : 'Extrapolated scattering intensity at zero angle I(0) (forward scattering)', + width : 75, + type : 'string', + renderer : function(val, y, sample) { + if (sample.raw.I0 != null) { + return BUI.formatValuesErrorUnitsScientificFormat(sample.raw.I0, sample.raw.i0stdev, ""); + } + } + }, { + text : 'Aggregated', + tooltip : "If aggregation was detected from the slope of the data curve at low angles the value is '1', otherwise '0'.", + dataIndex : 'isagregated', + hidden : true, + width : 75, + renderer : function(val, y, sample) { + if ((sample.raw.isagregated != null)) { + if (val == true) { + return "Yes"; + } else { + return "No"; + } + } + } + }, { + text : 'Guinier', + sortable : true, + dataIndex : 'subtractionId', + type : 'string', + width : 100, + hidden : true, + renderer : function(val, y, sample) { -ShipmentGrid.prototype._getColumns = function() { - var _this = this; - var columns = [ - { - text : 'Name', - dataIndex : 'shippingName', - flex : 1 + if (sample.raw.subtractionId != null) { + var url = BUI.getURL() + '&type=guinier&subtractionId=' + sample.raw.subtractionId; + var event = "OnClick= window.open('" + url + "')"; + return ''; + } + } + } ] }, { - header : 'Type', - dataIndex : 'shippingType', - flex : 1, - hidden : true, - renderer : function(val, comp, record) { - if (val != null) { - return val.toUpperCase(); - } + text : 'Gnom', + name : 'Gnom', - } - }, - { - header : 'Status', - type : 'string', - flex : 1, - hidden : false, - renderer : function(comp, val, record) { - if (record.raw.shippingStatus != null) { - if (new String(record.raw.shippingStatus).toUpperCase() == 'OPENED') { - return "" + new String(record.raw.shippingStatus).toUpperCase() + ""; + columns : [ { + text : 'Rg', + dataIndex : 'rgGnom', + type : 'string', + width : 65, + hidden : !this.isGnomTabVisible, + sortable : true, + renderer : function(val, y, sample) { + /** Show warning if rgGuinier and rgGnom differ more than 10% **/ + if (sample.raw.rgGnom != null) { + if (Math.abs(sample.raw.rgGuinier - sample.raw.rgGnom) > (sample.raw.rgGuinier * 0.1)) { + return "" + BUI.formatValuesUnits(sample.raw.rgGnom, "") + ""; + + } + return BUI.formatValuesUnits(sample.raw.rgGnom, "nm"); } - return "" + new String(record.raw.shippingStatus).toUpperCase() + ""; } - } + }, { + text : 'Total', + dataIndex : 'total', + width : 65, + hidden : !this.isGnomTabVisible, + sortable : true, + renderer : function(val, y, sample) { + if (sample.raw.total != null) + return BUI.formatValuesUnits(sample.raw.total, ''); + } + }, { + text : 'Dmax', + dataIndex : 'dmax', + sortable : true, + width : 75, + hidden : !this.isGnomTabVisible, + renderer : function(val, y, sample) { + if (sample.raw.dmax != null) + return BUI.formatValuesUnits(sample.raw.dmax, "") + " nm"; + } + }, { + text : 'P(r)', + sortable : true, + hidden : true, + width : 100, + dataIndex : 'subtractionId', + type : 'string', + renderer : function(val, y, sample) { + var url = BUI.getURL() + '&type=gnom&subtractionId=' + sample.raw.subtractionId; + var event = "OnClick= window.open('" + url + "')"; + return ''; + } + } ] }, + this._getPorod(), { - text : 'Cases', - flex : 1, - hidden : false, - renderer : function(comp, val, record) { - var shipment = BIOSAXS.proposal.getShipmentById(record.data.shippingId); - var container = ""; - if (shipment.dewarVOs.length > 0) { - container = container + ""; - } else { - return "Empty"; - } - return container + "
" + shipment.dewarVOs.length + "x
"; - } - }, { - header : 'Comments', - dataIndex : 'comments', - flex : 1, - hidden : false - }, { - header : 'Creation Date', - dataIndex : 'creationDate', - hidden : true - } ]; - - if (this.btnEditVisible) { - columns.push({ - id : _this.id + 'buttonEdit', - text : '', - hidden : !_this.btnEditVisible, - width : 100, - sortable : false, - renderer : function(value, metaData, record, rowIndex, colIndex, store) { - return BUI.getGreenButton('EDIT'); - } - }); - } - - columns.push({ - id : _this.id + 'buttonRemove', - text : '', - width : 100, - sortable : false, - renderer : function(value, metaData, record, rowIndex, colIndex, store) { - var shipment = BIOSAXS.proposal.getShipmentById(record.data.shippingId); - if (shipment.dewarVOs.length == 0) { - return BUI.getRedButton('REMOVE'); - } - - } - }); - - return columns; -}; - -ShipmentGrid.prototype._getTopButtons = function() { - var _this = this; - /** Actions buttons **/ - var actions = []; - actions.push(Ext.create('Ext.Action', { - icon : '../images/add.png', - text : 'Add Shipment', - handler : function(widget, event) { - //window.location = BUI.getCreateShipmentURL(); - var _this = this; - - var shipmentForm = new ShipmentForm({ - creationMode : true, - showTitle : false - }); - shipmentForm.onSaved.attach(function(sender, shipment) { - _this.showShipmentTabs(shipment, targetId); - }); - - var window = Ext.create('Ext.window.Window', { - title : 'New Shipment', - height : 600, - width : 800, - layout : 'fit', - items : [ shipmentForm.getPanel() ] - }).show(); - - } - })); - return actions; -}; - -ShipmentGrid.prototype.refresh = function(shippings) { - this.features = shippings; - this.store.loadData(this._prepareData(), false); -}; - -ShipmentGrid.prototype._prepareData = function() { - var data = []; - for ( var i = 0; i < this.features.length; i++) { - data.push(this.features[i]); - } - return data; -}; - -ShipmentGrid.prototype.getPanel = function(shipments) { - this.features = shipments; - return this._renderGrid(); -}; - -ShipmentGrid.prototype.edit = function(shippingId) { - window.location = BUI.getShippingURL(shippingId); -}; - -ShipmentGrid.prototype._getStoreFields = function(data) { - var _this = this; - return [ { - name : 'shippingId' - }, { - name : 'shippingName' - }, { - name : 'shippingStatus' - }, { - name : 'shippingType' - }, { - name : 'creationDate' - }, { - name : 'comments' - } ]; -}; - -ShipmentGrid.prototype._renderGrid = function() { - var _this = this; - - /** Store **/ - var data = this._prepareData(); - this.store = Ext.create('Ext.data.Store', { - fields : this._getStoreFields(data), - autoload : true, - data : data - }); - - this.store.loadData(data, false); + text : 'AbInitio Modeling', + name : 'AbInitio_modeling', - this.grid = Ext.create('Ext.grid.Panel', { - title : "Shipping", - icon : '/ispyb/images/plane.gif', - width : this.width, - minWidth : this.minWidth, - height : this.height, - store : this.store, - columns : this._getColumns(), - viewConfig : { - stripeRows : true, - listeners : { - itemdblclick : function(dataview, record, item, e) { - _this.edit(record.raw.shippingId); + columns : [ + { + text : 'NSD', + dataIndex : 'volumeEdna', + width : 100, + sortable : true, + hidden : true, + renderer : function(val, y, sample) { + var url = BUI.getNSDImageURL(sample.raw.modelListId); + var event = "OnClick= window.open('" + url + "')"; + return ''; + } }, - cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { - if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonEdit') { - _this.edit(record.data.shippingId); + { + text : 'Chi2', + dataIndex : 'volumeEdna', + sortable : true, + hidden : true, + width : 100, + renderer : function(val, y, sample) { + var url = BUI.getCHI2ImageURL(sample.raw.modelListId); + var event = "OnClick= window.open('" + url + "')"; + return ''; } + }, + { + text : 'Pdb', + dataIndex : 'volumeEdna', + tooltip : 'Damaver: The program suite DAMAVER is a set of programs to align ab initio models, select the most typical one and build an averaged model. Dammif : Rapid ab initio shape determination by simulated annealing using a single phase dummy atom model. Dammin :Ab initio shape determination by simulated annealing using a single phase dummy atom model', + sortable : true, + width : 125, + hidden : !this.isAbinitioTabVisible, + renderer : function(val, y, sample) { + var html = new String(); + var split = null; + var url = null; + var file = sample.raw.averagedModel; + if (file != null) { + split = file.split("/"); + if (split.length > 0) { + url = BUI.getPdbURL() + '&type=AVERAGED&abInitioModelId=' + sample.raw.abInitioModelId; + html = html + ''+ split[split.length - 1] + '

'; - if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonRemove') { - var shipment = BIOSAXS.proposal.getShipmentById(record.data.shippingId); - if (shipment.dewarVOs.length == 0) { + } + } - var adapter = new BiosaxsDataAdapter(); - _this.grid.setLoading("ISPyB: Removing shipment"); - adapter.onSuccess.attach(function(sender) { - BIOSAXS.proposal.onInitialized.attach(function(sender) { - _this.refresh(BIOSAXS.proposal.getShipments()); - _this.grid.setLoading(false); - }); - BIOSAXS.proposal.init(); - }); + file = sample.raw.rapidShapeDeterminationModel; + if (file != null) { + split = file.split("/"); + if (split.length > 0) { + url = BUI.getPdbURL() + '&type=RAPIDSHAPEDETERMINATIONMODEL&abInitioModelId=' + sample.raw.abInitioModelId; + html = html + '' + split[split.length - 1] + '

'; + } + } - adapter.onError.attach(function(sender) { - alert("Error"); - _this.grid.setLoading(false); - }); - adapter.removeShipment(record.data.shippingId); + file = sample.raw.shapeDeterminationModel; + if (file != null) { + split = file.split("/"); + if (split.length > 0) { + url = BUI.getPdbURL() + '&type=SHAPEDETERMINATIONMODEL&abInitioModelId=' + sample.raw.abInitioModelId; + html = html + ''+ split[split.length - 1] + ''; + } } + return html; } - } - } - }, - selModel : { - mode : 'SINGLE' - } - }); - - var actions = _this._getTopButtons(); - this.grid.addDocked({ - xtype : 'toolbar', - items : actions - }); - - this.grid.getSelectionModel().on({ - selectionchange : function(sm, selections) { - if (selections.length) { - for ( var i = 0; i < actions.length; i++) { - if (actions[i].enable) { - actions[i].enable(); + }, + { + text : 'Damaver', + dataIndex : 'volumeEdna', + hidden : true, + tooltip : 'Damaver: The program suite DAMAVER is a set of programs to align ab initio models, select the most typical one and build an averaged model. ', + sortable : true, + width : 125, + renderer : function(val, y, sample) { + var file = sample.raw.averagedModel; + if (file != null) { + var split = file.split("/"); + if (split.length > 0) { + var url = BUI.getPdbURL() + '&type=AVERAGED&abInitioModelId=' + sample.raw.abInitioModelId; + return '' + split[split.length - 1]+ ''; + } + } + return sample.raw.averagedModel; } - } - } else { - for ( var i = 0; i < actions.length; i++) { - if (actions[i].alwaysEnabled == false) { - if (actions[i].disable) { - actions[i].disable(); + }, + { + text : 'Dammif', + dataIndex : 'volumeEdna', + hidden : true, + tooltip : 'Dammif : Rapid ab initio shape determination by simulated annealing using a single phase dummy atom model', + sortable : true, + width : 125, + renderer : function(val, y, sample) { + var file = sample.raw.rapidShapeDeterminationModel; + if (file != null) { + var split = file.split("/"); + if (split.length > 0) { + var url = BUI.getPdbURL() + '&type=RAPIDSHAPEDETERMINATIONMODEL&abInitioModelId=' + sample.raw.abInitioModelId; + return '' + split[split.length - 1] + ''; + } } + return sample.raw.averagedModel; } - } - } - } - }); - return this.grid; -}; - -ShipmentGrid.prototype.input = function() { - return { - proposal : new MeasurementGrid().input().proposal, - shippings : DATADOC.getShippings_10() - }; -}; - -ShipmentGrid.prototype.test = function(targetId) { - var shipmentGrid = new ShipmentGrid({ - minHeight : 300, - height : 440 - }); - BIOSAXS.proposal = new Proposal(shipmentGrid.input().proposal); - var panel = shipmentGrid.getPanel(targetId); - panel.render(targetId); - shipmentGrid.refresh(shipmentGrid.input().shippings); -}; - -function SpecimenGrid(args) { - this.id = BUI.id(); - this.height = 500; - this.unitsFontSize = 9; - this.editEnabled = false; - this.isPositionColumnHidden = false; - this.removeBtnEnabled = false; - - this.selectionMode = "MULTI"; - this.updateRowEnabled = false; - this.grouped = true; - this.width = 900; - this.title = 'Specimens'; - - this.margin = "0 0 0 0"; -// this.experimentColorBased = false; - - if (args != null) { - if (args.height != null) { - this.height = args.height; - } - - if (args.showTitle == false) { - this.title = null; - } - - if (args.margin == false) { - this.margin = args.margin; - } - - if (args.grouped == false) { - this.grouped = null; - } - - if (args.width != null) { - this.width = args.width; - } - - - if (args.editEnabled != null) { - this.editEnabled = args.editEnabled; - } - if (args.removeBtnEnabled != null) { - this.removeBtnEnabled = args.removeBtnEnabled; - } - if (args.isPositionColumnHidden != null) { - this.isPositionColumnHidden = args.isPositionColumnHidden; - } - if (args.selectionMode != null) { - this.selectionMode = args.selectionMode; - } - if (args.updateRowEnabled != null) { - this.updateRowEnabled = args.updateRowEnabled; - } - - } - this.onClick = new Event(this); - this.onSelected = new Event(this); - this.onRemoved = new Event(this); - this.onSpecimenChanged = new Event(); -} - -SpecimenGrid.prototype._prepareData = function(experiment) { - var data = []; - - var samples = experiment.getSamples(); - for ( var i = 0; i < samples.length; i++) { - var sample = samples[i]; - if (sample.macromolecule3VO != null) { - sample.macromolecule = sample.macromolecule3VO.acronym; - sample.exposureTemperature = []; - sample.macromoleculeId = sample.macromolecule3VO.macromoleculeId; - } + }, + { + text : 'Dammin', + dataIndex : 'volumeEdna', + hidden : true, + tooltip : 'Dammin :Ab initio shape determination by simulated annealing using a single phase dummy atom model', + sortable : true, + width : 125, + renderer : function(val, y, sample) { + var file = sample.raw.shapeDeterminationModel; + if (file != null) { + var split = file.split("/"); + if (split.length > 0) { + var url = BUI.getPdbURL() + '&type=SHAPEDETERMINATIONMODEL&abInitioModelId=' + sample.raw.abInitioModelId; + return '' + split[split.length - 1]+ ''; + } + } + return sample.raw.averagedModel; + } + } ] + }, { + text : 'Time', + dataIndex : 'substractionCreationTime', + name : 'substractionCreationTime', + hidden : true, + width : 80, + sortable : true, + renderer : function(value, metaData, record, rowIndex, colIndex, store) { + try { + if (record.raw.substractionCreationTime != null) { + return moment(record.raw.substractionCreationTime).format('h:mm:ss a'); + } + } catch (e) { + return "NA"; + } + } + }, { + text : 'Date', + dataIndex : 'substractionCreationTime', + name : 'substractionCreationTime', + hidden : true, + width : 80, + sortable : true, + renderer : function(value, metaData, record, rowIndex, colIndex, store) { + try { + if (record.raw.substractionCreationTime != null) { + return moment(record.raw.substractionCreationTime).format("LL"); + } + } catch (e) { + return "NA"; + } + } + }, + this._getButtons()]; +}; - if (sample.sampleplateposition3VO != null) { - if (sample.sampleplateposition3VO.samplePlateId != null) { - sample.samplePlateId = sample.sampleplateposition3VO.samplePlateId; - sample.rowNumber = sample.sampleplateposition3VO.rowNumber; - sample.columnNumber = sample.sampleplateposition3VO.columnNumber; - if (experiment.getSamplePlateById(sample.sampleplateposition3VO.samplePlateId).plategroup3VO != null) { - sample.plateGroupName = experiment.getSamplePlateById(sample.sampleplateposition3VO.samplePlateId).plategroup3VO.name; - sample.samplePlateName = experiment.getSamplePlateById(sample.sampleplateposition3VO.samplePlateId).name + " [" + sample.plateGroupName + "]"; - sample.slotPositionColumn = experiment.getSamplePlateById(sample.sampleplateposition3VO.samplePlateId).slotPositionColumn; +AnalysisGrid.prototype._getButtons = function() { + return { + id : this.id + 'buttonPlot', + name : 'buttonPlot', + hidden : !this.showButtonsVisible, + width : 110, + sortable : false, + renderer : function(value, metaData, sample, rowIndex, colIndex, store) { + var html = ""; + + if (sample.raw.subtractionId) { + html = html + ""; + if (sample.raw.rapidShapeDeterminationModelId != null) { + html = html + ""; } } - } else { - sample.samplePlateName = "Unallocated Specimens"; + return html + "
" + BUI.getGreenButton('Calibration', { + height : 20, + width : 100 + }) + "
" + BUI.getGreenButton('Primary Data Proc.', { + height : 20, + width : 100 + }) + "
" + BUI.getGreenButton('AbInitio Modeling', { + height : 20, + width : 100 + }) + "
"; } - - /** For grouping, because sencha has not option for multiple grouping I add a field to your store with a convert function that concatenates these two fields and then group by that field.**/ - sample.groupIndex = sample.bufferId + sample.macromoleculeId; - var macromolecule = BIOSAXS.proposal.getMacromoleculeById(sample.macromoleculeId); - - sample.acronym = "Buffers"; - if (macromolecule != null) { - sample.acronym = BIOSAXS.proposal.getMacromoleculeById(sample.macromoleculeId).acronym; + }; +}; +AnalysisGrid.prototype._prepareData = function(data) { + if (this.hideNulls) { + var result = []; + for ( var i = 0; i < data.length; i++) { + if (data[i].subtractionId != null) { + data[i].groupeField = data[i].macromoleculeAcronym + " " + data[i].bufferAcronym; + result.push(data[i]); + } } - - sample.buffer = experiment.getBufferById(sample.bufferId); - - sample.volumeToLoad = experiment.getVolumeToLoadBySampleId(sample.sampleId); - data.push(sample); + return result; + } else { + return data; } - return data; }; -SpecimenGrid.prototype.deselectAll = function() { - this.grid.getSelectionModel().deselectAll(); -}; +AnalysisGrid.prototype.getPanel = function(data) { + var _this = this; + var columns = this._getColumns(); -SpecimenGrid.prototype.selectById = function(specimenId) { - this.grid.getSelectionModel().deselectAll(); - for ( var i = 0; i < this.grid.getStore().data.items.length; i++) { - var item = this.grid.getStore().data.items[i].raw; - if (item.specimenId == specimenId) { - this.grid.getSelectionModel().select(i); - } - } -}; + var fields = JSON.parse(JSON.stringify(columns)); + fields.push({ + name : 'experimentId', + dataIndex : 'experimentId' -SpecimenGrid.prototype.getStore = function() { - return this.store; -}; + }); + this.store = Ext.create('Ext.data.Store', { + fields : fields, + autoload : true, + data : this._prepareData(data), + groupField : 'groupeField' + }); -SpecimenGrid.prototype.getPlugins = function() { - var _this = this; + this.store.sort(this.sorters); - var plugins = []; + var features = []; - if (this.updateRowEnabled) { - plugins.push(Ext.create('Ext.grid.plugin.RowEditing', { - clicksToEdit : 1, + if (this.grouped) { + features.push({ + ftype : 'grouping', + groupHeaderTpl : '{name}', + hideGroupedHeader : false, + startCollapsed : false + }); + } + + this.grid = Ext.create('Ext.grid.Panel', { + style : { + padding : 5 + }, + maxWidth : this.maxWidth, + width : this.width, + height : this.height, + store : this.store, + columns : columns, + resizable : true, + features : features, + viewConfig : { + preserveScrollOnRefresh : true, + stripeRows : true, listeners : { - validateedit : function(grid, e) { - var measurements = []; + cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { - if (e.newValues.bufferId != e.record.raw.bufferId) { - /** If buffer has changed we have to change all the specimens sharing same datacollection **/ - var dataCollections = []; - if (e.record.raw.macromoleculeId == null) { - dataCollections = dataCollections.concat(_this.experiment.getDataCollectionsBySpecimenId(e.record.raw.specimenId)); - } else { - var sampleDataCollections = _this.experiment.getDataCollectionsBySpecimenId(e.record.raw.specimenId); - for ( var i = 0; i < sampleDataCollections.length; i++) { - var sampleDataCollection = sampleDataCollections[i]; - if (sampleDataCollection != null) { - for ( var j = 0; j < sampleDataCollection.measurementtodatacollection3VOs.length; j++) { - var measurementTODc = sampleDataCollection.measurementtodatacollection3VOs[j]; - if (measurementTODc.dataCollectionOrder == 1) { - dataCollections = dataCollections.concat(_this.experiment.getDataCollectionsBySpecimenId(_this.experiment - .getMeasurementById(measurementTODc.measurementId).specimenId)); - } - } - } - } - } - var i = null; - for ( i = 0; i < dataCollections.length; i++) { - var dataCollection = dataCollections[i]; - var specimens = _this.experiment.getSpecimenByDataCollectionId(dataCollection.dataCollectionId); - measurements = measurements.concat(specimens); + if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonPlot') { + if (e.target.defaultValue == 'AbInitio Modeling') { + var url = BUI.getPDBVisualizerURL(record.raw.averagedModelId, record.raw.subtractionId, record.raw.experimentId); + window.open(url, "_blank"); } - for ( i = 0; i < measurements.length; i++) { - var measurement = measurements[i]; - var specimen = _this.experiment.getSpecimenById(measurement.specimenId); - specimen.bufferId = e.newValues.bufferId; - new BiosaxsDataAdapter().saveSpecimen(specimen, _this.experiment); + if (e.target.defaultValue == 'Primary Data Proc.') { + _this._edit(record.raw); } - } - - /** Setting values **/ - e.record.raw.concentration = e.newValues.concentration; - e.record.raw.volume = e.newValues.volume; - /** Position **/ - if (e.record.raw.sampleplateposition3VO != null) { - var samplePlate = _this.experiment.getSamplePlateBySlotPositionColumn(e.newValues.slotPositionColumn); - if (samplePlate != null) { - e.record.raw.sampleplateposition3VO = { - columnNumber : e.newValues.columnNumber, - rowNumber : e.newValues.rowNumber, - samplePlateId : samplePlate.samplePlateId - }; - } - } else { - if (e.newValues.slotPositionColumn != null) { - var samplePlate = _this.experiment.getSamplePlateBySlotPositionColumn(e.newValues.slotPositionColumn); - if (samplePlate != null) { - e.record.raw.sampleplateposition3VO = { - columnNumber : e.newValues.columnNumber, - rowNumber : e.newValues.rowNumber, - samplePlateId : samplePlate.samplePlateId - }; - } + if (e.target.defaultValue == 'Calibration') { + _this._showCalibration(record.raw); } } - - var macromoleculeId = e.record.data.macromoleculeId; - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, specimen) { - /** Because macromolecule3VO is fecthed LAZY **/ - if (macromoleculeId != null) { - specimen.macromolecule3VO = BIOSAXS.proposal.getMacromoleculeById(macromoleculeId); - } - _this.onSpecimenChanged.notify(specimen); - _this.grid.setLoading(false); - }); - adapter.onError.attach(function() { - alert("Error"); - _this.grid.setLoading(false); - }); - _this.grid.setLoading(); - adapter.saveSpecimen(e.record.raw, _this.experiment); } } - })); - } - return plugins; + }, + selModel : { + mode : 'SINGLE' + } + }); + + return this.grid; + +}; + +AnalysisGrid.prototype._openVisualizarBySubstractionId = function(record) { + //experimentId, subtractionId) { + var experimentId = record.experimentId; + var subtractionId = record.subtractionId; + + var _this = this; + var adapter = new BiosaxsDataAdapter(); + this.subtractionId = subtractionId; + + adapter.onSuccess.attach(function(sender, data) { + var experiment = new Experiment(data); + var experimentList = new ExperimentList([ experiment ]); + var subtraction = experiment.getSubtractionById(_this.subtractionId); + _this.grid.setLoading(false); + _this._openVisualizer(experimentList, subtraction, record); + }); + this.grid.setLoading("ISPyB: Fetching experiment"); + adapter.getExperimentById(experimentId, "MEDIUM"); }; -SpecimenGrid.prototype._getRowCombo = function() { - var data = []; - for ( var i = 1; i <= 8; i++) { - data.push({ - rowNumber : i, - name : BUI.getSamplePlateLetters()[i - 1] +AnalysisGrid.prototype._edit = function(record) { + var experimentId = record.experimentId; + var _this = this; + + if (BIOSAXS.proposal.macromolecules == null) { + this.grid.setLoading("ISPyB: Fetching proposal"); + BIOSAXS.proposal.onInitialized.attach(function(sender, data) { + _this._openVisualizarBySubstractionId(record); }); + BIOSAXS.proposal.init(); + } else { + this._openVisualizarBySubstractionId(record); } +}; - var positionsStore = Ext.create('Ext.data.Store', { - fields : [ 'rowNumber', 'name' ], - data : data - }); - return Ext.create('Ext.form.ComboBox', { - store : positionsStore, - queryMode : 'local', - displayField : 'name', - valueField : 'rowNumber' +AnalysisGrid.prototype._showCalibration = function(row) { + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, data) { + Ext.create('Ext.window.Window', { + title : 'Calibration', + width : 900, + resizable : true, + height : 400, + modal : true, + frame : false, + draggable : true, + closable : true, + autoscroll : true, + paddin : 5, + layout : { + type : 'vbox', + align : 'stretch' + }, + items : [ new AnalysisGrid({ + isGuinierTabVisible : false, + isGnomTabVisible : false, + isPorodTabVisible : false, + isAbinitioTabVisible : false, + isI0Visible : true, + showButtonsVisible : false, + height : 350, + sorters : [ { + property : 'experimentId', + direction : 'DESC' + } ] + }).getPanel(data) ] + }).show(); }); + adapter.getAnalysisCalibrationByProposalId(); }; -SpecimenGrid.prototype._getColumnCombo = function() { - var data = []; - for ( var i = 1; i <= 12; i++) { - data.push({ - columnNumber : i - }); +AnalysisGrid.prototype._openVisualizer = function(experimentList, subtraction, record) { + var merges = experimentList.getMergesByDataCollectionId(subtraction.dataCollectionId); + var mergeIdList = []; + for ( var i = 0; i < merges.length; i++) { + mergeIdList.push(merges[i].mergeId); } - var positionsStore = Ext.create('Ext.data.Store', { - fields : [ 'columnNumber' ], - data : data + var dataAdapter = new BiosaxsDataAdapter(); + dataAdapter.onSuccess.attach(function(sender, merges) { + var dataCollectionCurveVisualizer = new DataCollectionCurveVisualizer(); + dataCollectionCurveVisualizer.draw(); + dataCollectionCurveVisualizer.refresh(merges, experimentList, subtraction.dataCollectionId, record); }); - return Ext.create('Ext.form.ComboBox', { - store : positionsStore, - queryMode : 'local', - displayField : 'columnNumber', - valueField : 'columnNumber' + dataAdapter.onError.attach(function(sender, error) { }); + + dataAdapter.getMergesByIdsList(mergeIdList); }; -SpecimenGrid.prototype._getSlotColumBombo = function() { - if (this.experiment){ - var length = this.experiment.getSamplePlates().length; - - var data = []; - for ( var i = 1; i <= length; i++) { - data.push({ - slotPositionColumn : i - }); - } +AnalysisGrid.prototype.input = function() { + return { + data : DATADOC.getData_3367().slice(20, 30),//[{"total":"0.447505552082","bufferBeforeMeasurementId":22150,"sampleMergeId":12373,"averagedModelId":43636,"I0":"32.50776","proposalCode":"OPD","averagedModel":"/data/pyarch/bm29/opd29/1137/1d/damaver.pdb","bufferAfterMergeId":12374,"framesCount":"10","bufferBeforeMergeId":12372,"averageFilePath":" /data/pyarch/bm29/opd29/1137/1d/MG386_016_ave.dat","bufferAfterMeasurementId":22152,"rgGuinier":"2.96883","subtractedFilePath":" /data/pyarch/bm29/opd29/1137/1d/MG386_016_sub.dat","firstPointUsed":"30","chi2RgFilePath":"/data/pyarch/bm29/opd29/1137/1d/chi2_R.png","abInitioModelId":272,"macromoleculeId":112,"code":"_20.0_1.25","transmission":"100.0","timeStart":"2013-07-17 17:10:25.743734","bufferAcronym":"MES","quality":"0.87091","shapeDeterminationModelId":43638,"expermientComments":"[BsxCube] Generated from BsxCube","bufferId":707,"isagregated":"False","dmax":"10.390905","bufferBeforeAverageFilePath":" /data/pyarch/bm29/opd29/1137/1d/MG386_015_ave.dat","exposureTemperature":"20.0","subtractionId":5165,"conc":"1.25","rapidShapeDeterminationModel":"/data/pyarch/bm29/opd29/1137/1d/damfilt.pdb","experimentCreationDate":"Jul 17, 2013 5:08:37 PM","lastPointUsed":"92","modelListId":272,"framesMerge":"10","rapidShapeDeterminationModelId":43637,"bufferAfterAverageFilePath":" /data/pyarch/bm29/opd29/1137/1d/MG386_017_ave.dat","nsdFilePath":"/data/pyarch/bm29/opd29/1137/1d/nsd.png","bufferAfterFramesMerged":"10","experimentId":1137,"macromoleculeAcronym":"MG386","i0stdev":"0.05726128","sampleMeasurementId":22151,"measurementComments":"[1] MES","priorityLevelId":2,"bufferBeforeFramesMerged":"10","substractionCreationTime":"Jul 17, 2013 5:12:32 PM","proposalNumber":"29","rgGnom":"3.05242714339","volume":"44.1406","shapeDeterminationModel":"/data/pyarch/bm29/opd29/1137/1d/dammin.pdb","comments":null},{"total":"0.582641941147","bufferBeforeMeasurementId":22152,"sampleMergeId":12375,"averagedModelId":43809,"I0":"31.4366153846","proposalCode":"OPD","averagedModel":"/data/pyarch/bm29/opd29/1137/1d/damaver.pdb","bufferAfterMergeId":12376,"framesCount":"10","bufferBeforeMergeId":12374,"averageFilePath":" /data/pyarch/bm29/opd29/1137/1d/MG386_018_ave.dat","bufferAfterMeasurementId":22155,"rgGuinier":"2.95541","subtractedFilePath":" /data/pyarch/bm29/opd29/1137/1d/MG386_018_sub.dat","firstPointUsed":"21","chi2RgFilePath":"/data/pyarch/bm29/opd29/1137/1d/chi2_R.png","abInitioModelId":273,"macromoleculeId":112,"code":"_20.0_0.65000000000000002","transmission":"100.0","timeStart":"2013-07-17 17:12:55.837462","bufferAcronym":"MES","quality":"0.870164","shapeDeterminationModelId":43811,"expermientComments":"[BsxCube] Generated from BsxCube","bufferId":707,"isagregated":"False","dmax":"10.343935","bufferBeforeAverageFilePath":" /data/pyarch/bm29/opd29/1137/1d/MG386_017_ave.dat","exposureTemperature":"20.0","subtractionId":5166,"conc":"0.65000000000000002","rapidShapeDeterminationModel":"/data/pyarch/bm29/opd29/1137/1d/damfilt.pdb","experimentCreationDate":"Jul 17, 2013 5:08:37 PM","lastPointUsed":"80","modelListId":273,"framesMerge":"10","rapidShapeDeterminationModelId":43810,"bufferAfterAverageFilePath":" /data/pyarch/bm29/opd29/1137/1d/MG386_019_ave.dat","nsdFilePath":"/data/pyarch/bm29/opd29/1137/1d/nsd.png","bufferAfterFramesMerged":"10","experimentId":1137,"macromoleculeAcronym":"MG386","i0stdev":"0.100250461538","sampleMeasurementId":22154,"measurementComments":"[2] MES","priorityLevelId":4,"bufferBeforeFramesMerged":"10","substractionCreationTime":"Jul 17, 2013 5:15:02 PM","proposalNumber":"29","rgGnom":"2.9963404542","volume":"42.2255","shapeDeterminationModel":"/data/pyarch/bm29/opd29/1137/1d/dammin.pdb","comments":null}], + proposal : new ResultsAssemblyGrid().input().proposal + }; +}; + +AnalysisGrid.prototype.test = function(targetId) { + var analysisGrid = new AnalysisGrid(); + BIOSAXS.proposal = new Proposal(analysisGrid.input().proposal); + var panel = analysisGrid.getPanel(analysisGrid.input().data); + panel.render(targetId); +}; + + +function HPLCAnalysisGrid(args) { + AnalysisGrid.prototype.constructor.call(this, args); +} - var positionsStore = Ext.create('Ext.data.Store', { - fields : [ 'slotPositionColumn' ], - data : data - }); +HPLCAnalysisGrid.prototype._edit = AnalysisGrid.prototype._edit; +HPLCAnalysisGrid.prototype._getColumns = AnalysisGrid.prototype._getColumns; +HPLCAnalysisGrid.prototype._openVisualizarBySubstractionId = AnalysisGrid.prototype._openVisualizarBySubstractionId; +HPLCAnalysisGrid.prototype._openVisualizer = AnalysisGrid.prototype._openVisualizer; +HPLCAnalysisGrid.prototype._prepareData = AnalysisGrid.prototype._prepareData; +HPLCAnalysisGrid.prototype._showCalibration = AnalysisGrid.prototype._showCalibration; +HPLCAnalysisGrid.prototype.getPanel = AnalysisGrid.prototype.getPanel; +HPLCAnalysisGrid.prototype.input = AnalysisGrid.prototype.input; +HPLCAnalysisGrid.prototype.test = AnalysisGrid.prototype.test; +HPLCAnalysisGrid.prototype.refresh = AnalysisGrid.prototype.refresh; +HPLCAnalysisGrid.prototype._getPorod = AnalysisGrid.prototype._getPorod; + +HPLCAnalysisGrid.prototype._getButtons = function() { + return { + id : this.id + 'buttonPlot', + name : 'buttonPlot', + hidden : !this.showButtonsVisible, + width : 110, + sortable : false, + renderer : function(value, metaData, sample, rowIndex, colIndex, store) { + var html = ""; - return Ext.create('Ext.form.ComboBox', { - store : positionsStore, - queryMode : 'local', - displayField : 'slotPositionColumn', - valueField : 'slotPositionColumn' - }); + if (sample.raw.subtractionId) { + if (sample.raw.rapidShapeDeterminationModelId != null) { + html = html + ""; + } + } + return html + "
" + BUI.getGreenButton('AbInitio Modeling', { + height : 20, + width : 100 + }) + "
"; + } + }; +}; + +HPLCAnalysisGrid.prototype._getFramesColumn = function() { + return { + text : 'Frames', + dataIndex : 'datacollection', + name : 'datacollection', + sortable : true, + width : 150, + renderer : function(dataCollections, y, data) { + molmerges = data.raw.framesMerge; + totalframes = data.raw.framesCount; + return molmerges +" - "+ totalframes; + } + }; +}; + + + + +/** + * Rigid body grid to show PDB, symmetry and multiplicity + * + * + * #onUploadFile click on upload file + */ +function AprioriRigidBodyGrid(args) { + + this.height = 250; + this.btnEditVisible = true; + this.btnRemoveVisible = true; + + if (args != null) { + if (args.height != null) { + this.height = args.height; + } + if (args.btnEditVisible != null) { + this.btnEditVisible = args.btnEditVisible; + } + if (args.btnRemoveVisible != null) { + this.btnRemoveVisible = args.btnRemoveVisible; + } + } + + /** Events **/ + this.onUploadFile = new Event(this); + this.onRemove = new Event(this); +} + +AprioriRigidBodyGrid.prototype._getColumns = function() { +}; + +AprioriRigidBodyGrid.prototype._getTopButtons = function() { + var _this = this; + /** Actions buttons **/ + var actions = []; + + /** ADD BUTTON **/ + actions.push(Ext.create('Ext.Action', { + icon : '../images/add.png', + text : 'Add', + disabled : false, + handler : function(widget, event) { + _this.onAddButtonClicked.notify(); + } + })); + + return actions; +}; + +AprioriRigidBodyGrid.prototype.refresh = function(macromolecule) { + this.macromolecule = macromolecule; + if (macromolecule != null){ + this.pdbStore.loadData(macromolecule.structure3VOs); + } +}; + +AprioriRigidBodyGrid.prototype._prepareData = function() { + var data = []; + for ( var i = 0; i < this.features.length; i++) { + data.push(this.features[i]); + } + return data; +}; + +AprioriRigidBodyGrid.prototype._getPlugins = function() { + var _this = this; + var plugins = []; + plugins.push(Ext.create('Ext.grid.plugin.RowEditing', { + clicksToEdit : 1, + listeners : { + validateedit : function(grid, e) { + /** Comments are always updatable* */ + e.record.raw.symmetry = e.newValues.symmetry; + e.record.raw.multiplicity = e.newValues.multiplicity; + + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, proposal) { + BIOSAXS.proposal.setItems(proposal); + _this.refresh(BIOSAXS.proposal.getMacromoleculeById(_this.macromolecule.macromoleculeId)); + _this.panel.setLoading(false); + }); + adapter.onError.attach(function() { + alert("Error"); + }); + + _this.panel.setLoading(); + adapter.saveStructure(e.record.raw); + } + } + })); + return plugins; +}; + +AprioriRigidBodyGrid.prototype.getPanel = function() { + var _this = this; + + this.pdbStore = Ext.create('Ext.data.Store', { + fields : [ 'filePath', 'structureId', 'structureType', 'symmetry', 'structureId', 'name', 'multiplicity' ], + groupField : 'structureType', + sorters : { + property : 'structureId', + direction : 'DESC' + } + }); + + var groupingFeature = Ext.create('Ext.grid.feature.Grouping', { + groupHeaderTpl : Ext.create('Ext.XTemplate', + "
{name:this.formatName}
", { + formatName : function(name) { + return name; + } + }), + hideGroupedHeader : true, + startCollapsed : false + }); + + this.panel = Ext.create('Ext.grid.Panel', { + margin : "15 10 0 10", + height : this.height, + store : this.pdbStore, + plugins : _this._getPlugins(), + tbar : [ { + text : 'Add Modeling Option (PDB)', + icon : '../images/add.png', + handler : function() { + _this.onUploadFile.notify('PDB', 'Upload PDB File'); + } + } + + ], + columns : [ + { + text : "structureId", + flex : 0.2, + hidden : true, + dataIndex : 'structureId', + sortable : true + }, + { + text : "File", + flex : 0.5, + dataIndex : 'filePath', + sortable : true, + hidden : true + }, + { + text : "PDB", + flex : 0.4, + dataIndex : 'name', + sortable : true + }, + { + text : "Symmetry", + flex : 0.2, + dataIndex : 'symmetry', + sortable : true, + editor : { + xtype : 'combobox', + typeAhead : true, + triggerAction : 'all', + selectOnTab : true, + store : [ [ "P1", "P1" ], [ "P2", "P2" ], [ "P3", "P3" ], [ "P4", "P4" ], [ "P5", "P5" ], [ "P6", "P6" ], [ "P32", "P32" ], [ "P42", "P42" ], + [ "P52", "P52" ], [ "P62", "P62" ], [ "P222", "P222" ] ], + } + }, { + text : "Multiplicity", + flex : 0.2, + dataIndex : 'multiplicity', + sortable : true, + editor : { + xtype : 'textfield' + } + + }, { + text : "Subunit", + flex : 0.2, + dataIndex : 'isSubunit', + sortable : true, + hidden : true + }, { + text : "Type", + flex : 0.2, + dataIndex : 'structureType', + sortable : true, + hidden : true + }, + + { + id : this.id + 'REMOVE', + flex : 0.2, + sortable : false, + renderer : function(value, metaData, record, rowIndex, colIndex, store) { + return BUI.getRedButton('REMOVE'); + } + }, ], + + listeners : { + itemdblclick : function(dataview, record, item, e) { + _this._editExperiment(record.raw.experimentId); + }, + cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + + if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'REMOVE') { + var dataAdapter = new BiosaxsDataAdapter(); + dataAdapter.onSuccess.attach(function() { + _this.panel.setLoading(false); + _this.onRemove.notify(); + }); + _this.panel.setLoading("Removing PDB file"); + dataAdapter.removeStructure(record.data.structureId); + } + + } + }, + viewConfig : { + getRowClass : function(record, rowIdx, params, store) { + if (record.raw.isSubunit != null) { + return "blue-row"; + } + } + } + }); + + return this.panel; +}; + + + + +AprioriRigidBodyGrid.prototype.input = function() { + return { + proposal : DATADOC.getProposal_10(), + dewars : DATADOC.getDewars_10() + + }; +}; + +AprioriRigidBodyGrid.prototype.test = function(targetId) { + var AprioriRigidBodyGrid = new AprioriRigidBodyGrid({ + height : 150 + }); + BIOSAXS.proposal = new Proposal(AprioriRigidBodyGrid.input().proposal); + var panel = AprioriRigidBodyGrid.getPanel(AprioriRigidBodyGrid.input().dewars); + panel.render(targetId); + +}; + +/** + * It shows buffer grid with a top bar with "Add" button + * + * @height + * @searchBar + * @collapsed + * @width + */ +function BufferGrid(args) { + this.height = 500; + this.searchBar = false; + this.tbar = false; + this.collapsed = false; + this.id = BUI.id(); + + if (args != null) { + if (args.height != null) { + this.height = args.height; + } + if (args.searchBar != null) { + this.searchBar = args.searchBar; + } + + if (args.tbar != null) { + this.tbar = args.tbar; + } + + if (args.collapsed != null) { + this.collapsed = args.collapsed; + } + + if (args.width != null) { + this.width = args.width; + } } +} + +BufferGrid.prototype._edit = function(bufferId) { + var _this = this; + var window = new BufferWindow(); + window.onSuccess.attach(function(sender, proposal) { + _this.store.loadData(BIOSAXS.proposal.getBuffers()); + }); + window.draw(BIOSAXS.proposal.getBufferById(bufferId)); }; -SpecimenGrid.prototype.getPanelByExperiment = function(experiment) { - this.experiment = experiment; - var data = this._prepareData(experiment); - return this.getPanel(data); +BufferGrid.prototype.refresh = function(buffers) { + this.store.loadData(this._prepareData(buffers), false); }; -SpecimenGrid.prototype.refresh = function(experiment) { - this.experiment = experiment; - var data = this._prepareData(experiment); - this.store.loadData(data); +BufferGrid.prototype._prepareData = function(buffers) { + return buffers; }; -SpecimenGrid.prototype.getPanel = function() { +BufferGrid.prototype._getTbar = function() { var _this = this; - this.store = Ext.create('Ext.data.Store', { - fields : [ - 'buffer', 'bufferId', 'code', 'macromolecule', 'acronym', 'macromoleculeId', 'concentration', 'volume', 'samplePlateId', - 'slotPositionColumn', 'rowNumber', 'columnNumber', 'groupIndex' ], - data : [], - groupField : 'acronym' - }); - this.store.sort([ { - property : 'concentration', - direction : 'ASC' - }, { - property : 'buffer', - direction : 'ASC' - } ]); + var actions = []; - var selModel = Ext.create('Ext.selection.RowModel', { - allowDeselect : true, - mode : this.selectionMode, - listeners : { - selectionchange : function(sm, selections) { - var selected = []; - for ( var i = 0; i < selections.length; i++) { - selected.push(selections[i].raw); - } - _this.onSelected.notify(selected); - } + actions.push(Ext.create('Ext.Action', { + icon : '../images/add.png', + text : 'Add buffer', + disabled : false, + handler : function(widget, event) { + var window = new BufferWindow(); + window.onSuccess.attach(function(sender) { + _this.store.loadData(BIOSAXS.proposal.getBuffers()); + }); + window.draw({}); } - }); - - var features = []; - - if (this.grouped) { - features.push({ - ftype : 'grouping', - groupHeaderTpl : '{name}', - hideGroupedHeader : false, - startCollapsed : false, - id : 'myGroupedStore' - }); - } + })); + return actions; +}; - this.grid = Ext.create( - 'Ext.grid.Panel', - { - title : this.title, - height : this.height, - width : this.width, - selModel : selModel, - store : this.store, - features : features, - margin : this.margin, - plugins : this.getPlugins(), - columns : [ - { - text : '', - dataIndex : 'macromolecule', - width : 20, - renderer : function(val, y, sample) { - var macromoleculeId = null; - if (sample.raw.macromolecule3VO != null) { - macromoleculeId = sample.raw.macromolecule3VO.macromoleculeId; - } - else{ - macromoleculeId = sample.raw.macromoleculeId; - } - - if (macromoleculeId == null) return; - return BUI.getRectangleColorDIV(_this.experiment.macromoleculeColors[macromoleculeId], 10, 10); - } - }, - { - text : 'Macromolecule', - dataIndex : 'macromolecule', - width : 100 - }, - { - text : '', - dataIndex : 'buffer', - width : 20, - renderer : function(val, y, sample) { - var color = "black"; - if (sample.raw.bufferId != null) { - if (_this.experiment.getDataCollectionsBySpecimenId(sample.raw.specimenId)[0] != null){ - color = _this.experiment.getSpecimenColorByBufferId(_this.experiment.getMeasurementById(_this.experiment.getDataCollectionsBySpecimenId(sample.raw.specimenId)[0].measurementtodatacollection3VOs[0].measurementId).specimenId); - } - return BUI.getRectangleColorDIV(color, 10, 10); - } - } - }, { - text : 'Buffer', - dataIndex : 'bufferId', - width : 140, - renderer : function(val, y, sample) { - if (sample.raw.bufferId != null) { - return BIOSAXS.proposal.getBufferById(val).acronym; - } - }, - editor : BIOSAXS_COMBOMANAGER.getComboBuffers(BIOSAXS.proposal.getBuffers(), { - noLabel : true, - width : 300 - }) - }, { - text : 'Conc.', - dataIndex : 'concentration', - width : 100, - editor : { - allowBlank : false - }, - renderer : function(val, meta, sample) { - if (isNaN(val)) { - meta.tdCls = 'yellow-cell'; - return val; - } else { - if (val != 0) { - return BUI.formatValuesUnits(val, 'mg/ml', { - fontSize : 16, - decimals : 3, - unitsFontSize : this.unitsFontSize - }); - } else { - return; - } - } - } - }, { - text : 'Vol. Well', - dataIndex : 'volume', - width : 70, - editor : { - allowBlank : true - }, - renderer : function(val, y, sample) { - return BUI.formatValuesUnits(sample.data.volume, 'µl', { - fontSize : 12, - decimals : 2, - unitsFontSize : this.unitsFontSize - }); - } - }, { - text : 'Position', - hidden : true, - flex : 1, - renderer : function(val, y, sample) { - return BUI.getSamplePositionHTML(sample.raw, _this.experiment); - } - }, { - text : 'samplePlateId', - dataIndex : 'samplePlateId', - hidden : true - }, { - text : 'Plate', - hidden : this.isPositionColumnHidden, - dataIndex : 'slotPositionColumn', - editor : _this._getSlotColumBombo(), - flex : 1, - renderer : function(val, meta, sample) { - if ((val != null) & (val != "")) { - return val; - } else { - meta.tdCls = 'yellow-cell'; - } - } - }, { - text : 'Row', - hidden : this.isPositionColumnHidden, - dataIndex : 'rowNumber', - editor : this._getRowCombo(), - flex : 1, - renderer : function(val, meta, sample) { - if ((val != null) && (val != "")) { - return BUI.getSamplePlateLetters()[val - 1]; - } else { - meta.tdCls = 'yellow-cell'; - } - } - }, { - text : 'Well', - hidden : this.isPositionColumnHidden, - dataIndex : 'columnNumber', - editor : this._getColumnCombo(), - flex : 1, - renderer : function(val, meta, sample) { - if ((val != null) && (val != "")) { - return val; - } else { - meta.tdCls = 'yellow-cell'; - } - } - }, { - id : _this.id + 'buttonEditSample', - text : 'Edit', - width : 80, - sortable : false, - hidden : !_this.editEnabled, - renderer : function(value, metaData, record, rowIndex, colIndex, store) { - if (_this.editEnabled) { - return BUI.getGreenButton('EDIT'); - } - } - }, { - id : _this.id + 'buttonRemoveSample', - text : '', - hidden : !_this.removeBtnEnabled, - width : 100, - sortable : false, - renderer : function(value, metaData, record, rowIndex, colIndex, store) { - if (_this.removeBtnEnabled) { - return BUI.getRedButton('REMOVE'); - } - } - } +BufferGrid.prototype.getPanel = function(buffers) { + var _this = this; - ], - viewConfig : { - preserveScrollOnRefresh : true, - stripeRows : true, - getRowClass : function(record) { - var specimens = _this.experiment.getSampleByPosition(record.data.samplePlateId, record.data.rowNumber, - record.data.columnNumber); - if (specimens.length > 1) { - return 'red-row'; + this.store = Ext.create('Ext.data.Store', { + fields : [ 'bufferId', 'acronym', 'name', 'composition' ], + data : buffers + }); - } - }, - listeners : { - selectionchange : function(grid, selected) { - _this.onClick.notify(record.raw); - }, - cellclick : function(grid, td, cellIndex, record, tr) { - if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonEditSample') { - } - if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonRemoveSample') { - grid.getStore().removeAt(rowIndex); - _this.onRemoved.notify(); - } + this.store.sort('acronym'); - } + var type = 'Ext.grid.Panel'; + if (this.searchBar == true) { + type = 'Ext.ux.LiveSearchGridPanel'; + } - } - } - }); + this.grid = Ext.create(type, { + title : 'Buffers', + collapsible : true, + collapsed : this.collapsed, + store : this.store, + height : this.height, + width : this.width, + columns : [ + /*{ + text : '', + dataIndex : 'bufferId', + width : 20, + renderer : function(val, y, sample) { + return BUI.getRectangleColorDIV(BIOSAXS.proposal.bufferColors[val], 10, 10); + } + },*/ + { + text : 'Acronym', + dataIndex : 'acronym', + flex : 1 + }, { + text : 'Name', + dataIndex : 'name', + flex : 1, + hidden : true + }, { + text : 'Composition', + dataIndex : 'composition', + flex : 1, + hidden : true + }, { + id : _this.id + 'buttonEditBuffer', + width : 80, + sortable : false, + renderer : function(value, metaData, record, rowIndex, colIndex, store) { + return BUI.getGreenButton('EDIT'); + } + }, { + id : _this.id + 'buttonRemoveBuffer', + width : 85, + hidden : true, + sortable : false, + renderer : function(value, metaData, record, rowIndex, colIndex, store) { + return BUI.getRedButton('REMOVE'); + } + } ], + flex : 1, + viewConfig : { + stripeRows : true, + listeners : { + 'celldblclick' : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + _this._edit(record.data.bufferId); + }, + 'cellclick' : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonEditBuffer') { + _this._edit(record.data.bufferId); + } + + if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonRemoveBuffer') { + BUI.showBetaWarning(); + } + } + + } + } + }); + + /** Adding the tbar **/ + if (this.tbar) { + this.grid.addDocked({ + xtype : 'toolbar', + items : this._getTbar() + }); + } return this.grid; }; -SpecimenGrid.prototype.input = function() { - return { - experiment : DATADOC.getExperiment_10(), - proposal : DATADOC.getProposal_10() - }; +BufferGrid.prototype.input = function() { + return new MacromoleculeGrid().input(); }; -SpecimenGrid.prototype.test = function(targetId) { - var specimenGrid = new SpecimenGrid({ - height : 400, - maxHeight : 400, - width : 1000 +BufferGrid.prototype.test = function(targetId) { + var bufferGrid = new BufferGrid({ + width : 800, + height : 350, + collapsed : false, + tbar : true }); - BIOSAXS.proposal = new Proposal(specimenGrid.input().proposal); - var experiment = new Experiment(specimenGrid.input().experiment); - var panel = specimenGrid.getPanelByExperiment(experiment); + BIOSAXS.proposal = new Proposal(bufferGrid.input().proposal); + var panel = bufferGrid.getPanel(BIOSAXS.proposal.macromolecules); panel.render(targetId); - }; /** - * Shows a list of stock solutions with macromolecule, buffer, storage temperature, concentration, shipment and comments + * A shipment may contains one or more cases where stock solutions and sample plates are stored * - * @multiselect allows multiple selection - * @height - * @minHeight - * @width - * @tbar - * @showTitle - * @isPackedVisible shows is stock solution is in a box - * @btnEditVisible shows edit button - * @btnAddVisible - * @btnAddExisting - * @btnUnpackVisible allows to unpack a stock solution - * @btnRemoveVisible allow to remove a stock solution + * @height + * @btnEditVisible + * @btnRemoveVisible + * + * #onEditButtonClicked + * #onAddButtonClicked + * #onRemoveButtonClicked + * #onDuplicateButtonClicked */ +function CaseGrid(args) { -function StockSolutionGrid(args) { - this.id = BUI.id(); this.height = 100; - this.width = null; - this.minHeight = null; - this.tbar = true; - - this.title = "Stock Solutions"; - - /** Visible buttons and actions **/ this.btnEditVisible = true; this.btnRemoveVisible = true; - this.btnAddVisible = true; - this.btnAddExisting = false; - this.isPackedVisible = true; - this.btnUnpackVisible = false; - - /** Selectors **/ - this.multiselect = false; - this.selectedStockSolutions = []; if (args != null) { - if (args.btnUnpackVisible != null) { - this.btnUnpackVisible = args.btnUnpackVisible; - } - if (args.multiselect != null) { - this.multiselect = args.multiselect; - } if (args.height != null) { this.height = args.height; } if (args.btnEditVisible != null) { this.btnEditVisible = args.btnEditVisible; } - if (args.btnAddVisible != null) { - this.btnAddVisible = args.btnAddVisible; - } - if (args.btnAddExisting != null) { - this.btnAddExisting = args.btnAddExisting; - } - if (args.width != null) { - this.width = args.width; - } - if (args.minHeight != null) { - this.minHeight = args.minHeight; - } - if (args.tbar != null) { - this.tbar = args.tbar; - } if (args.btnRemoveVisible != null) { this.btnRemoveVisible = args.btnRemoveVisible; } - if (args.isPackedVisible != null) { - this.isPackedVisible = args.isPackedVisible; - } - if (args.showTitle != null) { - this.showTitle = args.showTitle; - if (this.showTitle == false) { - this.title = null; + } + + /** Events **/ + this.onEditButtonClicked = new Event(this); + this.onAddButtonClicked = new Event(this); + this.onRemoveButtonClicked = new Event(this); + this.onDuplicateButtonClicked = new Event(this); +} + +CaseGrid.prototype._getColumns = function() { + var _this = this; + + function actionItemRenderer(value, meta, record, rowIx, ColIx, store) { + /*if (record.raw.specimen3VOs.length > 0){ + return 'x-hide-display'; + }*/ + } + + var columns = [ + { + header : 'Code', + dataIndex : 'code', + name : 'code', + type : 'string', + flex : 1 + }, + { + header : 'Bar Code', + dataIndex : 'barCode', + name : 'barCode', + type : 'string', + flex : 1, + hidden : true + }, + { + header : 'BeamLine', + dataIndex : 'barCode', + type : 'string', + flex : 1, + renderer : function(comp, val, record) { + if (record.raw.sessionVO != null) { + return "" + record.raw.sessionVO.beamlineName + ""; + } + } + }, + { + header : 'Session', + dataIndex : 'barCode', + type : 'string', + flex : 1, + renderer : function(comp, val, record) { + if (record.raw.sessionVO != null) { + return "" + moment(record.raw.sessionVO.startDate).format("MMM Do YY") + ""; + } } - } - - } - - /** Events **/ - this.onProposalChanged = new Event(this); - this.onStockSolutionSelected = new Event(this); -} - -StockSolutionGrid.prototype._getColumns = function() { - var _this = this; - var columns = [ - - { - header : 'Macromolecule', - dataIndex : 'macromolecule', - id : _this.id + 'macromolecule', - type : 'string', - renderer : function(val, y, specimen) { - return '' + val + ''; }, - hidden : false, - flex : 1 - }, { - header : 'Buffer', - dataIndex : 'buffer', - name : 'buffer', - hidden : false, - renderer : function(val, y, specimen) { - return '' + val + ''; + { + header : 'Status', + dataIndex : 'dewarStatus', + name : 'dewarStatus', + type : 'string', + flex : 1, + renderer : function(comp, val, record) { + if (new String(record.raw.dewarStatus).toUpperCase() == 'OPENED') { + return "" + new String(record.raw.dewarStatus).toUpperCase() + ""; + } + return "" + new String(record.raw.dewarStatus).toUpperCase() + ""; + } }, - type : 'string', - flex : 1 - }, { - header : 'Acronym', - dataIndex : 'name', - name : 'name', - type : 'string', - flex : 1, - hidden : true - }, { - header : 'Storage Temp.', - dataIndex : 'storageTemperature', - name : 'storageTemperature', - type : 'string', - flex : 1, - hidden : false, - renderer : function(val) { - return BUI.formatValuesUnits(val, 'C', { - fontSize : 12, - decimals : 2, - unitsFontSize : 10 - }); - } - }, { - header : 'Volume', - dataIndex : 'volume', - type : 'string', - flex : 1, - hidden : false, - renderer : function(val) { - return BUI.formatValuesUnits(val, 'µl', { - fontSize : 12, - decimals : 2, - unitsFontSize : 10 - }); - } - }, { - header : 'Concentration', - dataIndex : 'concentration', - name : 'concentration', - type : 'string', - flex : 1, - renderer : function(val) { - return BUI.formatValuesUnits(val, 'mg/ml', { - fontSize : 12, - decimals : 2, - unitsFontSize : 10 - }); - } - }, { - header : 'Packed', - dataIndex : 'comments', - id : _this.id + "box", - type : 'string', - width : 50, - hidden : !this.isPackedVisible, - renderer : function(val, cmp, a) { - if (a.raw.boxId != null) { - return "
"; + { + header : 'Type', + dataIndex : 'type', + name : 'type', + type : 'string', + flex : 1, + hidden : true + }, + { + header : 'Sample Plates', + dataIndex : 'plates', + name : 'plates', + type : 'string', + flex : 1, + hidden : true + }, + { + header : 'Stock Solutions', + dataIndex : 'plates', + name : 'plates', + type : 'string', + width : 100, + renderer : function(comp, val, record) { + var html = "
"; + var stockSolutions = BIOSAXS.proposal.getStockSolutionsByDewarId(record.raw.dewarId); + html = html + "" + stockSolutions.length + " x"; + return html + "
"; } - + }, { + header : 'isStorageDewar', + dataIndex : 'isStorageDewar', + name : 'isStorageDewar', + type : 'string', + flex : 1, + hidden : true + }, { + header : 'Tracking Number From Synchrotron', + dataIndex : 'trackingNumberFromSynchrotron', + name : 'trackingNumberFromSynchrotron', + type : 'string', + flex : 1, + hidden : true + }, { + header : 'Tracking Number To Synchrotron', + dataIndex : 'trackingNumberToSynchrotron', + name : 'trackingNumberToSynchrotron', + type : 'string', + flex : 1, + hidden : true + }, { + header : 'Storage Location', + dataIndex : 'storageLocation', + name : 'storageLocation', + type : 'string', + flex : 1, + hidden : false + }, { + header : 'Comments', + dataIndex : 'comments', + name : 'comments', + type : 'string', + flex : 1, + hidden : false } - }, { - header : 'Comments', - dataIndex : 'comments', - type : 'string', - flex : 1 - } ]; + ]; if (this.btnEditVisible) { columns.push({ id : _this.id + 'buttonEdit', + text : '', + hidden : !_this.btnEditVisible, width : 85, sortable : false, renderer : function(value, metaData, record, rowIndex, colIndex, store) { - if (_this.btnEditVisible) { - return BUI.getGreenButton('EDIT'); - } + return BUI.getGreenButton('EDIT'); } }); } @@ -22014,4646 +16857,9803 @@ StockSolutionGrid.prototype._getColumns = function() { }); } - if (this.btnUnpackVisible) { - columns.push({ - id : _this.id + 'buttonUnpack', - width : 85, - sortable : false, - renderer : function(value, metaData, record, rowIndex, colIndex, store) { - if (_this.btnUnpackVisible) { - return BUI.getBlueButton('UNPACK'); - } - } - }); - } + columns.push({ + dataIndex : 'comments', + type : 'string', + width : 85, + hidden : false, + renderer : function(comp, val, record) { + return BUI.getBlueButton("LABELS", { + href : BUI.getPrintcomponentURL(record.raw.dewarId) + }); + } + }); + return columns; }; -StockSolutionGrid.prototype._getTopButtons = function() { - var _this = this; - /** Actions buttons **/ - var actions = []; - - /** ADD BUTTON **/ - if (this.btnAddVisible) { - actions.push(Ext.create('Ext.Action', { - icon : '../images/add.png', - text : 'Add Stock Solution', - tooltip : 'Will create a new stock solution', - disabled : false, - alwaysEnabled : true, - handler : function(widget, event) { - _this.edit(); - } - })); - } - - if (this.btnAddExisting) { - actions.push(Ext.create('Ext.Action', { - icon : '../images/add.png', - text : 'Add Existing', - tooltip : 'Allows to select upacked stock solutions', - disabled : false, - alwaysEnabled : true, - handler : function(widget, event) { - var stockSolutionGrid = new StockSolutionGrid({ - btnAddVisible : false, - btnEditVisible : false, - btnRemoveVisible : false, - btnAddExisting : false, - isPackedVisible : true, - multiselect : true - }); - - var window = Ext.create('Ext.window.Window', { - title : 'Select', - height : 400, - width : 800, - layout : 'fit', - items : [ stockSolutionGrid.getPanel() ], - buttons : [ { - text : 'Pack', - handler : function() { - _this.onStockSolutionSelected.notify(stockSolutionGrid.selectedStockSolutions); - window.close(); - } - }, { - text : 'Cancel', - handler : function() { - window.close(); - } - } ] - - }).show(); +CaseGrid.prototype._getTopButtons = function() { + var _this = this; + /** Actions buttons **/ + var actions = []; - stockSolutionGrid.refresh(BIOSAXS.proposal.getUnpackedStockSolutions()); - } - })); - } + /** ADD BUTTON **/ + actions.push(Ext.create('Ext.Action', { + icon : '../images/add.png', + text : 'Add', + disabled : false, + handler : function(widget, event) { + _this.onAddButtonClicked.notify(); + } + })); return actions; }; -StockSolutionGrid.prototype.refresh = function(stockSolutions) { - this.features = stockSolutions; - this.store.loadData(this._prepareData(), false); +CaseGrid.prototype.refresh = function(dewars) { + this.features = dewars; + this.store.loadData(this._prepareData(dewars), false); }; -StockSolutionGrid.prototype._prepareData = function() { +CaseGrid.prototype._sort = function(store) { + store.sort('dewarId', 'DESC'); +}; + +CaseGrid.prototype._prepareData = function() { var data = []; for ( var i = 0; i < this.features.length; i++) { - var stockSolution = this.features[i]; - stockSolution.buffer = BIOSAXS.proposal.getBufferById(stockSolution.bufferId).acronym; - if (stockSolution.macromoleculeId != null) { - stockSolution.macromolecule = BIOSAXS.proposal.getMacromoleculeById(stockSolution.macromoleculeId).acronym; - } - data.push(stockSolution); + data.push(this.features[i]); } return data; }; -StockSolutionGrid.prototype.getPanel = function() { +CaseGrid.prototype.getPanel = function(dewars, plates) { + this.features = dewars; + this.plates = plates; return this._renderGrid(); }; -StockSolutionGrid.prototype.edit = function(stockSolutionId) { +CaseGrid.prototype._edit = function(dewar) { var _this = this; - var stockSolutionWindow = new StockSolutionWindow(); - /** On stock solution SAVED **/ - stockSolutionWindow.onSaved.attach(function(sender, stockSolution) { - _this.onProposalChanged.notify(stockSolution); + var caseWindow = new CaseWindow(); + /**SAVED **/ + caseWindow.onSaved.attach(function(sender, dewar) { + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, shipment) { + _this.refresh(shipment.dewarVOs); + }); + adapter.saveCase(BIOSAXS.proposal.getShipmentByDewarId(dewar.dewarId).shippingId, dewar); }); - stockSolutionWindow.draw(BIOSAXS.proposal.getStockSolutionById(stockSolutionId)); + caseWindow.draw(dewar); }; -StockSolutionGrid.prototype._getStoreFields = function() { +CaseGrid.prototype._getStoreFields = function(data) { return [ { - name : 'name', + name : 'dewarId', type : 'string' }, { - name : 'stockSolutionId', + name : 'barCode', type : 'string' }, { - name : 'macromolecule', + name : 'code', type : 'string' }, { - name : 'buffer', + name : 'comments', type : 'string' }, { - name : 'storageTemperature', - type : 'numeric' + name : 'dewarStatus', + type : 'string' }, { - name : 'volume', + name : 'isStorageDewar', type : 'string' }, { - name : 'concentration', + name : 'plates', type : 'string' }, { - name : 'buffer', + name : 'transportValue', type : 'string' }, { - name : 'comments', + name : 'trackingNumberFromSynchrotron', type : 'string' - } ]; -}; + }, { + name : 'trackingNumberToSynchrotron', + type : 'string' + }, { + name : 'timeStamp', + type : 'string' + }, { + name : 'storageLocation', + type : 'string' + }, { + name : 'type', + type : 'string' + } -//StockSolutionGrid.prototype.refresh = function() { -// this.proposal.onInitialized.attach(function(sender){ -// -// }); -// this.proposal.init(Ext.urlDecode(window.location.href).sessionId); -//}; + ]; +}; -StockSolutionGrid.prototype._renderGrid = function() { +CaseGrid.prototype._renderGrid = function() { var _this = this; /** Store **/ + var data = this._prepareData(); this.store = Ext.create('Ext.data.Store', { - fields : this._getStoreFields(), //columns, - autoload : true + fields : this._getStoreFields(data), + autoload : true, + data : data }); + this._sort(this.store); - var filters = { - ftype : 'filters', - local : true, - filters : this.filters - }; - - var selModel = null; - - if (this.multiselect) { - selModel = Ext.create('Ext.selection.CheckboxModel', { - //multiSelect : false,//this.multiselect, - mode : 'SINGLE', - listeners : { - selectionchange : function(sm, selections) { - _this.selectedStockSolutions = []; - for ( var i = 0; i < selections.length; i++) { - _this.selectedStockSolutions.push(selections[i].raw); - } - } - } - }); - } else { - selModel = { - mode : 'SINGLE' - }; - } - - this.store.sort("stockSolutionId", "desc"); + this.store.loadData(data, false); this.grid = Ext.create('Ext.grid.Panel', { style : { padding : 5 }, - icon : '/ispyb/images/SampleHolder_24x24_01.png', - title : this.title, height : this.height, - width : this.width, - minWidth : this.minWidth, - selModel : selModel, store : this.store, columns : this._getColumns(), viewConfig : { stripeRows : true, listeners : { itemdblclick : function(dataview, record, item, e) { - _this.edit(record.raw.stockSolutionId); - }, - cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { - var adapter = null; - if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonUnpack') { - _this.grid.setLoading("ISPyB: Unpacking stock solution"); - adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender) { - _this.onProposalChanged.notify(); - _this.grid.setLoading(false); - }); - adapter.onError.attach(function(sender) { - _this.onProposalChanged.notify(); - _this.grid.setLoading(false); - }); - record.raw.boxId = null; - adapter.saveStockSolution(record.raw); - } - - if (grid.getGridColumns()[cellIndex].getId() == _this.id + "box") { - window.location = BUI.getShippingURL(BIOSAXS.proposal.getShipmentByDewarId(record.raw.boxId).shippingId); - } - if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonEdit') { - _this.edit(record.data.stockSolutionId); - } - - if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonRemove') { - _this.grid.setLoading("ISPyB: Removing stock solution"); - adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender) { - _this.onProposalChanged.notify(); - _this.grid.setLoading(false); - }); - adapter.onError.attach(function(sender) { - _this.onProposalChanged.notify(); - _this.grid.setLoading(false); - }); - adapter.removeStockSolution(record.data.stockSolutionId); - } - } - } - } - - }); - - var actions = _this._getTopButtons(); - this.grid.addDocked({ - xtype : 'toolbar', - items : actions - }); - - var i = null; - this.grid.getSelectionModel().on({ - selectionchange : function(sm, selections) { - if (selections.length) { - for ( i = 0; i < actions.length; i++) { - if (actions[i].enable) { - actions[i].enable(); - } - } - } else { - for ( i = 0; i < actions.length; i++) { - if (actions[i].alwaysEnabled == false) { - if (actions[i].disable) { - actions[i].disable(); - } - } - } - } - } - }); - return this.grid; -}; - -StockSolutionGrid.prototype.input = function() { - return { - proposal : DATADOC.getProposal_10() - }; -}; - -StockSolutionGrid.prototype.test = function(targetId) { - var stockSolutionGrid = new StockSolutionGrid({ - height : 300, - width : 900 - }); - BIOSAXS.proposal = new Proposal(stockSolutionGrid.input().proposal); - var panel = stockSolutionGrid.getPanel(); - stockSolutionGrid.refresh(BIOSAXS.proposal.getStockSolutions()); - panel.render(targetId); -}; - - -function SuperpositionGrid(args) { - this.height = null; - this.width = null; - this.id = BUI.id(); - - if (args != null) { - if (args.height != null) { - this.height = args.height; - - if (args.width != null) { - this.width = args.width; - } - } - } - - this.onSelected = new Event(this); -} - -SuperpositionGrid.prototype._prepareData = function(data) { - return data; -}; + _this._edit(record.raw); + }, + cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonEdit') { + _this._edit(record.raw); + } -SuperpositionGrid.prototype.refresh = function(subtractions) { - var data = []; - if (subtractions != null){ - if (subtractions.length > 0){ - for (j in subtractions){ - var subtraction = subtractions[j]; - if (subtraction.superposition3VOs != null){ - for (i in subtraction.superposition3VOs){ - data.push(subtraction.superposition3VOs[i]); + if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonRemove') { + _this.onRemoveButtonClicked.notify(_this.store.getAt(rowIndex).raw.dewarId); } } } + }, + selModel : { + mode : 'SINGLE' } - } - this.store.loadData(data); -}; + }); -SuperpositionGrid.prototype.getPanel = function() { - var _this = this; - this.store = Ext.create('Ext.data.Store', { - fields : [ 'abinitioModelPdbFilePath', 'aprioriPdbFilePath', 'alignedPdbFilePath' ], - data : [] + var actions = _this._getTopButtons(); + this.grid.addDocked({ + xtype : 'toolbar', + items : actions }); - this.selModel = Ext.create('Ext.selection.RowModel', { - allowDeselect : true, - mode : 'MULTI', - listeners : { - selectionchange : function(sm, selections) { - var selected = []; - for (var i = 0; i < selections.length; i++) { - selected.push(selections[i].raw); + this.grid.getSelectionModel().on({ + selectionchange : function(sm, selections) { + if (selections.length) { + for ( var i = 0; i < actions.length; i++) { + if (actions[i].enable) { + actions[i].enable(); + } + } + } else { + for ( var i = 0; i < actions.length; i++) { + if (actions[i].alwaysEnabled == false) { + if (actions[i].disable) { + actions[i].disable(); + } + } } - _this.onSelected.notify(selected); } } }); - - this.store.sort([ { - property : 'name', - direction : 'ASC' - } ]); + return this.grid; +}; - this.panel = Ext.create('Ext.grid.Panel', { - store : this.store, - selModel : this.selModel, - width : this.width, - height : this.height, - margin : 10, - deferredRender : false, - columns : [ { - text : 'Abinitio', - dataIndex : 'abinitioModelPdbFilePath', - hidden : false, - flex : 1, - renderer : function(val){ - return BUI.getFileName(val); - } - }, { - text : 'Apriori', - dataIndex : 'aprioriPdbFilePath', - hidden : false, - flex : 1, - renderer : function(val){ - return BUI.getFileName(val); - } - }, { - text : 'Aligned', - dataIndex : 'alignedPdbFilePath', - hidden : false, - flex : 1, - renderer : function(val){ - return BUI.getFileName(val); - } - } ], +CaseGrid.prototype.input = function() { + return { + proposal : DATADOC.getProposal_10(), + dewars : DATADOC.getDewars_10() - viewConfig : { - enableTextSelection : true, - preserveScrollOnRefresh : true - }, - listeners : { - cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + }; +}; - }, - afterrender : function() { - } - } +CaseGrid.prototype.test = function(targetId) { + var CaseGrid = new CaseGrid({ + height : 150 }); - return this.panel; + BIOSAXS.proposal = new Proposal(CaseGrid.input().proposal); + var panel = CaseGrid.getPanel(CaseGrid.input().dewars); + panel.render(targetId); + }; +/** + * A shipment may contains one or more cases where stock solutions and sample plates are stored + * + * @height + * @btnEditVisible + * @btnRemoveVisible + * + * #onEditButtonClicked + * #onAddButtonClicked + * #onRemoveButtonClicked + * #onDuplicateButtonClicked + */ +function ExampleGrid(args) { + + this.height = 100; + this.btnEditVisible = true; + this.btnRemoveVisible = true; + + if (args != null) { + if (args.height != null) { + this.height = args.height; + } + if (args.btnEditVisible != null) { + this.btnEditVisible = args.btnEditVisible; + } + if (args.btnRemoveVisible != null) { + this.btnRemoveVisible = args.btnRemoveVisible; + } + } + + /** Events **/ + this.onEditButtonClicked = new Event(this); + this.onAddButtonClicked = new Event(this); + this.onRemoveButtonClicked = new Event(this); + this.onDuplicateButtonClicked = new Event(this); +} + +ExampleGrid.prototype._getColumns = function() { + var _this = this; + + function actionItemRenderer(value, meta, record, rowIx, ColIx, store) { + /*if (record.raw.specimen3VOs.length > 0){ + return 'x-hide-display'; + }*/ + } + + var columns = [ + { + header : 'Code', + dataIndex : 'code', + name : 'code', + type : 'string', + flex : 1 + }, + { + header : 'Bar Code', + dataIndex : 'barCode', + name : 'barCode', + type : 'string', + flex : 1, + hidden : true + }, + { + header : 'BeamLine', + dataIndex : 'barCode', + type : 'string', + flex : 1, + renderer : function(comp, val, record) { + if (record.raw.sessionVO != null) { + return "" + record.raw.sessionVO.beamlineName + ""; + } + } + }, + { + header : 'Session', + dataIndex : 'barCode', + type : 'string', + flex : 1, + renderer : function(comp, val, record) { + if (record.raw.sessionVO != null) { + return "" + moment(record.raw.sessionVO.startDate).format("MMM Do YY") + ""; + } + } + }, + { + header : 'Status', + dataIndex : 'dewarStatus', + name : 'dewarStatus', + type : 'string', + flex : 1, + renderer : function(comp, val, record) { + if (new String(record.raw.dewarStatus).toUpperCase() == 'OPENED') { + return "" + new String(record.raw.dewarStatus).toUpperCase() + ""; + } + return "" + new String(record.raw.dewarStatus).toUpperCase() + ""; + } + }, + { + header : 'Type', + dataIndex : 'type', + name : 'type', + type : 'string', + flex : 1, + hidden : true + }, + { + header : 'Sample Plates', + dataIndex : 'plates', + name : 'plates', + type : 'string', + flex : 1, + hidden : true + }, + { + header : 'Stock Solutions', + dataIndex : 'plates', + name : 'plates', + type : 'string', + width : 100, + renderer : function(comp, val, record) { + var html = "
"; + var stockSolutions = BIOSAXS.proposal.getStockSolutionsByDewarId(record.raw.dewarId); + html = html + "" + stockSolutions.length + " x"; + return html + "
"; + } + }, { + header : 'isStorageDewar', + dataIndex : 'isStorageDewar', + name : 'isStorageDewar', + type : 'string', + flex : 1, + hidden : true + }, { + header : 'Tracking Number From Synchrotron', + dataIndex : 'trackingNumberFromSynchrotron', + name : 'trackingNumberFromSynchrotron', + type : 'string', + flex : 1, + hidden : true + }, { + header : 'Tracking Number To Synchrotron', + dataIndex : 'trackingNumberToSynchrotron', + name : 'trackingNumberToSynchrotron', + type : 'string', + flex : 1, + hidden : true + }, { + header : 'Storage Location', + dataIndex : 'storageLocation', + name : 'storageLocation', + type : 'string', + flex : 1, + hidden : false + }, { + header : 'Comments', + dataIndex : 'comments', + name : 'comments', + type : 'string', + flex : 1, + hidden : false + } + ]; + + if (this.btnEditVisible) { + columns.push({ + id : _this.id + 'buttonEdit', + text : '', + hidden : !_this.btnEditVisible, + width : 85, + sortable : false, + renderer : function(value, metaData, record, rowIndex, colIndex, store) { + return BUI.getGreenButton('EDIT'); + } + }); + } + + if (this.btnRemoveVisible) { + columns.push({ + id : _this.id + 'buttonRemove', + width : 85, + sortable : false, + renderer : function(value, metaData, record, rowIndex, colIndex, store) { + if (_this.btnRemoveVisible) { + return BUI.getRedButton('REMOVE'); + } + } + }); + } + + columns.push({ + dataIndex : 'comments', + type : 'string', + width : 85, + hidden : false, + renderer : function(comp, val, record) { + return BUI.getBlueButton("LABELS", { + href : BUI.getPrintcomponentURL(record.raw.dewarId) + }); + } + }); + + return columns; +}; + +ExampleGrid.prototype._getTopButtons = function() { + var _this = this; + /** Actions buttons **/ + var actions = []; + + /** ADD BUTTON **/ + actions.push(Ext.create('Ext.Action', { + icon : '../images/add.png', + text : 'Add', + disabled : false, + handler : function(widget, event) { + _this.onAddButtonClicked.notify(); + } + })); + + return actions; +}; + +ExampleGrid.prototype.refresh = function(dewars) { + this.features = dewars; + this.store.loadData(this._prepareData(dewars), false); +}; + +ExampleGrid.prototype._sort = function(store) { + store.sort('dewarId', 'DESC'); +}; + +ExampleGrid.prototype._prepareData = function() { + var data = []; + for ( var i = 0; i < this.features.length; i++) { + data.push(this.features[i]); + } + return data; +}; + +ExampleGrid.prototype.getPanel = function() { + this.grid = Ext.create('Ext.grid.Panel', { + style : { + padding : 5 + }, + height : this.height, + store : this.store, + columns : this._getColumns(), + viewConfig : { + stripeRows : true, + listeners : { + itemdblclick : function(dataview, record, item, e) { + _this._edit(record.raw); + }, + cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonEdit') { + _this._edit(record.raw); + } + + if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonRemove') { + _this.onRemoveButtonClicked.notify(_this.store.getAt(rowIndex).raw.dewarId); + } + } + } + }, + selModel : { + mode : 'SINGLE' + } + }); + + var actions = _this._getTopButtons(); + this.grid.addDocked({ + xtype : 'toolbar', + items : actions + }); + + this.grid.getSelectionModel().on({ + selectionchange : function(sm, selections) { + if (selections.length) { + for ( var i = 0; i < actions.length; i++) { + if (actions[i].enable) { + actions[i].enable(); + } + } + } else { + for ( var i = 0; i < actions.length; i++) { + if (actions[i].alwaysEnabled == false) { + if (actions[i].disable) { + actions[i].disable(); + } + } + } + } + } + }); + return this.grid; +}; + + +ExampleGrid.prototype._getStoreFields = function(data) { + return [ { + name : 'dewarId', + type : 'string' + }, { + name : 'barCode', + type : 'string' + }, { + name : 'code', + type : 'string' + }, { + name : 'comments', + type : 'string' + }, { + name : 'dewarStatus', + type : 'string' + }, { + name : 'isStorageDewar', + type : 'string' + }, { + name : 'plates', + type : 'string' + }, { + name : 'transportValue', + type : 'string' + }, { + name : 'trackingNumberFromSynchrotron', + type : 'string' + }, { + name : 'trackingNumberToSynchrotron', + type : 'string' + }, { + name : 'timeStamp', + type : 'string' + }, { + name : 'storageLocation', + type : 'string' + }, { + name : 'type', + type : 'string' + } + + ]; +}; + +ExampleGrid.prototype._renderGrid = function() { + var _this = this; + + /** Store **/ + var data = this._prepareData(); + this.store = Ext.create('Ext.data.Store', { + fields : this._getStoreFields(data), + autoload : true, + data : data + }); + this._sort(this.store); + + this.store.loadData(data, false); + + this.grid = Ext.create('Ext.grid.Panel', { + style : { + padding : 5 + }, + height : this.height, + store : this.store, + columns : this._getColumns(), + viewConfig : { + stripeRows : true, + listeners : { + itemdblclick : function(dataview, record, item, e) { + _this._edit(record.raw); + }, + cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonEdit') { + _this._edit(record.raw); + } + + if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonRemove') { + _this.onRemoveButtonClicked.notify(_this.store.getAt(rowIndex).raw.dewarId); + } + } + } + }, + selModel : { + mode : 'SINGLE' + } + }); + + var actions = _this._getTopButtons(); + this.grid.addDocked({ + xtype : 'toolbar', + items : actions + }); + + this.grid.getSelectionModel().on({ + selectionchange : function(sm, selections) { + if (selections.length) { + for ( var i = 0; i < actions.length; i++) { + if (actions[i].enable) { + actions[i].enable(); + } + } + } else { + for ( var i = 0; i < actions.length; i++) { + if (actions[i].alwaysEnabled == false) { + if (actions[i].disable) { + actions[i].disable(); + } + } + } + } + } + }); + return this.grid; +}; + +ExampleGrid.prototype.input = function() { + return { + proposal : DATADOC.getProposal_10(), + dewars : DATADOC.getDewars_10() + + }; +}; + +ExampleGrid.prototype.test = function(targetId) { + var ExampleGrid = new ExampleGrid({ + height : 150 + }); + BIOSAXS.proposal = new Proposal(ExampleGrid.input().proposal); + var panel = ExampleGrid.getPanel(ExampleGrid.input().dewars); + panel.render(targetId); + +}; + + /** - * See ExperimentGrid - * + * Shows a list of experiment AKA data acquisitions + * @height + * @sorters + * @minHeight + * @gridType: Ext.ux.LiveSearchGridPanel or Ext.grid.Panel + * @tbar true or false + * @grouping true or false + * @width + * @title + * #onEditButtonClicked */ -function TemplateGrid(args) { - - if (args == null) { - args = {}; - } - args.sorters = [ { - property : 'experimentId', - direction : 'DESC' - } ]; - - ExperimentGrid.prototype.constructor.call(this, args); -} - -TemplateGrid.prototype._getFilterTypes = ExperimentGrid.prototype._getFilterTypes; -TemplateGrid.prototype._prettyPrintMacromolecules = ExperimentGrid.prototype._prettyPrintMacromolecules; -TemplateGrid.prototype._getPercentage = ExperimentGrid.prototype._getPercentage; -TemplateGrid.prototype._getPercentageCollected = ExperimentGrid.prototype._getPercentageCollected; -TemplateGrid.prototype.getPercentageMerged = ExperimentGrid.prototype.getPercentageMerged; -TemplateGrid.prototype._prepareData = ExperimentGrid.prototype._prepareData; -TemplateGrid.prototype.getPanel = ExperimentGrid.prototype.getPanel; -TemplateGrid.prototype._renderGrid = ExperimentGrid.prototype._renderGrid; -TemplateGrid.prototype._editExperiment = ExperimentGrid.prototype._editExperiment; -TemplateGrid.prototype._removeExperimentById = ExperimentGrid.prototype._removeExperimentById; - -TemplateGrid.prototype._getTopButtons = function() { - /** Actions buttons * */ - var actions = []; - - actions.push(Ext.create('Ext.Action', { - icon : '../images/add.png', - text : 'Add experiment', - handler : function(widget, event) { - var wizardWidget = new WizardWidget({ - windowMode : true - }); - - wizardWidget.onFinished.attach(function(sender, result) { - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, data) { - var experiment = new Experiment(data); - BIOSAXS.openExperimentByExperiment(experiment); - wizardWidget.window.close(); - }); - wizardWidget.current.setLoading("ISPyB: Creating experiment"); - adapter.createTemplate(result.name, "comments", result.data); - }); - -// wizardWidget.draw(this.targetId, new ExperimentTypeWizardForm()); - wizardWidget.draw(this.targetId, new MeasurementCreatorStepWizardForm(BIOSAXS.proposal.getMacromolecules())); - } - })); - return actions; -}; - -TemplateGrid.prototype.refresh = function(data) { - var filtered = []; - for ( var i = 0; i < data.length; i++) { - if (data[i].experimentType == "TEMPLATE") { - filtered.push(data[i]); - } - } - this.store.loadData(this._prepareData(filtered), false); -}; - -TemplateGrid.prototype._getColumns = function() { - var _this = this; - function actionItemRenderer(value, meta, record, rowIx, ColIx, store) { - if (record.raw.buffer3VOs != null) { - if (record.raw.buffer3VOs.length > 0) { - return 'x-hide-display'; - } - } +function ExperimentGrid(args) { + this.width = "100%"; + this.height = 700; + this.minHeight = 500; - if (record.data.platesCount > 0) { - return 'x-hide-display'; - } - } + this.id = BUI.id(); + this.gridType = 'Ext.grid.Panel'; //'Ext.ux.LiveSearchGridPanel'; + this.tbar = false; + this.hideHeaders = false; + this.grouping = true; + this.title = null; - return [ { - xtype : 'rownumberer', - width : 40 - }, { - text : 'experimentId', - dataIndex : 'experimentId', - name : 'experimentId', - type : 'string', - hidden : true + this.filtered = null; + + /** maximum row count **/ + this.limit = 100; + + this.sessionIdFilter = null; + + /** if not null filtered by date **/ + this.date = null; + + this.sorters = [ { + property : 'date', + direction : 'DESC' }, { - text : 'Name', - dataIndex : 'name', - name : 'name', - type : 'string', - flex : 1 - }, + property : 'time', + direction : 'DESC' + } ]; - { - text : 'Type', - dataIndex : 'type', - name : 'type', - type : 'string', - hidden : true, - flex : 1, - renderer : function(val) { - return val; + this.dates = {}; + if (args != null) { + if (args.height != null) { + this.height = args.height; } - }, { - text : 'Macromolecules', - name : 'macromolecules_names', - dataIndex : 'macromolecules_names', - flex : 1, - renderer : function(val) { - return " " + val + ""; + if (args.limit != null) { + this.limit = args.limit; } - }, { - id : _this.id + 'GO', - width : 80, - sortable : false, - renderer : function(value, metaData, record, rowIndex, colIndex, store) { - return BUI.getGreenButton('EDIT'); + if (args.sorters != null) { + this.sorters = args.sorters; } - }, { - id : _this.id + 'REMOVE', - width : 100, - sortable : false, - renderer : function(value, metaData, record, rowIndex, colIndex, store) { - return BUI.getRedButton('REMOVE'); + if (args.sessionId != null){ + this.sessionIdFilter = args.sessionId; + } + if (args.filtered != null) { + this.filtered = args.filtered; + } + if (args.minHeight != null) { + this.minHeight = args.minHeight; + } + if (args.gridType != null) { + this.gridType = args.gridType; + } + if (args.tbar != null) { + this.tbar = args.tbar; + } + if (args.grouping != null) { + this.grouping = args.grouping; + } + if (args.width != null) { + this.width = args.width; + } + if (args.title != null) { + this.title = args.title; } - } ]; -}; - -TemplateGrid.prototype.input = function() { - var experiments = DATADOC.getExperimentList_10(); - return { - experiments : experiments, - proposal : new MeasurementGrid().input().proposal - - }; -}; - -TemplateGrid.prototype.test = function(targetId) { - var experimentGrid = new TemplateGrid({ - height : 350, - minHeight : 350, - width : 1000, - gridType : 'Ext.grid.Panel', - title : 'Experiments', - grouping : false, - tbar : true - }); - BIOSAXS.proposal = new Proposal(experimentGrid.input().proposal); - var panel = experimentGrid.getPanel(experimentGrid.input().experiments); - experimentGrid.refresh(experimentGrid.input().experiments); - panel.render(targetId); -}; - -function VolumeGrid() { - this.id = BUI.id(); + } + /** Events **/ + this.onEditButtonClicked = new Event(this); } -VolumeGrid.prototype.getPanel = function(experiment) { - this.experiment = experiment; - return this.render(); +ExperimentGrid.prototype._getFilterTypes = function() { + return []; }; -VolumeGrid.prototype.getVolumesPanel = function(data, title) { - var _this = this; - var store = Ext.create('Ext.data.Store', { - fields : [ 'name', 'volume', 'macromoleculeId', 'bufferId' ], - data : data - }); - store.sort([ { - property : 'name', - direction : 'ASC' - } ]); - - var grid = Ext.create('Ext.grid.Panel', { - title : title, - height : 400, - maxHeight : 400, - width : 900, - store : store, - margin : '10 0 50 10', - tbar : [ { - text : 'Go to Shipment', - icon : '../images/plane-small.gif', - handler : function() { - window.location = BUI.getCreateShipmentList(); - } - } ], - viewConfig : { - stripeRows : true, - listeners : { - cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { - if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonCreate') { - var stockSolutionWindow = new StockSolutionWindow(); - /** On stock solution SAVED **/ - stockSolutionWindow.onSaved.attach(function(sender, stockSolution) { - BIOSAXS.proposal.onInitialized.attach(function(sender, data) { - _this.refresh(_this.experiment); - }); - BIOSAXS.proposal.init(); - }); - var acronym = "ST"; - if (record.raw.macromoleculeId != null) { - acronym = acronym + "_" + BIOSAXS.proposal.getMacromoleculeById(record.raw.macromoleculeId).acronym; - } - if (record.raw.bufferId != null) { - acronym = acronym + "_" + BIOSAXS.proposal.getBufferById(record.raw.bufferId).acronym; - } - stockSolutionWindow.draw({ - concentration : record.raw.concentration, - macromoleculeId : record.raw.macromoleculeId, - bufferId : record.raw.bufferId, - name : acronym, - volume : record.raw.volume - }); - } - - if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonStockSolutions') { - var stockSolutionGrid = new StockSolutionGrid({ - btnAddVisible : false, - btnEditVisible : false, - btnRemoveVisible : false, - btnAddExisting : false, - isPackedVisible : true, - multiselect : false - }); - - var window = Ext.create('Ext.window.Window', { - title : 'Stock solutions by specimen', - height : 400, - width : 800, - layout : 'fit', - items : [ stockSolutionGrid.getPanel() ], - buttons : [ { - text : 'Close', - handler : function() { - window.close(); - } - } ] - - }).show(); - stockSolutionGrid.refresh(BIOSAXS.proposal.getStockSolutionsBySpecimen(record.raw.macromoleculeId, record.raw.bufferId)); - } - } - } - }, - columns : [ -// { -// text : '', -// dataIndex : 'macromoleculeId', -// width : 20, -// renderer : function(val, y, sample) { -// if (val != null) { -//// return BUI.getRectangleColorDIV(BIOSAXS.proposal.macromoleculeColors[val], 10, 10); -// return BUI.getRectangleColorDIV(_this.experiment.macromoleculeColors[val], 10, 10); -// } -// } -// }, -// { -// text : '', -// dataIndex : 'bufferId', -// width : 20, -// renderer : function(val, y, sample) { -// if (val != null) { -// return BUI.getRectangleColorDIV(_this.experiment.getSpecimenColorByBufferId(val), 10, 10); -// } -// } -// }, - { - text : 'Specimen', - dataIndex : 'name', - flex : 0.5 - }, - { - text : 'Estimated Volume', - dataIndex : 'volume', - tooltip : 'Estimation of the maximum volume needed for making this experiment', - flex : 0.5, - editor : { - allowBlank : true - }, - renderer : function(val, y, sample) { - return BUI.formatValuesUnits(sample.data.volume, 'µl', { - fontSize : 16, - decimals : 2, - unitsFontSize : this.unitsFontSize - }); - } - }, - { - text : 'Stock Solution', - id : _this.id + 'buttonStockSolutions', - dataIndex : 'name', - flex : 0.5, - tooltip : 'Stock Solutions containing this specimen in this proposal', - renderer : function(value, metaData, record, rowIndex, colIndex, store) { - var macromoleculeId = record.raw.macromoleculeId; - var bufferId = record.raw.bufferId; - var stockSolutions = BIOSAXS.proposal.getStockSolutionsBySpecimen(macromoleculeId, bufferId); - if (stockSolutions.length > 0) { - return "
" + stockSolutions.length + " x
"; - } +ExperimentGrid.prototype._prepareData = function(rows) { + var data = []; + var count = 0; + + rows.sort(function(a,b){return b.experimentId - a.experimentId;}); + for ( var i = 0; i < rows.length; i++) { + var row = rows[i]; + this.dates[moment(row.creationDate).format("YYYYMMDD")] = moment(row.creationDate).format("MMM Do YY"); + if ( + ( this.filtered == null || row.experimentType == this.filtered ) && (count < this.limit || this.limit == null ) && (this.sessionIdFilter == null || this.date != null || (this.date == null && this.sessionIdFilter == row.sessionId)) + ){ - } - }, { - id : _this.id + 'buttonCreate', - text : '', - tooltip : 'Create a new stock solution for shipping', - width : 170, - sortable : false, - renderer : function(value, metaData, record, rowIndex, colIndex, store) { - return BUI.getGreenButton('NEW STOCK SOLUTION', { - width : 160 + data.push({ + experimentId : row.experimentId, + status : row.status, + dataAcquisitionFilePath : row.dataAcquisitionFilePath, + type : row.experimentType, + name : row.name, + macromolecules_names : row.macromolecules, + percentageAnalysed : { + value : (row.dataCollectionDoneCount / row.dataCollectionCount) * 100, + text : row.dataCollectionDoneCount + " of " + row.dataCollectionCount + }, + percentageCollected : { + value : (row.measurementDoneCount / row.measurementCount) * 100, + text : row.measurementDoneCount + " of " + row.measurementCount + }, + percentageMerged : { + value : (row.measurementAveragedCount / row.measurementCount) * 100, + text : row.measurementAveragedCount + " of " + row.measurementCount + }, + date : moment(row.creationDate).format("YYYYMMDD"), + time : moment(row.creationDate).format("YYYYMMDDHHmmss"), + creationDate : row.creationDate }); - } - } ] - }); - return grid; + count ++; + } + } + return data; +}; +ExperimentGrid.prototype.getPanel = function(experiments) { + this.features = experiments; + return this._renderGrid(experiments); }; -VolumeGrid.prototype._prepareData = function(experiment) { - var keys = {}; - for ( var i = 0; i < experiment.getSamples().length; i++) { - var sample = experiment.getSamples()[i]; - var key = ""; - if (sample.macromoleculeId == null) { - key = experiment.getBufferById(sample.bufferId).acronym; - if (keys[key] == null) { - keys[key] = { - macromoleculeId : sample.macromoleculeId, - name : key, - bufferId : sample.bufferId, - volume : 0 - }; - } - keys[key].volume = Number(sample.volume) + Number(keys[key].volume); +ExperimentGrid.prototype.refresh = function(experiments) { + this.experiments = experiments; + var filtered = []; + for ( var i = 0; i < experiments.length; i++) { + if (experiments[i].experimentType != "TEMPLATE") { + filtered.push(experiments[i]); } + } - if ((sample.macromolecule3VO != null) || (sample.macromoleculeId != null)) { - macromoleculeId = sample.macromoleculeId; - if (sample.macromoleculeId == null) { - sample.macromoleculeId = sample.macromolecule3VO.macromoleculeId; - } - key = BIOSAXS.proposal.getMacromoleculeById(sample.macromoleculeId).acronym + " + " + experiment.getBufferById(sample.bufferId).acronym; - if (keys[key] == null) { - keys[key] = { - macromoleculeId : sample.macromoleculeId, - name : key, - bufferId : sample.bufferId, - volume : 0 - }; - } - keys[key].volume = Number(sample.volume) + Number(keys[key].volume); + this.parsedData = this._prepareData(filtered); + + var day = {}; + var dates = []; + for ( var i = 0; i < this.experiments.length; i++) { + var date = moment(this.experiments[i].creationDate).format("MMM Do YYYY"); + if (day[date] == null){ + dates.push({ + date : date, + value : moment(this.experiments[i].creationDate) + }); + day[date] = true; } } - var data = []; - for (var keyId in keys) { - data.push(keys[keyId]); + + this.storeDate.loadData(dates, false); + this.store.loadData(this.parsedData, false); + + /** If it has already been filtered by date we keep the filter **/ + if (this.date != null){ + this._filterByDate(this.date); } - - return data; }; -VolumeGrid.prototype.refresh = function(experiment) { - this.experiment = experiment; - this.macromoleculeGrid.getStore().loadData(this._prepareData(this.experiment), false); -}; +ExperimentGrid.prototype._getTopButtons = function() { + var _this = this; + /** Actions buttons **/ + var actions = []; -VolumeGrid.prototype.render = function() { - this.macromoleculeGrid = this.getVolumesPanel(this._prepareData(this.experiment), "Estimation of required Volume"); + actions.push(Ext.create('Ext.Action', { + icon : '../images/calendar_icon.png', + text : 'Show Calendar', + disabled : false, + handler : function(widget, event) { + var window = Ext.create('Ext.window.Window', { + title : 'Calendar', + width : 600, + height : 600, + modal : true, + closable : true, + layout : { + type : 'vbox', + align : 'stretch' + }, + items : [ { + xtype : 'label', + html : 'Click on a data acquisition to select:', + margin : '5 5 5 5' + }, { + html : '
', + margin : '5 5 5 5' + } - return { - xtype : 'container', - layout : 'vbox', - margin : "0, 0, 0, 5", - items : [ { - xtype : 'container', - layout : 'hbox', - margin : "0, 0, 0, 0", - items : [ this.macromoleculeGrid ] - } ] - }; + ] + }).show(); + + var calendarWidget = new CalendarWidget({ + height : 450 + }); + var aux = _this.limit; + /** we remove the limit temporarily **/ + _this.limit = null; + _this.sessionIdFilter = null; + calendarWidget.loadData(_this._prepareData(_this.experiments)); + _this.limit = aux; + calendarWidget.draw('calendar'); + calendarWidget.onClick.attach(function(sender, date) { + date = moment(date, "YYYY-MM-DD"); + _this._filterByDate(date); + window.close(); + + }); + + } + })); + this.storeDate = Ext.create('Ext.data.ArrayStore', { + fields: ['date', 'value'], + data : [] + }); + + this.dateMenu = Ext.create('Ext.form.field.ComboBox', { + hideLabel: true, + store: this.storeDate, + displayField: 'date', + typeAhead: true, + queryMode: 'local', + margin : '0 0 0 30', + triggerAction: 'all', + emptyText:'Select a date...', + selectOnFocus:true, + width:135, + listeners:{ + scope: this, + 'select': function (a,b,c){ + _this.limit = null; + _this._filterByDate(moment(b[0].raw.value, "YYYY-MM-DD")); + } + } + }); + + actions.push(this.dateMenu); + + actions.push("->"); + if (_this.filtered != null){ + actions.push({ + html : "Experiment Type: " + _this.filtered +"" + }); + } + else{ + actions.push({ + html : "Experiment Type: ALL" + }); + } + return actions; }; -VolumeGrid.prototype.input = function(experiment) { - return { - experiment : DATADOC.getExperiment_10() - }; +/** + * Date format: "YYYY-MM-DD" + */ +ExperimentGrid.prototype._filterByDate = function(date) { + var experimentsFiltered = []; + /** Getting all the experiments of date**/ + for ( var i = 0; i < this.experiments.length; i++) { + var experiment = this.experiments[i]; + if (experiment.creationDate != null) { + var experimentDate = moment(experiment.creationDate); + if (experimentDate.year() == date.year()) { + if (experimentDate.month() == date.month()) { + if (experimentDate.date() == date.date()) { + experimentsFiltered.push(experiment); + } + } + } + } + } + var parsedData = this._prepareData(experimentsFiltered); + this.store.loadData(parsedData); + this.date = date; }; -VolumeGrid.prototype.test = function(targetId) { - var volumeGrid = new VolumeGrid(); - BIOSAXS.proposal = new Proposal(new MeasurementGrid().input().proposal); - var panel = volumeGrid.getPanel(new Experiment(new VolumeGrid().input().experiment)); - Ext.create('Ext.panel.Panel', { - height : 500, - width : 1000, - renderTo : targetId, - items : [ panel ] +/** Only for templates **/ +ExperimentGrid.prototype._removeExperimentById = function(experimentId) { + var _this = this; + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(evt, args) { + _this.grid.setLoading(false); + document.getElementById(BIOSAXS.targetId).innerHTML = ""; + BIOSAXS.start(BIOSAXS.targetId); }); + this.grid.setLoading("Removing experiment "); + adapter.removeExperimentById(experimentId); }; - -/** - * Example of a tab panel to be populated with widgets - * - * @width width in pixels - * @height height in pixels - * - * #myEvent event that this class is supposed to throw - * - **/ -function ExampleTabs(args) { - this.width = 500; - this.height = 500; + +ExperimentGrid.prototype._renderGrid = function() { + var _this = this; + + /** Store **/ + this.store = Ext.create('Ext.data.Store', { + fields : this._getColumns(), + groupField : 'date', + autoload : true, + data : [], + remoteSort: false, + sorters : this.sorters + }); + - if (args != null){ - if (args.width != null){ - this.width= args.width; - } - if (args.height != null){ - this.height= args.height; - } - } - /** Events **/ - this.myEvent = new Event(); -} - -/** Populate the widget **/ -ExampleTabs.prototype.refresh = function(macromolecule) { -}; + var groupingFeature = Ext.create('Ext.grid.feature.Grouping', { + groupHeaderTpl : Ext.create('Ext.XTemplate', + "
{name:this.formatName}
", { + formatName : function(name) { + return _this.dates[name]; + } + }), + hideGroupedHeader : true, + startCollapsed : false + }); -/** It creates a tab panel with the specified with and height **/ -ExampleTabs.prototype.getPanel = function() { - this.panel = Ext.createWidget('tabpanel', { - height : this.height, - width : this.width, - style : { - padding : 2 + this.features = []; + if (this.grouping) { + this.features.push(groupingFeature); + } + + /** Grid **/ + this.grid = Ext.create(this.gridType, { + hideHeaders : this.hideHeaders, + resizable : true, + title : this.title, + width : this.width, + minHeight : this.minHeight, + height : this.height, + features : this.features, + store : this.store, + columns : this._getColumns(), + selModel : { + mode : 'SINGLE' }, - items : [ { - tabConfig : { - title : 'Test', - icon : '/ispyb/images/plane-small.gif' + viewConfig : { + stripeRows : true, + getRowClass : function(record, rowIdx, params, store) { + if (record.raw.type == "TEMPLATE") { + return "template-color-row"; + } + if ((record.raw.type == "CALIBRATION") && (record.raw.status == "FINISHED")) { + return "blue-row"; + } }, - items : [ { - xtype : 'container', - margin : '5 5 5 5', - items : [ ] - } ] - } ] + listeners : { + itemdblclick : function(dataview, record, item, e) { + _this._editExperiment(record.raw.experimentId); + }, + cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'GO') { + _this._editExperiment(record.raw.experimentId); + } + + if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'REMOVE') { + _this._removeExperimentById(record.raw.experimentId); + } + + } + } + } }); - return this.panel; -}; - -/** - * Shows an experiments with the specimens, measurements, analysis tabs where - * results are shown and the frames widget - * - * @targetId - */ -function ExperimentTabs(targetId) { - this.height = 900; - this.targetId = targetId; + var actions = _this._getTopButtons(); - this.id = BUI.id(); - var _this = this; + if (this.tbar) { + this.grid.addDocked({ + xtype : 'toolbar', + items : actions + }); + this.grid.getSelectionModel().on({ + selectionchange : function(sm, selections) { + if (selections.length) { + for ( var i = 0; i < actions.length; i++) { + if (actions[i].enable) { + actions[i].enable(); + } + } + } else { + for ( var i = 0; i < actions.length; i++) { + if (actions[i].alwaysEnabled == false) { + if (actions[i].disable) { + actions[i].disable(); + } + } + } + } + } + }); + } - this.INTERVAL_UPDATE = BUI.getUpdateInterval(); + return this.grid; +}; - this.gridHeight = 1000; - /** data * */ - this.experiment = null; +ExperimentGrid.prototype._getColumns = function() { + var _this = this; + function actionItemRenderer(value, meta, record, rowIx, ColIx, store) { + if (record.raw.buffer3VOs != null) { + if (record.raw.buffer3VOs.length > 0) { + return 'x-hide-display'; + } + } - - /** Specimen Widget contains a specimenGrid and a sampleChangerWidget than can be displayed with are vertical or horizontal layout **/ - this.specimenWidget = new SpecimenWidget({ - height : 600 - }); - - - /** For Overview * */ - /*this.samplePlateGroupWidget = new SamplePlateGroupWidget({ - showTitle : false, - height : 250, - margin : 5, - border : 0 + if (record.data.platesCount > 0) { + return 'x-hide-display'; + } + } - });*/ + return [ + { + text : 'experimentId', + dataIndex : 'experimentId', + name : 'experimentId', + type : 'string', + hidden : true + }, + { + xtype : 'rownumberer', + width : 40 + }, + { + text : 'Name', + dataIndex : 'name', + name : 'name', + type : 'string', + flex : 1 + }, - /** For Measurements * */ - /*this.measurementSamplePlateGroupWidget = new SamplePlateGroupWidget({ - showTitle : false, - height : 250, - margin : '5 0 0 0', - border : 0 - });*/ + { + text : 'Type', + dataIndex : 'type', + name : 'type', + type : 'string', + flex : 1, + renderer : function(val) { + if (val == "CALIBRATION") { + return "" + val + ""; + } + + return val; + } + }, + { + text : 'Macromolecules', + name : 'macromolecules_names', + dataIndex : 'macromolecules_names', + flex : 1, + renderer : function(val) { + if (val != null) { + return " " + val + ""; + } + return " Information not available"; + } + }, + { + text : 'Buffers', + dataIndex : 'buffer_names', + name : 'buffer_names', + flex : 1, + hidden : true, + renderer : function(val) { + return "Buffer/s: " + val + ""; + } + }, + { + text : 'Status', + dataIndex : 'status', + name : 'status', + type : 'string', + flex : 1, + renderer : function(val, x, sample) { + if (sample.raw.type == "TEMPLATE") { + return "READY"; + } + if (sample.raw.status == "ABORTED") { + return "" + val + ""; + } + return "" + val + ""; + } + }, + { + text : 'Download', + dataIndex : 'creationDate', + name : 'creationDate', + renderer : function(val, x, sample) { + if (sample != null) { + if (sample.raw.type == "HPLC") { + return; + } + return BUI.getZipHTMLByExperimentId(sample.raw.experimentId, sample.raw.name); + } + }, + width : 100 - this.measurementGridDone = new MeasurementGrid({ - height : 600, - minHeight : 400, - maxHeight : 800, - positionColumnsHidden : true, - showTitle : false, - estimateTime : true, - width : 900, - maxWidth : 1500, - addBtnEnable : false, - markDone : true, - removeBtnEnabled : false - }); - this.measurementGridDone.onSelected.attach(function(sender, measurements) { - var specimens = []; - for ( var i = 0; i < measurements.length; i++) { - specimens.push(_this.experiment.getSampleById(measurements[i].specimenId)); - } - //_this.measurementSamplePlateGroupWidget.selectSpecimens(specimens); - }); + }, + { + header : 'Measurements', + dataIndex : 'percentageCollected', + name : 'percentageCollected', + type : 'string', + renderer : function(val, y, sample) { + if ((sample.raw.type == "TEMPLATE") || (sample.raw.type == "HPLC")) { + return; + } + return "
" + BUI.getProgessBar(sample.raw.percentageCollected.value, sample.raw.percentageCollected.text) + "
"; + }, + width : 100 + }, + { + header : 'Averaged', + dataIndex : 'percentageMerged', + name : 'percentageMerged', + type : 'string', + renderer : function(val, y, sample) { + if ((sample.raw.type == "TEMPLATE") || (sample.raw.type == "HPLC")) { + return; + } + return "
" + BUI.getProgessBar(sample.raw.percentageMerged.value, sample.raw.percentageMerged.text) + "
"; + }, + width : 100 + }, + { + header : 'Subtractions', + dataIndex : 'percentageAnalysed', + name : 'percentageAnalysed', + type : 'string', + renderer : function(val, y, sample) { + if ((sample.raw.type == "TEMPLATE") || (sample.raw.type == "HPLC")) { + return; + } + return "
" + BUI.getProgessBar(sample.raw.percentageAnalysed.value, sample.raw.percentageAnalysed.text) + "
"; + }, + width : 100 + }, - /** AnalysisGrid * */ - this.analysisGrid = new AnalysisGrid({ - height : Ext.getBody().getViewSize().height * 0.9 - 300, - positionColumnsHidden : true, - sorters : [ { - property : 'priorityLevelId', - direction : 'ASC' - } ] - }); - - - /** Queue * */ - this.queueGrid = new QueueGrid({ - url : BUI.getQueueUrlByExperiment(), - height : Ext.getBody().getViewSize().height * 0.9 - 300, - width : Ext.getBody().getViewSize().width * 0.9 - 300, - isGrouped : true, - tbar : false, - bbar : false, - sorter : [{ - property: 'measurementId', - direction: 'DESC' - }] - }); + { + text : 'time', + dataIndex : 'time', + name : 'time', + hidden : true, + renderer : function(val) { + return val; + }, + width : 100 -} + }, { + text : 'Date', + dataIndex : 'date', + name : 'date', + renderer : function(val) { + return val; + }, + width : 100 -ExperimentTabs.prototype.error = function(error) { - var e = JSON.parse(error); - showError(e); - this.panel.setLoading(false); -}; + }, -ExperimentTabs.prototype.draw = function(experiment) { - this.renderDataAcquisition(experiment); -}; + { + text : 'Time', + dataIndex : 'creationDate', + name : 'creationDate', + renderer : function(val) { + return moment(val).format(" HH:mm:ss"); + }, + width : 100 -ExperimentTabs.prototype.refreshAnalysisData = function() { - var _this = this; - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, data) { - _this.analysisGrid.refresh(data, {experiment : _this.experiment}); - }); - adapter.getAnalysisInformationByExperimentId(this.experiment.experimentId); - - /** Auto load **/ - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, data) { - _this.queueGrid.refresh(data); - }); - adapter.getCompactAnalysisByExperimentId(this.experiment.experimentId); - this.queueGrid.store.proxy.url = BUI.getQueueUrlByExperiment(this.experiment.experimentId); - this.queueGrid.refresh(); + }, { + id : _this.id + 'GO', + width : 80, + sortable : false, + renderer : function(value, metaData, record, rowIndex, colIndex, store) { + return BUI.getGreenButton('GO'); + } + } ]; }; -ExperimentTabs.prototype.getSpecimenContainerHeight = function(experiment) { - var maxItems = 0; - if (maxItems < experiment.getSamples().length + 1) { - maxItems = experiment.getSamples().length + 1; - } - - var height = (maxItems + 1) * 40 + 40; - if (height > 400) { - height = 400; +/** Changes location.href in order to edit the experiment **/ +ExperimentGrid.prototype._editExperiment = function(experimentId) { + if (Ext.urlDecode(window.location.href).sessionId != null) { + location.href = 'viewProjectList.do?reqCode=display&experimentId=' + experimentId + '&sessionId='+ Ext.urlDecode(window.location.href).sessionId; + } else { + location.href = 'viewProjectList.do?reqCode=display&experimentId=' + experimentId; } - return height; -}; - -ExperimentTabs.prototype.getExperimentTitle = function() { - var experimentHeaderForm = new ExperimentHeaderForm(); - return experimentHeaderForm.getPanel(this.experiment); }; -ExperimentTabs.prototype.renderDataAcquisition = function(experiment) { - var _this = this; - this.experiment = experiment; +ExperimentGrid.prototype.input = function() { + var experiments = DATADOC.getExperimentList_10(); + return { + experiments : experiments, + proposal : new MeasurementGrid().input().proposal - var specimenGrid = new SpecimenGrid({ - height : 400, - maxHeight : 500, - width : 890 - }); + }; +}; - specimenGrid.onClick.attach(function(sender, args) { - }); +ExperimentGrid.prototype.test = function(targetId) { + var experimentGrid = new ExperimentGrid({ + height : 350, + minHeight : 350, + width : 1000 - specimenGrid.onSelected.attach(function(sender, specimens) { - //_this.samplePlateGroupWidget.selectSpecimens(specimens); - }); - var specimenContainer = this.specimenWidget.getPanel(); - this.specimenWidget.refresh(experiment); - /* - var specimenContainer = Ext.create('Ext.container.Container', { - layout : 'hbox', - width : 900, - padding : '10 0 0 2', - items : [ specimenGrid.getPanel() ] }); + BIOSAXS.proposal = new Proposal(experimentGrid.input().proposal); + var panel = experimentGrid.getPanel(experimentGrid.input().experiments); + experimentGrid.refresh(experimentGrid.input().experiments); + panel.render(targetId); +}; + + +function FitStructureToExperimentDataGrid(args) { + this.height = null; + this.width = null; + this.id = BUI.id(); + + if (args != null) { + if (args.height != null) { + this.height = args.height; + + if (args.width != null) { + this.width = args.width; + } + } + } + + this.onSelected = new Event(this); +} + +FitStructureToExperimentDataGrid.prototype.getStructuresByMacromolecule = function(macromolecule) { + var structures = []; + if (macromolecule.structure3VOs != null) { + if (macromolecule.structure3VOs.length > 0) { + for (var i = 0; i < macromolecule.structure3VOs.length; i++) { + structures.push(macromolecule.structure3VOs[i]); + } + } + } + return structures; +}; + +FitStructureToExperimentDataGrid.prototype._prepareData = function(subtractions) { + var data = []; + for (var i = 0; i < subtractions.length; i++) { + + for (var j = 0; j < subtractions[i].fitStructureToExperimentalData3VOs.length; j++) { + var fit = subtractions[i].fitStructureToExperimentalData3VOs[j]; + data.push({ + fit : fit.fitFilePath, + fitStructureToExperimentalDataId : fit.fitStructureToExperimentalDataId, + mixtureToStructure3VOs : fit.mixtureToStructure3VOs, + subtractedFile : subtractions[i].substractedFilePath + }); + } + } + return data; + +}; + +FitStructureToExperimentDataGrid.prototype.refresh = function(subtractions) { + this.store.loadData(this._prepareData(subtractions), false); +}; + +FitStructureToExperimentDataGrid.prototype.getPanel = function() { + var _this = this; + this.store = Ext.create('Ext.data.Store', { + fields : [ 'fit', 'subtractedFile', 'structureId', 'fitFilePath', 'mixtureToStructure3VOs' ], + data : [] + }); + + this.store.sort([ { + property : 'name', + direction : 'ASC' + } ]); + + this.selModel = Ext.create('Ext.selection.RowModel', { + allowDeselect : true, + mode : 'MULTI', + listeners : { + selectionchange : function(sm, selections) { + var selected = []; + for (var i = 0; i < selections.length; i++) { + selected.push(selections[i].raw); + } + _this.onSelected.notify(selected); + } + } + }); + + this.panel = Ext.create('Ext.grid.Panel', { + store : this.store, + deferredRender : false, + width : this.width, + height : this.height, + margin : 10, + selModel : this.selModel, + columns : [ { + text : 'Name', + dataIndex : 'fit', + flex : 1, + renderer : function(val, b, record) { + return BUI.getFileName(val); + } + }, { + text : 'PDB', + dataIndex : 'mixtureToStructure3VOs', + flex : 1, + renderer : function(values, b, record) { + var html = ""; + for (var i = 0; i < values.length; i++) { + var structure = BIOSAXS.proposal.getStructuresById(values[i].structureId); + html = html + ""; + if (structure != null){ + html = html + ""; + } + html = html + ""; + } + return html + "
" + structure.name + "
"; + } + }, { + text : 'Volume Fraction', + dataIndex : 'mixtureToStructure3VOs', + flex : 1, + renderer : function(values, b, record) { + var html = ""; + for (var i = 0; i < values.length; i++) { + html = html + ""; + html = html + ""; + html = html + ""; + } + + return html + "
" + values[i].volumeFraction + "
"; + } + },{ + text : 'Subtraction', + dataIndex : 'subtractedFile', + flex : 1, + renderer : function(values, b, record) { + return values.split("/")[values.split("/").length - 1]; + } + },], + + viewConfig : { + enableTextSelection : true, + preserveScrollOnRefresh : true + }, + listeners : { + cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + }, + afterrender : function() { + } + } + }); + return this.panel; +}; + +/** Static method **/ +FitStructureToExperimentDataGrid.prototype.RUNFitScattering = function(subtractionId, structureId) { + var _this = this; + /** Add to Workflow **/ + var adapter = new BiosaxsDataAdapter(); + var workflow = { + 'workflowTitle' : 'FitExperimentalDatatoStructure', + 'comments' : 'FitExperimentalDatatoStructure run from ISPyB for subtractionId: ' + subtractionId + ' and structureId ' + structureId + } + + var inputs = [ { + name : 'subtractionId', + value : subtractionId + }, { + name : 'structureId', + value : structureId + } ]; + + adapter.onSuccess.attach(function(sender, data) { + /** Add to Fit **/ + var adapter2 = new BiosaxsDataAdapter(); + var fit = { + 'workflowId' : data.workflowId, + 'subtractionId' : subtractionId, + 'structureId' : structureId, + 'comments' : 'Sent to workflow engine' + } + adapter2.onSuccess.attach(function(sender, fit) { + _this.refresh(_this.subtractionId, _this.macromolecule); + }); + adapter2.addFitStructureData(fit); + + }); + adapter.addWorkflow(workflow, inputs); + +}; + +/** + * It shows buffer grid with a top bar with "Add" button + * + * @height + * @searchBar + * @collapsed + * @width + */ +function FrameGrid(args) { + this.height = 500; + this.width = 500; + this.searchBar = false; + this.tbar = false; + this.collapsed = false; - specimenGrid.refresh(experiment);*/ - var experimentList = new ExperimentList([ _this.experiment ]); - var measurementContainer = Ext.create('Ext.container.Container', { - layout : 'vbox', - padding : '5px 0px 0px 10px', - items : [] - }); - measurementContainer.insert(0, _this.measurementGridDone.getPanel(this.experiment.getMeasurements(), experimentList)); -// measurementContainer.insert(1, _this.measurementSamplePlateGroupWidget.getPanel(experiment)); + if (args != null) { + if (args.height != null) { + this.height = args.height; + } + + if (args.searchBar != null) { + this.searchBar = args.searchBar; + } - // this.dataCollectionCurveVisualizer = new DataCollectionCurveVisualizer(); - // this.dataCollectionCurveVisualizer.experiments = [experiment]; - // this.dataCollectionCurveVisualizer.dataCollectionFrameTree.experiments = - // [experiment]; - // this.dataCollectionCurveVisualizer.dataCollections = - // experiment.getDataCollections(); + if (args.tbar != null) { + this.tbar = args.tbar; + } - this.panel = Ext.createWidget('tabpanel', { - plain : true, - style : { - padding : 2 - }, - items : [ { - tabConfig : { - id : 'genralTabl', - title : "Overview" - }, - items : [ specimenContainer ] - }, { - tabConfig : { - title : 'Measurements' - }, - items : [ measurementContainer] - }, { - tabConfig : { - id : 'SpecimenTab', - title : 'Analysis', - hidden : this.isTemplate() - }, - items : [ { - xtype : 'container', - layout : 'vbox', - padding : '5px 0px 10px 10px', - items : [ _this.analysisGrid.getPanel([]) ] - } ] - }, - { - tabConfig : { - id : 'newAnalysisTab', - title : 'Analysis BETA', - hidden : this.isTemplate() - }, - items : [ { - xtype : 'container', - layout : 'vbox', - padding : '5px 0px 10px 10px', - items : [ _this.queueGrid.getPanel([]) ] - } ] + if (args.collapsed != null) { + this.collapsed = args.collapsed; } - ] + + if (args.width != null) { + this.width = args.width; + } + } +} + +FrameGrid.prototype._edit = function(bufferId) { + var _this = this; + var window = new BufferWindow(); + window.onSuccess.attach(function(sender, proposal) { + _this.store.loadData(BIOSAXS.proposal.getBuffers()); }); + window.draw(BIOSAXS.proposal.getBufferById(bufferId)); +}; - return this.getPanel(this.panel); +FrameGrid.prototype.refresh = function(buffers, experimentId) { + this.experimentId = experimentId; + this.store.loadData(this._prepareData(buffers), false); }; -ExperimentTabs.prototype.isTemplate = function() { - if (this.experiment.json.type == "TEMPLATE") { - return true; - } - return false; +FrameGrid.prototype._prepareData = function(buffers) { + return buffers; }; -ExperimentTabs.prototype.update = function() { +FrameGrid.prototype._getTbar = function() { var _this = this; - var inter; - if (!_this.isTemplate()) { - function updateExperiments() { - _this.refreshAnalysisData(); - window.clearInterval(inter); - inter = setInterval(function() { - updateExperiments(); - }, _this.INTERVAL_UPDATE); - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, data) { - var experiment = new Experiment(data); - _this.measurementGridDone.refresh(experiment.getMeasurements(), new ExperimentList([ experiment ])); + var actions = []; + + actions.push(Ext.create('Ext.Action', { + icon : '../images/add.png', + text : 'Add Buffer', + disabled : false, + handler : function(widget, event) { + var window = new BufferWindow(); + window.onSuccess.attach(function(sender) { + _this.store.loadData(BIOSAXS.proposal.getBuffers()); }); - adapter.getExperimentById(_this.experiment.json.experimentId, "MEDIUM"); + window.draw({}); } - inter = setInterval(function() { - updateExperiments(); - }, _this.INTERVAL_UPDATE); - } + })); + return actions; }; -ExperimentTabs.prototype.getPanel = function(panel) { +FrameGrid.prototype.getPanel = function(buffers) { var _this = this; - if (this.experimentPanel == null) { - this.experimentPanel = Ext.create('Ext.container.Container', { - bodyPadding : 2, - width : Ext.getBody().getViewSize().width * 0.9, - renderTo : this.targetId, - style : { - padding : 2 - }, - items : [ this.getExperimentTitle(), this.panel ], - listeners : { - afterrender : function() { - _this.refreshAnalysisData(); - _this.update(); - } - } - }); - } - - return this.experimentPanel; -}; - -ExperimentTabs.prototype.input = function() { - return { - experiment : DATADOC.getExperiment_10(), - proposal : DATADOC.getProposal_10() - }; -}; - -ExperimentTabs.prototype.test = function(targetId) { - var experimentTabs = new ExperimentTabs(targetId); - BIOSAXS.proposal = new Proposal(experimentTabs.input().proposal); - experimentTabs.draw(new Experiment(experimentTabs.input().experiment)); -}; + this.store = Ext.create('Ext.data.Store', { + fields : [ 'frameNumber', 'I0', 'Rg', 'Mass', 'Vc', 'Qr', 'quality'], + data : buffers + }); - -/** - * Shows an experiments with the specimens, measurements, analysis tabs where results are shown and the frames widget - * - * @targetId - */ -function HPLCTabs(targetId) { - this.height = 1400; - this.targetId = targetId; + this.store.sort('frameNumber'); - this.id = BUI.id(); + var type = 'Ext.grid.Panel'; + if (this.searchBar == true) { + type = 'Ext.ux.LiveSearchGridPanel'; + } - this.gridHeight = 600; - this.pointsCount = 1036; - this.plotHeight = 350; - this.plotWidth = 800; - this.plotPanelPadding = 5; + this.grid = Ext.create(type, { + store : this.store, + height : this.height, + width : this.width, + margin : 5, + columns : [{ + text : 'Frame', + dataIndex : 'frameNumber', + flex : 1 + },{ + text : 'I0', + dataIndex : 'I0', + flex : 1, + renderer : function(val, y, sample) { + return BUI.formatValuesUnits(sample.data.I0, "", 12, 3); + } + },{ + text : 'Rg', + dataIndex : 'Rg', + flex : 1, + renderer : function(val, y, sample) { + return BUI.formatValuesUnits(sample.data.Rg, "nm", 12, 3); + } + },{ + text : 'Mass', + dataIndex : 'Mass', + flex : 1, + renderer : function(val, y, sample) { + return BUI.formatValuesUnits(sample.data.Mass, "", 12, 3); + } + },{ + text : 'Vc', + dataIndex : 'Vc', + flex : 1, + renderer : function(val, y, sample) { + return BUI.formatValuesUnits(sample.data.Vc, "", 12, 3); + } + },{ + text : 'Qr', + dataIndex : 'Qr', + flex : 1, + renderer : function(val, y, sample) { + return BUI.formatValuesUnits(sample.data.Qr, "", 12, 3); + } + },{ + text : 'Quality', + dataIndex : 'quality', + flex : 1, + renderer : function(val, y, sample) { + return (Number(sample.data.quality) * 100).toFixed(2) + "%"; + } + }, + { + text : '', + renderer : function(val, x, sample) { + if (sample != null) { + return BUI.getZipHTMLByFrameRangeId(_this.experimentId, sample.raw.frameNumber, sample.raw.frameNumber); + } + }, + width : 100 + }], + flex : 1, + viewConfig : { + stripeRows : true + } + }); - var _this = this; + /** Adding the tbar **/ + if (this.tbar) { + this.grid.addDocked({ + xtype : 'toolbar', + items : this._getTbar() + }); + } + return this.grid; +}; - this.analysisGrid = new HPLCAnalysisGrid({ - height : Ext.getBody().getViewSize().height * 0.9 - 300, - positionColumnsHidden : true, - sorters : [ { - property : 'priorityLevelId', - direction : 'ASC' - } ] - }); +FrameGrid.prototype.input = function() { + return []; +}; - this.frameGrid = new FrameGrid({ - width : 600, - height : 60, - collapsed : false, - tbar : false - }); - - this.peakGrid = new PeakGrid({ - width : 550, - height : 500, - collapsed : false, - tbar : false - }); - - this.peakGrid2 = new PeakGrid({ - width : 102, - height : this.plotHeight, +FrameGrid.prototype.test = function(targetId) { + var frameGrid = new FrameGrid({ + width : 800, + height : 350, collapsed : false, - tbar : false, - showExtendedColumns : true + tbar : true }); - - this.mainPlotPanel = new HPLCGraph({ - title : 'I0', - width : this.plotWidth - 110, - height : this.plotHeight, - bbar : true, - plots : { - "I0" : true, - "Rg" : true - }, - xlabel : "HPLC Frames", - scaled : true, - interactionModel : { - 'dblclick' : function(event, g, context) { - _this._selectFrame(g.lastx_); - var annotations = []; - annotations.push({ - series : g.selPoints_[0].name, - x : g.lastx_, - width : 100, - height : 23, - tickHeight : 4, - shortText : g.lastx_, - text : g.lastx_, - attachAtBottom : true - }); - g.setAnnotations(annotations); - } + var panel = frameGrid.getPanel([]); + panel.render(targetId); +}; + + +function PeakGrid(args) { + this.height = 500; + this.searchBar = false; + this.tbar = false; + this.collapsed = false; + this.width = 500; + this.showExtendedColumns = false; + + if (args != null) { + if (args.showExtendedColumns != null) { + this.showExtendedColumns = args.showExtendedColumns; + } + if (args.height != null) { + this.height = args.height; + } + if (args.searchBar != null) { + this.searchBar = args.searchBar; } - }); - this.intensityPlotPanel = new MergesHPLCGraph({ - title : 'Scattering', - width : this.plotWidth, - height : 500, - showRangeSelector : false, - xParam : 0, - xlabel : "scattering_I", - plots : { - "scattering_I" : true, - "subtracted_I" : true, - "buffer_I" : true + if (args.tbar != null) { + this.tbar = args.tbar; } - }); -} + if (args.collapsed != null) { + this.collapsed = args.collapsed; + } -HPLCTabs.prototype._selectFrame = function(frameNumber) { - try{ - this._renderScatteringCurve(frameNumber); - this.frameGrid.refresh([this.mainPlotPanel.getDataByFrameNumber(frameNumber)], this.experiment.experimentId); - } - catch(e){ - console.log(e); + if (args.width != null) { + this.width = args.width; + } } -}; +} -HPLCTabs.prototype.error = function(error) { - var e = JSON.parse(error); - showError(e); - this.panel.setLoading(false); -}; -HPLCTabs.prototype.loadDataMainPlot = function(data) { - var zeroArray = []; - for ( var i = 0; i < data.I0.length; i++) { - zeroArray.push(0); - } - data = [ { - param : "I0", - data : data.I0, - std : data.I0_Stdev, - color : '#0066CC', - label : "I0" - }, - { - param : "sum_I", - label : "sum_I", - color : "#00FF00", - data : data.sum_I, - std : zeroArray - }, - { - param : "Rg", - label : "Rg", - color : "#21610B", - data : data.Rg, - std : data.Rg_Stdev - }, { - param : "Mass", - data : data.mass, - std : data.mass_Stdev, - color : '#FF9900', - label : "Mass" - }, { - param : "Vc", - data : data.Vc, - std : data.Vc_Stdev, - color : '#990099', - label : "Vc" - }, { - param : "Qr", - data : data.Qr, - std : data.Qr_Stdev, - color : '#FF0066', - label : "Qr" - }, { - param : "quality", - label : "quality", - color : "#FF00FF", - data : data.quality, - std : zeroArray - } ]; - this.data = data; - this.mainPlotPanel.loadData(data); +PeakGrid.prototype.refresh = function(buffers) { + this.store.loadData(this._prepareData(buffers), false); }; -HPLCTabs.prototype._loadIntensityPlotByFrameNumber = function(frameNumber) { - var _this = this; +PeakGrid.prototype._prepareData = function(buffers) { - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, data) { - var array = []; - data = [ { - param : "q", - data : data[frameNumber].q, - std : data[frameNumber].q, - fstd : function(a) { - return parseFloat(a); - }, - color : '#green', - label : "q", - showOnMenu : false - }, - { - param : "scattering_I", - data : data[frameNumber].scattering_I, - fdata : function(a) { - return Math.log(parseFloat(a)); - }, - std : data[frameNumber].scattering_Stdev, - fstd : function(a) { - return Math.log(Math.abs(parseFloat(a))); - }, - color : 'green', - label : "Log(I) Sample" - }, - { - param : "buffer_I", - data : data[frameNumber].buffer_I, - fdata : function(a) { - return Math.log(parseFloat(a)); - }, - fstd : function(a) { - return Math.log(Math.abs(parseFloat(a))); - }, - std : data[frameNumber].subtracted_Stdev, - color : '#0000FF', - label : "Log(I) Avg Buf." - }, - { - param : "subtracted_I", - data : data[frameNumber].subtracted_I, - fdata : function(a) { - var value = (Math.log(parseFloat(a))); - if (isNumber(value)) - return value; - }, - fstd : function(a) { - var value = Math.log(Math.abs(parseFloat(a))); - if (isNumber(value)) - return value; - }, - std : data[frameNumber].subtracted_Stdev, - color : 'red', - label : "Log(I) Sub." - } - ]; +// for ( var i = 0; i < buffers.length; i++) { +// buffers[i].name = "Peak #" + (i+1); +// } + /** Adding information of buffer **/ + if (buffers.length > 0){ + buffers.unshift({ + name : "BUFFER", + start : 0, + experimentId : buffers[0].experimentId, + end : buffers[0].start - 1 + }); + } + + + return buffers; +}; - _this.intensityPlotPanel.xlabel = "Frame " + frameNumber + " (q, nm-1)"; +PeakGrid.prototype._getTbar = function() { + var _this = this; + var actions = []; - if (_this.intensityPlotPanel.hplcData == null) { - /** It creates also top bar **/ - _this.intensityPlotPanel.loadData(data); - } else { - _this.intensityPlotPanel.reloadData(data); - + actions.push(Ext.create('Ext.Action', { + icon : '../images/add.png', + text : 'Add Buffer', + disabled : false, + handler : function(widget, event) { + var window = new BufferWindow(); + window.onSuccess.attach(function(sender) { + _this.store.loadData(BIOSAXS.proposal.getBuffers()); + }); + window.draw({}); } - _this.intensityPlotPanel.panel.setLoading(false); - }); - adapter.onError.attach(function(sender, data) { - _this.intensityPlotPanel.panel.setLoading(false); - }); - this.intensityPlotPanel.panel.setLoading("Retrieving Frame " + frameNumber); - adapter.getH5FrameScattering(this.experiment.json.experimentId, frameNumber); + })); + return actions; }; -HPLCTabs.prototype._renderScatteringCurve = function(frameNumber) { +PeakGrid.prototype.getPanel = function(buffers) { var _this = this; + this.peakCount = 0; + this.store = Ext.create('Ext.data.Store', { + fields : ['name', {name:'start', type : 'int'}, {name:'end', type : 'int'}], + data : buffers + + }); - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, data) { - _this.intensityPlotPanel.setPeaks(data); - _this._loadIntensityPlotByFrameNumber(frameNumber); - try{ - var peaks = []; - for(key in data){ - peaks.push({ - start : key.split("-")[0], - end : key.split("-")[1], - experimentId : _this.experiment.json.experimentId - }); + this.store.sort('start'); + + var type = 'Ext.grid.Panel'; + if (this.searchBar == true) { + type = 'Ext.ux.LiveSearchGridPanel'; + } + this.grid = Ext.create(type, { + store : this.store, + height : this.height, + width : this.width, + margin : 5, + columns : [ + { + text : '', + dataIndex : 'name', + width : 100, + hidden : _this.showExtendedColumns, + renderer : function(val, y, sample) { + if (sample.data.name == "BUFFER") + return "BUFFER"; + _this.peakCount++; + return "Peak #" + _this.peakCount; + } + + + }, + { + text : 'Peaks', + dataIndex : 'start', + sortable : true, + width : 100, + hidden: !_this.showExtendedColumns, + renderer : function(val, y, sample) { + return "From " + Number(sample.raw.start) + " to " + Number(sample.raw.end); + } + }, + { + text : 'Frames', + hidden : _this.showExtendedColumns, + columns : [ + { + text : 'Start', + dataIndex : 'start', + sortable : true, + hidden : _this.showExtendedColumns, + width : 100, + renderer : function(val, y, sample) { + return Number(val); + } + },{ + text : 'End', + dataIndex : 'end', + width : 100, + hidden : _this.showExtendedColumns, + renderer : function(val, y, sample) { + return Number(val); + } + },{ + text : 'Total', + dataIndex : 'end', + width : 100, + hidden : _this.showExtendedColumns, + renderer : function(val, y, sample) { + return "" + (sample.raw.end - sample.raw.start) + " frames"; + } + } + ] + } +// , +// { +// text : '', +// hidden : _this.showExtendedColumns, +// renderer : function(val, x, sample) { +// if (sample != null) { +// return BUI.getZipHTMLByFrameRangeId(sample.raw.experimentId, sample.raw.start, sample.raw.end); +// } +// }, +// width : 100 +// } + ], + flex : 1, + viewConfig : { + stripeRows : true, + getRowClass : function(record, rowIdx, params, store) { + if (record.raw.name == "BUFFER") { + return "blue-row"; + } } - _this.peakGrid2.refresh(JSON.parse(JSON.stringify(peaks))); - _this.peakGrid.refresh(peaks); -// console.log(peaks); - } - catch(e){ - showError(e); } }); - adapter.onError.attach(function(sender, data) { - _this.intensityPlotPanel.setLoading(false); + + /** Adding the tbar **/ + if (this.tbar) { + this.grid.addDocked({ + xtype : 'toolbar', + items : this._getTbar() + }); + } + return this.grid; +}; + +PeakGrid.prototype.input = function() { + return []; +}; + +PeakGrid.prototype.test = function(targetId) { + var PeakGrid = new PeakGrid({ + width : 800, + height : 350, + collapsed : false, + tbar : true }); - this.intensityPlotPanel.panel.setLoading("Reading HDF5 File "); - adapter.getH5FramesMerge(this.experiment.json.experimentId); + + var panel = PeakGrid.getPanel([]); + panel.render(targetId); }; + +/** + * Macromolecule Grid showing macromolecules and adding anb updating buttons + * + * @height + * @maxHeight + * @width + * @cssFontStyle + * @searchBar makes this grid as Ext.ux.LiveSearchGridPanel + * @tbar top bar containing "Add" and "Update From SMIS" button + * @collapsed + * @collapsible + * @btnEditVisible + * @btnRemoveVisible + * @multiselect makes it multiselect using Ext.selection.CheckboxModel + * + * #onSelected + * #onMacromoleculesChanged + */ +function MacromoleculeGrid(args) { + this.height = 500; + this.width = 500; + this.id = BUI.id(); + this.maxHeight = this.height; -HPLCTabs.prototype._postRenderOverviewPanel = function() { - var _this = this; - var adapter = new BiosaxsDataAdapter(); - adapter.onError.attach(function(sender, data) { - data = data.replace(/NaN/g, '0'); - _this.loadDataMainPlot(JSON.parse(data)); - + this.searchBar = false; + this.tbar = false; + + this.collapsible = true; + this.collapsed = true; + + this.btnEditVisible = true; + this.btnRemoveVisible = false; + this.multiselect = false; + + /** Font style applied to the acronym column **/ + this.cssFontStyle = null; + + if (args != null) { + if (args.height != null) { + this.height = args.height; + this.maxHeight = this.height; + } + if (args.maxHeight != null) { + this.maxHeight = args.maxHeight; + } + if (args.width != null) { + this.width = args.width; + } + if (args.cssFontStyle != null) { + this.cssFontStyle = args.cssFontStyle; + } + + if (args.searchBar != null) { + this.searchBar = args.searchBar; + } + if (args.tbar != null) { + this.tbar = args.tbar; + } + if (args.collapsible != null) { + this.collapsible = args.collapsible; + } + if (args.collapsed != null) { + this.collapsed = args.collapsed; + } + if (args.btnEditVisible != null) { + this.btnEditVisible = args.btnEditVisible; + } + if (args.btnRemoveVisible != null) { + this.btnRemoveVisible = args.btnRemoveVisible; + } + if (args.multiselect != null) { + this.multiselect = args.multiselect; + } + } - }); - adapter.onSuccess.attach(function(sender, data) { - _this.loadDataMainPlot(data); - _this._renderScatteringCurve(0); - }); - adapter.getH5fileParameters(this.experiment.json.experimentId, [ - "I0", "I0_Stdev", "sum_I","Rg", "Rg_Stdev", "Vc", "Vc_Stdev", "Qr", "Qr_Stdev", "mass", "mass_Stdev", "quality" ]); + this.onSelected = new Event(); -}; + this.onMacromoleculesChanged = new Event(); +} -HPLCTabs.prototype._postRenderDetailsPanel = function() { - if (this.data != null) { - this.I0PlotPanel.loadData(this.data); - this.RgPlotPanel.loadData(this.data); - this.MassPlotPanel.loadData(this.data); - this.VcPlotPanel.loadData(this.data); - } -}; -HPLCTabs.prototype.draw = function(experiment) { - var _this = this; - this.renderDataAcquisition(experiment); -}; -HPLCTabs.prototype.getExperimentTitle = function() { - var experimentHeaderForm = new ExperimentHeaderForm(); - return experimentHeaderForm.getPanel(this.experiment); -}; -HPLCTabs.prototype.renderDataAcquisition = function(experiment) { +MacromoleculeGrid.prototype.edit = function(macromolecule) { var _this = this; - this.experiment = experiment; - if (this.experimentPanel == null) { - this.experimentPanel = Ext.create('Ext.container.Container', { - bodyPadding : 2, - height : this.height, - width : Ext.getBody().getViewSize().width * 0.9, - renderTo : this.targetId, - style : { - padding : 2 - }, - items : [ this.getExperimentTitle(), this.getPanel() ], - listeners : { - afterrender : function() { - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, data) { - _this.analysisGrid.refresh(data); - }); - adapter.getAnalysisInformationByExperimentId(_this.experiment.experimentId); - } - } - }); - } - return this.experimentPanel; + var window = new MacromoleculeWindow(); + window.onSave.attach(function(sender) { + _this.store.loadData(BIOSAXS.proposal.getMacromolecules()); + _this.onMacromoleculesChanged.notify(); + }); + window.draw(macromolecule); }; -HPLCTabs.prototype.getPanel = function() { +MacromoleculeGrid.prototype.getTbar = function() { var _this = this; - this.panel = Ext.createWidget('tabpanel', - { - plain : true, - height : this.height, -// width : 900, - style : { - padding : 2 - }, - items : [ - { - tabConfig : { - title : "Overview" + var actions = []; - }, - items : [ { - xtype : 'container', - layout : 'hbox', - border : 1, - flex : 1, - items : [ { - xtype : 'container', - layout : 'vbox', - border : 1, - items : [ { - xtype : 'container', - margin : '10px', - border : 1, - items : [ - { - xtype : 'container', - margin : '0px', - layout : 'hbox', - border : 1, - items : [ - this.peakGrid2.getPanel([]), - this.mainPlotPanel.getPanel() - ] - }, - { - xtype : 'container', - layout : 'vbox', - border : 1, - items : [ - { - html: '
Select a frame by double-clicking on the HPLC Frames plot
', - margin: 5, - border : 0 - }, - this.frameGrid.getPanel([]), - this.intensityPlotPanel.getPanel() - ] - } - - ] - }], - listeners : { - afterrender : function() { - _this._postRenderOverviewPanel(); - } - } - } ] - } ] - }, -// { -// tabConfig : { -// title : "Details" -// }, -// items : [ { -// xtype : 'container', -// layout : 'hbox', -// border : 1, -// flex : 1, -// items : [ { -// xtype : 'container', -// layout : 'vbox', -// border : 1, -// items : [ { -// xtype : 'container', -// padding : '1px', -// items : [ -// this.I0PlotPanel.getPanel(), this.RgPlotPanel.getPanel(), this.MassPlotPanel.getPanel(), -// this.VcPlotPanel.getPanel() ] -// } ], -// listeners : { -// afterrender : function() { -// _this._postRenderDetailsPanel(); -// } -// } -// } ] -// } ] -// }, - { - tabConfig : { - title : "Analysis" - }, - items : [ { - xtype : 'container', - layout : 'vbox', - padding : '5px 0px 10px 10px', - items : [ _this.analysisGrid.getPanel([]) ] - } ] - }, - { - tabConfig : { - title : "File Manager" - }, - items : [ { - xtype : 'container', - layout : 'vbox', - padding : '5px 0px 10px 10px', - items : [ - { - html: '
Custom Download
', - margin : '10 0 10 5', - border : 0 - }, - { - html: '
Download has been temporary disabled. Contact your labcontact if you want to extract the frames from the HDF5 file
', - margin : '10 0 10 5', - hidden : _this.experiment.experimentId == 2602, - border : 0 - }, - { - xtype : 'container', - layout : 'hbox', - margin : '0 0 0 20', - flex : 1, - items :[ - { - xtype: 'numberfield', - id: 'field_start', - fieldLabel: 'Frames from', - value: 0, - minValue: 0 - }, - { - xtype: 'numberfield', - id: 'field_end', - fieldLabel: 'to', - labelWidth : 20, - margin : '0 0 0 10', - minValue: 0 - }, - { - xtype: 'button', - /** allowing test account to download frames **/ - disabled : _this.experiment.experimentId != 2602, - text : 'Download', - margin : '0 0 0 10', - handler : function() { - var experimentId = _this.experiment.experimentId; - var start = Ext.getCmp("field_start").getValue(); - var end = Ext.getCmp("field_end").getValue(); - /** Checking if start is bigger than end **/ - if (start > end){ - var aux = end; - end = start; - start = aux; - } - var url = BUI.getZipFrameHPLCUrl(experimentId, start, end); - window.open(url) - } - } - ] - }, - _this.peakGrid.getPanel([]) - - ] - } - ] - } - ] + actions.push(Ext.create('Ext.Action', { + icon : '../images/add.png', + text : 'Add macromolecule', + disabled : false, + handler : function(widget, event) { + _this.edit(); + } + })); + actions.push("->"); + actions.push(Ext.create('Ext.Action', { + icon : '../images/folder_go.png', + text : 'Update From SMIS', + tooltip : "Retrieve all the macromolecules of your proposal from SMIS database", + disabled : false, + handler : function(widget, event) { + _this.grid.setLoading("Connecting to SMIS"); + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, data) { + BIOSAXS.proposal.setMacromolecules(data.macromolecules); + _this.refresh(BIOSAXS.proposal.macromolecules); + _this.grid.setLoading(false); + }); + adapter.onError.attach(function(sender, data) { + _this.grid.setLoading(false); }); + adapter.updateDataBaseFromSMIS(); + } + })); + return actions; +}; - return this.panel; +MacromoleculeGrid.prototype.deselectAll = function() { + this.grid.getSelectionModel().deselectAll(); +}; + +MacromoleculeGrid.prototype.selectById = function(macromoleculeId) { + this.grid.getSelectionModel().deselectAll(); + for ( var i = 0; i < this.grid.getStore().data.items.length; i++) { + var item = this.grid.getStore().data.items[i].raw; + if (item.macromoleculeId == macromoleculeId) { + this.grid.getSelectionModel().select(i); + } + } +}; + +MacromoleculeGrid.prototype.refresh = function(macromolecules) { + this.store.loadData(macromolecules, false); +}; + +MacromoleculeGrid.prototype.getColumns = function() { + var _this = this; + var columns = [ + { + text : 'Acronym', + dataIndex : 'acronym', + id : this.id + "acronym", + flex : 1, + renderer : function(value, metaData, record, rowIndex, colIndex, store) { + if (_this.cssFontStyle != null) { + return "" + value + ""; + } + return value; + } + }, { + text : 'Name', + dataIndex : 'name', + id : this.id + "name", + flex : 1, + hidden : true + } ]; + + if (this.btnEditVisible) { + columns.push({ + id : _this.id + 'buttonEditMacromolecule', + width : 85, + sortable : false, + renderer : function(value, metaData, record, rowIndex, colIndex, store) { + if (_this.btnEditVisible) { + return BUI.getGreenButton('EDIT'); + } + return null; + } + }); + } + if (this.btnRemoveVisible) { + columns.push({ + id : _this.id + 'buttonRemoveMacromolecule', + width : 85, + sortable : false, + renderer : function(value, metaData, record, rowIndex, colIndex, store) { + if (_this.btnRemoveVisible) { + return BUI.getRedButton('REMOVE'); + } + return null; + } + }); + } + + return columns; +}; + + +/** Returns the grid **/ +MacromoleculeGrid.prototype.getPanel = function() { + var _this = this; + + this.store = Ext.create('Ext.data.Store', { + fields : [ 'macromoleculeId', 'name', 'acronym' ] + }); + + var type = 'Ext.grid.Panel'; + if (this.searchBar == true) { + type = 'Ext.ux.LiveSearchGridPanel'; + } + + this.selModel = Ext.create('Ext.selection.RowModel', { +// allowDeselect : true, +// mode : 'MULTI', + listeners : { + selectionchange : function(sm, selections) { + var selected = []; + for (var i = 0; i < selections.length; i++) { + selected.push(selections[i].raw); + } + _this.onSelected.notify(selected); + } + } + }); + + this.grid = Ext.create(type, { + id : this.id, + title : 'Macromolecules', + collapsible : this.collapsible, + collapsed : this.collapsed, + store : this.store, + height : this.height, + maxHeight : this.maxHeight, + selModel : this.selModel, + columns : this.getColumns(), + width : this.width, + viewConfig : { + stripeRows : true, + listeners : { + 'celldblclick' : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + _this.edit(BIOSAXS.proposal.getMacromoleculeById(record.data.macromoleculeId)); + }, + 'cellclick' : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonEditMacromolecule') { + _this.edit(BIOSAXS.proposal.getMacromoleculeById(record.data.macromoleculeId)); + } + if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonRemoveMacromolecule') { + BUI.showBetaWarning(); + } + } + + } + } + }); + + /** Adding the tbar **/ + if (this.tbar) { + this.grid.addDocked({ + xtype : 'toolbar', + items : this.getTbar() + }); + } + return this.grid; +}; + +MacromoleculeGrid.prototype.input = function() { + return { + proposal : DATADOC.getProposal_10() + }; +}; + +MacromoleculeGrid.prototype.test = function(targetId) { + var macromoleculeGrid = new MacromoleculeGrid({ + width : 800, + height : 350, + collapsed : false, + tbar : true + }); + + BIOSAXS.proposal = new Proposal(macromoleculeGrid.input().proposal); + var panel = macromoleculeGrid.getPanel(BIOSAXS.proposal.macromolecules); + panel.render(targetId); }; /** - * Main form tab for macromolecule + * Shows measurements with their attributes as specimen, exposure temperature, + * volume to load, transmission etc... * - * @width + * @addBtnMultipleEdit: if true add a button for changing measurements' + * parameters by choosing multiple measurements. It opens + * MultipleEditMeasurementGridWindow + * @collapsed: if true it doesn't show buffer's measurements + * @editor: hashmap containing columns to be edited. It is an array of a json + * like editor of Ext + * @selModel + * @collapseBtnEnable + * @removeBtnEnabled Ext.selection.RowModel + * @addBtnEnable + * @updateRowEnabled true/false if update plugin is set to the grid + * @showTitle + * @title + * @isStatusColumnHidden + * @isPriorityColumnHidden + * @isTimeColumnHidden + * @estimateTime + * @margin + * @tbar * @height + * @maxHeight + * @minHeight + * @width + * @maxWidth + * @resizable + * @experimentColorBased when colors for buffers and macromolecules are not + * selected by the proposal but by experiment, so the + * number of colors is smaller * - * #onClose when user clicks on close button in the MacromoleculeForm - * #onSave when macromole is saved by macromoleculeForm + * #onClick #onSelected #onRemoved #onUpdateTime #onMeasurementChanged + * #onExperimentChanged */ -function MacromoleculeTabs(args) { - this.width = 500; +function MeasurementGrid(args) { + this.id = BUI.id(); + this.height = 500; + this.width = 900; - if (args != null) { - if (args.width != null) { - this.width = args.width; + this.maxWidth = 1200; + this.maxHeight = 600; + this.minHeight = 500; + + this.unitsFontSize = 9; + this.title = "Measurements"; + this.estimateTime = false; + this.collapsed = true; + this.tbar = true; + + this.showTitle = true; + this.resizable = true; + this.updateRowEnabled = true; + + /** + * Hash map containing the keys of the editable columns. Ex: + * 'exposureTemperature' * + */ + this.editor = { + comments : { + xtype : 'textfield', + allowBlank : true } - if (args.height != null) { - this.height = args.height; + }; + + this.isTimeColumnHidden = false; + this.isStatusColumnHidden = false; + this.isPriorityColumnHidden = true; + this.margin = "0 0 0 0"; + + this.addBtnEnable = true; + this.sorter = [ { + property : 'priority', + direction : 'ASC' + } ]; + + this.removeBtnEnabled = false; + this.collapseBtnEnable = true; + this.addBtnMultipleEdit = false; + this.sortingBtnEnable = false; + + var _this = this; + this.selModel = Ext.create('Ext.selection.RowModel', { + allowDeselect : true, + mode : 'MULTI', + listeners : { + selectionchange : function(sm, selections) { + var selected = []; + for (var i = 0; i < selections.length; i++) { + selected.push(selections[i].raw); + } + _this.onSelected.notify(selected); + } } - } + }); - var _this = this; + if (args != null) { - /** Widgets **/ + if (args.selModel != null) { + this.selModel = args.selModel; + } - /** Macromolecule Form **/ - this.macromoleculeForm = new MacromoleculeForm({ - width : this.width - 30, - height : this.height - 50, - }); + if (args.removeBtnEnabled != null) { + this.removeBtnEnabled = args.removeBtnEnabled; + } - this.macromoleculeForm.onClose.attach(function(sender) { - _this.onClose.notify(); - }); + if (args.addBtnMultipleEdit != null) { + this.addBtnMultipleEdit = args.addBtnMultipleEdit; + } - this.macromoleculeForm.onSave.attach(function(sender, macromolecule) { - _this.onSave.notify(macromolecule); - }); + // if (args.experimentColorBased != null) { + // this.experimentColorBased = args.experimentColorBased; + // } - this.assemblyForm = new AssemblyForm({ - width : this.width - 30, - height : this.height - 50, - }); - - this.rigibBodyModelingForm = new RigibBodyModelingForm({ - width : this.width - 30, - height : this.height - 50, - }); - - this.rigibBodyModelingForm.onSave.attach(function(sender, macromolecule) { - _this.onSave.notify(macromolecule); - }); - + if (args.collapsed != null) { + this.collapsed = args.collapsed; + } + if (args.resizable != null) { + this.resizable = args.resizable; + } - /** Events **/ - this.onClose = new Event(this); - this.onSave = new Event(this); -} + if (args.editor != null) { + this.editor = args.editor; + } -/** Populate the widget **/ -MacromoleculeTabs.prototype.refresh = function(macromolecule) { - this.macromoleculeForm.refresh(macromolecule); - this.assemblyForm.refresh(macromolecule); - this.rigibBodyModelingForm.refresh(macromolecule); + if (args.collapseBtnEnable != null) { + this.collapseBtnEnable = args.collapseBtnEnable; + } - if (macromolecule != null){ - if (macromolecule.macromoleculeId == null){ - Ext.getCmp(this.id + "_advancedTab").disable(); - Ext.getCmp(this.id + "_assemblyTab").disable(); + if (args.addBtnEnable != null) { + this.addBtnEnable = args.addBtnEnable; } - else{ - Ext.getCmp(this.id + "_advancedTab").enable(); - Ext.getCmp(this.id + "_assemblyTab").enable(); + if (args.sortingBtnEnable != null) { + this.sortingBtnEnable = args.sortingBtnEnable; } - } - else{ - Ext.getCmp(this.id + "_advancedTab").disable(); - Ext.getCmp(this.id + "_assemblyTab").disable(); - } -}; -MacromoleculeTabs.prototype.getItems = function() { - return [ { - tabConfig : { - title : 'General' - }, - items : [ { - xtype : 'container', - items : [ this.macromoleculeForm.getPanel() ] - } ] - }, { - id : this.id + "_assemblyTab", - tabConfig : { - title : 'Assembly' - }, - items : [ { - xtype : 'container', - items : [ this.assemblyForm.getPanel() ] - } ] - },{ - id : this.id + "_advancedTab", - tabConfig : { - title : 'Advanced' - }, - items : [ this.rigibBodyModelingForm.getPanel() ] - } ]; -}; + if (args.isPriorityColumnHidden != null) { + this.isPriorityColumnHidden = args.isPriorityColumnHidden; + } -MacromoleculeTabs.prototype.getPanel = function() { - this.panel = Ext.createWidget('tabpanel', { - height : this.height, - width : this.width, - plain : true, - margin : 5, - border : 0, - items : this.getItems() - }); - return this.panel; -}; - -function ResultTabs() { -} + if (args.width != null) { + this.width = args.width; + } -ResultTabs.prototype.draw = function(targetId, data, macromoleculeId) { - var panel = this.getPanel(targetId, data, macromoleculeId); - return Ext.create('Ext.container.Container', { - renderTo : targetId, - items : [ BUI.getMacromoleculeHeader(macromoleculeId), panel ] - }); + if (args.updateRowEnabled != null) { + this.updateRowEnabled = args.updateRowEnabled; + } -}; + if (args.showTitle != null) { + this.showTitle = args.showTitle; + if (this.showTitle == false) { + this.title = null; + } + } -ResultTabs.prototype._splitBySpecimen = function(data) { - var splitted = {}; + if (args.height != null) { + this.height = args.height; + } - for ( var i = 0; i < data.length; i++) { - var row = data[i]; - if (splitted[row.macromoleculeId + "_" + row.bufferId] == null) { - splitted[row.macromoleculeId + "_" + row.bufferId] = []; + if (args.maxHeight != null) { + this.maxHeight = args.maxHeight; } - splitted[row.macromoleculeId + "_" + row.bufferId].push(row); - } - return splitted; -}; + if (args.minHeight != null) { + this.minHeight = args.minHeight; + } -ResultTabs.prototype._getTabTitle = function(key, data) { - var macromoleculeId = key.split("_")[0]; - var bufferId = key.split("_")[1]; + if (args.maxWidth != null) { + this.maxWidth = args.maxWidth; + } - return BIOSAXS.proposal.getMacromoleculeById(macromoleculeId).acronym + " + " + BIOSAXS.proposal.getBufferById(bufferId).acronym + "(" + data.length + ")"; -}; + if (args.isStatusColumnHidden != null) { + this.isStatusColumnHidden = args.isStatusColumnHidden; + } + if (args.isTimeColumnHidden != null) { + this.isTimeColumnHidden = args.isTimeColumnHidden; + } -ResultTabs.prototype.getPanel = function(targetId, data, macromoleculeId) { + if (args.title != null) { + this.title = args.title; + } + if (args.estimateTime != null) { + this.estimateTime = args.estimateTime; + } - var dataFiltered = new AnalysisGrid({ - hideNulls : true, - grouped : true, - sorters : [ { - property : 'quality', - direction : 'DESC' - } ] - })._prepareData(data); + if (args.margin != null) { + this.margin = args.margin; + } - var items = [ { - tabConfig : { - title : 'Analysis (' + dataFiltered.length + ')' - }, - items : [ { - xtype : 'container', - layout : 'vbox', - padding : 10, - items : [ new AnalysisGrid({ - isScatteringPlotVisible : false - }).getPanel(dataFiltered) ] - } ] - }, { - tabConfig : { - title : 'Concentration Effects' - }, - items : [ new ResultSummaryForm().getPanel(data) ] - } ]; + if (args.tbar != null) { + this.tbar = args.tbar; + } + + if (args.sorter != null) { + this.sorter = args.sorter; + } - var splitted = this._splitBySpecimen(dataFiltered); - for ( var key in splitted) { - items.push({ - tabConfig : { - title : this._getTabTitle(key, splitted[key]) - }, - items : [ new ResultSummaryForm().getPanel(splitted[key]) ] - }); } + this.onClick = new Event(this); + this.onSelected = new Event(this); + this.onRemoved = new Event(this); + this.onUpdateTime = new Event(this); + this.onMeasurementChanged = new Event(this); + this.onExperimentChanged = new Event(this); +} - this.panel = Ext.createWidget('tabpanel', { - style : { - padding : 2 - }, - items : items +MeasurementGrid.prototype._sortBy = function(sort) { + var _this = this; + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, data) { + _this.onExperimentChanged.notify(data); + _this.grid.setLoading(false); + // wizardWidget.window.close(); }); - return this.panel; - + adapter.onError.attach(function(sender, data) { + _this.grid.setLoading(false); + alert("Oops, there was a problem"); + }); + _this.grid.setLoading("Sorting"); + adapter.sortMeasurements(this.experiments.experiments[0].experimentId, sort); }; - - -function ShipmentTabs(targetId) { - this.targetId = targetId; +MeasurementGrid.prototype._getMenu = function() { var _this = this; - this.gridHeight = 500; - /** data **/ - this.shipment = null; - - /** Shipment Form **/ - this.shipmentForm = new ShipmentForm({ - creationMode : false, - showTitle : false - }); - this.shipmentForm.onSaved.attach(function(sender, data) { - _this.refresh(data); + if (this.tbar) { - }); + var items = []; + if (_this.addBtnEnable) { + items.push({ + icon : '../images/add.png', + text : 'Add measurements', + handler : function() { + _this._openAddMeasurementWindow(); + } + }); + } + if (_this.addBtnMultipleEdit) { + items.push({ + icon : '../images/Edit_16x16_01.png', + text : 'Multiple Edit', + handler : function() { + var multipleEditMeasurementGridWindow = new MultipleEditMeasurementGridWindow(); + multipleEditMeasurementGridWindow.onExperimentChanged.attach(function(sender, data) { + _this.onExperimentChanged.notify(data); + }); - /** Cases grid **/ - this.caseGrid = new CaseGrid({ - height : this.gridHeight - }); + multipleEditMeasurementGridWindow.draw(_this.measurements, _this.experiments); - this.caseGrid.onAddButtonClicked.attach(function(sender, dewar) { - _this.caseGrid.grid.setLoading("ISPyB: Creating a new case"); - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, shipment) { - /** updateing shipment on proposal **/ - for ( var i = 0; i < BIOSAXS.proposal.shippings.length; i++) { - if (BIOSAXS.proposal.shippings[i].shippingId == shipment.shippingId) { - BIOSAXS.proposal.shippings[i] = shipment; } - } - _this.refresh(shipment); - }); - adapter.onError.attach(function(sender, shipment) { - _this.caseGrid.grid.setLoading(false); - }); - adapter.addCase(_this.shipment.json.shippingId); - }); + }); + } - this.caseGrid.onRemoveButtonClicked.attach(function(sender, dewarId) { - _this.panel.setLoading("ISPyB: removing case"); - _this.shipment.onSaved.attach(function(sender, shipment) { - _this.refresh(shipment); + items.push("->"); - }); - _this.shipment.removeCase(dewarId); - }); -} + if (_this.sortingBtnEnable) { + var split = Ext.create('Ext.button.Split', { + text : 'Sort by', + // handle a click on the button itself + handler : function() { + // alert("The button was clicked"); + }, + menu : new Ext.menu.Menu({ + items : [ + { + text : 'First Created First Measured', + handler : function() { + _this._sortBy("FIFO"); + } + }, "-", { + text : 'Default', + handler : function() { + _this._sortBy("DEFAULT"); + } + } ] + }) + }); + items.push(split); + } -ShipmentTabs.prototype.refresh = function(shipment) { + if (_this.collapseBtnEnable) { + items.push({ + text : 'Collapse buffers', + enableToggle : true, + scope : this, + toggleHandler : function(item, pressed) { + this.collapsed = pressed; + this.grid.getStore().loadData(this._prepareData(this.measurements, this.experiments), false); + }, + pressed : this.collapsed + }); + } + var tb = Ext.create('Ext.toolbar.Toolbar', { + items : items + }); + return tb; + } + return null; +}; +/** Opens WizardWidget for adding new measurements * */ +MeasurementGrid.prototype._openAddMeasurementWindow = function(measurements, experiments) { var _this = this; - this.shipment = shipment; - var proposal = new Proposal(); - proposal.onDataRetrieved.attach(function(sender, plates) { - - _this.refreshWithPlates(shipment, plates); - _this.caseGrid.grid.setLoading(false); + var wizardWidget = new WizardWidget(); + wizardWidget.onFinished.attach(function(sender, result) { + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, data) { + _this.onExperimentChanged.notify(data); + _this.grid.setLoading(false); + wizardWidget.window.close(); + }); + wizardWidget.current.setLoading("ISPyB: Adding measurements"); + adapter.addMeasurements(result.name, "comments", result.data, _this.experiments.experiments[0].experimentId); }); - proposal.getPlatesByProposal(); + + wizardWidget.draw(null, new MeasurementCreatorStepWizardForm(BIOSAXS.proposal.getMacromolecules(), { + noNext : true + })); }; -ShipmentTabs.prototype.refreshWithPlates = function(shipment, plates) { - this.shipment = new Shipment(shipment); +/******************************************************************************* + * Opens WizardWidget for adding new measurements + * + * @Measurements + * @Experiments experimentList Object + ******************************************************************************/ +MeasurementGrid.prototype._prepareData = function(measurements, experiments) { + var data = []; + for (var i = 0; i < measurements.length; i++) { + var measurement = measurements[i]; + var specimen = experiments.getSampleById(measurement.specimenId); + var buffer = experiments.getBufferById(specimen.bufferId); + measurement.buffer_acronym = buffer.acronym; + measurement.bufferId = buffer.bufferId; + measurement.volume = specimen.volume; + if (specimen.macromolecule3VO != null) { + measurement.acronym = specimen.macromolecule3VO.acronym; + measurement.macromoleculeId = specimen.macromolecule3VO.macromoleculeId; + } + measurement.concentration = specimen.concentration; + if (measurement.run3VO != null) { + measurement.energy = measurement.run3VO.energy; + measurement.expExposureTemperature = measurement.run3VO.exposureTemperature; + measurement.storageTemperature = measurement.run3VO.storageTemperature; + measurement.timePerFrame = measurement.run3VO.timePerFrame; + measurement.radiationAbsolute = measurement.run3VO.radiationAbsolute; + measurement.radiationRelative = measurement.run3VO.radiationRelative; + measurement.status = "DONE"; - this.caseGrid.refresh(this.shipment.getDewars(), plates); - this.panel.setLoading(false); + try { + if (measurement.run3VO.timeStart != null) { + if (measurement.run3VO.timeStart != "") { + measurement.miliseconds = moment(measurement.run3VO.timeStart).format("X"); + } + } + } catch (E) { + console.log(E); + } + } + + if (experiments.getDataCollectionByMeasurementId(measurement.measurementId).length > 0) { + var measurementtodatacollection3VOs = experiments.getDataCollectionByMeasurementId(measurement.measurementId)[0].measurementtodatacollection3VOs; + for (var k = 0; k < measurementtodatacollection3VOs.length; k++) { + if (measurementtodatacollection3VOs[k].dataCollectionOrder == 1) { + var specimenBuffer = experiments.getSampleById(experiments.getMeasurementById(measurementtodatacollection3VOs[k].measurementId).specimenId); + if (specimenBuffer.sampleplateposition3VO != null) { + measurement.bufferSampleplateposition3VO = specimenBuffer.sampleplateposition3VO; + measurement.bufferSampleplate = (experiments.getSamplePlateById(specimenBuffer.sampleplateposition3VO.samplePlateId)); + } + } + } + } + + if (this.collapsed) { + /** If collapsed only the samples * */ + if (specimen.macromolecule3VO != null) { + data.push(measurement); + } + } else { + data.push(measurement); + } + + } + return data; }; -ShipmentTabs.prototype.error = function(error) { - var e = JSON.parse(error); - showError(e); - this.panel.setLoading(false); +/** + * Refresh data grid with the measurements and the experiments + * + * @measurements array with measurement3VO objects + * @experiments array with experiments objects + */ +MeasurementGrid.prototype.refresh = function(measurements, experiments) { + this.experiments = experiments; + this.measurements = measurements; + this.store.loadData(this._prepareData(measurements, experiments), false); }; -//ShipmentTabs.prototype.refreshTabTitles = function() { -/*Ext.getCmp("MacromoleculeTab").setText(this.getMacromoleculeTitle()); -Ext.getCmp("BufferTab").setText(this.getBuffersTitle()); -Ext.getCmp("SpecimenTab").setText(this.getSpecimenTitle()); -Ext.getCmp("PlatesTab").setText(this.getPlatesTitle()); -Ext.getCmp("AssembliesTab").setText(this.getShipmentTitle()); -Ext.getCmp("PlateGroupsTab").setText(this.getPlateGroupsTitle());*/ -//}; -ShipmentTabs.prototype.draw = function(shipment) { - var _this = this; - _this.plates = []; - _this.shipment = shipment; - _this.render(shipment); +/** + * Set status bar to busy (refreshing icon) + * + * @msg message to be displayed on the bar + */ +MeasurementGrid.prototype._showStatusBarBusy = function(msg) { + var statusBar = Ext.getCmp(this.id + 'basic-statusbar'); + statusBar.setStatus({ + text : msg, + iconCls : 'x-status-busy', + clear : false + }); +}; - // var proposal = new Proposal(); - // proposal.onDataRetrieved.attach(function(sender, plates){ - // _this.plates = plates; - // _this.shipment = shipment; - // _this.render(shipment); - // - // }); - // proposal.getPlatesByProposal(); +/** + * Set status bar to ready (ok icon) + * + * @msg message to be displayed on the bar + */ +MeasurementGrid.prototype._showStatusBarReady = function(msg) { + var statusBar = Ext.getCmp(this.id + 'basic-statusbar'); + statusBar.setStatus({ + text : msg, + iconCls : 'x-status-valid', + clear : false + }); }; -//ShipmentTabs.prototype.getShipmentTitle = function() { -// return 'Shipment'; -//}; +/** + * If updateRowEnabled returns an array with Ext.grid.plugin.RowEditing + */ +MeasurementGrid.prototype._getPlugins = function() { + var _this = this; + var plugins = []; + if (this.updateRowEnabled) { + plugins.push(Ext.create('Ext.grid.plugin.RowEditing', { + clicksToEdit : 1, + listeners : { + validateedit : function(grid, e) { + /** Setting values * */ + for ( var key in _this.editor) { + e.record.raw[key] = e.newValues[key]; + } + /** Comments are always updatable* */ + e.record.raw.comments = e.newValues.comments; -//ShipmentTabs.prototype.getMacromoleculeTitle = function() { -// return 'Macromolecules (' + this.experiment.getMacromolecules().length + ')'; -//}; + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, measurement) { + _this.onMeasurementChanged.notify(measurement); + _this.grid.setLoading(false); + }); + adapter.onError.attach(function() { + alert("Error"); + _this.grid.setLoading(false); + }); -//ShipmentTabs.prototype.getBuffersTitle = function() { -// return 'Buffers (' + this.experiment.getBuffers().length + ')'; -//}; + _this.grid.setLoading(); + adapter.saveMeasurement(e.record.raw, _this.experiments.experiments[0]); + } + } + })); + } + return plugins; +}; -//ShipmentTabs.prototype.getPlateGroupsTitle = function() { -// return 'Plate Groups (' + this.experiment.getPlateGroups().length + ')'; -//}; +/** + * @key name of the columns mathing the this.editor[key] + */ +MeasurementGrid.prototype._getEditor = function(key) { + if (this.editor[key] != null) { + return this.editor[key]; + } + return null; +}; -//ShipmentTabs.prototype.getSampleChangerTitle = function() { -// return 'Sample Changer'; -//}; +MeasurementGrid.prototype.getPanel = function(measurements, experiments) { + this.experiments = experiments; + this.measurements = measurements; + var _this = this; + this.store = Ext.create('Ext.data.Store', { + fields : [ { + name : 'miliseconds', + type : 'int' + }, 'priority', 'bufferId', { + name : 'exposureTemperature', + type : 'numeric' + }, 'volumeToLoad','code', 'transmission', 'viscosity', 'waitTime', 'flow', 'buffer_acronym', 'macromoleculeId', 'acronym', 'concentration', 'extraFlowTime', 'volume', 'energy', + 'expExposureTemperature', 'storageTemperature', 'timePerFrame', 'radiationAbsolute', 'radiationRelative', 'status', 'comments' ], + data : this._prepareData(measurements, experiments) + }); + this.store.sort(this.sorter); + var bbar = {}; + try { + bbar = Ext.create('Ext.ux.StatusBar', { + id : _this.id + 'basic-statusbar', + defaultText : 'Ready', + text : 'Ready', + iconCls : 'x-status-valid', + items : [] + }); + } catch (exp) { + console.log("bbar error"); + } -//ShipmentTabs.prototype.getSpecimenTitle = function() { -// return 'Specimens(' + this.experiment.getSpecimenCount() + ')'; -//}; + this.grid = Ext + .create( + 'Ext.grid.Panel', + { + id : this.id, + title : this.title, + store : this.store, + selModel : this.selModel, + plugins : this._getPlugins(), + resizable : this.resizable, + margin : this.margin, + maxHeight : this.maxHeight, + minHeight : this.minHeight, + maxWidth : this.maxWidth, + width : this.width, + tbar : this._getMenu(), + columns : [ + { + text : 'Order', + dataIndex : 'priority', + width : 50, + hidden : _this.isPriorityColumnHidden, + sortable : true -//ShipmentTabs.prototype.getPlatesTitle = function() { -// return 'Plates(' + this.experiment.getSamplePlates().length + ')'; -//}; + }, + { + text : 'Run Number', + dataIndex : 'code', + width : 50, + hidden : true, + sortable : true -//ShipmentTabs.prototype.getBuffersTip = function() { -/*if (this.experiment.getBuffers().length == 0){ - return BUI.getWarningHTML("There are no buffers. Click on add to create new ones. Click on edit button or double click to edit them"); - -} -else{ - return BUI.getTipHTML("Click on edit button or double click to edit them. Click on duplicate to create an identical buffer including its additives") -}*/ -//}; + }, -//ShipmentTabs.prototype.refreshTips = function() { -/*Ext.getCmp("BufferTabTip").update(this.getBuffersTip()); -Ext.getCmp("SpecimenTabTip").update(this.getSpecimensTip());*/ -//}; + { + text : 'Specimen', + columns : [ -ShipmentTabs.prototype.render = function(shipment) { - return this.getPanel(shipment); -}; + { + text : '', + dataIndex : 'macromoleculeId', + width : 30, + renderer : function(val, y, sample) { + if (val != null) { + if (_this.experiments == null) { + return BUI.getRectangleColorDIV(BIOSAXS.proposal.macromoleculeColors[val], 10, 10); + } else { + return BUI.getRectangleColorDIV(_this.experiments.macromoleculeColors[val], 10, 10); + } + } + }, + sortable : true + }, + { + text : 'Macromo.', + dataIndex : 'acronym', + width : 80, + renderer : function(val, y, sample) { + return val; + }, + sortable : true + }, + { + text : 'Conc. ', + dataIndex : 'concentration', + width : 80, + renderer : function(val, y, sample) { + if (sample.raw.macromoleculeId == null) { + return ""; + } + if (isNaN(val)) + return val; -ShipmentTabs.prototype.getShipmentHeader = function(shipment) { - var _this = this; - function getHTMLSource() { - var name = shipment.json.shippingName; - var status = shipment.json.shippingStatus; - var creationDate = shipment.json.creationDate; - var html = BUI.createFormLabel("Name :", name, 75, 400); - html = html + BUI.createFormLabel("Status :", status, 75, 400); - html = html + BUI.createFormLabel("Date :", creationDate, 75, 400); - return html; - } + if (val != 0) { + return BUI.formatValuesUnits(val, '', { + fontSize : 16, + decimals : 3, + unitsFontSize : this.unitsFontSize + }); + } else { + return; + } - return Ext.create('Ext.container.Container', { - frame : false, - layout : 'hbox', - title : 'General', - padding : 5, - bodyPadding : '5 5 0 0', - width : 890, - margin : '0 0 10 0', - height : 100, - style : { - borderColor : '#BDBDBD', - borderStyle : 'solid', - borderWidth : '1px' - }, - fieldDefaults : { - msgTarget : 'side', - labelWidth : 100 - }, - items : [ { - margin : "0 0 0 0", - width : 475, - border : 0, - html : getHTMLSource() - } - ] - }); -}; + }, + sortable : true + }, + { + text : '', + dataIndex : 'bufferId', + width : 30, + hidden : false, + renderer : function(val, y, sample) { + if (val != null) { + var color = '#FFCCFF'; + if (_this.experiments != null) { + var dc = _this.experiments.getDataCollectionByMeasurementId(sample.raw.measurementId); + if (dc != null) { + if (dc.length > 0) { + color = _this.experiments.getSpecimenColorByBufferId(_this.experiments + .getMeasurementById(dc[0].measurementtodatacollection3VOs[0].measurementId).specimenId); + } + } + } else { + color = BIOSAXS.proposal.bufferColors[val]; + } + return BUI.getRectangleColorDIV(color, 10, 10); + } + }, + sortable : true + }, + { + text : 'Buffer', + dataIndex : 'buffer_acronym', + width : 120, + renderer : function(val, y, sample) { + if (sample.raw.bufferSampleplateposition3VO != null) { + return BIOSAXS.proposal.getBufferById(sample.raw.bufferId).acronym + " Plate: [" + + sample.raw.bufferSampleplate.slotPositionColumn + ", " + + BUI.getSamplePlateLetters()[sample.raw.bufferSampleplateposition3VO.rowNumber - 1] + "-" + + sample.raw.bufferSampleplateposition3VO.columnNumber + "]"; + } + return val; + }, + sortable : true + }, { + text : 'Position', + width : 100, + hidden : true, + renderer : function(val, y, sample) { + if (_this.experiments != null) { + return BUI.getSamplePositionHTML(_this.experiments.getSampleById(sample.raw.specimenId), _this.experiments.experiments[0]); + } + } + } ] + }, + { + text : 'Parameters', + columns : [ + { + text : 'Ex. Flow. time (s)', + dataIndex : 'extraFlowTime', + width : 100, + hidden : true, + renderer : function(val, y, sample) { + return BUI.formatValuesUnits(val, 's', { + fontSize : 12, + decimals : 0, + unitsFontSize : this.unitsFontSize + }); + } + }, + { + text : 'Exp. Temp.', + dataIndex : 'exposureTemperature', + width : 70, + renderer : function(val, y, sample) { + if (Number(val)) { + return BUI.formatValuesUnits(val, 'C', { + fontSize : 12, + decimals : 2, + unitsFontSize : this.unitsFontSize + }); + } + return null; + }, + sortable : true, + editor : this._getEditor("exposureTemperature") + }, + { + text : 'Vol. Load', + dataIndex : 'volumeToLoad', + width : 60, + hidden : false, + editor : this._getEditor("volumeToLoad"), + renderer : function(val, y, sample) { + // return val+ " µl"; + return BUI.formatValuesUnits(val, 'µl', { + fontSize : 12, + decimals : 2, + unitsFontSize : this.unitsFontSize + }); + } + }, + { + text : 'Volume in Well', + dataIndex : 'volume', + hidden : true, + editor : this._getEditor("volume"), + width : 80, + renderer : function(val, y, sample) { + // return val + "(µl)"; + return BUI.formatValuesUnits(val, 'µl', { + fontSize : 12, + decimals : 2, + unitsFontSize : this.unitsFontSize + }); + } + }, + { + text : 'Trans.', + dataIndex : 'transmission', + width : 60, + editor : this._getEditor("transmission"), + renderer : function(val, y, sample) { + return BUI.formatValuesUnits(val, '%', { + fontSize : 12, + decimals : 0, + unitsFontSize : this.unitsFontSize + }); + } + }, + { + text : 'Wait T.', + dataIndex : 'waitTime', + editor : this._getEditor("waitTime"), + width : 50, + renderer : function(val, y, sample) { + // if (val != 0) { + return BUI.formatValuesUnits(val, 's', { + fontSize : 12, + decimals : 0, + unitsFontSize : this.unitsFontSize + }); + // } + } + }, + { + text : 'Flow', + dataIndex : 'flow', + editor : this._getEditor("flow"), + width : 50, + renderer : function(val, y, sample) { + if (val == true) { + return "yes"; + } + return null; + } + }, + { + text : 'Viscosity', + dataIndex : 'viscosity', + tooltip : 'The viscosity of a fluid is a measure of its resistance to gradual deformation by shear stress or tensile stress. For liquids, it corresponds to the informal notion of "thickness"', + editor : this._getEditor("viscosity"), + width : 50, + renderer : function(val, y, sample) { + return val; + } + } ] + }, { + text : 'Status', + dataIndex : 'status', + width : 50, + hidden : _this.isStatusColumnHidden, + renderer : function(val, y, sample) { + if (val != null) { + if (val == 'DONE') { + return "" + val + " "; + } + } + } + }, { + text : 'Time', + dataIndex : 'time', + width : 80, + hidden : _this.isTimeColumnHidden, + renderer : function(val, y, sample) { + if (sample.raw.run3VO != null) { + if (sample.raw.run3VO.timeStart != null) { + if (sample.raw.run3VO.timeStart != "") { + var m = moment(sample.raw.run3VO.timeStart); + return m.format("hh:mm:ss a"); + } + } + } + } + }, { + text : 'Energy', + dataIndex : 'energy', + width : 100, + hidden : true + }, { + text : 'Real Exp. Temp.(C)', + width : 100, + dataIndex : 'expExposureTemperature', + hidden : true + }, { + text : 'Storage Temp.(C)', + width : 100, + dataIndex : 'storageTemperature', + hidden : true + }, { + text : 'Time/Frame (s)', + width : 100, + dataIndex : 'timePerFrame', + hidden : true + }, { + text : 'Radiation Relative', + dataIndex : 'radiationRelative', + width : 100, + hidden : true + }, { + text : 'Radiation Absolute', + dataIndex : 'radiationAbsolute', + width : 100, + hidden : true + }, { + text : 'Comments', + dataIndex : 'comments', + flex : 1, + hidden : false, + editor : this._getEditor("comments") -ShipmentTabs.prototype.getTabPanel = function(shipment) { - this.panel = Ext.createWidget('tabpanel', { - height : 600, - style : { - padding : 2 - }, - items : [ { - tabConfig : { - id : 'Shipment', - title : 'Shipment', - icon : '/ispyb/images/plane-small.gif' - }, - items : [ { - xtype : 'container', - margin : '5 5 5 5', - items : [ this.shipmentForm.getPanel(shipment) ] - } ] - }, { - tabConfig : { - id : 'Cases', - title : 'Cases', - icon : '../images/box-icon-very-small.png' - }, - items : [ { - xtype : 'container', - margin : '5 5 5 5', - items : [ this.caseGrid.getPanel(shipment.getDewars(), this.plates) ] - } ] - } ] - }); - return this.panel; -}; + }, { + id : _this.id + 'buttonRemoveSample', + text : '', + hidden : !_this.removeBtnEnabled, + width : 100, + sortable : false, + renderer : function(value, metaData, record, rowIndex, colIndex, store) { + if (record.raw.macromoleculeId != null) { + if (_this.removeBtnEnabled) { + return BUI.getRedButton('REMOVE'); + } + } + } + } ], + bbar : bbar, + viewConfig : { + stripeRows : true, + getRowClass : function(record, index, rowParams, store) { + if (record.data.status == "DONE") { + return 'green-row'; + } -ShipmentTabs.prototype.getPanel = function(shipment) { - var _this = this; - this.shipment = shipment; - if (this.plates == null) { - this.plates = []; - } + }, + listeners : { + 'itemclick' : function(grid, record, item, index, e, eOpts) { + _this.onClick.notify({ + specimen : record.raw + }); + }, + 'cellclick' : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonRemoveSample') { + grid.getStore().removeAt(rowIndex); - if (this.shipPanel == null) { - this.shipPanel = Ext.create('Ext.container.Container', { - bodyPadding : 2, - width : Ext.getBody().getViewSize().width * 0.9, - renderTo : this.targetId, - style : { - padding : 2 - }, - items : [ this.getShipmentHeader(shipment), this.getTabPanel(shipment) ] - }); - } + if (record.raw.measurementId != null) { + /** For testing * */ + grid.setLoading("ISPyB: Removing measurement"); + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, data) { + grid.setLoading(false); + /** + * We get and refresh experiment + * because specimens has changed * + */ + var adapter2 = new BiosaxsDataAdapter(); + adapter2.onSuccess.attach(function(sender, experiment) { + _this.onRemoved.notify(experiment); + _this._showStatusBarReady('Ready'); + }); + if (_this.experiments.experiments[0].experimentId != null) { + adapter2.getExperimentById(_this.experiments.experiments[0].experimentId, "MEDIUM"); + _this._showStatusBarBusy("ISPyB: Removing Unused Specimens"); + } + }); - return this.shipPanel; -}; - -/** - * Shows an template with the specimens, measurements and the experiment's - * requirement - * - * @targetId - */ -function TemplateTabs(targetId) { - this.height = 600; - this.targetId = targetId; + adapter.onError.attach(function(sender, data) { + alert("Error: " + data); + grid.setLoading(false); + }); - this.id = BUI.id(); - var _this = this; + adapter.removeMeasurement(record.raw); + } + } - this.gridHeight = 1000; - /** data * */ - this.experiment = null; + } - this.specimenSelected = null; + } + } + }); - /** Specimen Widget contains a specimenGrid and a sampleChangerWidget than can be displayed with are vertical or horizontal layout **/ - this.specimenWidget = new SpecimenWidget({ - height : this.height - }); - - this.samplePlateGroupWidget = new SamplePlateGroupWidget({ - showTitle : false, - height : 250, - margin : 5, - bbar : true - }); - - this.samplePlateGroupWidget.onExperimentChanged.attach(function(sender, experiment) { - _this.refresh(experiment); - }); + this.grid.on("afterrender", function() { - this.samplePlateGroupWidget.onClick.attach(function(sender, args) { - }); + function updateTime() { + try { + _this.estimatedTime = _this.estimatedTime - 1; - this.volumePlanificator = new VolumeGrid(); + _this.onUpdateTime.notify({ + hours : (Number(_this.estimatedTime / 3600).toFixed()), + minutes : (Number((_this.estimatedTime / 60) % 60).toFixed()), + seconds : (Number(_this.estimatedTime % 60).toFixed()) - /** For Measurements * */ - var storeViscosity = Ext.create('Ext.data.Store', { - fields : [ 'name' ], - data : [ { - "name" : "low" - }, { - "name" : "medium" - }, { - "name" : "high" - } ] - }); + }); - // Create the combo box, attached to the states data store - var viscosityEditor = Ext.create('Ext.form.ComboBox', { - fieldLabel : '', - store : storeViscosity, - queryMode : 'local', - displayField : 'name', - valueField : 'name' - }); + if (Number(_this.estimatedTime) < 0) { + _this.timer = null; + grid.setTitle(_this.title); + } - this.measurementGrid = new MeasurementGrid({ - maxWidth : 1500, - estimateTime : false, - positionColumnsHidden : true, - isPriorityColumnHidden : true, - isStatusColumnHidden : true, - isTimeColumnHidden : true, - updateRowEnabled : true, - collapsed : true, - removeBtnEnabled : true, - showTitle : false, - collapseBtnEnable : false, - addBtnMultipleEdit : true, - sortingBtnEnable : true, - editor : { - exposureTemperature : { - xtype : 'textfield', - allowBlank : true - }, - comments : { - xtype : 'textfield', - allowBlank : true - }, - volumeToLoad : { - xtype : 'numberfield', - allowBlank : true - }, - transmission : { - xtype : 'numberfield', - allowBlank : true - }, - viscosity : viscosityEditor, - waitTime : { - xtype : 'numberfield', - allowBlank : true - }, - flow : { - xtype : 'checkbox', - allowBlank : true + } catch (e) { + console.log(e); + _this.timer = null; } } - }); - - this.measurementGrid.onSelected.attach(function(sender, measurements) { - var specimens = []; - for ( var i = 0; i < measurements.length; i++) { - specimens.push(_this.experiment.getSampleById(measurements[i].specimenId)); - } - }); - this.measurementGrid.onMeasurementChanged.attach(function(sender, measurement) { - _this.experiment.setMeasurement(measurement); - _this.refresh(_this.experiment); - }); + if (_this.estimateTime) { + var experimentList = _this.experiments; + var collected = experimentList.getMeasurementsCollected(); + if (collected.length > 0) { + if (collected[0].run3VO != null) { + try { + var end = collected[0].run3VO.timeEnd; + var start = collected[0].run3VO.timeStart; + var dstart = moment(start); + var dend = moment(end); + var seconds = Number(dend.diff(dstart) / 1000).toFixed(); - this.measurementGrid.onExperimentChanged.attach(function(sender, json) { - _this.refresh(new Experiment(json)); - }); + _this.estimatedTime = (seconds * experimentList.getMeasurementsNotCollected().length); - this.measurementGrid.onRemoved.attach(function(sender, experiment) { - _this.refreshSpecimen(new Experiment(experiment)); - }); + if (_this.estimatedTime > 0) { + updateTime(); + _this.timer = setInterval(function() { + updateTime(); + }, 1000); + } - this.measurementGrid.onUpdateTime.attach(function(sender, args) { - document.getElementById(_this.id + "_counter").innerHTML = args.hours + 'h, ' + args.minutes + 'min and ' + args.seconds + ' seconds'; + } catch (e) { + } + } + } + } }); - -} - -TemplateTabs.prototype.refreshMeasurement = function(experiment) { - this.experiment = experiment; - this.experiment.onSaved = new Event(this); - this.experiment.onSpecimenSaved = new Event(this); - var experimentList = new ExperimentList([ this.experiment ]); - this.measurementGrid.refresh(experimentList.getMeasurementsNotCollected(), experimentList); -}; -TemplateTabs.prototype.refreshSpecimen = function(experiment) { - this.experiment = experiment; - this.experiment.onSaved = new Event(this); - this.experiment.onSpecimenSaved = new Event(this); - this.samplePlateGroupWidget.refresh(this.experiment); - this.specimenWidget.refresh(this.experiment); - this.volumePlanificator.refresh(this.experiment); + return this.grid; }; -TemplateTabs.prototype.refresh = function(experiment) { - // var start = new Date().getTime(); - this.experiment = experiment; - this.experiment.onSaved = new Event(this); - this.experiment.onSpecimenSaved = new Event(this); - - // var experimentList = new ExperimentList([this.experiment]); - this.refreshMeasurement(experiment); - this.refreshSpecimen(experiment); - /** Refreshing grids * */ - this.panel.setLoading(false); - +/** Method for testing * */ +MeasurementGrid.prototype.input = function() { + var experiment = DATADOC.getExperiment_10(); + var measurements = DATADOC.getMeasurements_10(); + var proposal = DATADOC.getProposal_10(); + return { + experiment : experiment, + measurements : measurements, + proposal : proposal + }; }; -TemplateTabs.prototype.error = function(error) { - var e = JSON.parse(error); - showError(e); - this.panel.setLoading(false); +MeasurementGrid.prototype.test = function(targetId) { + var measurementGrid = new MeasurementGrid({ + tbar : true + }); + BIOSAXS.proposal = new Proposal(measurementGrid.input().proposal); + var panel = measurementGrid.getPanel(measurementGrid.input().measurements, new ExperimentList([ new Experiment(measurementGrid.input().experiment) ])); + panel.render(targetId); }; + +/** + * A shipment may contains one or more cases where stock solutions and sample + * plates are stored + * + * @height + * @btnEditVisible + * @btnRemoveVisible + * + * #onEditButtonClicked + */ +function MolarityGrid(args) { + this.height = 100; + this.width = 100; + + if (args != null) { + if (args.height != null) { + this.height = args.height; + } + if (args.width != null) { + this.width = args.width; + } + } + + var _this = this; + + this.molarityForm = new MolarityForm({height : 180, width : 455}); + + this.molarityForm.onSave.attach(function(sender){ + _this.molarityWindow.destroy(); + _this.updateProposal(); + + }); + + this.molarityForm.onClose.attach(function(sender){ + _this.molarityWindow.destroy(); + + }); + + /** Events * */ + this.onEditButtonClicked = new Event(this); +} + +MolarityGrid.prototype._getColumns = function() { + return [ { + text : 'Subunit', + columns : [ { + text : "Acronym", + width : 100, + hidden : false, + dataIndex : 'acronym', + sortable : true + }, { + text : "Name", + width : 125, + hidden : false, + dataIndex : 'name', + sortable : true + }, { + text : "MM Est.", + width : 100, + dataIndex : 'molecularMass', + sortable : true, + renderer : function(grid, cls, record){ + return BUI.formatValuesUnits(record.data.molecularMass , "Da", 10, 2); + + } + } ] + }, { +// text : "Number
in assymmetric units", + text : "Ratio", + width : 100, + dataIndex : 'ratio', + tooltip : 'Number of times the subunit is present in the macromolecule', + sortable : true + }, { + id : this.id + 'MOLARITY_REMOVE', + flex : 0.1, + sortable : false, + renderer : function(value, metaData, record, rowIndex, colIndex, store) { + return BUI.getRedButton('REMOVE'); + } + } ]; +}; + +MolarityGrid.prototype._openMolarityWindow = function() { + this.molarityWindow = Ext.create('Ext.window.Window', { + title : 'Molarity', + height : 220, + width : 500, + modal : true, + items : [this.molarityForm.getPanel() ] + }).show(); +}; + +MolarityGrid.prototype._getButtons = function() { + var _this = this; + return [ { + text : 'Add subunit', + icon : '../images/add.png', + handler : function() { + _this._openMolarityWindow(); + } + }]; +}; + + +MolarityGrid.prototype.updateProposal = function() { + var _this = this; + this.panel.setLoading(); + BIOSAXS.proposal.onInitialized.attach(function() { + if (BIOSAXS.proposal != null) { + var macromolecules = BIOSAXS.proposal.macromolecules; + for (var i = 0; i < macromolecules.length; i++) { + + if (macromolecules[i].macromoleculeId == _this.macromolecule.macromoleculeId) { + _this.refresh(macromolecules[i]); + _this.panel.setLoading(false); + } + } + } + }); + BIOSAXS.proposal.init(); +}; + + +MolarityGrid.prototype.getPanel = function() { + var _this = this; + + this.molarityStore = Ext.create('Ext.data.Store', { + fields : [ 'acronym', 'ratio', 'comments', 'stoichiometryId', 'name', 'molecularMass' ], + sorters : { + property : 'ratio', + direction : 'DESC' + } + }); + + this.panel = Ext.create('Ext.grid.Panel', { + store : this.molarityStore, + height : this.height, + padding : 5, + columns : this._getColumns(), + listeners : { + cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + + /** Remove entry * */ + if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'MOLARITY_REMOVE') { + var dataAdapter = new BiosaxsDataAdapter(); + dataAdapter.onSuccess.attach(function(sender) { + _this.updateProposal(); + + }); + dataAdapter.removeStoichiometry(record.data.stoichiometryId); + _this.panel.setLoading("Removing Structure"); + } + } + }, + tbar : this._getButtons() + }); + return this.panel; +}; + +MolarityGrid.prototype._prepareData = function(macromolecule) { + /** Return an array of [{ratio,acronym, stoichiometryId, name}] **/ + var data = []; + if (macromolecule.stoichiometry != null) { + for (var i = 0; i < macromolecule.stoichiometry.length; i++) { + var hostMacromolecule = BIOSAXS.proposal.getMacromoleculeById(macromolecule.stoichiometry[i].macromoleculeId); + data.push({ + ratio : macromolecule.stoichiometry[i].ratio, + acronym : hostMacromolecule.acronym, + comments : hostMacromolecule.comments, + molecularMass : hostMacromolecule.molecularMass, + stoichiometryId : macromolecule.stoichiometry[i].stoichiometryId, + name : hostMacromolecule.name + }); + } + } + return data; +}; + +MolarityGrid.prototype.refresh = function(macromolecule) { + if (macromolecule != null){ + this.molarityStore.loadData(this._prepareData(macromolecule)); + this.molarityForm.refresh(macromolecule); + this.macromolecule = macromolecule; + } +}; + +MolarityGrid.prototype.input = function() { + return { + proposal : DATADOC.getProposal_10(), + dewars : DATADOC.getDewars_10() + + }; +}; + +MolarityGrid.prototype.test = function(targetId) { + var MolarityGrid = new MolarityGrid({ + height : 150 + }); + BIOSAXS.proposal = new Proposal(MolarityGrid.input().proposal); + var panel = MolarityGrid.getPanel(MolarityGrid.input().dewars); + panel.render(targetId); + +}; + + +/** + * Given data analysis parsed with ResultsAssemblyWidget makes a selector grid with buffer/protein + **/ +function SpecimenSelectorResultGrid() { + this.id = BUI.id(); +} -TemplateTabs.prototype.draw = function(experiment) { - this.render(experiment); +SpecimenSelectorResultGrid.prototype._prepareData = function(data) { + var parsed = []; + for ( var i = 0; i < data.length; i++) { + var row = data[i]; + for ( var j = 0; j < row.conditions.length; j++) { + parsed.push({ + bufferId : row.conditions[j].bufferId, + macromoleculeId : row.macromoleculeId, + macromoleculeAcronym : BIOSAXS.proposal.getMacromoleculeById(row.macromoleculeId).acronym, + bufferAcronym : BIOSAXS.proposal.getBufferById(row.conditions[j].bufferId).acronym + }); + } + } + return parsed; }; - -TemplateTabs.prototype.getExperimentTitle = function() { - var _this = this; - var experimentHeaderForm = new ExperimentHeaderForm(); - return experimentHeaderForm.getPanel(this.experiment); +SpecimenSelectorResultGrid.prototype.refresh = function(data) { + this.store.loadData(this._prepareData(data)); }; -TemplateTabs.prototype.render = function(experiment) { +SpecimenSelectorResultGrid.prototype.getPanel = function(data) { var _this = this; - this.experiment = experiment; + this.store = Ext.create('Ext.data.Store', { + fields : [ 'macromoleculeId', 'bufferId', 'macromoleculeAcronym', 'bufferAcronym', 'concentration' ], + data : this._prepareData(data), + groupField : 'macromoleculeAcronym' + }); - /** - * - * Depending on the sample Changer configuration we want to display the plates vertically or horizontally - * Default is vertical - * - * */ - - var specimenContainer = this.specimenWidget.getPanel(); - this.specimenWidget.refresh(experiment); - - var experimentList = new ExperimentList([ _this.experiment ]); - var measurementContainer = Ext.create('Ext.container.Container', { - layout : { - type : 'vbox' - }, - defaults : { - style : { - padding : '5px 0px 0px 10px' + this.store.sort('concentration'); + this.grid = Ext.create('Ext.grid.Panel', { + id : this.id, + store : this.store, + width : this.width, + height : this.height, + maxHeight : this.maxHeight, + border : 1, + features : [ { + ftype : 'grouping', + groupHeaderTpl : '{name}', + hideGroupedHeader : true, + startCollapsed : false + } ], + selModel : Ext.create('Ext.selection.CheckboxModel', { + allowDeselect : true, + mode : 'MULTI', + listeners : { + selectionchange : function(sm, selections) { + _this.selected = []; + for ( var i = 0; i < selections.length; i++) { + _this.selected.push(selections[i].raw); + } + } } - }, - items : [ _this.measurementGrid.getPanel(experimentList.getMeasurementsNotCollected(), experimentList) ] + }), + margin : 10, + sortableColumns : true, + columns : [ { + text : 'Macromolecule', + dataIndex : 'macromoleculeAcronym', + flex : 1 + }, { + text : '', + dataIndex : 'bufferId', + width : 20, + hidden : false, + renderer : function(val, y, sample) { + return BUI.getRectangleColorDIV(BIOSAXS.proposal.bufferColors[val], 10, 10); + } + }, { + text : 'Buffer', + dataIndex : 'bufferAcronym', + flex : 1 + }, { + text : 'Concentration', + dataIndex : 'concentration', + flex : 1, + renderer : function(val, y, sample) { + return val + " mg/ml"; + } + } ] }); - this.panel = Ext - .createWidget( - 'tabpanel', - { - plain : true, - items : [ - { - tabConfig : { - title : 'Measurements' - }, - items : [ { - xtype : 'container', - layout : 'vbox', - border : 1, -// height : _this.gridHeight, - margin : "0 0 10 0", - items : [ measurementContainer ] - } - - ] - }, - { - tabConfig : { - id : 'genralTabl', - title : "Specimens" - // width : 900, - // border : 3 - - }, - items : [ specimenContainer ] - }, - { - tabConfig : { - title : "Requirements" + /** Adding the tbar **/ + if (this.tbar) { + this.grid.addDocked({ + xtype : 'toolbar', + items : this.getTbar() + }); + } + return this.grid; +}; - }, - items : [ - { - html : BUI.getTipHTML("Estimated volume is the maximum volume required. Depending on the order of your measurements you may use less. Click on create stock solutions if you plan to ship these stock solutions"), - margin : "10 10 10 10", - border : 0 - }, this.volumePlanificator.getPanel(experiment) ] - } ] - }); - // ); - return this.getPanel(this.panel); +SpecimenSelectorResultGrid.prototype.input = function() { + return { + data : new ResultsAssemblyGrid()._prepareData(new ResultsAssemblyGrid().input().data), + proposal : new ResultsAssemblyGrid().input().proposal + }; }; -TemplateTabs.prototype.isTemplate = function() { - if (this.experiment.json.type == "TEMPLATE") { - return true; - } - return false; +SpecimenSelectorResultGrid.prototype.test = function(targetId) { + var specimenSelectorResultGrid = new SpecimenSelectorResultGrid(); + BIOSAXS.proposal = new Proposal(specimenSelectorResultGrid.input().proposal); + var panel = specimenSelectorResultGrid.getPanel(specimenSelectorResultGrid.input().data); + panel.render(targetId); }; +/** + * Show all buffer conditions for each macromolecules pointing out the measurements quality + * + * @height + * @maxHeight + * @width + * @searchBar + * @tbar + * @btnResultVisible + * + * #onClick + */ +function ResultsAssemblyGrid(args) { + this.height = 500; + this.id = BUI.id(); + this.maxHeight = this.height; -TemplateTabs.prototype.getPanel = function(panel) { - var _this = this; - if (this.experimentPanel == null) { - this.experimentPanel = Ext.create('Ext.container.Container', { - bodyPadding : 2, - width : 1000,//Ext.getBody().getViewSize().width * 0.9, - renderTo : this.targetId, - height : 500, -// style : { -// padding : 2 -// }, - items : [ - this.getExperimentTitle(), - this.panel - ], - listeners : { - afterrender : function(thisCmp) { - $("#SchemeReport" + _this.experiment.experimentId).click(function() { - $(this).target = "_blank"; - window.open('viewProjectList.do?reqCode=display&menu=platescheme&experimentId=' + _this.experiment.experimentId); - return false; - }); + this.width = 900; + this.searchBar = false; + this.tbar = false; + this.btnResultVisible = false; - } - } - }); - } - return this.experimentPanel; -}; + /** For processing **/ + this.processed = {}; + this.renderedPlotIndex = 0; -TemplateTabs.prototype.input = function(targetId) { - return new ExperimentTabs().input(); -}; + this.plotWidth = 210; + this.plotHeight = 80; -TemplateTabs.prototype.test = function(targetId) { - var experimentTabs = new TemplateTabs(targetId); - BIOSAXS.proposal = new Proposal(experimentTabs.input().proposal); - experimentTabs.draw(new Experiment(experimentTabs.input().experiment)); -}; - -var BUI = { - //interval : 60000, - interval : 40000, - rainbow : function(numOfSteps, step) { - // This function generates vibrant, "evenly spaced" colours (i.e. no clustering). This is ideal for creating easily distinguishable vibrant markers in Google Maps and other apps. - // Adam Cole, 2011-Sept-14 - // HSV to RBG adapted from: http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript - var r, g, b; - var h = step / numOfSteps; - var i = ~~(h * 6); - var f = h * 6 - i; - var q = 1 - f; - switch (i % 6) { - case 0: - r = 1, g = f, b = 0; - break; - case 1: - r = q, g = 1, b = 0; - break; - case 2: - r = 0, g = 1, b = f; - break; - case 3: - r = 0, g = q, b = 1; - break; - case 4: - r = f, g = 0, b = 1; - break; - case 5: - r = 1, g = 0, b = q; - break; + /** Colors **/ + this.validColor = BUI.getValidColor(); + this.warningColor = BUI.getWarningColor(); + this.notValidColor = BUI.getErrorColor(); + + /** Show warning if guinier quality less than **/ + this.guinierQuality = BUI.getQualityThreshold(); + this.framePercentageThreshold = BUI.getRadiationDamageThreshold(); + + if (args != null) { + if (args.height != null) { + this.height = args.height; + this.maxHeight = this.height; } - var c = "#" + ("00" + (~~(r * 255)).toString(16)).slice(-2) + ("00" + (~~(g * 255)).toString(16)).slice(-2) - + ("00" + (~~(b * 255)).toString(16)).slice(-2); - return (c); - }, - getFileNameByPath : function(filePath) { - if (filePath != null){ - var split = filePath.split("/"); - if (split.length > 0){ - return split[split.length - 1]; - } + if (args.maxHeight != null) { + this.maxHeight = args.maxHeight; + } + if (args.width != null) { + this.width = args.width; + } + if (args.searchBar != null) { + this.searchBar = args.searchBar; } - return "Not file"; - }, - getUpdateInterval : function() { - this.interval = this.interval + 2000; - return this.interval; - }, - getRadiationDamageThreshold : function() { - return 0.7; - }, - getQualityThreshold : function() { - return 0.7; - }, - getCreateShipmentURL : function() { - return '/ispyb/user/viewProjectList.do?reqCode=display&menu=create_shipment'; - }, - getCreateShipmentList : function() { - return '/ispyb/user/viewProjectList.do?reqCode=display&menu=list_shipment'; - }, - getShippingURL : function(shippingId) { - return '/ispyb/user/viewProjectList.do?reqCode=display&menu=shipment&shippingId=' + shippingId; - }, - getMacromoleculeResultsURLByMultipleSearch : function(array) { - return '/ispyb/user/viewProjectList.do?reqCode=display&menu=macromolecule&search=' - + JSON.stringify(array).replace(new RegExp("\"", 'g'), "'"); - }, - getMacromoleculeResultsURL : function(macromoleculeId) { - return '/ispyb/user/viewProjectList.do?reqCode=display&menu=macromolecule¯omoleculeId=' + macromoleculeId; - }, - getMacromoleculeBufferResultsURL : function(macromoleculeId, bufferId) { - return '/ispyb/user/viewProjectList.do?reqCode=display&menu=macromolecule&bufferId=' + bufferId + '¯omoleculeId=' + macromoleculeId; - }, + if (args.tbar != null) { + this.tbar = args.tbar; + } + if (args.btnResultVisible != null) { + this.btnResultVisible = args.btnResultVisible; + } - getMacromoleculeHeader : function(macromoleculeId) { + } - function getHTMLSource(macromoleculeId) { - if (macromoleculeId != null) { - var html = BUI.createFormLabel("Name :", BIOSAXS.proposal.getMacromoleculeById(macromoleculeId).name, 75, 400); - html = html + BUI.createFormLabel("Acronym :", BIOSAXS.proposal.getMacromoleculeById(macromoleculeId).acronym, 75, 400); - if (BIOSAXS.proposal.getMacromoleculeById(macromoleculeId).comments != null) { - html = html + BUI.createFormLabel("Comments :", BIOSAXS.proposal.getMacromoleculeById(macromoleculeId).comments, 75, 400); - } - return html; - } - } + this.onClick = new Event(); +} - return Ext.create('Ext.container.Container', { - frame : false, - layout : 'hbox', - title : 'Macromolecule', - bodyPadding : '5', - width : 890, - margin : '0 0 10 0', - height : 100, - style : { - borderColor : '#BDBDBD', - borderStyle : 'solid', - borderWidth : '1px' - }, - fieldDefaults : { - msgTarget : 'side', - labelWidth : 100 - }, - items : [ { - margin : "10 0 0 10", - width : 475, - border : 0, - html : getHTMLSource(macromoleculeId) - }, { - margin : "10 0 0 10", - width : 475, - border : 0, - html : BUI.getZipHTMLByMacromoleculeId(macromoleculeId) - } - ] - }); - }, +ResultsAssemblyGrid.prototype.edit = function(macromoleculeId) { + var _this = this; + var window = new MacromoleculeWindow(); + window.onSuccess.attach(function(sender, proposal) { + _this.store.loadData(_this._prepareData(BIOSAXS.proposal.getMacromolecules())); + }); + window.draw(BIOSAXS.proposal.getMacromoleculeById(macromoleculeId)); +}; - getZipHTMLByMacromoleculeId : function(macromoleculeId) { - if (macromoleculeId != null) { - var fileName = BIOSAXS.proposal.getMacromoleculeById(macromoleculeId).acronym; - return ""; - } - }, - getZipHTMLByExperimentId : function(experimentId, filename) { - if (filename == null){ - filename = "experiment"; - } - return ""; - }, - - getZipURLByAverageId : function(averageId, filename) { - if (filename == null){ - filename = "experiment"; - } - return "/ispyb/user/dataadapter.do?reqCode=getZipFileByAverageListId&f&mergeIdsList=" + averageId + "&fileName=" + filename; - }, - getZipURLBySubtractionId : function(subtractionId, filename) { - if (filename == null){ - filename = "experiment"; +ResultsAssemblyGrid.prototype.getTbar = function() { + var _this = this; + var actions = []; + + actions.push(Ext.create('Ext.Action', { + icon : '../images/add.png', + text : 'Add Macromolecule', + disabled : false, + handler : function(widget, event) { + var window = new MacromoleculeWindow(); + window.onSuccess.attach(function(sender) { + _this.refresh(); + }); + window.draw({}); } - return "/ispyb/user/dataadapter.do?reqCode=getZipFileByAverageListId&f&subtractionIdList=" + subtractionId + "&fileName=" + filename; - }, - - - getZipHTMLByFrameRangeId : function(experimentId, start, end) { - var fileName = "experiment"; - return ""; - }, - getZipFrameHPLCUrl : function(experimentId, start, end) { - return "/ispyb/user/dataadapter.do?reqCode=getZipFileH5ByFramesRange&f&experimentId=" + experimentId + "&start=" + Number(start) +"&end="+ Number(end); - }, - - getQueueUrl : function() { - return "/ispyb/user/dataadapter.do?reqCode=getPagingCompactAnalysisByProposalId"; - }, - - getQueueUrlByExperiment: function(experimentId) { - return "/ispyb/user/dataadapter.do?reqCode=getPagingCompactAnalysisByExperimentId&f&experimentId=" + experimentId; - }, - getStandardDeviation : function(values) { - var sum = 0; - var count = 0; - var avg = null; + })); + + actions.push(Ext.create('Ext.Action', { + icon : '../images/add.png', + text : 'Define an Assembly', + disabled : false, + handler : function(widget, event) { + var createAssemblywindow = new CreateAssemblyWindow(); + createAssemblywindow.onSaved.attach(function(evt, args) { + if (args.macromoleculeIds.length > 0) { + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, proposal) { + _this.refresh(); + }); + adapter.saveAssembly(args.assemblyId, args.macromoleculeIds); - var curatedValues = new Array(); - for ( var i = 0; i < values.length; i++) { - var value = values[i]; - if (value != null) { - if (!isNaN(value)) { - count = count + 1; - sum = sum + Number(value); - curatedValues.push(Number(value)); } - } - } - if (count > 0) { - avg = sum / count; - } else { - avg = sum; - } - var aux = 0; - for ( var i = 0; i < curatedValues.length; i++) { - var value = curatedValues[i]; - aux = aux + Math.pow(value - avg, 2); + }); + createAssemblywindow.draw(_this.experiment); } - /** std **/ - var std = Math.sqrt(aux / count); + })); + + return actions; +}; + +ResultsAssemblyGrid.prototype.refresh = function() { + this.store.loadData(this._prepareData()); +}; + +ResultsAssemblyGrid.prototype.addCondition = function(record, data_parsed, i) { + function getCondition(record) { return { - std : (std), - sum : (sum), - avg : (avg), - validNumber : count, - totalNumber : values.length, - values : values + concentration : record.conc, + quality : record.quality, + bufferBeforeFramesMerged : record.bufferBeforeFramesMerged, + bufferAfterFramesMerged : record.bufferAfterFramesMerged, + framesCount : record.framesCount, + framesMerge : record.framesMerge }; - }, - - getHTMLTableForFrameAveraged : function(bufferAcronym, macromoleculeAcronym, bbmerges, molmerges, bamerges, totalframes, bufferId,macromoleculeId, macromoleculeColor) { - - function getFrameSpan(framesMerged, total) { - return "(" + framesMerged + " of " + total + ")"; - } + } - function getColorFrame(framesMerged, total) { - if (framesMerged / total < 0.5) { - return "#FA5858"; - } - if ((framesMerged / total >= 0.5) && (framesMerged / total < 0.8)) { - return "#FF9900"; + if (data_parsed[i].conditions != null) { + var bufferFound = false; + for ( var index in data_parsed[i].conditions) { + condition = data_parsed[i].conditions[index]; + + if ((condition.macromoleculeId == record.macromoleculeId) && (condition.bufferId == record.bufferId)) { + data_parsed[i].conditions[index].concentrations.push(getCondition(record)); + bufferFound = true; } - return "white"; } - - function getRow(color, acroynm, framesMerged, totalframes) { - return " " - + BUI.getRectangleColorDIV(color, 10, 10) - + " " + acroynm + "" - + getFrameSpan(framesMerged, totalframes) + ""; + if (!bufferFound) { + data_parsed[i].conditions.push({ + macromoleculeId : record.macromoleculeId, + bufferId : record.bufferId, + concentrations : [ getCondition(record) ] + }); } + } +}; - var html = ""; +ResultsAssemblyGrid.prototype.process = function(record, data_parsed) { + if (this.processed[record.macromoleculeId] == null) { + this.processed[record.macromoleculeId] = true; + record.measurementCount = 1; + record.subtractionCount = 1; + record.averageCount = 1; + record.conditions = []; + data_parsed.push(record); + } - /** Buffer Before **/ - if (bufferAcronym != null) { - if (bbmerges != null) { - color = BIOSAXS.proposal.bufferColors[bufferId]; - html = html + getRow(color, bufferAcronym, bbmerges, totalframes); + for ( var i = 0; i < data_parsed.length; i++) { + if (data_parsed[i].macromoleculeId == record.macromoleculeId) { + data_parsed[i].measurementCount = data_parsed[i].measurementCount + 1; + + if (record.subtractionId != null) { + data_parsed[i].subtractionCount = data_parsed[i].subtractionCount + 1; + this.addCondition(record, data_parsed, i); } - } - /** Molecule **/ - if (macromoleculeAcronym != null) { - if (molmerges != null) { - if (macromoleculeColor == null){ - color = BIOSAXS.proposal.macromoleculeColors[macromoleculeId]; - } - else{ - color = macromoleculeColor; //BIOSAXS.proposal.macromoleculeColors[macromoleculeId]; + if (record.framesMerge != null) { + data_parsed[i].averageCount = data_parsed[i].averageCount + 1; + } + + if (record.timeStart != null) { + if (data_parsed[i].timeStart != null) { + if (moment(data_parsed[i].timeStart).format("X") > moment(record.timeStart).format("X")) { + data_parsed[i].timeStart = record.timeStart; + } } - html = html + getRow(color, macromoleculeAcronym, molmerges, totalframes); } } + } - /** Buffer After **/ - if (bufferAcronym != null) { - if (bamerges != null) { - color = BIOSAXS.proposal.bufferColors[bufferId]; - html = html + getRow(color, bufferAcronym, bamerges, totalframes); + return data_parsed; + +}; + +ResultsAssemblyGrid.prototype._prepareData = function(data) { + var data_parsed = []; + for ( var i = 0; i < data.length; i++) { + data_parsed = this.process(data[i], data_parsed); + } + this.data = data_parsed; + return data_parsed; +}; + +/** Given an array of conditions it returns distinct(concentrations) order by concentration and hash map with number of ocurrences**/ +ResultsAssemblyGrid.prototype.parseConcentrations = function(val, conditions, differentConcentration, quality) { + var conditions = []; + var differentConcentration = {}; + var quality = []; + for ( var i = 0; i < val.length; i++) { + var conc = Number(val[i].concentration).toFixed(1); + var quality = Number(val[i].quality).toFixed(2); + if (differentConcentration[conc] == null) { + differentConcentration[conc] = 0; + conditions.push({ + concentration : conc, + quality : [ quality ], + frames : [ { + bufferAfterFramesMerged : val[i].bufferAfterFramesMerged, + bufferBeforeFramesMerged : val[i].bufferBeforeFramesMerged, + framesMerge : val[i].framesMerge, + framesCount : val[i].framesCount + } ] + }); + } else { + /** Add quality **/ + for ( var j = 0; j < conditions.length; j++) { + if (conditions[j].concentration == conc) { + conditions[j].quality.push(quality); + conditions[j].frames.push({ + bufferAfterFramesMerged : val[i].bufferAfterFramesMerged, + bufferBeforeFramesMerged : val[i].bufferBeforeFramesMerged, + framesMerge : val[i].framesMerge, + framesCount : val[i].framesCount + }); + } } } - return html + "
"; - }, - isWebGLEnabled : function(return_context) { - if (!!window.WebGLRenderingContext) { - var canvas = document.createElement("canvas"); - names = [ "webgl", "experimental-webgl", "moz-webgl", "webkit-3d" ]; - context = false; - for ( var i = 0; i < 4; i++) { - try { - context = canvas.getContext(names[i]); - if (context && typeof context.getParameter == "function") { - // WebGL is enabled - if (return_context) { - // return WebGL object if the function's argument is present - return { - name : names[i], - gl : context - }; - } - // else, return just true - return true; - } - } catch (e) { - } + + differentConcentration[conc] = differentConcentration[conc] + 1; + } + /** sorting concentrations **/ + conditions.sort(function(a, b) { + return a.concentration - b.concentration; + }); + return { + concentrations : conditions, + differentConcentration : differentConcentration + }; +}; + +ResultsAssemblyGrid.prototype.getConditionWarnings = function(condition) { + + var withWarnings = 0; + + for ( var i = 0; i < condition.frames.length; i++) { + if (condition.quality[i] == null) { + withWarnings = withWarnings + 1; + continue; + } else { + if (Number(condition.quality[i]) < this.guinierQuality) { + withWarnings = withWarnings + 1; + continue; } - // WebGL is supported, but disabled - return false; } - // WebGL not supported28. - return false; - }, - getHTMLTableForPrefixes : function(bufferBeforeaverageFilePath, averageFilePath, bufferAfterAverageFilePath) { - function getRow(bufferBeforeaverageFilePath) { - file = bufferBeforeaverageFilePath; - try { - file = bufferBeforeaverageFilePath.split("/")[bufferBeforeaverageFilePath.split("/").length - 1]; - } catch (e) { - file = "NA"; + if (condition.frames[i].bufferBeforeFramesMerged / condition.frames[i].framesCount < this.framePercentageThreshold|| condition.frames[i].framesMerged / condition.frames[i].framesCount < this.framePercentageThreshold|| condition.frames[i].bufferAfterFramesMerged / condition.frames[i].framesCount < this.framePercentageThreshold) { + withWarnings = withWarnings + 1; + continue; + } + } + + return { + withWarnings : withWarnings, + withoutWarnings : condition.frames.length - withWarnings + }; +}; + +ResultsAssemblyGrid.prototype.getFrameHTMLTable = function(warnings) { + var html = ""; + if (warnings.withWarnings > 0) { + html = html + "
" + warnings.withWarnings + + "x
"; + } + + if (warnings.withoutWarnings > 0) { + html = html + "
" + warnings.withoutWarnings + + "x
"; + } + return html; +}; + +ResultsAssemblyGrid.prototype.createConcentrationRow = function(numberOcu, condition, warnings){ + var html = ""; + if (numberOcu > 1){ + html = html + "" +numberOcu + "x " + BUI.formatConcentration(condition.concentration); + } + else{ + html = html + BUI.formatConcentration(condition.concentration); + } + html = html + ""; + html = html + this.getFrameHTMLTable(warnings); + ""; + html = html + ""; + return html; +}; + + +ResultsAssemblyGrid.prototype.getConditionHTMLTable = function(val, style, record) { + var maxNumberColumns = 2; + var html = "
"; + var nColumns = 0; + for ( var r = 0; r < val.length; r++) { + if (nColumns == maxNumberColumns) { + nColumns = 0; + html = html + ""; + } + html = html + ""; } - var html = "
"; + var value = val[r]; + + var bufferAcronym = (BIOSAXS.proposal.getBufferById(value.bufferId).acronym); + + var parsed = (this.parseConcentrations(value.concentrations)); + var conditions = parsed.concentrations; + var differentConcentration = parsed.differentConcentration; + + /** Checking warnings **/ + var warnings = []; + var concentrationValidPerconcentration = 0; + var measurements = 0; + for ( var i = 0; i < conditions.length; i++) { + measurements = measurements + conditions[i].frames.length; + var warning = this.getConditionWarnings(conditions[i]); + warnings.push(warning); + if (warning.withoutWarnings > 0) { + concentrationValidPerconcentration = concentrationValidPerconcentration + 1; } - return "
" + file + "
"; - /** Buffer Before **/ - if (bufferBeforeaverageFilePath != null) { - html = html + getRow(bufferBeforeaverageFilePath); - } + this.validColor = '#E0F8E0'; + this.warningColor = '#F5DA81'; + this.notValidColor = '#F6CED8'; - /** Molecule **/ - if (averageFilePath != null) { - html = html + getRow(averageFilePath); + var color = this.warningColor; + if (concentrationValidPerconcentration > 2) { + color = this.validColor; + } + /** More measurement need to be done **/ + if (measurements < 3) { + color = this.notValidColor; } - /** Buffer After **/ - if (bufferAfterAverageFilePath != null) { - html = html + getRow(bufferAfterAverageFilePath); + html = html + "
"; + html = html + ""; + for ( var i = 0; i < conditions.length; i++) { + html = html + this.createConcentrationRow(differentConcentration[conditions[i].concentration], conditions[i], warnings[i]); } - return html + "
" + bufferAcronym.toUpperCase() + + "
"; - }, - getBaseURL : function() { - return '/ispyb/user/dataadapter.do'; - }, + html = html + ""; + html = html + ""; - getPrintcomponentURL : function(dewarId) { - return '/ispyb/user/viewDewarAction.do?reqCode=generateLabels&dewarId=' + dewarId; + html = html + ""; + nColumns = nColumns + 1; + } + html = html + "
"; + return html; +}; - }, - getPDBVisualizerURL : function(modelId, subtractionId, experimentId) { - return '/ispyb/user/viewProjectList.do?reqCode=display&menu=PDBVisualizer&modelId=' + modelId + '&experimentId=' + experimentId - + '&subtractionId=' + subtractionId; - }, +ResultsAssemblyGrid.prototype._getTbar = function() { + function goTo(url) { + window.location = url; + } - getURL : function() { - return this.getBaseURL() + '?reqCode=getImage'; + var _this = this; + return [ { + icon : '../images/application_view_list.png', + text : 'Multiple Select', + handler : function() { + var specimenSelectorResultGrid = new SpecimenSelectorResultGrid(); + var window = Ext.create('Ext.window.Window', { + title : 'Multiple select', + height : 600, + width : 600, + layout : 'fit', + items : [ specimenSelectorResultGrid.getPanel(_this.data) ], + buttons : [ { + text : 'Go', + handler : function() { + var array = []; + for ( var i = 0; i < specimenSelectorResultGrid.selected.length; i++) { + var row = specimenSelectorResultGrid.selected[i]; + array.push({ + macromoleculeId : row.macromoleculeId, + bufferId : row.bufferId + }); + } + goTo(BUI.getMacromoleculeResultsURLByMultipleSearch(array)); + + } + }, { + text : 'Cancel', + handler : function() { + window.close(); + } + } ] + + }).show(); - }, - getAbinitioImageURL : function() { - return this.getBaseURL() + '?reqCode=getAbinitioImage'; - }, - getNSDImageURL : function(modelListId) { - return BUI.getAbinitioImageURL() + '&type=NSD&modelListId=' + modelListId; - }, - getCHI2ImageURL : function(modelListId) { - return BUI.getAbinitioImageURL() + '&type=CHI2&modelListId=' + modelListId; - }, - getModelFile : function(type, modelId, format) { - return this.getBaseURL() + '?reqCode=getModelFile' + "&type=" + type + "&modelId=" + modelId + "&format=" + format; - }, - getPdbURL : function() { - return this.getBaseURL() + '?reqCode=getPdbFiles'; - }, - getStvArray : function(value, error) { - value = Number(value); - error = Number(error); - return [ value - error, value, value + error ]; - }, - getPointArrayForDygraph : function(x, y, error) { - return [ x, BUI.getStvArray(y, error) ]; - }, - createDIV : function(text, width, className, backgroundColor) { - var nameContainer = document.createElement("div"); - var nameSpan = document.createElement("span"); - if (className != null) { - nameSpan.setAttribute("class", className); - } - if (backgroundColor != null) { - nameSpan.setAttribute("style", "float:left;width:" + width + "px;height:18px;background-color:" + backgroundColor); - } else { - nameSpan.setAttribute("style", "float:left;width:" + width + "px;height:18px;"); } - nameSpan.appendChild(document.createTextNode(text)); - nameContainer.appendChild(nameSpan); - return nameContainer; - }, + } ]; +}; - createFormLabel : function(labelText, text, labelWidth, textWidth, backgroundColor) { - var div = document.createElement("div"); +ResultsAssemblyGrid.prototype.getLegendPanel = function() { + return { + html : '
' + BUI.getRectangleColorDIV(this.validColor, 10, 10) + + 'Good quality measurements' + + BUI.getRectangleColorDIV(this.warningColor, 10, 10) + + 'Probably valid with manual processing' + + BUI.getRectangleColorDIV(this.notValidColor, 10, 10) + + 'More measurements need to be done
' + }; +}; +/** Returns the grid **/ +ResultsAssemblyGrid.prototype.getPanel = function(macromolecules) { + var _this = this; - div.appendChild(BUI.createDIV(labelText, labelWidth, "bLabel", backgroundColor)); - div.appendChild(BUI.createDIV(text, textWidth, "btext", backgroundColor)); - return div.innerHTML; - }, + this.store = Ext.create('Ext.data.Store', { + fields : [ + 'macromoleculeId', 'macromoleculeAcronym', 'measurementCount', 'subtractionCount', 'averageCount', 'timeStart', 'concentrationArray', + 'bufferConditions', 'conditions' ], + data : this._prepareData(macromolecules) + }); - createTextArea : function(labelText, text, labelWidth, rows, cols) { - var div = document.createElement("div"); - div.appendChild(BUI.createDIV(labelText, labelWidth, "bLabel")); - var textArea = document.createElement("textarea"); - textArea.setAttribute("rows", rows); - textArea.setAttribute("cols", cols); - textArea.appendChild(document.createTextNode(text)); - div.appendChild(textArea); - return div.innerHTML; - }, + this.store.sort('macromoleculeAcronym'); - showBetaWarning : function() { - alert("ISPyB for Biosaxs version Beta has not this functionality enabled"); - }, + this.grid = Ext.create('Ext.grid.Panel', { + id : this.id, + title : 'Macromolecules', + store : this.store, + tbar : this._getTbar(), + bbar : [ this.getLegendPanel() ], + width : this.width, + height : this.height, + maxHeight : this.maxHeight, + sortableColumns : true, + columns : [ + { + text : '', + dataIndex : 'macromoleculeId', + width : 20, + hidden : false, + renderer : function(val, y, sample) { + return BUI.getRectangleColorDIV(BIOSAXS.proposal.macromoleculeColors[val], 10, 10); + } + }, + { + text : 'Macromolecule', + dataIndex : 'macromoleculeAcronym', + width : 200 + }, + { + text : 'Buffer Conditions', + dataIndex : 'conditions', + flex : 1, + renderer : function(val, style, record) { + return _this.getConditionHTMLTable(val, style, record); + } + }, + { + header : 'Average', + dataIndex : 'percentageCollected', + name : 'percentageCollected', + type : 'string', + hidden : true, + renderer : function(val, y, sample) { + return "
" + + BUI.getProgessBar((sample.raw.averageCount / sample.raw.measurementCount) * 100, sample.raw.averageCount + "/" + + sample.raw.measurementCount) + "
"; + }, + width : 100, + sorter : false + }, + { + header : 'Subtractions', + dataIndex : 'percentageCollected', + name : 'percentageCollected', + type : 'string', + hidden : true, + renderer : function(val, y, sample) { + return "
"+ BUI.getProgessBar((sample.raw.subtractionCount / sample.raw.measurementCount) * 100, sample.raw.subtractionCount + "/" + + sample.raw.measurementCount) + "
"; + }, + width : 100 + }, { + header : 'Date', + dataIndex : 'timeStart', + name : 'timeStart', + type : 'string', + renderer : function(value, metaData, record, rowIndex, colIndex, store) { + if (record.raw.timeStart != null) { + return moment(record.raw.timeStart).format("MMM Do YY"); + } + } + }, { + header : 'Download', + dataIndex : 'percentageCollected', + name : 'percentageCollected', + type : 'string', + renderer : function(val, y, sample) { + return BUI.getZipHTMLByMacromoleculeId(sample.raw.macromoleculeId); + }, + width : 100 + }, - formatValuesErrorUnitsScientificFormat : function(val, error, unit, args) { - var fontSize = 14; - var decimals = 2; - var errorFontSize = 10; - /** line break **/ - var lineBreak = true; - if (args != null) { - if (args.fontSize != null) { - fontSize = args.fontSize; - } - if (args.decimals != null) { - decimals = args.decimals; - } - if (args.errorFontSize != null) { - errorFontSize = args.errorFontSize; - } - if (args.lineBreak != null) { - lineBreak = args.lineBreak; + { + id : 'btnResultVisible', + width : 85, + sortable : false, + renderer : function(value, metaData, record, rowIndex, colIndex, store) { + return BUI.getGreenButton('GO'); + } + } ], + viewConfig : { + stripeRows : true, + listeners : { + afterrender : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + }, + celldblclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + _this.edit(record.data.macromoleculeId); + }, + cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + if (grid.getGridColumns()[cellIndex].getId() == 'buttonEditMacromolecule') { + _this.edit(record.data.macromoleculeId); + } + if (grid.getGridColumns()[cellIndex].getId() == 'buttonRemoveMacromolecule') { + BUI.showBetaWarning(); + } + if (grid.getGridColumns()[cellIndex].getId() == 'btnResultVisible') { + window.location = BUI.getMacromoleculeResultsURL(record.data.macromoleculeId); + } + } } - } + }); - if (val == "") { - return ""; - } - if (error == null) { - return "" + Number(val).toFixed(decimals) + ""; - } - var html = "" + Number(val).toFixed(decimals) + " " + unit + ""; - if (lineBreak) { - html = html + "
"; - } - return html + " ± " + Number(Number(error).toFixed(3)).toExponential() - + ""; - }, + /** Adding the tbar **/ + if (this.tbar) { + this.grid.addDocked({ + xtype : 'toolbar', + items : this.getTbar() + }); + } + return this.grid; +}; - formatValuesErrorUnits : function(val, error, unit, args) { - var fontSize = 16; - var decimals = 2; - var errorFontSize = 10; - /** line break **/ - var lineBreak = true; - if (args != null) { - if (args.fontSize != null) { - fontSize = args.fontSize; - } - if (args.decimals != null) { - decimals = args.decimals; - } - if (args.errorFontSize != null) { - errorFontSize = args.errorFontSize; - } - if (args.lineBreak != null) { - lineBreak = args.lineBreak; - } +ResultsAssemblyGrid.prototype.input = function(targetId) { + return { + data : DATADOC.getData_3367(), + proposal : DATADOC.getProposal_3367() + }; +}; - } +ResultsAssemblyGrid.prototype.test = function(targetId) { + var grid = new ResultsAssemblyGrid({ + height : 600, + searchBar : false, + tbar : false, + btnResultVisible : true, + width : 1000 + }); + BIOSAXS.proposal = new Proposal(grid.input().proposal); + var panel = grid.getPanel(grid.input().data); + panel.render(targetId); - if (val == "") { - return ""; +}; + + +function RigidModelGrid(args) { + this.height = null; + this.width = null; + this.id = BUI.id(); + + if (args != null) { + if (args.height != null) { + this.height = args.height; + + if (args.width != null) { + this.width = args.width; + } + } + } + + this.onSelected = new Event(this); +} + + +RigidModelGrid.prototype.refresh = function(subtractions) { + var data = []; + if (subtractions != null){ + if (subtractions.length > 0){ + for (j in subtractions){ + var subtraction = subtractions[j]; + if (subtraction.rigidBodyModeling3VOs != null){ + for (i in subtraction.rigidBodyModeling3VOs){ + data.push(subtraction.rigidBodyModeling3VOs[i]); + } + } + } + } + } + this.store.loadData(data); +}; + +RigidModelGrid.prototype.getPanel = function() { + var _this = this; + this.store = Ext.create('Ext.data.Store', { + fields : [ 'symmetry', 'subtractionId', 'subUnitConfigFilePath', 'rigidBodyModelingId', 'rigidBodyModelFilePath', 'logFilePath', 'fitFilePath', 'curveConfigFilePath', + 'crossCorrConfigFilePath', 'contactDescriptionFilePath' ], + data : [] + }); + + this.store.sort([ { + property : 'name', + direction : 'ASC' + } ]); + + this.panel = Ext.create('Ext.grid.Panel', { + store : this.store, + width : this.width, + height : this.height, + margin : 10, + deferredRender : false, + columns : [ { + text : 'RBM', + dataIndex : 'rigidBodyModelFilePath', + hidden : false, + flex : 1, + renderer : function(val){ + return BUI.getFileName(val); + } + }, { + text : 'Sub Unit Conf.', + dataIndex : 'subUnitConfigFilePath', + hidden : true, + flex : 1, + renderer : function(val){ + return BUI.getFileName(val); + } + }, { + text : 'Log', + dataIndex : 'logFilePath', + hidden : true, + flex : 1, + renderer : function(val){ + return BUI.getFileName(val); + } + }, { + text : 'Fit', + dataIndex : 'fitFilePath', + hidden : false, + flex : 1, + renderer : function(val){ + return BUI.getFileName(val); + } + }, { + text : 'Curve Conf.', + dataIndex : 'curveConfigFilePath', + hidden : false, + flex : 1, + renderer : function(val){ + return BUI.getFileName(val); + } + }, { + text : 'Cross Corr.', + dataIndex : 'crossCorrConfigFilePath', + hidden : false, + flex : 1, + renderer : function(val){ + return BUI.getFileName(val); + } + }, { + text : 'Contact Desc.', + dataIndex : 'contactDescriptionFilePath', + hidden : true, + flex : 1, + renderer : function(val){ + return BUI.getFileName(val); + } + } ], + + viewConfig : { + enableTextSelection : true, + preserveScrollOnRefresh : true + }, + listeners : { + cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + + }, + afterrender : function() { + } + } + }); + return this.panel; +}; + + +/** + * shows shipments + * + * @height + * @width + * @minHeight + * @btnEditVisible + */ +function ShipmentGrid(args) { + this.id = BUI.id(); + this.height = 100; + this.width = null; + this.minHeight = null; + this.btnEditVisible = true; + + if (args != null) { + if (args.height != null) { + this.height = args.height; } - if (error == null) { - return "" + Number(val).toFixed(decimals) + ""; + if (args.width != null) { + this.width = args.width; } - var html = "" + Number(val).toFixed(decimals) + " " + unit + ""; - if (lineBreak) { - html = html + "
"; + if (args.minHeight != null) { + this.minHeight = args.minHeight; } - return html + " ± " + Number(Number(error).toFixed(8)).toExponential() - + ""; - }, - - formatValuesUnits : function(val, unit, args) { - var fontSize = 12; - var decimals = 2; - var unitsFontSize = 10; - if (args != null) { - if (args.fontSize != null) { - fontSize = args.fontSize; - } - if (args.decimals != null) { - decimals = args.decimals; - } - if (args.unitsFontSize != null) { - unitsFontSize = args.unitsFontSize; - } - + if (args.btnEditVisible != null) { + this.btnEditVisible = args.btnEditVisible; } + } +} - if (val === "") { - return ""; - } - if (unit == null) { - return "" + Number(val).toFixed(decimals) + ""; - } - return "" + Number(val).toFixed(decimals) + " " + unit + ""; - }, +ShipmentGrid.prototype._getColumns = function() { + var _this = this; + var columns = [ + { + text : 'Name', + dataIndex : 'shippingName', + flex : 1 + }, + { + header : 'Type', + dataIndex : 'shippingType', + flex : 1, + hidden : true, + renderer : function(val, comp, record) { + if (val != null) { + return val.toUpperCase(); + } - getGreenButton : function(text, args) { - var width = 70; - var height = 20; - if (args != null) { - if (args.width != null) { - width = args.width; } - if (args.height != null) { - height = args.height; + }, + { + header : 'Status', + type : 'string', + flex : 1, + hidden : false, + renderer : function(comp, val, record) { + if (record.raw.shippingStatus != null) { + if (new String(record.raw.shippingStatus).toUpperCase() == 'OPENED') { + return "" + new String(record.raw.shippingStatus).toUpperCase() + ""; + } + return "" + new String(record.raw.shippingStatus).toUpperCase() + ""; + } + } + }, + { + text : 'Cases', + flex : 1, + hidden : false, + renderer : function(comp, val, record) { + var shipment = BIOSAXS.proposal.getShipmentById(record.data.shippingId); + var container = ""; + if (shipment.dewarVOs.length > 0) { + container = container + ""; + } else { + return "Empty"; + } + return container + "
" + shipment.dewarVOs.length + "x
"; } - } - - return ''; - }, -// getBlueButton : function(text, args) { -// var width = 70; -// var height = 20; -// if (args != null) { -// if (args.width != null) { -// width = args.width; -// } -// if (args.height != null) { -// height = args.height; -// } -// } -// -// return ''; -// }, + }, { + header : 'Comments', + dataIndex : 'comments', + flex : 1, + hidden : false + }, { + header : 'Creation Date', + dataIndex : 'creationDate', + hidden : true + } ]; - getBlueButton : function(text, args) { - var width = 70; - var height = 20; - var href = null; - if (args != null) { - if (args.width != null) { - width = args.width; - } - if (args.height != null) { - height = args.height; + if (this.btnEditVisible) { + columns.push({ + id : _this.id + 'buttonEdit', + text : '', + hidden : !_this.btnEditVisible, + width : 100, + sortable : false, + renderer : function(value, metaData, record, rowIndex, colIndex, store) { + return BUI.getGreenButton('EDIT'); } - if (args.href != null) { - href = args.href; + }); + } + + columns.push({ + id : _this.id + 'buttonRemove', + text : '', + width : 100, + sortable : false, + renderer : function(value, metaData, record, rowIndex, colIndex, store) { + var shipment = BIOSAXS.proposal.getShipmentById(record.data.shippingId); + if (shipment.dewarVOs.length == 0) { + return BUI.getRedButton('REMOVE'); } + } - if (href != null) { - return ''; - } else { - return ''; -// return ''; - } - }, - - getSubmitGreenButton : function(text) { - return ''; - }, + }); - getRedButton : function(text) { - return ''; + return columns; +}; - }, - getRectangleColorDIV : function(color, height, width) { - return '
'; - }, +ShipmentGrid.prototype._getTopButtons = function() { + var _this = this; + /** Actions buttons **/ + var actions = []; + actions.push(Ext.create('Ext.Action', { + icon : '../images/add.png', + text : 'Add Shipment', + handler : function(widget, event) { + //window.location = BUI.getCreateShipmentURL(); + var _this = this; - openBufferWindow : function(bufferId) { - var window = new BufferWindow(); - window.draw(tabs.experiment.getBufferById(bufferId), tabs.experiment); - }, + var shipmentForm = new ShipmentForm({ + creationMode : true, + showTitle : false + }); + shipmentForm.onSaved.attach(function(sender, shipment) { + _this.showShipmentTabs(shipment, targetId); + }); + + var window = Ext.create('Ext.window.Window', { + title : 'New Shipment', + height : 600, + width : 800, + layout : 'fit', + items : [ shipmentForm.getPanel() ] + }).show(); - /** Render for safety levels on grids **/ - safetyRenderer : function(val, y, specimen) { - var color = val; - if (val == "YELLOW") { - color = "#E9AB17"; } - return '' + val + ''; - }, + })); + return actions; +}; - getSampleColor : function() { - return '#CB181D'; - }, +ShipmentGrid.prototype.refresh = function(shippings) { + this.features = shippings; + this.store.loadData(this._prepareData(), false); +}; - getLightSampleColor : function() { - return '#FCBBA1'; - }, +ShipmentGrid.prototype._prepareData = function() { + var data = []; + for ( var i = 0; i < this.features.length; i++) { + data.push(this.features[i]); + } + return data; +}; - getBufferColor : function() { - return '#6A51A3'; - }, - getLightBufferColor : function() { - return '#BCBDDC'; - }, +ShipmentGrid.prototype.getPanel = function(shipments) { + this.features = shipments; + return this._renderGrid(); +}; - formatConcentration : function(val) { - if (val != null) { - return "" + Number(val).toFixed(2) + " mg/ml "; - } - return val; - }, +ShipmentGrid.prototype.edit = function(shippingId) { + window.location = BUI.getShippingURL(shippingId); +}; - formatVolume : function(sample, volume) { - if (Number(sample.data.volumeToLoad) > Number(sample.data.volume)) { - return "" + volume + " �l"; - } - if (Number(sample.data.volumeToLoad) == Number(sample.data.volume)) { - return "" + volume + " �l"; - } - return "" + volume + " �l"; - }, +ShipmentGrid.prototype._getStoreFields = function(data) { + var _this = this; + return [ { + name : 'shippingId' + }, { + name : 'shippingName' + }, { + name : 'shippingStatus' + }, { + name : 'shippingType' + }, { + name : 'creationDate' + }, { + name : 'comments' + } ]; +}; - getProposal : function() { - return new Proposal(); - }, +ShipmentGrid.prototype._renderGrid = function() { + var _this = this; - getSampleNameRenderer : function(val, y, record) { - var sample = record.data; - if (record.raw.macromolecule3VO == null) { - return '' + sample.code + ''; - } else { - return '' + sample.code + ''; - } - }, + /** Store **/ + var data = this._prepareData(); + this.store = Ext.create('Ext.data.Store', { + fields : this._getStoreFields(data), + autoload : true, + data : data + }); - getSafetyLevels : function() { - var safetyLevels = new Array(); - safetyLevels.push({ - safetyLevelId : "", - name : "UNKNOWN" - }); - safetyLevels.push({ - safetyLevelId : 1, - name : "GREEN" - }); - safetyLevels.push({ - safetyLevelId : 2, - name : "YELLOW" - }); - safetyLevels.push({ - safetyLevelId : 3, - name : "RED" - }); - return safetyLevels; - }, + this.store.loadData(data, false); + + this.grid = Ext.create('Ext.grid.Panel', { + title : "Shipping", + icon : '/ispyb/images/plane.gif', + width : this.width, + minWidth : this.minWidth, + height : this.height, + store : this.store, + columns : this._getColumns(), + viewConfig : { + stripeRows : true, + listeners : { + itemdblclick : function(dataview, record, item, e) { + _this.edit(record.raw.shippingId); + }, + cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonEdit') { + _this.edit(record.data.shippingId); + } - getErrorColor : function() { - return '#F6CED8'; - }, - getWarningColor : function() { - return '#F5DA81'; - }, - getValidColor : function() { - return '#E0F8E0'; - }, - getSamplePlateLetters : function() { - return [ "A", "B", "C", "D", "E", "F", "G", "H" ]; - }, - getSamplePositionHTML : function(sample, experiment) { - var plate = ""; - var row = ""; - var column = ""; - var rows = this.getSamplePlateLetters(); - if (sample.sampleplateposition3VO != null) { - var samplePlate = experiment.getSamplePlateById(sample.sampleplateposition3VO.samplePlateId); - if (samplePlate != null) { - plate = (samplePlate.slotPositionColumn); - row = (sample.sampleplateposition3VO.rowNumber); - column = (sample.sampleplateposition3VO.columnNumber); - // var html = "Plate: " + "" + plate + ""; - // html = html + ", Row: " + "" + rows[row - 1] + ""; - // html = html + ", Column: " + "" + column + ""; - var html = "Plate: " + plate - + "-" + rows[row - 1] + "" + column + ""; - return html; + if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonRemove') { + var shipment = BIOSAXS.proposal.getShipmentById(record.data.shippingId); + if (shipment.dewarVOs.length == 0) { + + var adapter = new BiosaxsDataAdapter(); + _this.grid.setLoading("ISPyB: Removing shipment"); + adapter.onSuccess.attach(function(sender) { + BIOSAXS.proposal.onInitialized.attach(function(sender) { + _this.refresh(BIOSAXS.proposal.getShipments()); + _this.grid.setLoading(false); + }); + BIOSAXS.proposal.init(); + }); + + adapter.onError.attach(function(sender) { + alert("Error"); + _this.grid.setLoading(false); + }); + adapter.removeShipment(record.data.shippingId); + } + } + } } + }, + selModel : { + mode : 'SINGLE' } - return ""; - }, + }); - getSafetyLabelName : function(safetyLevelId) { - var safetyLevels = BUI.getSafetyLevels(); - for ( var i = 0; i < safetyLevels.length; i++) { - if (safetyLevels[i].safetyLevelId == safetyLevelId) { - return safetyLevels[i].name; + var actions = _this._getTopButtons(); + this.grid.addDocked({ + xtype : 'toolbar', + items : actions + }); + + this.grid.getSelectionModel().on({ + selectionchange : function(sm, selections) { + if (selections.length) { + for ( var i = 0; i < actions.length; i++) { + if (actions[i].enable) { + actions[i].enable(); + } + } + } else { + for ( var i = 0; i < actions.length; i++) { + if (actions[i].alwaysEnabled == false) { + if (actions[i].disable) { + actions[i].disable(); + } + } + } } } - return "UNKNOWN"; - }, - /** generate random id **/ - id : function() { - var text = ""; - var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; - - for ( var i = 0; i < 5; i++) - text += possible.charAt(Math.floor(Math.random() * possible.length)); + }); + return this.grid; +}; - return text; - }, - showWarning : function(message) { - Ext.Msg.show({ - title : 'Warning', - msg : message, - icon : Ext.Msg.WARNING, - animEl : 'elId' - }); - }, - showError : function(message) { - Ext.Msg.show({ - title : 'Warning', - msg : message, - icon : Ext.Msg.ERROR, - animEl : 'elId' - }); - }, - getTipHTML : function(message) { - //return "
Tip
" - // + message + "
"; - return "
Tip
" - + message + "
"; - }, +ShipmentGrid.prototype.input = function() { + return { + proposal : new MeasurementGrid().input().proposal, + shippings : DATADOC.getShippings_10() + }; +}; - getWarningHTML : function(message) { - return "
Warning
" - + message + "
"; - }, +ShipmentGrid.prototype.test = function(targetId) { + var shipmentGrid = new ShipmentGrid({ + minHeight : 300, + height : 440 + }); + BIOSAXS.proposal = new Proposal(shipmentGrid.input().proposal); + var panel = shipmentGrid.getPanel(targetId); + panel.render(targetId); + shipmentGrid.refresh(shipmentGrid.input().shippings); +}; + +function SpecimenGrid(args) { + this.id = BUI.id(); + this.height = 500; + this.unitsFontSize = 9; + this.editEnabled = false; + this.isPositionColumnHidden = false; + this.removeBtnEnabled = false; - getErrorHTML : function(message) { - return "
Error
" - + message + "
"; - }, + this.selectionMode = "MULTI"; + this.updateRowEnabled = false; + this.grouped = true; + this.width = 900; + this.title = 'Specimens'; + + this.margin = "0 0 0 0"; +// this.experimentColorBased = false; - getProgessBar : function(percentage, text) { - /** percentage 100% green **/ - var color = "#0a0"; + if (args != null) { + if (args.height != null) { + this.height = args.height; + } - color = "#99CC00"; - if (percentage > 100) { - color = "yellow"; - percentage = 100; + if (args.showTitle == false) { + this.title = null; } - if (isNaN(percentage)) { - color = "white"; - percentage = 0; + + if (args.margin == false) { + this.margin = args.margin; } - var defaultText = percentage + "%"; - if (text != null) { - defaultText = text; + if (args.grouped == false) { + this.grouped = null; } - return "
" + defaultText + "
"; - }, - getFileName : function(filePath){ - if (filePath != null){ - return filePath.split("/")[filePath.split("/").length - 1] + if (args.width != null) { + this.width = args.width; } - return ""; - } -}; -Ext.ux.form.RequiredCombo = Ext.extend(Ext.form.ComboBox, { - config : { - cls : 'custom-field-text-required' - }, - initComponent : function() { - Ext.ux.form.RequiredCombo.superclass.initComponent.apply(this, arguments); - }, - checkChange : function() { - if ((this.getValue() == null) || String(this.getValue()).length == 0) { - this.addCls('custom-field-text-required'); - } else { - this.removeCls('custom-field-text-required'); + if (args.editEnabled != null) { + this.editEnabled = args.editEnabled; + } + if (args.removeBtnEnabled != null) { + this.removeBtnEnabled = args.removeBtnEnabled; + } + if (args.isPositionColumnHidden != null) { + this.isPositionColumnHidden = args.isPositionColumnHidden; + } + if (args.selectionMode != null) { + this.selectionMode = args.selectionMode; + } + if (args.updateRowEnabled != null) { + this.updateRowEnabled = args.updateRowEnabled; } + } -}); + this.onClick = new Event(this); + this.onSelected = new Event(this); + this.onRemoved = new Event(this); + this.onSpecimenChanged = new Event(); +} -Ext.define('Ext.form.field.RequiredNumber', { - extend : 'Ext.form.field.Number', - alias : 'widget.requirednumberfield', - alternateClassName : [ 'Ext.form.RequiredNumberField', 'Ext.form.RequiredNumber' ], - config : { - cls : 'custom-field-text-required' - }, +SpecimenGrid.prototype._prepareData = function(experiment) { + var data = []; - initComponent : function() { - var me = this; - me.callParent(); - me.setMinValue(me.minValue); - me.setMaxValue(me.maxValue); - }, + var samples = experiment.getSamples(); + for ( var i = 0; i < samples.length; i++) { + var sample = samples[i]; + if (sample.macromolecule3VO != null) { + sample.macromolecule = sample.macromolecule3VO.acronym; + sample.exposureTemperature = []; + sample.macromoleculeId = sample.macromolecule3VO.macromoleculeId; + } - onChange : function() { - if ((this.getValue() == null) || String(this.getValue()).length == 0) { - this.addCls('custom-field-text-required'); + if (sample.sampleplateposition3VO != null) { + if (sample.sampleplateposition3VO.samplePlateId != null) { + sample.samplePlateId = sample.sampleplateposition3VO.samplePlateId; + sample.rowNumber = sample.sampleplateposition3VO.rowNumber; + sample.columnNumber = sample.sampleplateposition3VO.columnNumber; + if (experiment.getSamplePlateById(sample.sampleplateposition3VO.samplePlateId).plategroup3VO != null) { + sample.plateGroupName = experiment.getSamplePlateById(sample.sampleplateposition3VO.samplePlateId).plategroup3VO.name; + sample.samplePlateName = experiment.getSamplePlateById(sample.sampleplateposition3VO.samplePlateId).name + " [" + sample.plateGroupName + "]"; + sample.slotPositionColumn = experiment.getSamplePlateById(sample.sampleplateposition3VO.samplePlateId).slotPositionColumn; + } + } } else { - this.removeCls('custom-field-text-required'); + sample.samplePlateName = "Unallocated Specimens"; } - this.toggleSpinners(); - this.callParent(arguments); - } -}); -Ext.define('Ext.form.field.RequiredText', { - extend : 'Ext.form.field.Text', - alias : 'widget.requiredtext', - requires : [ 'Ext.form.field.VTypes', 'Ext.layout.component.field.Text' ], - alternateClassName : [ 'Ext.form.RequiredTextField', 'Ext.form.RequiredText' ], - config : { - cls : 'custom-field-text-required' - }, - initComponent : function() { - var me = this; - if (me.allowOnlyWhitespace === false) { - me.allowBlank = false; + /** For grouping, because sencha has not option for multiple grouping I add a field to your store with a convert function that concatenates these two fields and then group by that field.**/ + sample.groupIndex = sample.bufferId + sample.macromoleculeId; + var macromolecule = BIOSAXS.proposal.getMacromoleculeById(sample.macromoleculeId); + + sample.acronym = "Buffers"; + if (macromolecule != null) { + sample.acronym = BIOSAXS.proposal.getMacromoleculeById(sample.macromoleculeId).acronym; } - me.callParent(); - me.addEvents( - /** - * @event autosize - * Fires when the **{@link #autoSize}** function is triggered and the field is resized according to the - * {@link #grow}/{@link #growMin}/{@link #growMax} configs as a result. This event provides a hook for the - * developer to apply additional logic at runtime to resize the field if needed. - * @param {Ext.form.field.Text} this This text field - * @param {Number} width The new field width - */ - 'autosize', - /** - * @event keydown - * Keydown input field event. This event only fires if **{@link #enableKeyEvents}** is set to true. - * @param {Ext.form.field.Text} this This text field - * @param {Ext.EventObject} e - */ - 'keydown', - /** - * @event keyup - * Keyup input field event. This event only fires if **{@link #enableKeyEvents}** is set to true. - * @param {Ext.form.field.Text} this This text field - * @param {Ext.EventObject} e - */ - 'keyup', - /** - * @event keypress - * Keypress input field event. This event only fires if **{@link #enableKeyEvents}** is set to true. - * @param {Ext.form.field.Text} this This text field - * @param {Ext.EventObject} e - */ - 'keypress'); - me.addStateEvents('change'); - me.setGrowSizePolicy(); - }, - checkChange : function() { - if ((this.getValue() == null) || String(this.getValue()).length == 0) { - this.addCls('custom-field-text-required'); - } else { - this.removeCls('custom-field-text-required'); + sample.buffer = experiment.getBufferById(sample.bufferId); + + sample.volumeToLoad = experiment.getVolumeToLoadBySampleId(sample.sampleId); + data.push(sample); + } + return data; +}; + +SpecimenGrid.prototype.deselectAll = function() { + this.grid.getSelectionModel().deselectAll(); +}; + +SpecimenGrid.prototype.selectById = function(specimenId) { + this.grid.getSelectionModel().deselectAll(); + for ( var i = 0; i < this.grid.getStore().data.items.length; i++) { + var item = this.grid.getStore().data.items[i].raw; + if (item.specimenId == specimenId) { + this.grid.getSelectionModel().select(i); } } -}); +}; -var BIOSAXS_COMBOMANAGER = { +SpecimenGrid.prototype.getStore = function() { + return this.store; +}; - getComboMacromoleculeByMacromolecules : function(macromolecules, args) { - var labelWidth = 150; - var margin = "0 0 5 0"; - var width = 300; +SpecimenGrid.prototype.getPlugins = function() { + var _this = this; - if (args != null) { - if (args.labelWidth != null) { - labelWidth = args.labelWidth; - } - if (args.margin != null) { - margin = args.margin; - } - if (args.width != null) { - width = args.width; - } - } + var plugins = []; - var store = Ext.create('Ext.data.Store', { - fields : [ 'macromoleculeId', 'acronym' ], - data : macromolecules, - sorters : [ 'acronym' ] - }); + if (this.updateRowEnabled) { + plugins.push(Ext.create('Ext.grid.plugin.RowEditing', { + clicksToEdit : 1, + listeners : { + validateedit : function(grid, e) { + var measurements = []; - return Ext.create('Ext.form.ComboBox', { - fieldLabel : 'Macromolecules', - labelWidth : labelWidth, - width : width, - margin : margin, - store : store, - editable: false, - queryMode : 'local', - displayField : 'acronym', - valueField : 'macromoleculeId' - }); - }, - getComboBuffers : function(buffers, args) { - var labelWidth = 150; - var margin = "0 0 5 0"; - var width = 300; - var fieldLabel = 'Buffer'; + if (e.newValues.bufferId != e.record.raw.bufferId) { + /** If buffer has changed we have to change all the specimens sharing same datacollection **/ + var dataCollections = []; + if (e.record.raw.macromoleculeId == null) { + dataCollections = dataCollections.concat(_this.experiment.getDataCollectionsBySpecimenId(e.record.raw.specimenId)); + } else { + var sampleDataCollections = _this.experiment.getDataCollectionsBySpecimenId(e.record.raw.specimenId); + for ( var i = 0; i < sampleDataCollections.length; i++) { + var sampleDataCollection = sampleDataCollections[i]; + if (sampleDataCollection != null) { + for ( var j = 0; j < sampleDataCollection.measurementtodatacollection3VOs.length; j++) { + var measurementTODc = sampleDataCollection.measurementtodatacollection3VOs[j]; + if (measurementTODc.dataCollectionOrder == 1) { + dataCollections = dataCollections.concat(_this.experiment.getDataCollectionsBySpecimenId(_this.experiment + .getMeasurementById(measurementTODc.measurementId).specimenId)); + } + } + } + } + } + var i = null; + for ( i = 0; i < dataCollections.length; i++) { + var dataCollection = dataCollections[i]; + var specimens = _this.experiment.getSpecimenByDataCollectionId(dataCollection.dataCollectionId); + measurements = measurements.concat(specimens); + } - if (args != null) { - if (args.labelWidth != null) { - labelWidth = args.labelWidth; - } - if (args.margin != null) { - margin = args.margin; - } - if (args.width != null) { - width = args.width; - } - if (args.noLabel != null) { - fieldLabel = null; - } - } + for ( i = 0; i < measurements.length; i++) { + var measurement = measurements[i]; + var specimen = _this.experiment.getSpecimenById(measurement.specimenId); + specimen.bufferId = e.newValues.bufferId; + new BiosaxsDataAdapter().saveSpecimen(specimen, _this.experiment); + } + } - var store = Ext.create('Ext.data.Store', { - fields : [ 'bufferId', 'acronym' ], - data : buffers, - sorters : [ 'acronym' ] - }); + /** Setting values **/ + e.record.raw.concentration = e.newValues.concentration; + e.record.raw.volume = e.newValues.volume; - return Ext.create('Ext.form.ComboBox', { - fieldLabel : fieldLabel, - labelWidth : labelWidth, - width : width, - margin : margin, - editable: false, - store : store, - queryMode : 'local', - displayField : 'acronym', - valueField : 'bufferId' - }); - }, - getComboSessions : function(sessions, args) { - var labelWidth = 150; - var margin = "0 0 5 0"; - var width = 300; + /** Position **/ + if (e.record.raw.sampleplateposition3VO != null) { + var samplePlate = _this.experiment.getSamplePlateBySlotPositionColumn(e.newValues.slotPositionColumn); + if (samplePlate != null) { + e.record.raw.sampleplateposition3VO = { + columnNumber : e.newValues.columnNumber, + rowNumber : e.newValues.rowNumber, + samplePlateId : samplePlate.samplePlateId + }; + } + } else { + if (e.newValues.slotPositionColumn != null) { + var samplePlate = _this.experiment.getSamplePlateBySlotPositionColumn(e.newValues.slotPositionColumn); + if (samplePlate != null) { + e.record.raw.sampleplateposition3VO = { + columnNumber : e.newValues.columnNumber, + rowNumber : e.newValues.rowNumber, + samplePlateId : samplePlate.samplePlateId + }; + } + } + } - if (args != null) { - if (args.labelWidth != null) { - labelWidth = args.labelWidth; - } - if (args.margin != null) { - margin = args.margin; - } - if (args.width != null) { - width = args.width; + var macromoleculeId = e.record.data.macromoleculeId; + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, specimen) { + /** Because macromolecule3VO is fecthed LAZY **/ + if (macromoleculeId != null) { + specimen.macromolecule3VO = BIOSAXS.proposal.getMacromoleculeById(macromoleculeId); + } + _this.onSpecimenChanged.notify(specimen); + _this.grid.setLoading(false); + }); + adapter.onError.attach(function() { + alert("Error"); + _this.grid.setLoading(false); + }); + _this.grid.setLoading(); + adapter.saveSpecimen(e.record.raw, _this.experiment); + } } - } - - for ( var i = 0; i < sessions.length; i++) { - sessions[i]["startDateFormatted"] = moment(sessions[i].startDate).format("MMM Do YY"); - sessions[i]["sorter"] = moment(sessions[i].startDate).format("YYYYMMDD"); - } + })); + } + return plugins; +}; - var store = Ext.create('Ext.data.Store', { - fields : [ 'sessionId', 'startDateFormatted', 'beamlineName', 'startDate', 'endDate', 'beamlineOperator' ], - data : sessions, - sorters : [ 'sorter' ] +SpecimenGrid.prototype._getRowCombo = function() { + var data = []; + for ( var i = 1; i <= 8; i++) { + data.push({ + rowNumber : i, + name : BUI.getSamplePlateLetters()[i - 1] }); + } - return Ext.create('Ext.form.ComboBox', { - fieldLabel : 'Sessions', - labelWidth : labelWidth, - width : width, - margin : margin, - store : store, - queryMode : 'local', - // displayField : 'startDate', - valueField : 'sessionId', - // Template for the dropdown menu. - // Note the use of "x-boundlist-item" class, - // this is required to make the items selectable. - tpl : Ext.create('Ext.XTemplate', '', - '
{startDateFormatted} {beamlineName}
', '
'), - // template for the content inside text field - displayTpl : Ext.create('Ext.XTemplate', '', '{startDateFormatted}', '') + var positionsStore = Ext.create('Ext.data.Store', { + fields : [ 'rowNumber', 'name' ], + data : data + }); - }); - } + return Ext.create('Ext.form.ComboBox', { + store : positionsStore, + queryMode : 'local', + displayField : 'name', + valueField : 'rowNumber' + }); }; - - function GenericWindow(args){ - this.title = "title"; - this.width = 700; - this.height = 500; - this.id = BUI.id(); - - this.close = false; - this.draggable = true; - this.modal = true; - - if (args != null){ - if (args.actions != null){ - this.actions = args.actions; - } - if (args.form != null){ - this.form = args.form; - } - if (args.width != null){ - this.width = args.width; - } - if (args.modal != null){ - this.modal = args.modal; - } - - if (args.height != null){ - this.height = args.height; - } - if (args.title != null){ - this.title = args.title; - } - if (args.form != null){ - this.form = args.form; - } - if (args.close != null){ - this.close = args.close; - } - if (args.draggable != null){ - this.draggable = args.draggable; - } - - } - /** Events **/ - this.onSaved = new Event(this); - }; - - -GenericWindow.prototype.getButtons = function() { - var _this = this; - - if (this.close){ - return [ { - text : 'Close', - handler : function() { - _this.panel.close(); - } - } ]; - } - else{ - return [ { - text : 'Save', - handler : function() { - _this.save(); - - } - }, { - text : 'Cancel', - handler : function() { - _this.panel.close(); - } - } ]; +SpecimenGrid.prototype._getColumnCombo = function() { + var data = []; + for ( var i = 1; i <= 12; i++) { + data.push({ + columnNumber : i + }); } -}; -GenericWindow.prototype.save = function (){ - alert("Method save of GenerciWindow class is abstract"); + var positionsStore = Ext.create('Ext.data.Store', { + fields : [ 'columnNumber' ], + data : data + }); + + return Ext.create('Ext.form.ComboBox', { + store : positionsStore, + queryMode : 'local', + displayField : 'columnNumber', + valueField : 'columnNumber' + }); }; -GenericWindow.prototype._postRender = function(data, experiment){ +SpecimenGrid.prototype._getSlotColumBombo = function() { + if (this.experiment){ + var length = this.experiment.getSamplePlates().length; + + var data = []; + for ( var i = 1; i <= length; i++) { + data.push({ + slotPositionColumn : i + }); + } + + var positionsStore = Ext.create('Ext.data.Store', { + fields : [ 'slotPositionColumn' ], + data : data + }); + return Ext.create('Ext.form.ComboBox', { + store : positionsStore, + queryMode : 'local', + displayField : 'slotPositionColumn', + valueField : 'slotPositionColumn' + }); + } }; - -GenericWindow.prototype.draw = function (data, experiment){ - this._render(data, experiment); +SpecimenGrid.prototype.getPanelByExperiment = function(experiment) { + this.experiment = experiment; + var data = this._prepareData(experiment); + return this.getPanel(data); }; -GenericWindow.prototype.refresh = function(data, experiment){ - this.data = data; - this.experiment = experiment; - this.form.refresh(data, experiment); +SpecimenGrid.prototype.refresh = function(experiment) { + this.experiment = experiment; + var data = this._prepareData(experiment); + this.store.loadData(data); }; -GenericWindow.prototype._render = function(data, experiment){ - this.data = data; - var _this = this; - if (this.panel == null){ - this.panel = Ext.create('Ext.Window', { - id : this.id, - title : this.title, - resizable : true, - constrain : true, - border : 1, - modal : this.modal, - frame : false, - draggable : this.draggable, - closable : true, - autoscroll : true, - layout : { type: 'vbox',align: 'stretch'}, - width : this.width, - height : this.form.height, - buttonAlign :'right', - buttons : this.getButtons(), - items : this.form.getPanel(data, experiment), - listeners: { - scope : this, - minimize : function(){ - this.panel.hide(); - }, - destroy : function(){ - delete this.panel; - } - } - }); - this.panel.setLoading(); - } - - this.panel.show(); - this._postRender(); -}; -function CalendarWidget(args) { - this.height = 740; +SpecimenGrid.prototype.getPanel = function() { + var _this = this; + this.store = Ext.create('Ext.data.Store', { + fields : [ + 'buffer', 'bufferId', 'code', 'macromolecule', 'acronym', 'macromoleculeId', 'concentration', 'volume', 'samplePlateId', + 'slotPositionColumn', 'rowNumber', 'columnNumber', 'groupIndex' ], + data : [], + groupField : 'acronym' + }); + this.store.sort([ { + property : 'concentration', + direction : 'ASC' + }, { + property : 'buffer', + direction : 'ASC' + } ]); - if (args != null) { - if (args.height != null) { - this.height = args.height; + var selModel = Ext.create('Ext.selection.RowModel', { + allowDeselect : true, + mode : this.selectionMode, + listeners : { + selectionchange : function(sm, selections) { + var selected = []; + for ( var i = 0; i < selections.length; i++) { + selected.push(selections[i].raw); + } + _this.onSelected.notify(selected); + } } + }); + + var features = []; + + if (this.grouped) { + features.push({ + ftype : 'grouping', + groupHeaderTpl : '{name}', + hideGroupedHeader : false, + startCollapsed : false, + id : 'myGroupedStore' + }); } - this.onClick = new Event(); -} + this.grid = Ext.create( + 'Ext.grid.Panel', + { + title : this.title, + height : this.height, + width : this.width, + selModel : selModel, + store : this.store, + features : features, + margin : this.margin, + plugins : this.getPlugins(), + columns : [ + { + text : '', + dataIndex : 'macromolecule', + width : 20, + renderer : function(val, y, sample) { + var macromoleculeId = null; + if (sample.raw.macromolecule3VO != null) { + macromoleculeId = sample.raw.macromolecule3VO.macromoleculeId; + } + else{ + macromoleculeId = sample.raw.macromoleculeId; + } + + if (macromoleculeId == null) return; + return BUI.getRectangleColorDIV(_this.experiment.macromoleculeColors[macromoleculeId], 10, 10); + } + }, + { + text : 'Macromolecule', + dataIndex : 'macromolecule', + width : 100 + }, + { + text : '', + dataIndex : 'buffer', + width : 20, + renderer : function(val, y, sample) { + var color = "black"; + if (sample.raw.bufferId != null) { + if (_this.experiment.getDataCollectionsBySpecimenId(sample.raw.specimenId)[0] != null){ + color = _this.experiment.getSpecimenColorByBufferId(_this.experiment.getMeasurementById(_this.experiment.getDataCollectionsBySpecimenId(sample.raw.specimenId)[0].measurementtodatacollection3VOs[0].measurementId).specimenId); + } + return BUI.getRectangleColorDIV(color, 10, 10); + } + } + }, { + text : 'Buffer', + dataIndex : 'bufferId', + width : 140, + renderer : function(val, y, sample) { + if (sample.raw.bufferId != null) { + return BIOSAXS.proposal.getBufferById(val).acronym; + } + }, + editor : BIOSAXS_COMBOMANAGER.getComboBuffers(BIOSAXS.proposal.getBuffers(), { + noLabel : true, + width : 300 + }) + }, { + text : 'Conc.', + dataIndex : 'concentration', + width : 100, + editor : { + allowBlank : false + }, + renderer : function(val, meta, sample) { + if (isNaN(val)) { + meta.tdCls = 'yellow-cell'; + return val; + } else { + if (val != 0) { + return BUI.formatValuesUnits(val, 'mg/ml', { + fontSize : 16, + decimals : 3, + unitsFontSize : this.unitsFontSize + }); + } else { + return; + } + } + } + }, { + text : 'Vol. Well', + dataIndex : 'volume', + width : 70, + editor : { + allowBlank : true + }, + renderer : function(val, y, sample) { + return BUI.formatValuesUnits(sample.data.volume, 'µl', { + fontSize : 12, + decimals : 2, + unitsFontSize : this.unitsFontSize + }); + } + }, { + text : 'Position', + hidden : true, + flex : 1, + renderer : function(val, y, sample) { + return BUI.getSamplePositionHTML(sample.raw, _this.experiment); + } + }, { + text : 'samplePlateId', + dataIndex : 'samplePlateId', + hidden : true + }, { + text : 'Plate', + hidden : this.isPositionColumnHidden, + dataIndex : 'slotPositionColumn', + editor : _this._getSlotColumBombo(), + flex : 1, + renderer : function(val, meta, sample) { + if ((val != null) & (val != "")) { + return val; + } else { + meta.tdCls = 'yellow-cell'; + } + } + }, { + text : 'Row', + hidden : this.isPositionColumnHidden, + dataIndex : 'rowNumber', + editor : this._getRowCombo(), + flex : 1, + renderer : function(val, meta, sample) { + if ((val != null) && (val != "")) { + return BUI.getSamplePlateLetters()[val - 1]; + } else { + meta.tdCls = 'yellow-cell'; + } + } + }, { + text : 'Well', + hidden : this.isPositionColumnHidden, + dataIndex : 'columnNumber', + editor : this._getColumnCombo(), + flex : 1, + renderer : function(val, meta, sample) { + if ((val != null) && (val != "")) { + return val; + } else { + meta.tdCls = 'yellow-cell'; + } + } + }, { + id : _this.id + 'buttonEditSample', + text : 'Edit', + width : 80, + sortable : false, + hidden : !_this.editEnabled, + renderer : function(value, metaData, record, rowIndex, colIndex, store) { + if (_this.editEnabled) { + return BUI.getGreenButton('EDIT'); + } + } + }, { + id : _this.id + 'buttonRemoveSample', + text : '', + hidden : !_this.removeBtnEnabled, + width : 100, + sortable : false, + renderer : function(value, metaData, record, rowIndex, colIndex, store) { + if (_this.removeBtnEnabled) { + return BUI.getRedButton('REMOVE'); + } + } + } -CalendarWidget.prototype.loadData = function(data) { - this.events = []; + ], + viewConfig : { + preserveScrollOnRefresh : true, + stripeRows : true, + getRowClass : function(record) { + var specimens = _this.experiment.getSampleByPosition(record.data.samplePlateId, record.data.rowNumber, + record.data.columnNumber); + if (specimens.length > 1) { + return 'red-row'; - for ( var i = 0; i < data.length; i++) { - var date = moment(data[i].creationDate); - var textColor = 'black'; + } + }, + listeners : { + selectionchange : function(grid, selected) { + _this.onClick.notify(record.raw); + }, + cellclick : function(grid, td, cellIndex, record, tr) { + if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonEditSample') { + } + if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonRemoveSample') { + grid.getStore().removeAt(rowIndex); + _this.onRemoved.notify(); + } - if (data[i].status == "FINISHED") { - textColor = 'green'; - } - if (data[i].status == "ABORTED") { - textColor = 'red'; - } - this.events.push({ - title : data[i].name, - start : date.format("YYYY-MM-DD HH:mm:ss"), - end : date.format("YYYY-MM-DD HH:mm:ss"), - date : date, - allDay : false, - color : textColor, - className : date.format("YYYY-MM-DD") - }); - } + } + } + } + }); + return this.grid; }; -CalendarWidget.prototype.draw = function(targetId) { - var _this = this; - $('#' + targetId).fullCalendar({ - eventClick : function(calEvent, jsEvent, view) { - _this.onClick.notify(calEvent.className[0]); - - }, - contentHeight : _this.height, - header : { - left : 'prev,next today', - center : 'title', - right : 'month,basicWeek,basicDay' - }, - editable : false, - events : this.events +SpecimenGrid.prototype.input = function() { + return { + experiment : DATADOC.getExperiment_10(), + proposal : DATADOC.getProposal_10() + }; +}; + +SpecimenGrid.prototype.test = function(targetId) { + var specimenGrid = new SpecimenGrid({ + height : 400, + maxHeight : 400, + width : 1000 }); + BIOSAXS.proposal = new Proposal(specimenGrid.input().proposal); + + var experiment = new Experiment(specimenGrid.input().experiment); + var panel = specimenGrid.getPanelByExperiment(experiment); + panel.render(targetId); }; /** - * It shows a table with the guinier and gnom data as well as passed and - * discarded measurements + * Shows a list of stock solutions with macromolecule, buffer, storage temperature, concentration, shipment and comments * - * @height - * @showBufferColumns + * @multiselect allows multiple selection + * @height + * @minHeight + * @width + * @tbar + * @showTitle + * @isPackedVisible shows is stock solution is in a box + * @btnEditVisible shows edit button + * @btnAddVisible + * @btnAddExisting + * @btnUnpackVisible allows to unpack a stock solution + * @btnRemoveVisible allow to remove a stock solution */ -function ConcentrationHTMLTableWidget(args) { + +function StockSolutionGrid(args) { this.id = BUI.id(); + this.height = 100; + this.width = null; + this.minHeight = null; + this.tbar = true; - this.showBufferColumns = true; + this.title = "Stock Solutions"; + + /** Visible buttons and actions **/ + this.btnEditVisible = true; + this.btnRemoveVisible = true; + this.btnAddVisible = true; + this.btnAddExisting = false; + this.isPackedVisible = true; + this.btnUnpackVisible = false; + + /** Selectors **/ + this.multiselect = false; + this.selectedStockSolutions = []; if (args != null) { + if (args.btnUnpackVisible != null) { + this.btnUnpackVisible = args.btnUnpackVisible; + } + if (args.multiselect != null) { + this.multiselect = args.multiselect; + } if (args.height != null) { this.height = args.height; } - if (args.showBufferColumns != null) { - this.showBufferColumns = args.showBufferColumns; + if (args.btnEditVisible != null) { + this.btnEditVisible = args.btnEditVisible; + } + if (args.btnAddVisible != null) { + this.btnAddVisible = args.btnAddVisible; + } + if (args.btnAddExisting != null) { + this.btnAddExisting = args.btnAddExisting; + } + if (args.width != null) { + this.width = args.width; + } + if (args.minHeight != null) { + this.minHeight = args.minHeight; + } + if (args.tbar != null) { + this.tbar = args.tbar; + } + if (args.btnRemoveVisible != null) { + this.btnRemoveVisible = args.btnRemoveVisible; + } + if (args.isPackedVisible != null) { + this.isPackedVisible = args.isPackedVisible; + } + if (args.showTitle != null) { + this.showTitle = args.showTitle; + if (this.showTitle == false) { + this.title = null; + } } - } -} - -ConcentrationHTMLTableWidget.prototype.refresh = function(parsedData) { - document.getElementById(this.id).innerHTML = this.getPanel(parsedData); -}; - -ConcentrationHTMLTableWidget.prototype.getPanel = function(parsedData) { - var html = "
"; - html = html + ""; - /** Title * */ - html = html + ""; - html = html + ""; - if (this.showBufferColumns) { - html = html + ""; } - html = html + ""; - html = html + ""; - html = html + ""; - html = html + ""; - html = html + ""; - html = html + ""; - html = html + ""; - html = html + ""; - html = html + ""; - html = html + ""; - html = html + ""; - html = html + ""; - html = html + ""; + /** Events **/ + this.onProposalChanged = new Event(this); + this.onStockSolutionSelected = new Event(this); +} - parsedData.concentration.concentrations.sort(function(a, b) { - return a.concentration - b.concentration; - }); - /** Row * */ - for ( var i = 0; i < parsedData.concentration.concentrations.length; i++) { - var row = parsedData[i]; +StockSolutionGrid.prototype._getColumns = function() { + var _this = this; + var columns = [ - var tr = ""; - if (i % 2 == 1) { - tr = ""; + { + header : 'Macromolecule', + dataIndex : 'macromolecule', + id : _this.id + 'macromolecule', + type : 'string', + renderer : function(val, y, specimen) { + return '' + val + ''; + }, + hidden : false, + flex : 1 + }, { + header : 'Buffer', + dataIndex : 'buffer', + name : 'buffer', + hidden : false, + renderer : function(val, y, specimen) { + return '' + val + ''; + }, + type : 'string', + flex : 1 + }, { + header : 'Acronym', + dataIndex : 'name', + name : 'name', + type : 'string', + flex : 1, + hidden : true + }, { + header : 'Storage Temp.', + dataIndex : 'storageTemperature', + name : 'storageTemperature', + type : 'string', + flex : 1, + hidden : false, + renderer : function(val) { + return BUI.formatValuesUnits(val, 'C', { + fontSize : 12, + decimals : 2, + unitsFontSize : 10 + }); } - var goodMeasurements = (parsedData.concentration.concentrations[i].frames.length - parsedData.concentration.concentrations[i].frames_warning); - if (goodMeasurements == 0) { - tr = ""; + }, { + header : 'Volume', + dataIndex : 'volume', + type : 'string', + flex : 1, + hidden : false, + renderer : function(val) { + return BUI.formatValuesUnits(val, 'µl', { + fontSize : 12, + decimals : 2, + unitsFontSize : 10 + }); } - html = html + tr; - html = html + ""; - if (this.showBufferColumns) { - html = html + ""; + }, { + header : 'Concentration', + dataIndex : 'concentration', + name : 'concentration', + type : 'string', + flex : 1, + renderer : function(val) { + return BUI.formatValuesUnits(val, 'mg/ml', { + fontSize : 12, + decimals : 2, + unitsFontSize : 10 + }); } - html = html + ""; - html = html + ""; - html = html + ""; - html = html + ""; - html = html + ""; - html = html + ""; - html = html + ""; - html = html + ""; + }, { + header : 'Packed', + dataIndex : 'comments', + id : _this.id + "box", + type : 'string', + width : 50, + hidden : !this.isPackedVisible, + renderer : function(val, cmp, a) { + if (a.raw.boxId != null) { + return "
"; + } + + } + }, { + header : 'Comments', + dataIndex : 'comments', + type : 'string', + flex : 1 + } ]; + + if (this.btnEditVisible) { + columns.push({ + id : _this.id + 'buttonEdit', + width : 85, + sortable : false, + renderer : function(value, metaData, record, rowIndex, colIndex, store) { + if (_this.btnEditVisible) { + return BUI.getGreenButton('EDIT'); + } + } + }); + } + + if (this.btnRemoveVisible) { + columns.push({ + id : _this.id + 'buttonRemove', + width : 85, + sortable : false, + renderer : function(value, metaData, record, rowIndex, colIndex, store) { + if (_this.btnRemoveVisible) { + return BUI.getRedButton('REMOVE'); + } + } + }); } - return html + "
ConcentrationBufferMeasurementsGuinierGnom
PassedDiscardedRgI0QualityRgdMax
" + parsedData.concentration.concentrations[i].concentration + "" + parsedData.concentration.concentrations[i].bufferAcronym + "" + goodMeasurements + " of " + parsedData.concentration.concentrations[i].frames.length + "" + parsedData.concentration.concentrations[i].frames_warning + "" + BUI.formatValuesErrorUnits(parsedData.concentration.concentrations[i].calculation.rgGuinier.avg, parsedData.concentration.concentrations[i].calculation.rgGuinier.std, "nm", { - lineBreak : false - }) + "" + BUI.formatValuesErrorUnits(parsedData.concentration.concentrations[i].calculation.i0Guinier.avg, parsedData.concentration.concentrations[i].calculation.i0Guinier.std, " ", { - lineBreak : false - }) + "" + BUI.formatValuesErrorUnits(parsedData.concentration.concentrations[i].calculation.quality.avg, parsedData.concentration.concentrations[i].calculation.quality.std, " ", { - lineBreak : false - }) + "" + BUI.formatValuesErrorUnits(parsedData.concentration.concentrations[i].calculation.rgGnom.avg, parsedData.concentration.concentrations[i].calculation.rgGnom.std, " ", { - lineBreak : false - }) + "" + BUI.formatValuesErrorUnits(parsedData.concentration.concentrations[i].calculation.dMax.avg, parsedData.concentration.concentrations[i].calculation.dMax.std, " ", { - lineBreak : false - }) + "
"; + + if (this.btnUnpackVisible) { + columns.push({ + id : _this.id + 'buttonUnpack', + width : 85, + sortable : false, + renderer : function(value, metaData, record, rowIndex, colIndex, store) { + if (_this.btnUnpackVisible) { + return BUI.getBlueButton('UNPACK'); + } + } + }); + } + return columns; }; -ConcentrationHTMLTableWidget.prototype.input = function() { - return { - data : { - "dataCollectionCount" : 4, - "buffers" : [ { - "bufferId" : "422" - } ], - "concentration" : { - "concentrations" : [ { - "concentration" : "7.17", - "id" : "7.1699999999999999", - "bufferId" : "422.00", - "bufferAcronym" : "B1", - "rgGuinier" : [ "5.12723" ], - "frames" : [ { - "total" : "0.515705406286", - "bufferBeforeMeasurementId" : 15045, - "sampleMergeId" : 8176, - "averagedModelId" : null, - "I0" : "183.457461646", - "proposalCode" : "OPD", - "averagedModel" : null, - "bufferAfterMergeId" : 8177, - "framesCount" : "10", - "bufferBeforeMergeId" : 8175, - "averageFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_065_ave.dat", - "bufferAfterMeasurementId" : 15048, - "rgGuinier" : "5.12723", - "subtractedFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_065_sub.dat", - "firstPointUsed" : "17", - "chi2RgFilePath" : null, - "abInitioModelId" : null, - "macromoleculeId" : 649, - "code" : "_4.0_7.17", - "transmission" : "100.0", - "timeStart" : "2013-06-05 15:43:54.348723", - "bufferAcronym" : "B1", - "quality" : "0.853011", - "shapeDeterminationModelId" : null, - "expermientComments" : "[BsxCube] Generated from BsxCube", - "bufferId" : 422, - "isagregated" : "False", - "dmax" : "25.63615", - "bufferBeforeAverageFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_064_ave.dat", - "exposureTemperature" : "4.0", - "subtractionId" : 3377, - "conc" : "7.1699999999999999", - "rapidShapeDeterminationModel" : null, - "experimentCreationDate" : "Jun 5, 2013 3:30:30 PM", - "lastPointUsed" : "36", - "modelListId" : null, - "framesMerge" : "9", - "rapidShapeDeterminationModelId" : null, - "bufferAfterAverageFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_066_ave.dat", - "nsdFilePath" : null, - "bufferAfterFramesMerged" : "10", - "experimentId" : 700, - "macromoleculeAcronym" : "L9", - "i0stdev" : "0.139364435146", - "sampleMeasurementId" : 15047, - "measurementComments" : "[5]", - "priorityLevelId" : 10, - "bufferBeforeFramesMerged" : "10", - "substractionCreationTime" : "Jun 5, 2013 3:46:31 PM", - "proposalNumber" : "29", - "rgGnom" : "5.40297914939", - "volume" : "475.302", - "shapeDeterminationModel" : null, - "comments" : null, - "groupeField" : "L9 B1" - } ], - "frames_warning" : 0, - "calculation" : { - "rgGuinier" : { - "std" : 0, - "sum" : 5.12723, - "avg" : 5.12723, - "validNumber" : 1, - "totalNumber" : 1, - "values" : [ "5.12723" ] - }, - "i0Guinier" : { - "std" : 0, - "sum" : 183.457461646, - "avg" : 183.457461646, - "validNumber" : 1, - "totalNumber" : 1, - "values" : [ "183.457461646" ] - }, - "quality" : { - "std" : 0, - "sum" : 0.853011, - "avg" : 0.853011, - "validNumber" : 1, - "totalNumber" : 1, - "values" : [ "0.853011" ] - }, - "rgGnom" : { - "std" : 0, - "sum" : 5.40297914939, - "avg" : 5.40297914939, - "validNumber" : 1, - "totalNumber" : 1, - "values" : [ "5.40297914939" ] - }, - "dMax" : { - "std" : 0, - "sum" : 25.63615, - "avg" : 25.63615, - "validNumber" : 1, - "totalNumber" : 1, - "values" : [ "25.63615" ] - } - } - }, { - "concentration" : "3.53", - "id" : "3.5299999999999998", - "bufferId" : "422.00", - "bufferAcronym" : "B1", - "rgGuinier" : [ "4.38278" ], - "frames" : [ { - "total" : "0.497164741078", - "bufferBeforeMeasurementId" : 15048, - "sampleMergeId" : 8178, - "averagedModelId" : null, - "I0" : "160.023229462", - "proposalCode" : "OPD", - "averagedModel" : null, - "bufferAfterMergeId" : 8179, - "framesCount" : "10", - "bufferBeforeMergeId" : 8177, - "averageFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_067_ave.dat", - "bufferAfterMeasurementId" : 15051, - "rgGuinier" : "4.38278", - "subtractedFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_067_sub.dat", - "firstPointUsed" : "36", - "chi2RgFilePath" : null, - "abInitioModelId" : null, - "macromoleculeId" : 649, - "code" : "_4.0_3.53", - "transmission" : "100.0", - "timeStart" : "2013-06-05 15:47:04.630129", - "bufferAcronym" : "B1", - "quality" : "0.742825", - "shapeDeterminationModelId" : null, - "expermientComments" : "[BsxCube] Generated from BsxCube", - "bufferId" : 422, - "isagregated" : "False", - "dmax" : "15.33973", - "bufferBeforeAverageFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_066_ave.dat", - "exposureTemperature" : "4.0", - "subtractionId" : 3378, - "conc" : "3.5299999999999998", - "rapidShapeDeterminationModel" : null, - "experimentCreationDate" : "Jun 5, 2013 3:30:30 PM", - "lastPointUsed" : "61", - "modelListId" : null, - "framesMerge" : "10", - "rapidShapeDeterminationModelId" : null, - "bufferAfterAverageFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_068_ave.dat", - "nsdFilePath" : null, - "bufferAfterFramesMerged" : "10", - "experimentId" : 700, - "macromoleculeAcronym" : "L9", - "i0stdev" : "0.1499898017", - "sampleMeasurementId" : 15050, - "measurementComments" : "[6]", - "priorityLevelId" : 12, - "bufferBeforeFramesMerged" : "10", - "substractionCreationTime" : "Jun 5, 2013 3:49:42 PM", - "proposalNumber" : "29", - "rgGnom" : "4.40615474861", - "volume" : "372.656", - "shapeDeterminationModel" : null, - "comments" : null, - "groupeField" : "L9 B1" - } ], - "frames_warning" : 0, - "calculation" : { - "rgGuinier" : { - "std" : 0, - "sum" : 4.38278, - "avg" : 4.38278, - "validNumber" : 1, - "totalNumber" : 1, - "values" : [ "4.38278" ] - }, - "i0Guinier" : { - "std" : 0, - "sum" : 160.023229462, - "avg" : 160.023229462, - "validNumber" : 1, - "totalNumber" : 1, - "values" : [ "160.023229462" ] - }, - "quality" : { - "std" : 0, - "sum" : 0.742825, - "avg" : 0.742825, - "validNumber" : 1, - "totalNumber" : 1, - "values" : [ "0.742825" ] - }, - "rgGnom" : { - "std" : 0, - "sum" : 4.40615474861, - "avg" : 4.40615474861, - "validNumber" : 1, - "totalNumber" : 1, - "values" : [ "4.40615474861" ] - }, - "dMax" : { - "std" : 0, - "sum" : 15.33973, - "avg" : 15.33973, - "validNumber" : 1, - "totalNumber" : 1, - "values" : [ "15.33973" ] +StockSolutionGrid.prototype._getTopButtons = function() { + var _this = this; + /** Actions buttons **/ + var actions = []; + + /** ADD BUTTON **/ + if (this.btnAddVisible) { + actions.push(Ext.create('Ext.Action', { + icon : '../images/add.png', + text : 'Add Stock Solution', + tooltip : 'Will create a new stock solution', + disabled : false, + alwaysEnabled : true, + handler : function(widget, event) { + _this.edit(); + } + })); + } + + if (this.btnAddExisting) { + actions.push(Ext.create('Ext.Action', { + icon : '../images/add.png', + text : 'Add Existing', + tooltip : 'Allows to select upacked stock solutions', + disabled : false, + alwaysEnabled : true, + handler : function(widget, event) { + var stockSolutionGrid = new StockSolutionGrid({ + btnAddVisible : false, + btnEditVisible : false, + btnRemoveVisible : false, + btnAddExisting : false, + isPackedVisible : true, + multiselect : true + }); + + var window = Ext.create('Ext.window.Window', { + title : 'Select', + height : 400, + width : 800, + layout : 'fit', + items : [ stockSolutionGrid.getPanel() ], + buttons : [ { + text : 'Pack', + handler : function() { + _this.onStockSolutionSelected.notify(stockSolutionGrid.selectedStockSolutions); + window.close(); + } + }, { + text : 'Cancel', + handler : function() { + window.close(); } + } ] + + }).show(); + + stockSolutionGrid.refresh(BIOSAXS.proposal.getUnpackedStockSolutions()); + } + })); + } + + return actions; +}; + +StockSolutionGrid.prototype.refresh = function(stockSolutions) { + this.features = stockSolutions; + this.store.loadData(this._prepareData(), false); +}; + +StockSolutionGrid.prototype._prepareData = function() { + var data = []; + for ( var i = 0; i < this.features.length; i++) { + var stockSolution = this.features[i]; + stockSolution.buffer = BIOSAXS.proposal.getBufferById(stockSolution.bufferId).acronym; + if (stockSolution.macromoleculeId != null) { + stockSolution.macromolecule = BIOSAXS.proposal.getMacromoleculeById(stockSolution.macromoleculeId).acronym; + } + data.push(stockSolution); + } + return data; +}; + +StockSolutionGrid.prototype.getPanel = function() { + return this._renderGrid(); +}; + +StockSolutionGrid.prototype.edit = function(stockSolutionId) { + var _this = this; + var stockSolutionWindow = new StockSolutionWindow(); + /** On stock solution SAVED **/ + stockSolutionWindow.onSaved.attach(function(sender, stockSolution) { + _this.onProposalChanged.notify(stockSolution); + }); + stockSolutionWindow.draw(BIOSAXS.proposal.getStockSolutionById(stockSolutionId)); +}; + +StockSolutionGrid.prototype._getStoreFields = function() { + return [ { + name : 'name', + type : 'string' + }, { + name : 'stockSolutionId', + type : 'string' + }, { + name : 'macromolecule', + type : 'string' + }, { + name : 'buffer', + type : 'string' + }, { + name : 'storageTemperature', + type : 'numeric' + }, { + name : 'volume', + type : 'string' + }, { + name : 'concentration', + type : 'string' + }, { + name : 'buffer', + type : 'string' + }, { + name : 'comments', + type : 'string' + } ]; +}; + +//StockSolutionGrid.prototype.refresh = function() { +// this.proposal.onInitialized.attach(function(sender){ +// +// }); +// this.proposal.init(Ext.urlDecode(window.location.href).sessionId); +//}; + +StockSolutionGrid.prototype._renderGrid = function() { + var _this = this; + + /** Store **/ + this.store = Ext.create('Ext.data.Store', { + fields : this._getStoreFields(), //columns, + autoload : true + }); + + var filters = { + ftype : 'filters', + local : true, + filters : this.filters + }; + + var selModel = null; + + if (this.multiselect) { + selModel = Ext.create('Ext.selection.CheckboxModel', { + //multiSelect : false,//this.multiselect, + mode : 'SINGLE', + listeners : { + selectionchange : function(sm, selections) { + _this.selectedStockSolutions = []; + for ( var i = 0; i < selections.length; i++) { + _this.selectedStockSolutions.push(selections[i].raw); } - }, { - "concentration" : "1.75", - "id" : "1.75", - "bufferId" : "422.00", - "bufferAcronym" : "B1", - "rgGuinier" : [], - "frames" : [ { - "total" : "0.5538035065", - "bufferBeforeMeasurementId" : 15051, - "sampleMergeId" : 8180, - "averagedModelId" : null, - "I0" : "146.927428571", - "proposalCode" : "OPD", - "averagedModel" : null, - "bufferAfterMergeId" : 8181, - "framesCount" : "10", - "bufferBeforeMergeId" : 8179, - "averageFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_069_ave.dat", - "bufferAfterMeasurementId" : 15054, - "rgGuinier" : "4.27139", - "subtractedFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_069_sub.dat", - "firstPointUsed" : "33", - "chi2RgFilePath" : null, - "abInitioModelId" : null, - "macromoleculeId" : 649, - "code" : "_4.0_1.75", - "transmission" : "100.0", - "timeStart" : "2013-06-05 15:50:09.395824", - "bufferAcronym" : "B1", - "quality" : "0.765999", - "shapeDeterminationModelId" : null, - "expermientComments" : "[BsxCube] Generated from BsxCube", - "bufferId" : 422, - "isagregated" : "False", - "dmax" : "14.949865", - "bufferBeforeAverageFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_068_ave.dat", - "exposureTemperature" : "4.0", - "subtractionId" : 3379, - "conc" : "1.75", - "rapidShapeDeterminationModel" : null, - "experimentCreationDate" : "Jun 5, 2013 3:30:30 PM", - "lastPointUsed" : "62", - "modelListId" : null, - "framesMerge" : "6", - "rapidShapeDeterminationModelId" : null, - "bufferAfterAverageFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_070_ave.dat", - "nsdFilePath" : null, - "bufferAfterFramesMerged" : "10", - "experimentId" : 700, - "macromoleculeAcronym" : "L9", - "i0stdev" : "0.191914285714", - "sampleMeasurementId" : 15053, - "measurementComments" : "[7]", - "priorityLevelId" : 14, - "bufferBeforeFramesMerged" : "10", - "substractionCreationTime" : "Jun 5, 2013 3:52:31 PM", - "proposalNumber" : "29", - "rgGnom" : "4.28379338749", - "volume" : "356.326", - "shapeDeterminationModel" : null, - "comments" : null, - "groupeField" : "L9 B1" - } ], - "frames_warning" : 1, - "calculation" : { - "rgGuinier" : { - "std" : null, - "sum" : 0, - "avg" : 0, - "validNumber" : 0, - "totalNumber" : 0, - "values" : [] - }, - "i0Guinier" : { - "std" : null, - "sum" : 0, - "avg" : 0, - "validNumber" : 0, - "totalNumber" : 0, - "values" : [] - }, - "quality" : { - "std" : null, - "sum" : 0, - "avg" : 0, - "validNumber" : 0, - "totalNumber" : 0, - "values" : [] - }, - "rgGnom" : { - "std" : null, - "sum" : 0, - "avg" : 0, - "validNumber" : 0, - "totalNumber" : 0, - "values" : [] - }, - "dMax" : { - "std" : null, - "sum" : 0, - "avg" : 0, - "validNumber" : 0, - "totalNumber" : 0, - "values" : [] + } + } + }); + } else { + selModel = { + mode : 'SINGLE' + }; + } + + this.store.sort("stockSolutionId", "desc"); + + this.grid = Ext.create('Ext.grid.Panel', { + style : { + padding : 5 + }, + icon : '/ispyb/images/SampleHolder_24x24_01.png', + title : this.title, + height : this.height, + width : this.width, + minWidth : this.minWidth, + selModel : selModel, + store : this.store, + columns : this._getColumns(), + viewConfig : { + stripeRows : true, + listeners : { + itemdblclick : function(dataview, record, item, e) { + _this.edit(record.raw.stockSolutionId); + }, + cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + var adapter = null; + if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonUnpack') { + _this.grid.setLoading("ISPyB: Unpacking stock solution"); + adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender) { + _this.onProposalChanged.notify(); + _this.grid.setLoading(false); + }); + adapter.onError.attach(function(sender) { + _this.onProposalChanged.notify(); + _this.grid.setLoading(false); + }); + record.raw.boxId = null; + adapter.saveStockSolution(record.raw); + } + + if (grid.getGridColumns()[cellIndex].getId() == _this.id + "box") { + window.location = BUI.getShippingURL(BIOSAXS.proposal.getShipmentByDewarId(record.raw.boxId).shippingId); + } + if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonEdit') { + _this.edit(record.data.stockSolutionId); + } + + if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonRemove') { + _this.grid.setLoading("ISPyB: Removing stock solution"); + adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender) { + _this.onProposalChanged.notify(); + _this.grid.setLoading(false); + }); + adapter.onError.attach(function(sender) { + _this.onProposalChanged.notify(); + _this.grid.setLoading(false); + }); + adapter.removeStockSolution(record.data.stockSolutionId); + } + } + } + } + + }); + + var actions = _this._getTopButtons(); + this.grid.addDocked({ + xtype : 'toolbar', + items : actions + }); + + var i = null; + this.grid.getSelectionModel().on({ + selectionchange : function(sm, selections) { + if (selections.length) { + for ( i = 0; i < actions.length; i++) { + if (actions[i].enable) { + actions[i].enable(); + } + } + } else { + for ( i = 0; i < actions.length; i++) { + if (actions[i].alwaysEnabled == false) { + if (actions[i].disable) { + actions[i].disable(); + } + } + } + } + } + }); + return this.grid; +}; + +StockSolutionGrid.prototype.input = function() { + return { + proposal : DATADOC.getProposal_10() + }; +}; + +StockSolutionGrid.prototype.test = function(targetId) { + var stockSolutionGrid = new StockSolutionGrid({ + height : 300, + width : 900 + }); + BIOSAXS.proposal = new Proposal(stockSolutionGrid.input().proposal); + var panel = stockSolutionGrid.getPanel(); + stockSolutionGrid.refresh(BIOSAXS.proposal.getStockSolutions()); + panel.render(targetId); +}; + + +function SuperpositionGrid(args) { + this.height = null; + this.width = null; + this.id = BUI.id(); + + if (args != null) { + if (args.height != null) { + this.height = args.height; + + if (args.width != null) { + this.width = args.width; + } + } + } + + this.onSelected = new Event(this); +} + +SuperpositionGrid.prototype._prepareData = function(data) { + return data; +}; + +SuperpositionGrid.prototype.refresh = function(subtractions) { + var data = []; + if (subtractions != null){ + if (subtractions.length > 0){ + for (j in subtractions){ + var subtraction = subtractions[j]; + if (subtraction.superposition3VOs != null){ + for (i in subtraction.superposition3VOs){ + data.push(subtraction.superposition3VOs[i]); + } + } + } + } + } + this.store.loadData(data); +}; + +SuperpositionGrid.prototype.getPanel = function() { + var _this = this; + this.store = Ext.create('Ext.data.Store', { + fields : [ 'abinitioModelPdbFilePath', 'aprioriPdbFilePath', 'alignedPdbFilePath' ], + data : [] + }); + + this.selModel = Ext.create('Ext.selection.RowModel', { + allowDeselect : true, + mode : 'MULTI', + listeners : { + selectionchange : function(sm, selections) { + var selected = []; + for (var i = 0; i < selections.length; i++) { + selected.push(selections[i].raw); + } + _this.onSelected.notify(selected); + } + } + }); + + this.store.sort([ { + property : 'name', + direction : 'ASC' + } ]); + + this.panel = Ext.create('Ext.grid.Panel', { + store : this.store, + selModel : this.selModel, + width : this.width, + height : this.height, + margin : 10, + deferredRender : false, + columns : [ { + text : 'Abinitio', + dataIndex : 'abinitioModelPdbFilePath', + hidden : false, + flex : 1, + renderer : function(val){ + return BUI.getFileName(val); + } + }, { + text : 'Apriori', + dataIndex : 'aprioriPdbFilePath', + hidden : false, + flex : 1, + renderer : function(val){ + return BUI.getFileName(val); + } + }, { + text : 'Aligned', + dataIndex : 'alignedPdbFilePath', + hidden : false, + flex : 1, + renderer : function(val){ + return BUI.getFileName(val); + } + } ], + + viewConfig : { + enableTextSelection : true, + preserveScrollOnRefresh : true + }, + listeners : { + cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + + }, + afterrender : function() { + } + } + }); + return this.panel; +}; + +/** + * See ExperimentGrid + * + */ +function TemplateGrid(args) { + + if (args == null) { + args = {}; + } + args.sorters = [ { + property : 'experimentId', + direction : 'DESC' + } ]; + + ExperimentGrid.prototype.constructor.call(this, args); +} + +TemplateGrid.prototype._getFilterTypes = ExperimentGrid.prototype._getFilterTypes; +TemplateGrid.prototype._prettyPrintMacromolecules = ExperimentGrid.prototype._prettyPrintMacromolecules; +TemplateGrid.prototype._getPercentage = ExperimentGrid.prototype._getPercentage; +TemplateGrid.prototype._getPercentageCollected = ExperimentGrid.prototype._getPercentageCollected; +TemplateGrid.prototype.getPercentageMerged = ExperimentGrid.prototype.getPercentageMerged; +TemplateGrid.prototype._prepareData = ExperimentGrid.prototype._prepareData; +TemplateGrid.prototype.getPanel = ExperimentGrid.prototype.getPanel; +TemplateGrid.prototype._renderGrid = ExperimentGrid.prototype._renderGrid; +TemplateGrid.prototype._editExperiment = ExperimentGrid.prototype._editExperiment; +TemplateGrid.prototype._removeExperimentById = ExperimentGrid.prototype._removeExperimentById; + +TemplateGrid.prototype._getTopButtons = function() { + /** Actions buttons * */ + var actions = []; + + actions.push(Ext.create('Ext.Action', { + icon : '../images/add.png', + text : 'Add experiment', + handler : function(widget, event) { + var wizardWidget = new WizardWidget({ + windowMode : true + }); + + wizardWidget.onFinished.attach(function(sender, result) { + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, data) { + var experiment = new Experiment(data); + BIOSAXS.openExperimentByExperiment(experiment); + wizardWidget.window.close(); + }); + wizardWidget.current.setLoading("ISPyB: Creating experiment"); + adapter.createTemplate(result.name, "comments", result.data); + }); + +// wizardWidget.draw(this.targetId, new ExperimentTypeWizardForm()); + wizardWidget.draw(this.targetId, new MeasurementCreatorStepWizardForm(BIOSAXS.proposal.getMacromolecules())); + } + })); + return actions; +}; + +TemplateGrid.prototype.refresh = function(data) { + var filtered = []; + for ( var i = 0; i < data.length; i++) { + if (data[i].experimentType == "TEMPLATE") { + filtered.push(data[i]); + } + } + this.store.loadData(this._prepareData(filtered), false); +}; + +TemplateGrid.prototype._getColumns = function() { + var _this = this; + function actionItemRenderer(value, meta, record, rowIx, ColIx, store) { + if (record.raw.buffer3VOs != null) { + if (record.raw.buffer3VOs.length > 0) { + return 'x-hide-display'; + } + } + + if (record.data.platesCount > 0) { + return 'x-hide-display'; + } + } + + return [ { + xtype : 'rownumberer', + width : 40 + }, { + text : 'experimentId', + dataIndex : 'experimentId', + name : 'experimentId', + type : 'string', + hidden : true + }, { + text : 'Name', + dataIndex : 'name', + name : 'name', + type : 'string', + flex : 1 + }, + + { + text : 'Type', + dataIndex : 'type', + name : 'type', + type : 'string', + hidden : true, + flex : 1, + renderer : function(val) { + return val; + } + }, { + text : 'Macromolecules', + name : 'macromolecules_names', + dataIndex : 'macromolecules_names', + flex : 1, + renderer : function(val) { + return " " + val + ""; + } + }, { + id : _this.id + 'GO', + width : 80, + sortable : false, + renderer : function(value, metaData, record, rowIndex, colIndex, store) { + return BUI.getGreenButton('EDIT'); + } + }, { + id : _this.id + 'REMOVE', + width : 100, + sortable : false, + renderer : function(value, metaData, record, rowIndex, colIndex, store) { + return BUI.getRedButton('REMOVE'); + } + } ]; +}; + +TemplateGrid.prototype.input = function() { + var experiments = DATADOC.getExperimentList_10(); + return { + experiments : experiments, + proposal : new MeasurementGrid().input().proposal + + }; +}; + +TemplateGrid.prototype.test = function(targetId) { + var experimentGrid = new TemplateGrid({ + height : 350, + minHeight : 350, + width : 1000, + gridType : 'Ext.grid.Panel', + title : 'Experiments', + grouping : false, + tbar : true + + }); + BIOSAXS.proposal = new Proposal(experimentGrid.input().proposal); + var panel = experimentGrid.getPanel(experimentGrid.input().experiments); + experimentGrid.refresh(experimentGrid.input().experiments); + panel.render(targetId); +}; + +function VolumeGrid() { + this.id = BUI.id(); +} + +VolumeGrid.prototype.getPanel = function(experiment) { + this.experiment = experiment; + return this.render(); +}; + +VolumeGrid.prototype.getVolumesPanel = function(data, title) { + var _this = this; + var store = Ext.create('Ext.data.Store', { + fields : [ 'name', 'volume', 'macromoleculeId', 'bufferId' ], + data : data + }); + store.sort([ { + property : 'name', + direction : 'ASC' + } ]); + + var grid = Ext.create('Ext.grid.Panel', { + title : title, + height : 400, + maxHeight : 400, + width : 900, + store : store, + margin : '10 0 50 10', + tbar : [ { + text : 'Go to Shipment', + icon : '../images/plane-small.gif', + handler : function() { + window.location = BUI.getCreateShipmentList(); + } + } ], + viewConfig : { + stripeRows : true, + listeners : { + cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonCreate') { + var stockSolutionWindow = new StockSolutionWindow(); + /** On stock solution SAVED **/ + stockSolutionWindow.onSaved.attach(function(sender, stockSolution) { + BIOSAXS.proposal.onInitialized.attach(function(sender, data) { + _this.refresh(_this.experiment); + }); + BIOSAXS.proposal.init(); + }); + var acronym = "ST"; + if (record.raw.macromoleculeId != null) { + acronym = acronym + "_" + BIOSAXS.proposal.getMacromoleculeById(record.raw.macromoleculeId).acronym; + } + if (record.raw.bufferId != null) { + acronym = acronym + "_" + BIOSAXS.proposal.getBufferById(record.raw.bufferId).acronym; } + stockSolutionWindow.draw({ + concentration : record.raw.concentration, + macromoleculeId : record.raw.macromoleculeId, + bufferId : record.raw.bufferId, + name : acronym, + volume : record.raw.volume + }); } - }, { - "concentration" : "0.91", - "id" : "0.91000000000000003", - "bufferId" : "422.00", - "bufferAcronym" : "B1", - "rgGuinier" : [], - "frames" : [ { - "total" : null, - "bufferBeforeMeasurementId" : 15054, - "sampleMergeId" : 8182, - "averagedModelId" : null, - "I0" : "0.0", - "proposalCode" : "OPD", - "averagedModel" : null, - "bufferAfterMergeId" : 8183, - "framesCount" : "10", - "bufferBeforeMergeId" : 8181, - "averageFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_071_ave.dat", - "bufferAfterMeasurementId" : 15057, - "rgGuinier" : null, - "subtractedFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_071_sub.dat", - "firstPointUsed" : "0", - "chi2RgFilePath" : null, - "abInitioModelId" : null, - "macromoleculeId" : 649, - "code" : "_4.0_.91", - "transmission" : "100.0", - "timeStart" : "2013-06-05 15:52:58.133705", - "bufferAcronym" : "B1", - "quality" : "0.0", - "shapeDeterminationModelId" : null, - "expermientComments" : "[BsxCube] Generated from BsxCube", - "bufferId" : 422, - "isagregated" : "False", - "dmax" : null, - "bufferBeforeAverageFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_070_ave.dat", - "exposureTemperature" : "4.0", - "subtractionId" : 3380, - "conc" : "0.91000000000000003", - "rapidShapeDeterminationModel" : null, - "experimentCreationDate" : "Jun 5, 2013 3:30:30 PM", - "lastPointUsed" : "0", - "modelListId" : null, - "framesMerge" : "10", - "rapidShapeDeterminationModelId" : null, - "bufferAfterAverageFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_072_ave.dat", - "nsdFilePath" : null, - "bufferAfterFramesMerged" : "10", - "experimentId" : 700, - "macromoleculeAcronym" : "L9", - "i0stdev" : "0.0", - "sampleMeasurementId" : 15056, - "measurementComments" : "[8]", - "priorityLevelId" : 16, - "bufferBeforeFramesMerged" : "10", - "substractionCreationTime" : "Jun 5, 2013 3:55:35 PM", - "proposalNumber" : "29", - "rgGnom" : null, - "volume" : null, - "shapeDeterminationModel" : null, - "comments" : null, - "groupeField" : "L9 B1" - } ], - "frames_warning" : 1, - "calculation" : { - "rgGuinier" : { - "std" : null, - "sum" : 0, - "avg" : 0, - "validNumber" : 0, - "totalNumber" : 0, - "values" : [] - }, - "i0Guinier" : { - "std" : null, - "sum" : 0, - "avg" : 0, - "validNumber" : 0, - "totalNumber" : 0, - "values" : [] + + if (grid.getGridColumns()[cellIndex].getId() == _this.id + 'buttonStockSolutions') { + var stockSolutionGrid = new StockSolutionGrid({ + btnAddVisible : false, + btnEditVisible : false, + btnRemoveVisible : false, + btnAddExisting : false, + isPackedVisible : true, + multiselect : false + }); + + var window = Ext.create('Ext.window.Window', { + title : 'Stock solutions by specimen', + height : 400, + width : 800, + layout : 'fit', + items : [ stockSolutionGrid.getPanel() ], + buttons : [ { + text : 'Close', + handler : function() { + window.close(); + } + } ] + + }).show(); + stockSolutionGrid.refresh(BIOSAXS.proposal.getStockSolutionsBySpecimen(record.raw.macromoleculeId, record.raw.bufferId)); + } + } + } + }, + columns : [ +// { +// text : '', +// dataIndex : 'macromoleculeId', +// width : 20, +// renderer : function(val, y, sample) { +// if (val != null) { +//// return BUI.getRectangleColorDIV(BIOSAXS.proposal.macromoleculeColors[val], 10, 10); +// return BUI.getRectangleColorDIV(_this.experiment.macromoleculeColors[val], 10, 10); +// } +// } +// }, +// { +// text : '', +// dataIndex : 'bufferId', +// width : 20, +// renderer : function(val, y, sample) { +// if (val != null) { +// return BUI.getRectangleColorDIV(_this.experiment.getSpecimenColorByBufferId(val), 10, 10); +// } +// } +// }, + { + text : 'Specimen', + dataIndex : 'name', + flex : 0.5 + }, + { + text : 'Estimated Volume', + dataIndex : 'volume', + tooltip : 'Estimation of the maximum volume needed for making this experiment', + flex : 0.5, + editor : { + allowBlank : true + }, + renderer : function(val, y, sample) { + return BUI.formatValuesUnits(sample.data.volume, 'µl', { + fontSize : 16, + decimals : 2, + unitsFontSize : this.unitsFontSize + }); + } + }, + { + text : 'Stock Solution', + id : _this.id + 'buttonStockSolutions', + dataIndex : 'name', + flex : 0.5, + tooltip : 'Stock Solutions containing this specimen in this proposal', + renderer : function(value, metaData, record, rowIndex, colIndex, store) { + var macromoleculeId = record.raw.macromoleculeId; + var bufferId = record.raw.bufferId; + var stockSolutions = BIOSAXS.proposal.getStockSolutionsBySpecimen(macromoleculeId, bufferId); + if (stockSolutions.length > 0) { + return "
" + stockSolutions.length + " x
"; + } + + } + }, { + id : _this.id + 'buttonCreate', + text : '', + tooltip : 'Create a new stock solution for shipping', + width : 170, + sortable : false, + renderer : function(value, metaData, record, rowIndex, colIndex, store) { + return BUI.getGreenButton('NEW STOCK SOLUTION', { + width : 160 + }); + } + } ] + }); + return grid; + +}; + +VolumeGrid.prototype._prepareData = function(experiment) { + var keys = {}; + for ( var i = 0; i < experiment.getSamples().length; i++) { + var sample = experiment.getSamples()[i]; + var key = ""; + if (sample.macromoleculeId == null) { + key = experiment.getBufferById(sample.bufferId).acronym; + if (keys[key] == null) { + keys[key] = { + macromoleculeId : sample.macromoleculeId, + name : key, + bufferId : sample.bufferId, + volume : 0 + }; + } + keys[key].volume = Number(sample.volume) + Number(keys[key].volume); + } + + if ((sample.macromolecule3VO != null) || (sample.macromoleculeId != null)) { + macromoleculeId = sample.macromoleculeId; + if (sample.macromoleculeId == null) { + sample.macromoleculeId = sample.macromolecule3VO.macromoleculeId; + } + key = BIOSAXS.proposal.getMacromoleculeById(sample.macromoleculeId).acronym + " + " + experiment.getBufferById(sample.bufferId).acronym; + if (keys[key] == null) { + keys[key] = { + macromoleculeId : sample.macromoleculeId, + name : key, + bufferId : sample.bufferId, + volume : 0 + }; + } + keys[key].volume = Number(sample.volume) + Number(keys[key].volume); + } + } + var data = []; + for (var keyId in keys) { + data.push(keys[keyId]); + } + + return data; +}; + +VolumeGrid.prototype.refresh = function(experiment) { + this.experiment = experiment; + this.macromoleculeGrid.getStore().loadData(this._prepareData(this.experiment), false); +}; + +VolumeGrid.prototype.render = function() { + this.macromoleculeGrid = this.getVolumesPanel(this._prepareData(this.experiment), "Estimation of required Volume"); + + return { + xtype : 'container', + layout : 'vbox', + margin : "0, 0, 0, 5", + items : [ { + xtype : 'container', + layout : 'hbox', + margin : "0, 0, 0, 0", + items : [ this.macromoleculeGrid ] + } ] + }; +}; + +VolumeGrid.prototype.input = function(experiment) { + return { + experiment : DATADOC.getExperiment_10() + }; +}; + +VolumeGrid.prototype.test = function(targetId) { + var volumeGrid = new VolumeGrid(); + BIOSAXS.proposal = new Proposal(new MeasurementGrid().input().proposal); + var panel = volumeGrid.getPanel(new Experiment(new VolumeGrid().input().experiment)); + Ext.create('Ext.panel.Panel', { + height : 500, + width : 1000, + renderTo : targetId, + items : [ panel ] + }); +}; + +/** + * Example of a tab panel to be populated with widgets + * + * @width width in pixels + * @height height in pixels + * + * #myEvent event that this class is supposed to throw + * + **/ +function ExampleTabs(args) { + this.width = 500; + this.height = 500; + + if (args != null){ + if (args.width != null){ + this.width= args.width; + } + if (args.height != null){ + this.height= args.height; + } + } + + /** Events **/ + this.myEvent = new Event(); +} + +/** Populate the widget **/ +ExampleTabs.prototype.refresh = function(macromolecule) { + +}; + +/** It creates a tab panel with the specified with and height **/ +ExampleTabs.prototype.getPanel = function() { + this.panel = Ext.createWidget('tabpanel', { + height : this.height, + width : this.width, + style : { + padding : 2 + }, + items : [ { + tabConfig : { + title : 'Test', + icon : '/ispyb/images/plane-small.gif' + }, + items : [ { + xtype : 'container', + margin : '5 5 5 5', + items : [ ] + } ] + } ] + }); + return this.panel; +}; + + +/** + * Shows an experiments with the specimens, measurements, analysis tabs where + * results are shown and the frames widget + * + * @targetId + */ +function ExperimentTabs(targetId) { + this.height = 900; + this.targetId = targetId; + + this.id = BUI.id(); + var _this = this; + + this.INTERVAL_UPDATE = BUI.getUpdateInterval(); + + this.gridHeight = 1000; + /** data * */ + this.experiment = null; + + + /** Specimen Widget contains a specimenGrid and a sampleChangerWidget than can be displayed with are vertical or horizontal layout **/ + this.specimenWidget = new SpecimenWidget({ + height : 600 + }); + + + /** For Overview * */ + /*this.samplePlateGroupWidget = new SamplePlateGroupWidget({ + showTitle : false, + height : 250, + margin : 5, + border : 0 + + });*/ + + /** For Measurements * */ + /*this.measurementSamplePlateGroupWidget = new SamplePlateGroupWidget({ + showTitle : false, + height : 250, + margin : '5 0 0 0', + border : 0 + });*/ + + this.measurementGridDone = new MeasurementGrid({ + height : 600, + minHeight : 400, + maxHeight : 800, + positionColumnsHidden : true, + showTitle : false, + estimateTime : true, + width : 900, + maxWidth : 1500, + addBtnEnable : false, + markDone : true, + removeBtnEnabled : false + }); + this.measurementGridDone.onSelected.attach(function(sender, measurements) { + var specimens = []; + for ( var i = 0; i < measurements.length; i++) { + specimens.push(_this.experiment.getSampleById(measurements[i].specimenId)); + } + //_this.measurementSamplePlateGroupWidget.selectSpecimens(specimens); + }); + + /** AnalysisGrid * */ + this.analysisGrid = new AnalysisGrid({ + height : Ext.getBody().getViewSize().height * 0.9 - 300, + positionColumnsHidden : true, + sorters : [ { + property : 'priorityLevelId', + direction : 'ASC' + } ] + }); + + + /** Queue * */ + this.queueGrid = new QueueGrid({ + url : BUI.getQueueUrlByExperiment(), + height : Ext.getBody().getViewSize().height * 0.9 - 300, + width : Ext.getBody().getViewSize().width * 0.9 - 300, + isGrouped : true, + tbar : false, + bbar : false, + sorter : [{ + property: 'measurementId', + direction: 'DESC' + }] + }); + +} + +ExperimentTabs.prototype.error = function(error) { + var e = JSON.parse(error); + showError(e); + this.panel.setLoading(false); +}; + +ExperimentTabs.prototype.draw = function(experiment) { + this.renderDataAcquisition(experiment); +}; + +ExperimentTabs.prototype.refreshAnalysisData = function() { + var _this = this; + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, data) { + _this.analysisGrid.refresh(data, {experiment : _this.experiment}); + }); + adapter.getAnalysisInformationByExperimentId(this.experiment.experimentId); + + /** Auto load **/ + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, data) { + _this.queueGrid.refresh(data); + }); + adapter.getCompactAnalysisByExperimentId(this.experiment.experimentId); + this.queueGrid.store.proxy.url = BUI.getQueueUrlByExperiment(this.experiment.experimentId); + this.queueGrid.refresh(); +}; + +ExperimentTabs.prototype.getSpecimenContainerHeight = function(experiment) { + var maxItems = 0; + if (maxItems < experiment.getSamples().length + 1) { + maxItems = experiment.getSamples().length + 1; + } + + var height = (maxItems + 1) * 40 + 40; + if (height > 400) { + height = 400; + } + return height; +}; + +ExperimentTabs.prototype.getExperimentTitle = function() { + var experimentHeaderForm = new ExperimentHeaderForm(); + return experimentHeaderForm.getPanel(this.experiment); +}; + +ExperimentTabs.prototype.renderDataAcquisition = function(experiment) { + var _this = this; + this.experiment = experiment; + + var specimenGrid = new SpecimenGrid({ + height : 400, + maxHeight : 500, + width : 890 + }); + + specimenGrid.onClick.attach(function(sender, args) { + }); + + specimenGrid.onSelected.attach(function(sender, specimens) { + //_this.samplePlateGroupWidget.selectSpecimens(specimens); + }); + var specimenContainer = this.specimenWidget.getPanel(); + this.specimenWidget.refresh(experiment); + /* + var specimenContainer = Ext.create('Ext.container.Container', { + layout : 'hbox', + width : 900, + padding : '10 0 0 2', + items : [ specimenGrid.getPanel() ] + }); + + specimenGrid.refresh(experiment);*/ + + var experimentList = new ExperimentList([ _this.experiment ]); + var measurementContainer = Ext.create('Ext.container.Container', { + layout : 'vbox', + padding : '5px 0px 0px 10px', + items : [] + }); + measurementContainer.insert(0, _this.measurementGridDone.getPanel(this.experiment.getMeasurements(), experimentList)); +// measurementContainer.insert(1, _this.measurementSamplePlateGroupWidget.getPanel(experiment)); + + // this.dataCollectionCurveVisualizer = new DataCollectionCurveVisualizer(); + // this.dataCollectionCurveVisualizer.experiments = [experiment]; + // this.dataCollectionCurveVisualizer.dataCollectionFrameTree.experiments = + // [experiment]; + // this.dataCollectionCurveVisualizer.dataCollections = + // experiment.getDataCollections(); + + this.panel = Ext.createWidget('tabpanel', { + plain : true, + style : { + padding : 2 + }, + items : [ { + tabConfig : { + id : 'genralTabl', + title : "Overview" + }, + items : [ specimenContainer ] + }, { + tabConfig : { + title : 'Measurements' + }, + items : [ measurementContainer] + }, { + tabConfig : { + id : 'SpecimenTab', + title : 'Analysis', + hidden : this.isTemplate() + }, + items : [ { + xtype : 'container', + layout : 'vbox', + padding : '5px 0px 10px 10px', + items : [ _this.analysisGrid.getPanel([]) ] + } ] + }, + { + tabConfig : { + id : 'newAnalysisTab', + title : 'Analysis BETA', + hidden : this.isTemplate() + }, + items : [ { + xtype : 'container', + layout : 'vbox', + padding : '5px 0px 10px 10px', + items : [ _this.queueGrid.getPanel([]) ] + } ] + } + ] + }); + + return this.getPanel(this.panel); +}; + +ExperimentTabs.prototype.isTemplate = function() { + if (this.experiment.json.type == "TEMPLATE") { + return true; + } + return false; +}; + +ExperimentTabs.prototype.update = function() { + var _this = this; + var inter; + if (!_this.isTemplate()) { + function updateExperiments() { + _this.refreshAnalysisData(); + window.clearInterval(inter); + inter = setInterval(function() { + updateExperiments(); + }, _this.INTERVAL_UPDATE); + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, data) { + var experiment = new Experiment(data); + _this.measurementGridDone.refresh(experiment.getMeasurements(), new ExperimentList([ experiment ])); + }); + adapter.getExperimentById(_this.experiment.json.experimentId, "MEDIUM"); + } + inter = setInterval(function() { + updateExperiments(); + }, _this.INTERVAL_UPDATE); + } +}; + +ExperimentTabs.prototype.getPanel = function(panel) { + var _this = this; + if (this.experimentPanel == null) { + this.experimentPanel = Ext.create('Ext.container.Container', { + bodyPadding : 2, + width : Ext.getBody().getViewSize().width * 0.9, + renderTo : this.targetId, + style : { + padding : 2 + }, + items : [ this.getExperimentTitle(), this.panel ], + listeners : { + afterrender : function() { + _this.refreshAnalysisData(); + _this.update(); + } + } + }); + } + + return this.experimentPanel; +}; + +ExperimentTabs.prototype.input = function() { + return { + experiment : DATADOC.getExperiment_10(), + proposal : DATADOC.getProposal_10() + }; +}; + +ExperimentTabs.prototype.test = function(targetId) { + var experimentTabs = new ExperimentTabs(targetId); + BIOSAXS.proposal = new Proposal(experimentTabs.input().proposal); + experimentTabs.draw(new Experiment(experimentTabs.input().experiment)); +}; + + + +/** + * Shows an experiments with the specimens, measurements, analysis tabs where results are shown and the frames widget + * + * @targetId + */ +function HPLCTabs(targetId) { + this.height = 1400; + this.targetId = targetId; + + this.id = BUI.id(); + + this.gridHeight = 600; + this.pointsCount = 1036; + this.plotHeight = 350; + this.plotWidth = 800; + this.plotPanelPadding = 5; + + var _this = this; + + this.analysisGrid = new HPLCAnalysisGrid({ + height : Ext.getBody().getViewSize().height * 0.9 - 300, + positionColumnsHidden : true, + sorters : [ { + property : 'priorityLevelId', + direction : 'ASC' + } ] + }); + + this.frameGrid = new FrameGrid({ + width : 600, + height : 60, + collapsed : false, + tbar : false + }); + + this.peakGrid = new PeakGrid({ + width : 550, + height : 500, + collapsed : false, + tbar : false + }); + + this.peakGrid2 = new PeakGrid({ + width : 102, + height : this.plotHeight, + collapsed : false, + tbar : false, + showExtendedColumns : true + }); + + this.mainPlotPanel = new HPLCGraph({ + title : 'I0', + width : this.plotWidth - 110, + height : this.plotHeight, + bbar : true, + plots : { + "I0" : true, + "Rg" : true + }, + xlabel : "HPLC Frames", + scaled : true, + interactionModel : { + 'dblclick' : function(event, g, context) { + _this._selectFrame(g.lastx_); + var annotations = []; + annotations.push({ + series : g.selPoints_[0].name, + x : g.lastx_, + width : 100, + height : 23, + tickHeight : 4, + shortText : g.lastx_, + text : g.lastx_, + attachAtBottom : true + }); + g.setAnnotations(annotations); + + } + } + }); + + this.intensityPlotPanel = new MergesHPLCGraph({ + title : 'Scattering', + width : this.plotWidth, + height : 500, + showRangeSelector : false, + xParam : 0, + xlabel : "scattering_I", + plots : { + "scattering_I" : true, + "subtracted_I" : true, + "buffer_I" : true + } + }); + +} + +HPLCTabs.prototype._selectFrame = function(frameNumber) { + try{ + this._renderScatteringCurve(frameNumber); + this.frameGrid.refresh([this.mainPlotPanel.getDataByFrameNumber(frameNumber)], this.experiment.experimentId); + } + catch(e){ + console.log(e); + } +}; + +HPLCTabs.prototype.error = function(error) { + var e = JSON.parse(error); + showError(e); + this.panel.setLoading(false); +}; + +HPLCTabs.prototype.loadDataMainPlot = function(data) { + var zeroArray = []; + for ( var i = 0; i < data.I0.length; i++) { + zeroArray.push(0); + } + data = [ { + param : "I0", + data : data.I0, + std : data.I0_Stdev, + color : '#0066CC', + label : "I0" + }, + { + param : "sum_I", + label : "sum_I", + color : "#00FF00", + data : data.sum_I, + std : zeroArray + }, + { + param : "Rg", + label : "Rg", + color : "#21610B", + data : data.Rg, + std : data.Rg_Stdev + }, { + param : "Mass", + data : data.mass, + std : data.mass_Stdev, + color : '#FF9900', + label : "Mass" + }, { + param : "Vc", + data : data.Vc, + std : data.Vc_Stdev, + color : '#990099', + label : "Vc" + }, { + param : "Qr", + data : data.Qr, + std : data.Qr_Stdev, + color : '#FF0066', + label : "Qr" + }, { + param : "quality", + label : "quality", + color : "#FF00FF", + data : data.quality, + std : zeroArray + } ]; + this.data = data; + this.mainPlotPanel.loadData(data); +}; + +HPLCTabs.prototype._loadIntensityPlotByFrameNumber = function(frameNumber) { + var _this = this; + + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, data) { + var array = []; + data = [ { + param : "q", + data : data[frameNumber].q, + std : data[frameNumber].q, + fstd : function(a) { + return parseFloat(a); + }, + color : '#green', + label : "q", + showOnMenu : false + }, + { + param : "scattering_I", + data : data[frameNumber].scattering_I, + fdata : function(a) { + return Math.log(parseFloat(a)); + }, + std : data[frameNumber].scattering_Stdev, + fstd : function(a) { + return Math.log(Math.abs(parseFloat(a))); + }, + color : 'green', + label : "Log(I) Sample" + }, + { + param : "buffer_I", + data : data[frameNumber].buffer_I, + fdata : function(a) { + return Math.log(parseFloat(a)); + }, + fstd : function(a) { + return Math.log(Math.abs(parseFloat(a))); + }, + std : data[frameNumber].subtracted_Stdev, + color : '#0000FF', + label : "Log(I) Avg Buf." + }, + { + param : "subtracted_I", + data : data[frameNumber].subtracted_I, + fdata : function(a) { + var value = (Math.log(parseFloat(a))); + if (isNumber(value)) + return value; + }, + fstd : function(a) { + var value = Math.log(Math.abs(parseFloat(a))); + if (isNumber(value)) + return value; + }, + std : data[frameNumber].subtracted_Stdev, + color : 'red', + label : "Log(I) Sub." + } + ]; + + _this.intensityPlotPanel.xlabel = "Frame " + frameNumber + " (q, nm-1)"; + + if (_this.intensityPlotPanel.hplcData == null) { + /** It creates also top bar **/ + _this.intensityPlotPanel.loadData(data); + } else { + _this.intensityPlotPanel.reloadData(data); + + } + _this.intensityPlotPanel.panel.setLoading(false); + }); + adapter.onError.attach(function(sender, data) { + _this.intensityPlotPanel.panel.setLoading(false); + }); + this.intensityPlotPanel.panel.setLoading("Retrieving Frame " + frameNumber); + adapter.getH5FrameScattering(this.experiment.json.experimentId, frameNumber); +}; + +HPLCTabs.prototype._renderScatteringCurve = function(frameNumber) { + var _this = this; + + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, data) { + _this.intensityPlotPanel.setPeaks(data); + _this._loadIntensityPlotByFrameNumber(frameNumber); + try{ + var peaks = []; + for(key in data){ + peaks.push({ + start : key.split("-")[0], + end : key.split("-")[1], + experimentId : _this.experiment.json.experimentId + }); + } + _this.peakGrid2.refresh(JSON.parse(JSON.stringify(peaks))); + _this.peakGrid.refresh(peaks); +// console.log(peaks); + } + catch(e){ + showError(e); + } + }); + adapter.onError.attach(function(sender, data) { + _this.intensityPlotPanel.setLoading(false); + }); + this.intensityPlotPanel.panel.setLoading("Reading HDF5 File "); + adapter.getH5FramesMerge(this.experiment.json.experimentId); +}; + +HPLCTabs.prototype._postRenderOverviewPanel = function() { + var _this = this; + var adapter = new BiosaxsDataAdapter(); + adapter.onError.attach(function(sender, data) { + data = data.replace(/NaN/g, '0'); + _this.loadDataMainPlot(JSON.parse(data)); + + + }); + adapter.onSuccess.attach(function(sender, data) { + _this.loadDataMainPlot(data); + _this._renderScatteringCurve(0); + }); + adapter.getH5fileParameters(this.experiment.json.experimentId, [ + "I0", "I0_Stdev", "sum_I","Rg", "Rg_Stdev", "Vc", "Vc_Stdev", "Qr", "Qr_Stdev", "mass", "mass_Stdev", "quality" ]); + +}; + +HPLCTabs.prototype._postRenderDetailsPanel = function() { + if (this.data != null) { + this.I0PlotPanel.loadData(this.data); + this.RgPlotPanel.loadData(this.data); + this.MassPlotPanel.loadData(this.data); + this.VcPlotPanel.loadData(this.data); + } + +}; + +HPLCTabs.prototype.draw = function(experiment) { + var _this = this; + this.renderDataAcquisition(experiment); +}; + +HPLCTabs.prototype.getExperimentTitle = function() { + var experimentHeaderForm = new ExperimentHeaderForm(); + return experimentHeaderForm.getPanel(this.experiment); +}; + +HPLCTabs.prototype.renderDataAcquisition = function(experiment) { + var _this = this; + this.experiment = experiment; + if (this.experimentPanel == null) { + this.experimentPanel = Ext.create('Ext.container.Container', { + bodyPadding : 2, + height : this.height, + width : Ext.getBody().getViewSize().width * 0.9, + renderTo : this.targetId, + style : { + padding : 2 + }, + items : [ this.getExperimentTitle(), this.getPanel() ], + listeners : { + afterrender : function() { + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, data) { + _this.analysisGrid.refresh(data); + }); + adapter.getAnalysisInformationByExperimentId(_this.experiment.experimentId); + } + } + }); + } + return this.experimentPanel; +}; + +HPLCTabs.prototype.getPanel = function() { + var _this = this; + this.panel = Ext.createWidget('tabpanel', + { + plain : true, + height : this.height, +// width : 900, + style : { + padding : 2 + }, + items : [ + { + tabConfig : { + title : "Overview" + }, - "quality" : { - "std" : null, - "sum" : 0, - "avg" : 0, - "validNumber" : 0, - "totalNumber" : 0, - "values" : [] + items : [ { + xtype : 'container', + layout : 'hbox', + border : 1, + flex : 1, + items : [ { + xtype : 'container', + layout : 'vbox', + border : 1, + items : [ { + xtype : 'container', + margin : '10px', + border : 1, + items : [ + { + xtype : 'container', + margin : '0px', + layout : 'hbox', + border : 1, + items : [ + this.peakGrid2.getPanel([]), + this.mainPlotPanel.getPanel() + ] + }, + { + xtype : 'container', + layout : 'vbox', + border : 1, + items : [ + { + html: '
Select a frame by double-clicking on the HPLC Frames plot
', + margin: 5, + border : 0 + }, + this.frameGrid.getPanel([]), + this.intensityPlotPanel.getPanel() + ] + } + + ] + }], + listeners : { + afterrender : function() { + _this._postRenderOverviewPanel(); + } + } + } ] + } ] + }, +// { +// tabConfig : { +// title : "Details" +// }, +// items : [ { +// xtype : 'container', +// layout : 'hbox', +// border : 1, +// flex : 1, +// items : [ { +// xtype : 'container', +// layout : 'vbox', +// border : 1, +// items : [ { +// xtype : 'container', +// padding : '1px', +// items : [ +// this.I0PlotPanel.getPanel(), this.RgPlotPanel.getPanel(), this.MassPlotPanel.getPanel(), +// this.VcPlotPanel.getPanel() ] +// } ], +// listeners : { +// afterrender : function() { +// _this._postRenderDetailsPanel(); +// } +// } +// } ] +// } ] +// }, + { + tabConfig : { + title : "Analysis" }, - "rgGnom" : { - "std" : null, - "sum" : 0, - "avg" : 0, - "validNumber" : 0, - "totalNumber" : 0, - "values" : [] + items : [ { + xtype : 'container', + layout : 'vbox', + padding : '5px 0px 10px 10px', + items : [ _this.analysisGrid.getPanel([]) ] + } ] + }, + { + tabConfig : { + title : "File Manager" }, - "dMax" : { - "std" : null, - "sum" : 0, - "avg" : 0, - "validNumber" : 0, - "totalNumber" : 0, - "values" : [] + items : [ { + xtype : 'container', + layout : 'vbox', + padding : '5px 0px 10px 10px', + items : [ + { + html: '
Custom Download
', + margin : '10 0 10 5', + border : 0 + }, + { + html: '
Download has been temporary disabled. Contact your labcontact if you want to extract the frames from the HDF5 file
', + margin : '10 0 10 5', + hidden : _this.experiment.experimentId == 2602, + border : 0 + }, + { + xtype : 'container', + layout : 'hbox', + margin : '0 0 0 20', + flex : 1, + items :[ + { + xtype: 'numberfield', + id: 'field_start', + fieldLabel: 'Frames from', + value: 0, + minValue: 0 + }, + { + xtype: 'numberfield', + id: 'field_end', + fieldLabel: 'to', + labelWidth : 20, + margin : '0 0 0 10', + minValue: 0 + }, + { + xtype: 'button', + /** allowing test account to download frames **/ + disabled : _this.experiment.experimentId != 2602, + text : 'Download', + margin : '0 0 0 10', + handler : function() { + var experimentId = _this.experiment.experimentId; + var start = Ext.getCmp("field_start").getValue(); + var end = Ext.getCmp("field_end").getValue(); + /** Checking if start is bigger than end **/ + if (start > end){ + var aux = end; + end = start; + start = aux; + } + var url = BUI.getZipFrameHPLCUrl(experimentId, start, end); + window.open(url) + } + } + ] + }, + _this.peakGrid.getPanel([]) + + ] + } + ] + } + ] + }); + + return this.panel; +}; + +/** + * Main form tab for macromolecule + * + * @width + * @height + * + * #onClose when user clicks on close button in the MacromoleculeForm + * #onSave when macromole is saved by macromoleculeForm + */ +function MacromoleculeTabs(args) { + this.width = 500; + this.height = 500; + + if (args != null) { + if (args.width != null) { + this.width = args.width; + } + if (args.height != null) { + this.height = args.height; + } + } + + var _this = this; + + /** Widgets **/ + + /** Macromolecule Form **/ + this.macromoleculeForm = new MacromoleculeForm({ + width : this.width - 30, + height : this.height - 50, + }); + + this.macromoleculeForm.onClose.attach(function(sender) { + _this.onClose.notify(); + }); + + this.macromoleculeForm.onSave.attach(function(sender, macromolecule) { + _this.onSave.notify(macromolecule); + }); + + this.assemblyForm = new AssemblyForm({ + width : this.width - 30, + height : this.height - 50, + }); + + this.rigibBodyModelingForm = new RigibBodyModelingForm({ + width : this.width - 30, + height : this.height - 50, + }); + + this.rigibBodyModelingForm.onSave.attach(function(sender, macromolecule) { + _this.onSave.notify(macromolecule); + }); + + + /** Events **/ + this.onClose = new Event(this); + this.onSave = new Event(this); +} + +/** Populate the widget **/ +MacromoleculeTabs.prototype.refresh = function(macromolecule) { + this.macromoleculeForm.refresh(macromolecule); + this.assemblyForm.refresh(macromolecule); + this.rigibBodyModelingForm.refresh(macromolecule); + + if (macromolecule != null){ + if (macromolecule.macromoleculeId == null){ + Ext.getCmp(this.id + "_advancedTab").disable(); + Ext.getCmp(this.id + "_assemblyTab").disable(); + } + else{ + Ext.getCmp(this.id + "_advancedTab").enable(); + Ext.getCmp(this.id + "_assemblyTab").enable(); + } + } + else{ + Ext.getCmp(this.id + "_advancedTab").disable(); + Ext.getCmp(this.id + "_assemblyTab").disable(); + } +}; + +MacromoleculeTabs.prototype.getItems = function() { + return [ { + tabConfig : { + title : 'General' + }, + items : [ { + xtype : 'container', + items : [ this.macromoleculeForm.getPanel() ] + } ] + }, { + id : this.id + "_assemblyTab", + tabConfig : { + title : 'Assembly' + }, + items : [ { + xtype : 'container', + items : [ this.assemblyForm.getPanel() ] + } ] + },{ + id : this.id + "_advancedTab", + tabConfig : { + title : 'Advanced' + }, + items : [ this.rigibBodyModelingForm.getPanel() ] + } ]; +}; + +MacromoleculeTabs.prototype.getPanel = function() { + this.panel = Ext.createWidget('tabpanel', { + height : this.height, + width : this.width, + plain : true, + margin : 5, + border : 0, + items : this.getItems() + }); + return this.panel; +}; + +function ResultTabs() { +} + +ResultTabs.prototype.draw = function(targetId, data, macromoleculeId) { + var panel = this.getPanel(targetId, data, macromoleculeId); + return Ext.create('Ext.container.Container', { + renderTo : targetId, + items : [ BUI.getMacromoleculeHeader(macromoleculeId), panel ] + }); + +}; + +ResultTabs.prototype._splitBySpecimen = function(data) { + var splitted = {}; + + for ( var i = 0; i < data.length; i++) { + var row = data[i]; + if (splitted[row.macromoleculeId + "_" + row.bufferId] == null) { + splitted[row.macromoleculeId + "_" + row.bufferId] = []; + } + splitted[row.macromoleculeId + "_" + row.bufferId].push(row); + } + return splitted; + +}; + +ResultTabs.prototype._getTabTitle = function(key, data) { + var macromoleculeId = key.split("_")[0]; + var bufferId = key.split("_")[1]; + + return BIOSAXS.proposal.getMacromoleculeById(macromoleculeId).acronym + " + " + BIOSAXS.proposal.getBufferById(bufferId).acronym + "(" + data.length + ")"; +}; + +ResultTabs.prototype.getPanel = function(targetId, data, macromoleculeId) { + + var dataFiltered = new AnalysisGrid({ + hideNulls : true, + grouped : true, + sorters : [ { + property : 'quality', + direction : 'DESC' + } ] + })._prepareData(data); + + var items = [ { + tabConfig : { + title : 'Analysis (' + dataFiltered.length + ')' + }, + items : [ { + xtype : 'container', + layout : 'vbox', + padding : 10, + items : [ new AnalysisGrid({ + isScatteringPlotVisible : false + }).getPanel(dataFiltered) ] + } ] + }, { + tabConfig : { + title : 'Concentration Effects' + }, + items : [ new ResultSummaryForm().getPanel(data) ] + } ]; + + var splitted = this._splitBySpecimen(dataFiltered); + for ( var key in splitted) { + items.push({ + tabConfig : { + title : this._getTabTitle(key, splitted[key]) + }, + items : [ new ResultSummaryForm().getPanel(splitted[key]) ] + }); + } + + this.panel = Ext.createWidget('tabpanel', { + style : { + padding : 2 + }, + items : items + }); + return this.panel; + +}; + + +function ShipmentTabs(targetId) { + this.targetId = targetId; + + var _this = this; + this.gridHeight = 500; + /** data **/ + this.shipment = null; + + /** Shipment Form **/ + this.shipmentForm = new ShipmentForm({ + creationMode : false, + showTitle : false + }); + this.shipmentForm.onSaved.attach(function(sender, data) { + _this.refresh(data); + + }); + + + /** Cases grid **/ + this.caseGrid = new CaseGrid({ + height : this.gridHeight + }); + + this.caseGrid.onAddButtonClicked.attach(function(sender, dewar) { + _this.caseGrid.grid.setLoading("ISPyB: Creating a new case"); + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, shipment) { + /** updateing shipment on proposal **/ + for ( var i = 0; i < BIOSAXS.proposal.shippings.length; i++) { + if (BIOSAXS.proposal.shippings[i].shippingId == shipment.shippingId) { + BIOSAXS.proposal.shippings[i] = shipment; + } + } + _this.refresh(shipment); + }); + adapter.onError.attach(function(sender, shipment) { + _this.caseGrid.grid.setLoading(false); + }); + adapter.addCase(_this.shipment.json.shippingId); + }); + + this.caseGrid.onRemoveButtonClicked.attach(function(sender, dewarId) { + _this.panel.setLoading("ISPyB: removing case"); + _this.shipment.onSaved.attach(function(sender, shipment) { + _this.refresh(shipment); + + }); + _this.shipment.removeCase(dewarId); + }); +} + +ShipmentTabs.prototype.refresh = function(shipment) { + + var _this = this; + this.shipment = shipment; + var proposal = new Proposal(); + proposal.onDataRetrieved.attach(function(sender, plates) { + + _this.refreshWithPlates(shipment, plates); + _this.caseGrid.grid.setLoading(false); + }); + proposal.getPlatesByProposal(); +}; + +ShipmentTabs.prototype.refreshWithPlates = function(shipment, plates) { + this.shipment = new Shipment(shipment); + + this.caseGrid.refresh(this.shipment.getDewars(), plates); + this.panel.setLoading(false); +}; + +ShipmentTabs.prototype.error = function(error) { + var e = JSON.parse(error); + showError(e); + this.panel.setLoading(false); +}; + +//ShipmentTabs.prototype.refreshTabTitles = function() { +/*Ext.getCmp("MacromoleculeTab").setText(this.getMacromoleculeTitle()); +Ext.getCmp("BufferTab").setText(this.getBuffersTitle()); +Ext.getCmp("SpecimenTab").setText(this.getSpecimenTitle()); +Ext.getCmp("PlatesTab").setText(this.getPlatesTitle()); +Ext.getCmp("AssembliesTab").setText(this.getShipmentTitle()); +Ext.getCmp("PlateGroupsTab").setText(this.getPlateGroupsTitle());*/ +//}; +ShipmentTabs.prototype.draw = function(shipment) { + var _this = this; + _this.plates = []; + _this.shipment = shipment; + _this.render(shipment); + + // var proposal = new Proposal(); + // proposal.onDataRetrieved.attach(function(sender, plates){ + // _this.plates = plates; + // _this.shipment = shipment; + // _this.render(shipment); + // + // }); + // proposal.getPlatesByProposal(); +}; + +//ShipmentTabs.prototype.getShipmentTitle = function() { +// return 'Shipment'; +//}; + +//ShipmentTabs.prototype.getMacromoleculeTitle = function() { +// return 'Macromolecules (' + this.experiment.getMacromolecules().length + ')'; +//}; + +//ShipmentTabs.prototype.getBuffersTitle = function() { +// return 'Buffers (' + this.experiment.getBuffers().length + ')'; +//}; + +//ShipmentTabs.prototype.getPlateGroupsTitle = function() { +// return 'Plate Groups (' + this.experiment.getPlateGroups().length + ')'; +//}; + +//ShipmentTabs.prototype.getSampleChangerTitle = function() { +// return 'Sample Changer'; +//}; + +//ShipmentTabs.prototype.getSpecimenTitle = function() { +// return 'Specimens(' + this.experiment.getSpecimenCount() + ')'; +//}; + +//ShipmentTabs.prototype.getPlatesTitle = function() { +// return 'Plates(' + this.experiment.getSamplePlates().length + ')'; +//}; + +//ShipmentTabs.prototype.getBuffersTip = function() { +/*if (this.experiment.getBuffers().length == 0){ + return BUI.getWarningHTML("There are no buffers. Click on add to create new ones. Click on edit button or double click to edit them"); + +} +else{ + return BUI.getTipHTML("Click on edit button or double click to edit them. Click on duplicate to create an identical buffer including its additives") +}*/ +//}; + +//ShipmentTabs.prototype.refreshTips = function() { +/*Ext.getCmp("BufferTabTip").update(this.getBuffersTip()); +Ext.getCmp("SpecimenTabTip").update(this.getSpecimensTip());*/ +//}; + +ShipmentTabs.prototype.render = function(shipment) { + return this.getPanel(shipment); +}; + +ShipmentTabs.prototype.getShipmentHeader = function(shipment) { + var _this = this; + function getHTMLSource() { + var name = shipment.json.shippingName; + var status = shipment.json.shippingStatus; + var creationDate = shipment.json.creationDate; + var html = BUI.createFormLabel("Name :", name, 75, 400); + html = html + BUI.createFormLabel("Status :", status, 75, 400); + html = html + BUI.createFormLabel("Date :", creationDate, 75, 400); + return html; + } + + return Ext.create('Ext.container.Container', { + frame : false, + layout : 'hbox', + title : 'General', + padding : 5, + bodyPadding : '5 5 0 0', + width : 890, + margin : '0 0 10 0', + height : 100, + style : { + borderColor : '#BDBDBD', + borderStyle : 'solid', + borderWidth : '1px' + }, + fieldDefaults : { + msgTarget : 'side', + labelWidth : 100 + }, + items : [ { + margin : "0 0 0 0", + width : 475, + border : 0, + html : getHTMLSource() + } + ] + }); +}; + +ShipmentTabs.prototype.getTabPanel = function(shipment) { + this.panel = Ext.createWidget('tabpanel', { + height : 600, + style : { + padding : 2 + }, + items : [ { + tabConfig : { + id : 'Shipment', + title : 'Shipment', + icon : '/ispyb/images/plane-small.gif' + }, + items : [ { + xtype : 'container', + margin : '5 5 5 5', + items : [ this.shipmentForm.getPanel(shipment) ] + } ] + }, { + tabConfig : { + id : 'Cases', + title : 'Cases', + icon : '../images/box-icon-very-small.png' + }, + items : [ { + xtype : 'container', + margin : '5 5 5 5', + items : [ this.caseGrid.getPanel(shipment.getDewars(), this.plates) ] + } ] + } ] + }); + return this.panel; +}; + +ShipmentTabs.prototype.getPanel = function(shipment) { + var _this = this; + this.shipment = shipment; + if (this.plates == null) { + this.plates = []; + } + + if (this.shipPanel == null) { + this.shipPanel = Ext.create('Ext.container.Container', { + bodyPadding : 2, + width : Ext.getBody().getViewSize().width * 0.9, + renderTo : this.targetId, + style : { + padding : 2 + }, + items : [ this.getShipmentHeader(shipment), this.getTabPanel(shipment) ] + }); + } + + return this.shipPanel; +}; + +/** + * Shows an template with the specimens, measurements and the experiment's + * requirement + * + * @targetId + */ +function TemplateTabs(targetId) { + this.height = 600; + this.targetId = targetId; + + this.id = BUI.id(); + var _this = this; + + this.gridHeight = 1000; + /** data * */ + this.experiment = null; + + this.specimenSelected = null; + + /** Specimen Widget contains a specimenGrid and a sampleChangerWidget than can be displayed with are vertical or horizontal layout **/ + this.specimenWidget = new SpecimenWidget({ + height : this.height + }); + + this.samplePlateGroupWidget = new SamplePlateGroupWidget({ + showTitle : false, + height : 250, + margin : 5, + bbar : true + }); + + this.samplePlateGroupWidget.onExperimentChanged.attach(function(sender, experiment) { + _this.refresh(experiment); + }); + + this.samplePlateGroupWidget.onClick.attach(function(sender, args) { + }); + + this.volumePlanificator = new VolumeGrid(); + + /** For Measurements * */ + var storeViscosity = Ext.create('Ext.data.Store', { + fields : [ 'name' ], + data : [ { + "name" : "low" + }, { + "name" : "medium" + }, { + "name" : "high" + } ] + }); + + // Create the combo box, attached to the states data store + var viscosityEditor = Ext.create('Ext.form.ComboBox', { + fieldLabel : '', + store : storeViscosity, + queryMode : 'local', + displayField : 'name', + valueField : 'name' + }); + + this.measurementGrid = new MeasurementGrid({ + maxWidth : 1500, + estimateTime : false, + positionColumnsHidden : true, + isPriorityColumnHidden : true, + isStatusColumnHidden : true, + isTimeColumnHidden : true, + updateRowEnabled : true, + collapsed : true, + removeBtnEnabled : true, + showTitle : false, + collapseBtnEnable : false, + addBtnMultipleEdit : true, + sortingBtnEnable : true, + editor : { + exposureTemperature : { + xtype : 'textfield', + allowBlank : true + }, + comments : { + xtype : 'textfield', + allowBlank : true + }, + volumeToLoad : { + xtype : 'numberfield', + allowBlank : true + }, + transmission : { + xtype : 'numberfield', + allowBlank : true + }, + viscosity : viscosityEditor, + waitTime : { + xtype : 'numberfield', + allowBlank : true + }, + flow : { + xtype : 'checkbox', + allowBlank : true + } + } + }); + + this.measurementGrid.onSelected.attach(function(sender, measurements) { + var specimens = []; + for ( var i = 0; i < measurements.length; i++) { + specimens.push(_this.experiment.getSampleById(measurements[i].specimenId)); + } + }); + + this.measurementGrid.onMeasurementChanged.attach(function(sender, measurement) { + _this.experiment.setMeasurement(measurement); + _this.refresh(_this.experiment); + }); + + this.measurementGrid.onExperimentChanged.attach(function(sender, json) { + _this.refresh(new Experiment(json)); + }); + + this.measurementGrid.onRemoved.attach(function(sender, experiment) { + _this.refreshSpecimen(new Experiment(experiment)); + }); + + this.measurementGrid.onUpdateTime.attach(function(sender, args) { + document.getElementById(_this.id + "_counter").innerHTML = args.hours + 'h, ' + args.minutes + 'min and ' + args.seconds + ' seconds'; + }); + +} + +TemplateTabs.prototype.refreshMeasurement = function(experiment) { + this.experiment = experiment; + this.experiment.onSaved = new Event(this); + this.experiment.onSpecimenSaved = new Event(this); + var experimentList = new ExperimentList([ this.experiment ]); + this.measurementGrid.refresh(experimentList.getMeasurementsNotCollected(), experimentList); +}; + +TemplateTabs.prototype.refreshSpecimen = function(experiment) { + this.experiment = experiment; + this.experiment.onSaved = new Event(this); + this.experiment.onSpecimenSaved = new Event(this); + this.samplePlateGroupWidget.refresh(this.experiment); + this.specimenWidget.refresh(this.experiment); + this.volumePlanificator.refresh(this.experiment); +}; + +TemplateTabs.prototype.refresh = function(experiment) { + // var start = new Date().getTime(); + this.experiment = experiment; + this.experiment.onSaved = new Event(this); + this.experiment.onSpecimenSaved = new Event(this); + + // var experimentList = new ExperimentList([this.experiment]); + this.refreshMeasurement(experiment); + this.refreshSpecimen(experiment); + /** Refreshing grids * */ + this.panel.setLoading(false); + +}; + +TemplateTabs.prototype.error = function(error) { + var e = JSON.parse(error); + showError(e); + this.panel.setLoading(false); + +}; + +TemplateTabs.prototype.draw = function(experiment) { + this.render(experiment); +}; + + +TemplateTabs.prototype.getExperimentTitle = function() { + var _this = this; + var experimentHeaderForm = new ExperimentHeaderForm(); + return experimentHeaderForm.getPanel(this.experiment); +}; + +TemplateTabs.prototype.render = function(experiment) { + var _this = this; + this.experiment = experiment; + + /** + * + * Depending on the sample Changer configuration we want to display the plates vertically or horizontally + * Default is vertical + * + * */ + + var specimenContainer = this.specimenWidget.getPanel(); + this.specimenWidget.refresh(experiment); + + var experimentList = new ExperimentList([ _this.experiment ]); + var measurementContainer = Ext.create('Ext.container.Container', { + layout : { + type : 'vbox' + }, + defaults : { + style : { + padding : '5px 0px 0px 10px' + } + }, + items : [ _this.measurementGrid.getPanel(experimentList.getMeasurementsNotCollected(), experimentList) ] + }); + + this.panel = Ext + .createWidget( + 'tabpanel', + { + plain : true, + items : [ + { + tabConfig : { + title : 'Measurements' + }, + items : [ { + xtype : 'container', + layout : 'vbox', + border : 1, +// height : _this.gridHeight, + margin : "0 0 10 0", + items : [ measurementContainer ] + } + + ] + }, + { + tabConfig : { + id : 'genralTabl', + title : "Specimens" + // width : 900, + // border : 3 + + }, + items : [ specimenContainer ] + }, + { + tabConfig : { + title : "Requirements" + + }, + items : [ + { + html : BUI.getTipHTML("Estimated volume is the maximum volume required. Depending on the order of your measurements you may use less. Click on create stock solutions if you plan to ship these stock solutions"), + margin : "10 10 10 10", + border : 0 + }, this.volumePlanificator.getPanel(experiment) ] + } ] + }); + // ); + return this.getPanel(this.panel); +}; + +TemplateTabs.prototype.isTemplate = function() { + if (this.experiment.json.type == "TEMPLATE") { + return true; + } + return false; +}; + + +TemplateTabs.prototype.getPanel = function(panel) { + var _this = this; + if (this.experimentPanel == null) { + this.experimentPanel = Ext.create('Ext.container.Container', { + bodyPadding : 2, + width : 1000,//Ext.getBody().getViewSize().width * 0.9, + renderTo : this.targetId, + height : 500, +// style : { +// padding : 2 +// }, + items : [ + this.getExperimentTitle(), + this.panel + ], + listeners : { + afterrender : function(thisCmp) { + $("#SchemeReport" + _this.experiment.experimentId).click(function() { + $(this).target = "_blank"; + window.open('viewProjectList.do?reqCode=display&menu=platescheme&experimentId=' + _this.experiment.experimentId); + return false; + }); + + } + } + }); + } + return this.experimentPanel; +}; + +TemplateTabs.prototype.input = function(targetId) { + return new ExperimentTabs().input(); +}; + +TemplateTabs.prototype.test = function(targetId) { + var experimentTabs = new TemplateTabs(targetId); + BIOSAXS.proposal = new Proposal(experimentTabs.input().proposal); + experimentTabs.draw(new Experiment(experimentTabs.input().experiment)); +}; + +var BUI = { + //interval : 60000, + interval : 40000, + rainbow : function(numOfSteps, step) { + // This function generates vibrant, "evenly spaced" colours (i.e. no clustering). This is ideal for creating easily distinguishable vibrant markers in Google Maps and other apps. + // Adam Cole, 2011-Sept-14 + // HSV to RBG adapted from: http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript + var r, g, b; + var h = step / numOfSteps; + var i = ~~(h * 6); + var f = h * 6 - i; + var q = 1 - f; + switch (i % 6) { + case 0: + r = 1, g = f, b = 0; + break; + case 1: + r = q, g = 1, b = 0; + break; + case 2: + r = 0, g = 1, b = f; + break; + case 3: + r = 0, g = q, b = 1; + break; + case 4: + r = f, g = 0, b = 1; + break; + case 5: + r = 1, g = 0, b = q; + break; + } + var c = "#" + ("00" + (~~(r * 255)).toString(16)).slice(-2) + ("00" + (~~(g * 255)).toString(16)).slice(-2) + + ("00" + (~~(b * 255)).toString(16)).slice(-2); + return (c); + }, + getFileNameByPath : function(filePath) { + if (filePath != null){ + var split = filePath.split("/"); + if (split.length > 0){ + return split[split.length - 1]; + } + } + return "Not file"; + }, + getUpdateInterval : function() { + this.interval = this.interval + 2000; + return this.interval; + }, + getRadiationDamageThreshold : function() { + return 0.7; + }, + getQualityThreshold : function() { + return 0.7; + }, + getCreateShipmentURL : function() { + return '/ispyb/user/viewProjectList.do?reqCode=display&menu=create_shipment'; + }, + getCreateShipmentList : function() { + return '/ispyb/user/viewProjectList.do?reqCode=display&menu=list_shipment'; + }, + getShippingURL : function(shippingId) { + return '/ispyb/user/viewProjectList.do?reqCode=display&menu=shipment&shippingId=' + shippingId; + }, + + getMacromoleculeResultsURLByMultipleSearch : function(array) { + return '/ispyb/user/viewProjectList.do?reqCode=display&menu=macromolecule&search=' + + JSON.stringify(array).replace(new RegExp("\"", 'g'), "'"); + }, + getMacromoleculeResultsURL : function(macromoleculeId) { + return '/ispyb/user/viewProjectList.do?reqCode=display&menu=macromolecule¯omoleculeId=' + macromoleculeId; + }, + getMacromoleculeBufferResultsURL : function(macromoleculeId, bufferId) { + return '/ispyb/user/viewProjectList.do?reqCode=display&menu=macromolecule&bufferId=' + bufferId + '¯omoleculeId=' + macromoleculeId; + }, + + getMacromoleculeHeader : function(macromoleculeId) { + + function getHTMLSource(macromoleculeId) { + if (macromoleculeId != null) { + var html = BUI.createFormLabel("Name :", BIOSAXS.proposal.getMacromoleculeById(macromoleculeId).name, 75, 400); + html = html + BUI.createFormLabel("Acronym :", BIOSAXS.proposal.getMacromoleculeById(macromoleculeId).acronym, 75, 400); + if (BIOSAXS.proposal.getMacromoleculeById(macromoleculeId).comments != null) { + html = html + BUI.createFormLabel("Comments :", BIOSAXS.proposal.getMacromoleculeById(macromoleculeId).comments, 75, 400); + } + return html; + } + } + + return Ext.create('Ext.container.Container', { + frame : false, + layout : 'hbox', + title : 'Macromolecule', + bodyPadding : '5', + width : 890, + margin : '0 0 10 0', + height : 100, + style : { + borderColor : '#BDBDBD', + borderStyle : 'solid', + borderWidth : '1px' + }, + fieldDefaults : { + msgTarget : 'side', + labelWidth : 100 + }, + items : [ { + margin : "10 0 0 10", + width : 475, + border : 0, + html : getHTMLSource(macromoleculeId) + }, { + margin : "10 0 0 10", + width : 475, + border : 0, + html : BUI.getZipHTMLByMacromoleculeId(macromoleculeId) + } + ] + }); + }, + + getZipHTMLByMacromoleculeId : function(macromoleculeId) { + if (macromoleculeId != null) { + var fileName = BIOSAXS.proposal.getMacromoleculeById(macromoleculeId).acronym; + return ""; + } + }, + getZipHTMLByExperimentId : function(experimentId, filename) { + if (filename == null){ + filename = "experiment"; + } + return ""; + }, + + getZipURLByAverageId : function(averageId, filename) { + if (filename == null){ + filename = "experiment"; + } + return "/ispyb/user/dataadapter.do?reqCode=getZipFileByAverageListId&f&mergeIdsList=" + averageId + "&fileName=" + filename; + }, + getZipURLBySubtractionId : function(subtractionId, filename) { + if (filename == null){ + filename = "experiment"; + } + return "/ispyb/user/dataadapter.do?reqCode=getZipFileByAverageListId&f&subtractionIdList=" + subtractionId + "&fileName=" + filename; + }, + + + getZipHTMLByFrameRangeId : function(experimentId, start, end) { + var fileName = "experiment"; + return ""; + }, + getZipFrameHPLCUrl : function(experimentId, start, end) { + return "/ispyb/user/dataadapter.do?reqCode=getZipFileH5ByFramesRange&f&experimentId=" + experimentId + "&start=" + Number(start) +"&end="+ Number(end); + }, + + getQueueUrl : function() { + return "/ispyb/user/dataadapter.do?reqCode=getPagingCompactAnalysisByProposalId"; + }, + + getQueueUrlByExperiment: function(experimentId) { + return "/ispyb/user/dataadapter.do?reqCode=getPagingCompactAnalysisByExperimentId&f&experimentId=" + experimentId; + }, + getStandardDeviation : function(values) { + var sum = 0; + var count = 0; + var avg = null; + + var curatedValues = new Array(); + for ( var i = 0; i < values.length; i++) { + var value = values[i]; + if (value != null) { + if (!isNaN(value)) { + count = count + 1; + sum = sum + Number(value); + curatedValues.push(Number(value)); + } + } + } + + if (count > 0) { + avg = sum / count; + } else { + avg = sum; + } + var aux = 0; + for ( var i = 0; i < curatedValues.length; i++) { + var value = curatedValues[i]; + aux = aux + Math.pow(value - avg, 2); + } + /** std **/ + var std = Math.sqrt(aux / count); + return { + std : (std), + sum : (sum), + avg : (avg), + validNumber : count, + totalNumber : values.length, + values : values + }; + }, + + getHTMLTableForFrameAveraged : function(bufferAcronym, macromoleculeAcronym, bbmerges, molmerges, bamerges, totalframes, bufferId,macromoleculeId, macromoleculeColor) { + + function getFrameSpan(framesMerged, total) { + return "(" + framesMerged + " of " + total + ")"; + } + + function getColorFrame(framesMerged, total) { + if (framesMerged / total < 0.5) { + return "#FA5858"; + } + if ((framesMerged / total >= 0.5) && (framesMerged / total < 0.8)) { + return "#FF9900"; + } + return "white"; + } + + function getRow(color, acroynm, framesMerged, totalframes) { + return " " + + BUI.getRectangleColorDIV(color, 10, 10) + + " " + acroynm + "" + + getFrameSpan(framesMerged, totalframes) + ""; + } + + var html = ""; + + /** Buffer Before **/ + if (bufferAcronym != null) { + if (bbmerges != null) { + color = BIOSAXS.proposal.bufferColors[bufferId]; + html = html + getRow(color, bufferAcronym, bbmerges, totalframes); + } + } + + /** Molecule **/ + if (macromoleculeAcronym != null) { + if (molmerges != null) { + if (macromoleculeColor == null){ + color = BIOSAXS.proposal.macromoleculeColors[macromoleculeId]; + } + else{ + color = macromoleculeColor; //BIOSAXS.proposal.macromoleculeColors[macromoleculeId]; + } + html = html + getRow(color, macromoleculeAcronym, molmerges, totalframes); + } + } + + /** Buffer After **/ + if (bufferAcronym != null) { + if (bamerges != null) { + color = BIOSAXS.proposal.bufferColors[bufferId]; + html = html + getRow(color, bufferAcronym, bamerges, totalframes); + } + } + return html + "
"; + }, + isWebGLEnabled : function(return_context) { + if (!!window.WebGLRenderingContext) { + var canvas = document.createElement("canvas"); + names = [ "webgl", "experimental-webgl", "moz-webgl", "webkit-3d" ]; + context = false; + for ( var i = 0; i < 4; i++) { + try { + context = canvas.getContext(names[i]); + if (context && typeof context.getParameter == "function") { + // WebGL is enabled + if (return_context) { + // return WebGL object if the function's argument is present + return { + name : names[i], + gl : context + }; } + // else, return just true + return true; } - } ] - }, - "concentrationLabel" : "7.17 mg/ml ,3.53 mg/ml ,1.75 mg/ml ,0.91 mg/ml " + } catch (e) { + } + } + // WebGL is supported, but disabled + return false; + } + // WebGL not supported28. + return false; + }, + getHTMLTableForPrefixes : function(bufferBeforeaverageFilePath, averageFilePath, bufferAfterAverageFilePath) { + + function getRow(bufferBeforeaverageFilePath) { + file = bufferBeforeaverageFilePath; + try { + file = bufferBeforeaverageFilePath.split("/")[bufferBeforeaverageFilePath.split("/").length - 1]; + } catch (e) { + file = "NA"; + } + return "" + file + ""; + } + var html = ""; + + /** Buffer Before **/ + if (bufferBeforeaverageFilePath != null) { + html = html + getRow(bufferBeforeaverageFilePath); + } + + /** Molecule **/ + if (averageFilePath != null) { + html = html + getRow(averageFilePath); + } + + /** Buffer After **/ + if (bufferAfterAverageFilePath != null) { + html = html + getRow(bufferAfterAverageFilePath); + } + return html + "
"; + }, + + getBaseURL : function() { + return '/ispyb/user/dataadapter.do'; + }, + + getPrintcomponentURL : function(dewarId) { + return '/ispyb/user/viewDewarAction.do?reqCode=generateLabels&dewarId=' + dewarId; + + }, + getPDBVisualizerURL : function(modelId, subtractionId, experimentId) { + return '/ispyb/user/viewProjectList.do?reqCode=display&menu=PDBVisualizer&modelId=' + modelId + '&experimentId=' + experimentId + + '&subtractionId=' + subtractionId; + }, + + getURL : function() { + return this.getBaseURL() + '?reqCode=getImage'; + + }, + getAbinitioImageURL : function() { + return this.getBaseURL() + '?reqCode=getAbinitioImage'; + }, + getNSDImageURL : function(modelListId) { + return BUI.getAbinitioImageURL() + '&type=NSD&modelListId=' + modelListId; + }, + getCHI2ImageURL : function(modelListId) { + return BUI.getAbinitioImageURL() + '&type=CHI2&modelListId=' + modelListId; + }, + getModelFile : function(type, modelId, format) { + return this.getBaseURL() + '?reqCode=getModelFile' + "&type=" + type + "&modelId=" + modelId + "&format=" + format; + }, + getPdbURL : function() { + return this.getBaseURL() + '?reqCode=getPdbFiles'; + }, + getStvArray : function(value, error) { + value = Number(value); + error = Number(error); + return [ value - error, value, value + error ]; + }, + getPointArrayForDygraph : function(x, y, error) { + return [ x, BUI.getStvArray(y, error) ]; + }, + createDIV : function(text, width, className, backgroundColor) { + var nameContainer = document.createElement("div"); + var nameSpan = document.createElement("span"); + if (className != null) { + nameSpan.setAttribute("class", className); + } + if (backgroundColor != null) { + nameSpan.setAttribute("style", "float:left;width:" + width + "px;height:18px;background-color:" + backgroundColor); + } else { + nameSpan.setAttribute("style", "float:left;width:" + width + "px;height:18px;"); + } + nameSpan.appendChild(document.createTextNode(text)); + nameContainer.appendChild(nameSpan); + return nameContainer; + }, + + createFormLabel : function(labelText, text, labelWidth, textWidth, backgroundColor) { + var div = document.createElement("div"); + + div.appendChild(BUI.createDIV(labelText, labelWidth, "bLabel", backgroundColor)); + div.appendChild(BUI.createDIV(text, textWidth, "btext", backgroundColor)); + return div.innerHTML; + }, + + createTextArea : function(labelText, text, labelWidth, rows, cols) { + var div = document.createElement("div"); + div.appendChild(BUI.createDIV(labelText, labelWidth, "bLabel")); + var textArea = document.createElement("textarea"); + textArea.setAttribute("rows", rows); + textArea.setAttribute("cols", cols); + textArea.appendChild(document.createTextNode(text)); + div.appendChild(textArea); + return div.innerHTML; + }, + + showBetaWarning : function() { + alert("ISPyB for Biosaxs version Beta has not this functionality enabled"); + }, + + formatValuesErrorUnitsScientificFormat : function(val, error, unit, args) { + var fontSize = 14; + var decimals = 2; + var errorFontSize = 10; + /** line break **/ + var lineBreak = true; + if (args != null) { + if (args.fontSize != null) { + fontSize = args.fontSize; + } + if (args.decimals != null) { + decimals = args.decimals; + } + if (args.errorFontSize != null) { + errorFontSize = args.errorFontSize; + } + if (args.lineBreak != null) { + lineBreak = args.lineBreak; + } + + } + + if (val == "") { + return ""; + } + if (error == null) { + return "" + Number(val).toFixed(decimals) + ""; + } + var html = "" + Number(val).toFixed(decimals) + " " + unit + ""; + if (lineBreak) { + html = html + "
"; + } + return html + " ± " + Number(Number(error).toFixed(3)).toExponential() + + ""; + }, + + formatValuesErrorUnits : function(val, error, unit, args) { + var fontSize = 16; + var decimals = 2; + var errorFontSize = 10; + /** line break **/ + var lineBreak = true; + if (args != null) { + if (args.fontSize != null) { + fontSize = args.fontSize; + } + if (args.decimals != null) { + decimals = args.decimals; + } + if (args.errorFontSize != null) { + errorFontSize = args.errorFontSize; + } + if (args.lineBreak != null) { + lineBreak = args.lineBreak; + } + + } + + if (val == "") { + return ""; + } + if (error == null) { + return "" + Number(val).toFixed(decimals) + ""; + } + var html = "" + Number(val).toFixed(decimals) + " " + unit + ""; + if (lineBreak) { + html = html + "
"; + } + return html + " ± " + Number(Number(error).toFixed(8)).toExponential() + + ""; + }, + + formatValuesUnits : function(val, unit, args) { + var fontSize = 12; + var decimals = 2; + var unitsFontSize = 10; + if (args != null) { + if (args.fontSize != null) { + fontSize = args.fontSize; + } + if (args.decimals != null) { + decimals = args.decimals; + } + if (args.unitsFontSize != null) { + unitsFontSize = args.unitsFontSize; + } + + } + + if (val === "") { + return ""; + } + if (unit == null) { + return "" + Number(val).toFixed(decimals) + ""; + } + return "" + Number(val).toFixed(decimals) + " " + unit + ""; + }, + + getGreenButton : function(text, args) { + var width = 70; + var height = 20; + if (args != null) { + if (args.width != null) { + width = args.width; + } + if (args.height != null) { + height = args.height; + } + } + + return ''; + }, +// getBlueButton : function(text, args) { +// var width = 70; +// var height = 20; +// if (args != null) { +// if (args.width != null) { +// width = args.width; +// } +// if (args.height != null) { +// height = args.height; +// } +// } +// +// return ''; +// }, + + getBlueButton : function(text, args) { + var width = 70; + var height = 20; + var href = null; + if (args != null) { + if (args.width != null) { + width = args.width; + } + if (args.height != null) { + height = args.height; + } + if (args.href != null) { + href = args.href; + } + } + if (href != null) { + return ''; + } else { + return ''; +// return ''; + } + }, + + getSubmitGreenButton : function(text) { + return ''; + }, + + getRedButton : function(text) { + return ''; + + }, + getRectangleColorDIV : function(color, height, width) { + return '
'; + }, + + openBufferWindow : function(bufferId) { + var window = new BufferWindow(); + window.draw(tabs.experiment.getBufferById(bufferId), tabs.experiment); + }, + + /** Render for safety levels on grids **/ + safetyRenderer : function(val, y, specimen) { + var color = val; + if (val == "YELLOW") { + color = "#E9AB17"; + } + return '' + val + ''; + }, + + getSampleColor : function() { + return '#CB181D'; + }, + + getLightSampleColor : function() { + return '#FCBBA1'; + }, + + getBufferColor : function() { + return '#6A51A3'; + }, + getLightBufferColor : function() { + return '#BCBDDC'; + }, + + formatConcentration : function(val) { + if (val != null) { + return "" + Number(val).toFixed(2) + " mg/ml "; + } + return val; + }, + + formatVolume : function(sample, volume) { + if (Number(sample.data.volumeToLoad) > Number(sample.data.volume)) { + return "" + volume + " �l"; + } + if (Number(sample.data.volumeToLoad) == Number(sample.data.volume)) { + return "" + volume + " �l"; + } + return "" + volume + " �l"; + }, + + getProposal : function() { + return new Proposal(); + }, + + getSampleNameRenderer : function(val, y, record) { + var sample = record.data; + if (record.raw.macromolecule3VO == null) { + return '' + sample.code + ''; + } else { + return '' + sample.code + ''; + } + }, + + getSafetyLevels : function() { + var safetyLevels = new Array(); + safetyLevels.push({ + safetyLevelId : "", + name : "UNKNOWN" + }); + safetyLevels.push({ + safetyLevelId : 1, + name : "GREEN" + }); + safetyLevels.push({ + safetyLevelId : 2, + name : "YELLOW" + }); + safetyLevels.push({ + safetyLevelId : 3, + name : "RED" + }); + return safetyLevels; + }, + + getErrorColor : function() { + return '#F6CED8'; + }, + getWarningColor : function() { + return '#F5DA81'; + }, + getValidColor : function() { + return '#E0F8E0'; + }, + getSamplePlateLetters : function() { + return [ "A", "B", "C", "D", "E", "F", "G", "H" ]; + }, + getSamplePositionHTML : function(sample, experiment) { + var plate = ""; + var row = ""; + var column = ""; + var rows = this.getSamplePlateLetters(); + if (sample.sampleplateposition3VO != null) { + var samplePlate = experiment.getSamplePlateById(sample.sampleplateposition3VO.samplePlateId); + if (samplePlate != null) { + plate = (samplePlate.slotPositionColumn); + row = (sample.sampleplateposition3VO.rowNumber); + column = (sample.sampleplateposition3VO.columnNumber); + // var html = "Plate: " + "" + plate + ""; + // html = html + ", Row: " + "" + rows[row - 1] + ""; + // html = html + ", Column: " + "" + column + ""; + var html = "Plate: " + plate + + "-" + rows[row - 1] + "" + column + ""; + return html; + } + } + return ""; + }, + + getSafetyLabelName : function(safetyLevelId) { + var safetyLevels = BUI.getSafetyLevels(); + for ( var i = 0; i < safetyLevels.length; i++) { + if (safetyLevels[i].safetyLevelId == safetyLevelId) { + return safetyLevels[i].name; + } + } + return "UNKNOWN"; + }, + /** generate random id **/ + id : function() { + var text = ""; + var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + + for ( var i = 0; i < 5; i++) + text += possible.charAt(Math.floor(Math.random() * possible.length)); + + return text; + }, + showWarning : function(message) { + Ext.Msg.show({ + title : 'Warning', + msg : message, + icon : Ext.Msg.WARNING, + animEl : 'elId' + }); + }, + showError : function(message) { + Ext.Msg.show({ + title : 'Warning', + msg : message, + icon : Ext.Msg.ERROR, + animEl : 'elId' + }); + }, + getTipHTML : function(message) { + //return "
Tip
" + // + message + "
"; + return "
Tip
" + + message + "
"; + }, + + getWarningHTML : function(message) { + return "
Warning
" + + message + "
"; + }, + + getErrorHTML : function(message) { + return "
Error
" + + message + "
"; + }, + + getProgessBar : function(percentage, text) { + /** percentage 100% green **/ + var color = "#0a0"; + + color = "#99CC00"; + if (percentage > 100) { + color = "yellow"; + percentage = 100; + } + if (isNaN(percentage)) { + color = "white"; + percentage = 0; + } + + var defaultText = percentage + "%"; + if (text != null) { + defaultText = text; + } + + return "
" + defaultText + "
"; + }, + getFileName : function(filePath){ + if (filePath != null){ + return filePath.split("/")[filePath.split("/").length - 1] + } + return ""; + } + +}; + +Ext.ux.form.RequiredCombo = Ext.extend(Ext.form.ComboBox, { + config : { + cls : 'custom-field-text-required' + }, + initComponent : function() { + Ext.ux.form.RequiredCombo.superclass.initComponent.apply(this, arguments); + }, + checkChange : function() { + if ((this.getValue() == null) || String(this.getValue()).length == 0) { + this.addCls('custom-field-text-required'); + } else { + this.removeCls('custom-field-text-required'); + } + } +}); + +Ext.define('Ext.form.field.RequiredNumber', { + extend : 'Ext.form.field.Number', + alias : 'widget.requirednumberfield', + alternateClassName : [ 'Ext.form.RequiredNumberField', 'Ext.form.RequiredNumber' ], + config : { + cls : 'custom-field-text-required' + }, + + initComponent : function() { + var me = this; + me.callParent(); + me.setMinValue(me.minValue); + me.setMaxValue(me.maxValue); + }, + + onChange : function() { + if ((this.getValue() == null) || String(this.getValue()).length == 0) { + this.addCls('custom-field-text-required'); + } else { + this.removeCls('custom-field-text-required'); + } + this.toggleSpinners(); + this.callParent(arguments); + } +}); + +Ext.define('Ext.form.field.RequiredText', { + extend : 'Ext.form.field.Text', + alias : 'widget.requiredtext', + requires : [ 'Ext.form.field.VTypes', 'Ext.layout.component.field.Text' ], + alternateClassName : [ 'Ext.form.RequiredTextField', 'Ext.form.RequiredText' ], + config : { + cls : 'custom-field-text-required' + }, + initComponent : function() { + var me = this; + if (me.allowOnlyWhitespace === false) { + me.allowBlank = false; } - }; -}; + me.callParent(); + me.addEvents( + /** + * @event autosize + * Fires when the **{@link #autoSize}** function is triggered and the field is resized according to the + * {@link #grow}/{@link #growMin}/{@link #growMax} configs as a result. This event provides a hook for the + * developer to apply additional logic at runtime to resize the field if needed. + * @param {Ext.form.field.Text} this This text field + * @param {Number} width The new field width + */ + 'autosize', -ConcentrationHTMLTableWidget.prototype.test = function(targetId) { - var concentrationHTMLTableWidget = new ConcentrationHTMLTableWidget(); - document.getElementById(targetId).innerHTML = concentrationHTMLTableWidget.getPanel(concentrationHTMLTableWidget.input().data); -}; - -function DataCollectionWidget() { + /** + * @event keydown + * Keydown input field event. This event only fires if **{@link #enableKeyEvents}** is set to true. + * @param {Ext.form.field.Text} this This text field + * @param {Ext.EventObject} e + */ + 'keydown', + /** + * @event keyup + * Keyup input field event. This event only fires if **{@link #enableKeyEvents}** is set to true. + * @param {Ext.form.field.Text} this This text field + * @param {Ext.EventObject} e + */ + 'keyup', + /** + * @event keypress + * Keypress input field event. This event only fires if **{@link #enableKeyEvents}** is set to true. + * @param {Ext.form.field.Text} this This text field + * @param {Ext.EventObject} e + */ + 'keypress'); + me.addStateEvents('change'); + me.setGrowSizePolicy(); + }, + checkChange : function() { + if ((this.getValue() == null) || String(this.getValue()).length == 0) { + this.addCls('custom-field-text-required'); + } else { + this.removeCls('custom-field-text-required'); + } + } +}); -}; +var BIOSAXS_COMBOMANAGER = { -DataCollectionWidget.prototype.refresh = function(subtractionId) { + getComboMacromoleculeByMacromolecules : function(macromolecules, args) { + var labelWidth = 150; + var margin = "0 0 5 0"; + var width = 300; -}; + if (args != null) { + if (args.labelWidth != null) { + labelWidth = args.labelWidth; + } + if (args.margin != null) { + margin = args.margin; + } + if (args.width != null) { + width = args.width; + } + } -DataCollectionWidget.prototype.getMacromolecule = function(data) { - for (var i = 0; i < data.length; i++) { - if (data[i].macromoleculeId != null) { - return BIOSAXS.proposal.getMacromoleculeById(data[i].macromoleculeId); + var store = Ext.create('Ext.data.Store', { + fields : [ 'macromoleculeId', 'acronym' ], + data : macromolecules, + sorters : [ 'acronym' ] + }); + + return Ext.create('Ext.form.ComboBox', { + fieldLabel : 'Macromolecules', + labelWidth : labelWidth, + width : width, + margin : margin, + store : store, + editable: false, + queryMode : 'local', + displayField : 'acronym', + valueField : 'macromoleculeId' + }); + }, + getComboBuffers : function(buffers, args) { + var labelWidth = 150; + var margin = "0 0 5 0"; + var width = 300; + var fieldLabel = 'Buffer'; + + if (args != null) { + if (args.labelWidth != null) { + labelWidth = args.labelWidth; + } + if (args.margin != null) { + margin = args.margin; + } + if (args.width != null) { + width = args.width; + } + if (args.noLabel != null) { + fieldLabel = null; + } } - } - return null; -}; -DataCollectionWidget.prototype.getMacromoleculeContainer = function(data) { - var macromolecule = this.getMacromolecule(data); + var store = Ext.create('Ext.data.Store', { + fields : [ 'bufferId', 'acronym' ], + data : buffers, + sorters : [ 'acronym' ] + }); - var disabled = false; - if (macromolecule == null) { - disabled = true; - } + return Ext.create('Ext.form.ComboBox', { + fieldLabel : fieldLabel, + labelWidth : labelWidth, + width : width, + margin : margin, + editable: false, + store : store, + queryMode : 'local', + displayField : 'acronym', + valueField : 'bufferId' + }); + }, + getComboSessions : function(sessions, args) { + var labelWidth = 150; + var margin = "0 0 5 0"; + var width = 300; - var acronym = (macromolecule == null ? "" : macromolecule.acronym); - var mm = (macromolecule == null ? "" : macromolecule.molecularMass); - var comments = (macromolecule == null ? "" : macromolecule.comments); + if (args != null) { + if (args.labelWidth != null) { + labelWidth = args.labelWidth; + } + if (args.margin != null) { + margin = args.margin; + } + if (args.width != null) { + width = args.width; + } + } - return Ext.create('Ext.Panel', { - width : 500, - height : 200, - disabled : disabled, - title : "Macromolecule", - layout : 'form', - bodyPadding : 5, - defaultType : 'textfield', - tbar : { - defaultButtonUI : 'default', - items : [ { - xtype : 'button', - text : 'Edit', - cls : 'btn-with-border', - handler : function() { - var macromoleculeWindow = new MacromoleculeWindow(); - macromoleculeWindow.draw(macromolecule); - /** TODO: update when save **/ - } - } ] - }, - items : [ { - fieldLabel : 'Acronym', - name : 'first', - readOnly : true, - value : acronym - }, { - fieldLabel : 'Molecular Mass', - name : 'last', - readOnly : true, - value : mm - }, { - xtype : 'textareafield', - fieldLabel : 'Comments', - value : '', - name : 'last', - readOnly : true, - value : comments - } ] - }); -}; + for ( var i = 0; i < sessions.length; i++) { + sessions[i]["startDateFormatted"] = moment(sessions[i].startDate).format("MMM Do YY"); + sessions[i]["sorter"] = moment(sessions[i].startDate).format("YYYYMMDD"); + } -DataCollectionWidget.prototype.getBufferContainer = function(data) { - var _this = this; - var buffer = BIOSAXS.proposal.getBufferById(data[0].bufferId); + var store = Ext.create('Ext.data.Store', { + fields : [ 'sessionId', 'startDateFormatted', 'beamlineName', 'startDate', 'endDate', 'beamlineOperator' ], + data : sessions, + sorters : [ 'sorter' ] + }); - return Ext.create('Ext.Panel', { - width : 500, - height : 200, - title : "Buffer", - layout : 'form', - // collapsed : true, - // collapsible : true, - bodyPadding : 5, - defaultType : 'textfield', - style : { - padding : '0px 0px 0px 2px' - }, - tbar : { - defaultButtonUI : 'default', - items : [ { - xtype : 'button', - text : 'Edit', - cls : 'btn-with-border', - handler : function() { - var bufferWindow = new BufferWindow(); - bufferWindow.draw(BIOSAXS.proposal.getBufferById(data[0].bufferId)); - /** TODO: update when save **/ - } + return Ext.create('Ext.form.ComboBox', { + fieldLabel : 'Sessions', + labelWidth : labelWidth, + width : width, + margin : margin, + store : store, + queryMode : 'local', + // displayField : 'startDate', + valueField : 'sessionId', + // Template for the dropdown menu. + // Note the use of "x-boundlist-item" class, + // this is required to make the items selectable. + tpl : Ext.create('Ext.XTemplate', '', + '
{startDateFormatted} {beamlineName}
', '
'), + // template for the content inside text field + displayTpl : Ext.create('Ext.XTemplate', '', '{startDateFormatted}', '') - } ] - }, - items : [ { - fieldLabel : 'Buffer', - name : 'acronym', - readOnly : true, - value : buffer.acronym - }, { - fieldLabel : 'Composition', - name : 'last', - readOnly : true, - value : buffer.composition + }); + } +}; + + function GenericWindow(args){ + this.title = "title"; + this.width = 700; + this.height = 500; + this.id = BUI.id(); + + this.close = false; + this.draggable = true; + this.modal = true; + + if (args != null){ + if (args.actions != null){ + this.actions = args.actions; + } + if (args.form != null){ + this.form = args.form; + } + if (args.width != null){ + this.width = args.width; + } + if (args.modal != null){ + this.modal = args.modal; + } + + if (args.height != null){ + this.height = args.height; + } + if (args.title != null){ + this.title = args.title; + } + if (args.form != null){ + this.form = args.form; + } + if (args.close != null){ + this.close = args.close; + } + if (args.draggable != null){ + this.draggable = args.draggable; + } + + } + /** Events **/ + this.onSaved = new Event(this); + }; + + + +GenericWindow.prototype.getButtons = function() { + var _this = this; + + if (this.close){ + return [ { + text : 'Close', + handler : function() { + _this.panel.close(); + } + } ]; + } + else{ + return [ { + text : 'Save', + handler : function() { + _this.save(); + + } }, { - xtype : 'textareafield', - fieldLabel : 'Comments', - value : buffer.comments, - name : 'last', - readOnly : true - - } ] - }); + text : 'Cancel', + handler : function() { + _this.panel.close(); + } + } ]; + } }; -DataCollectionWidget.prototype.getSpecimenContainer = function(data) { - return Ext.create('Ext.container.Container', { - layout : { - type : 'hbox' - }, - style : { - margin : '10px 10px 10px 10px' - }, - border : 0, - style : { - borderColor : '#000000', - borderStyle : 'solid', - borderWidth : '1px' - }, - defaults : { - labelWidth : 80, - // implicitly create Container by specifying xtype - xtype : 'datefield', - flex : 1, - - }, - items : [ this.getMacromoleculeContainer(data), this.getBufferContainer(data) ] - }); +GenericWindow.prototype.save = function (){ + alert("Method save of GenerciWindow class is abstract"); }; -DataCollectionWidget.prototype.getSeparator = function() { - return { - html : "
", - border : 0 - } +GenericWindow.prototype._postRender = function(data, experiment){ + }; -DataCollectionWidget.prototype.getFitStructurePanel = function(data) { - var _this = this; - - return Ext.create('Ext.container.Container', { - layout : { - type : 'hbox' - }, - style : { - margin : '10px 10px 10px 10px' - }, - border : 0, - style : { - borderColor : '#000000', - borderStyle : 'solid', - borderWidth : '1px' - }, - defaults : { - labelWidth : 80, - xtype : 'datefield', - flex : 1 - }, - items : [ - ] - }); +GenericWindow.prototype.draw = function (data, experiment){ + this._render(data, experiment); }; -DataCollectionWidget.prototype.getFitStructurePanelWorkflow = function(data) { - var _this = this; - var macromolecule = this.getMacromolecule(data); - - var items = []; +GenericWindow.prototype.refresh = function(data, experiment){ + this.data = data; + this.experiment = experiment; + this.form.refresh(data, experiment); +}; - /** Adding all the pdb files linked to the macromolecule **/ - // if (macromolecule.structure3VOs != null) { - // if (macromolecule.structure3VOs.length > 0) { - // - // items.push(); - // } - // } else { - // items.push({ - // html : "No apriori information added to this macromolecule. Apriori information needed for further analysis", - // margin : "5 5 5 5" - // }); - // } - var fitStructureToExperimentDataGrid = new FitStructureToExperimentDataGrid(); - - fitStructureToExperimentDataGrid.refresh(_this.data[0].subtractionId, _this.getMacromolecule(data)); - return Ext.create('Ext.container.Container', { - style : { - margin : '10px 10px 10px 10px' - }, - border : 0, +GenericWindow.prototype._render = function(data, experiment){ + this.data = data; + var _this = this; + if (this.panel == null){ + this.panel = Ext.create('Ext.Window', { + id : this.id, + title : this.title, + resizable : true, + constrain : true, + border : 1, + modal : this.modal, + frame : false, + draggable : this.draggable, + closable : true, + autoscroll : true, + layout : { type: 'vbox',align: 'stretch'}, + width : this.width, + height : this.form.height, + buttonAlign :'right', + buttons : this.getButtons(), + items : this.form.getPanel(data, experiment), + listeners: { + scope : this, + minimize : function(){ + this.panel.hide(); + }, + destroy : function(){ + delete this.panel; + } + } + }); + this.panel.setLoading(); + } + + this.panel.show(); + this._postRender(); +}; +function CalendarWidget(args) { + this.height = 740; - style : { - borderColor : '#000000', - borderStyle : 'solid', - borderWidth : '1px' - }, - defaults : { - labelWidth : 80, - xtype : 'datefield', - flex : 1 - }, - items : [ fitStructureToExperimentDataGrid.getPanel() ], - listeners : { - afterrender : function() { - } + if (args != null) { + if (args.height != null) { + this.height = args.height; } - }); -}; -/** Static method **/ -DataCollectionWidget.prototype.RUNFitScattering = function(subtractionId, structureId) { - var _this = this; - /** Add to Workflow **/ - var adapter = new BiosaxsDataAdapter(); - var workflow = { - 'workflowTitle' : 'FitExperimentalDatatoStructure', - 'comments' : 'FitExperimentalDatatoStructure run from ISPyB for subtractionId: ' + subtractionId + ' and structureId ' + structureId } - var inputs = [ { - name : 'subtractionId', - value : subtractionId - }, { - name : 'structureId', - value : structureId - } ]; + this.onClick = new Event(); +} - adapter.onSuccess.attach(function(sender, data) { - /** Add to Fit **/ - var adapter2 = new BiosaxsDataAdapter(); - var fit = { - 'workflowId' : data.workflowId, - 'subtractionId' : subtractionId, - 'structureId' : structureId, - 'comments' : 'Sent to workflow engine' - } - adapter2.onSuccess.attach(function(sender, fit) { +CalendarWidget.prototype.loadData = function(data) { + this.events = []; - }); - adapter2.addFitStructureData(fit); + for ( var i = 0; i < data.length; i++) { + var date = moment(data[i].creationDate); + var textColor = 'black'; - }); - adapter.addWorkflow(workflow, inputs); + if (data[i].status == "FINISHED") { + textColor = 'green'; + } + if (data[i].status == "ABORTED") { + textColor = 'red'; + } + this.events.push({ + title : data[i].name, + start : date.format("YYYY-MM-DD HH:mm:ss"), + end : date.format("YYYY-MM-DD HH:mm:ss"), + date : date, + allDay : false, + color : textColor, + className : date.format("YYYY-MM-DD") + }); + } }; -DataCollectionWidget.prototype.getSuperpositionTab = function(data) { +CalendarWidget.prototype.draw = function(targetId) { var _this = this; + $('#' + targetId).fullCalendar({ + eventClick : function(calEvent, jsEvent, view) { + _this.onClick.notify(calEvent.className[0]); - this.superpositionGrid = new SuperpositionGrid(); - - return Ext.create('Ext.container.Container', { - style : { - margin : '10px 10px 10px 10px' - }, - border : 0, - style : { - borderColor : '#000000', - borderStyle : 'solid', - borderWidth : '1px' - }, - items : [ _this.superpositionGrid.getPanel() ], - listeners : { - cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { - - }, - afterrender : function() { - /** Getting Rigid bodies **/ - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, result) { - _this.superpositionGrid.refresh(result); - }); - adapter.getSuperpositionBySubtractionId(data[1].subtractionId); - } - } - }); -}; - -DataCollectionWidget.prototype.getAdvancedTab = function(data) { - var _this = this; - return Ext.create('Ext.container.Container', { - style : { - margin : '10px 10px 10px 10px' }, - border : 0, - style : { - borderColor : '#000000', - borderStyle : 'solid', - borderWidth : '1px' + contentHeight : _this.height, + header : { + left : 'prev,next today', + center : 'title', + right : 'month,basicWeek,basicDay' }, - items : [ _this.getFitStructurePanel(data), _this.getFitStructurePanelWorkflow(data) ] + editable : false, + events : this.events }); -}; - -DataCollectionWidget.prototype.getRigiBodyForm = function(data) { - var _this = this; - _this.rigiBodyGrid = new RigidModelGrid(); - return _this.rigiBodyGrid.getPanel(); }; + +/** + * It shows a table with the guinier and gnom data as well as passed and + * discarded measurements + * + * @height + * @showBufferColumns + */ +function ConcentrationHTMLTableWidget(args) { + this.id = BUI.id(); -DataCollectionWidget.prototype.getRigidBodyModelingTab = function(data) { - var _this = this; - return Ext.create('Ext.container.Container', { - style : { - margin : '10px 10px 10px 10px' - }, - border : 0, - style : { - borderColor : '#000000', - borderStyle : 'solid', - borderWidth : '1px' - }, - items : [ _this.getRigiBodyForm(data) ], - listeners : { - cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + this.showBufferColumns = true; - }, - afterrender : function() { - /** Getting Rigid bodies **/ - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, result) { - _this.rigiBodyGrid.refresh(result); - }); - adapter.getRigidBodyModelingBySubtractionId(data[1].subtractionId); - } + if (args != null) { + if (args.height != null) { + this.height = args.height; } - }); -}; - -DataCollectionWidget.prototype.getTabs = function(data) { - var _this = this; - - this.subtractionCurveVisualizer = new SubtractionCurveVisualizer(); - - this.tabs = Ext.createWidget('tabpanel', { - activeTab : 1, - plain : true, - defaults : { - autoScroll : true, - bodyPadding : 10 - }, - items : [ { - title : 'Solution', - items : [ _this.getSpecimenContainer(data) ] - }, { - title : 'Primary Data Reduction', - active : true, - tabConfig : { - xtype : 'tab', - margins : '0 0 0 20', - }, - items : [ _this.subtractionCurveVisualizer.getPanel() ] - }, { - title : 'Abinitio', - items : [ _this.getAbinitioModellingContainer(data), _this.getSeparator(), _this.getModelViz(data) ] - }, { - title : 'Mixtures', - items : [ _this.getAdvancedTab(data) ] - }, { - title : 'Superpositions', - items : [ _this.getSuperpositionTab(data) ] - }, { - title : 'Rigid Body Modeling', - items : [ _this.getRigidBodyModelingTab(data) ] - } ] - }); - return this.tabs; -}; + if (args.showBufferColumns != null) { + this.showBufferColumns = args.showBufferColumns; + } + } +} -DataCollectionWidget.prototype.getPanel = function(data) { - var _this = this; - _this.data = data; - this.panel = Ext.create('Ext.container.Container', { - width : 1000, - layout : { - type : 'vbox', - align : 'stretch', - padding : 5 - }, - items : [ _this.getTabs(data) +ConcentrationHTMLTableWidget.prototype.refresh = function(parsedData) { + document.getElementById(this.id).innerHTML = this.getPanel(parsedData); +}; - ] - }); +ConcentrationHTMLTableWidget.prototype.getPanel = function(parsedData) { + var html = "
"; + html = html + ""; - this.panel.on("afterrender", function() { - _this.subtractionCurveVisualizer.refresh([ _this.data[1].mergeId ], [ _this.data[1].subtractionId ]); - }); - return this.panel; -}; + /** Title * */ + html = html + ""; + html = html + ""; + if (this.showBufferColumns) { + html = html + ""; + } + html = html + ""; + html = html + ""; + html = html + ""; + html = html + ""; -DataCollectionWidget.prototype.getModelViz = function(data) { - this.modelViz = new ModelVisualizerForm({ - height : 800, - width : 1000 - }); - return this.modelViz.getPanel(); -}; + html = html + ""; + html = html + ""; + html = html + ""; + html = html + ""; + html = html + ""; + html = html + ""; + html = html + ""; + html = html + ""; + html = html + ""; -/** - * PRIMARY DATA PROCESSING - */ -DataCollectionWidget.prototype.getPrimaryDataProcessingContainer = function(data) { - var _this = this; - this.primaryDataProcessingGrid = new PrimaryDataProcessingGrid({ - height : 250, - width : 1000, - title : 'Primary Data Processing' + parsedData.concentration.concentrations.sort(function(a, b) { + return a.concentration - b.concentration; }); + /** Row * */ + for ( var i = 0; i < parsedData.concentration.concentrations.length; i++) { + var row = parsedData[i]; - this.primaryDataProcessingGrid.onSelected.attach(function(sender, selected) { - /** Refresh tabs **/ - var averagesId = []; - var subtractionIds = []; - - var subtractionKeys = {}; - for (var i = 0; i < selected.length; i++) { - if (selected[i].mergeId != null) { - averagesId.push(selected[i].mergeId); - } + var tr = ""; + if (i % 2 == 1) { + tr = ""; + } + var goodMeasurements = (parsedData.concentration.concentrations[i].frames.length - parsedData.concentration.concentrations[i].frames_warning); + if (goodMeasurements == 0) { + tr = ""; + } + html = html + tr; + html = html + ""; + if (this.showBufferColumns) { + html = html + ""; + } + html = html + ""; + html = html + ""; + html = html + ""; + html = html + ""; + html = html + ""; + html = html + ""; + html = html + ""; + html = html + ""; + } + return html + "
ConcentrationBufferMeasurementsGuinierGnom
PassedDiscardedRgI0QualityRgdMax
" + parsedData.concentration.concentrations[i].concentration + "" + parsedData.concentration.concentrations[i].bufferAcronym + "" + goodMeasurements + " of " + parsedData.concentration.concentrations[i].frames.length + "" + parsedData.concentration.concentrations[i].frames_warning + "" + BUI.formatValuesErrorUnits(parsedData.concentration.concentrations[i].calculation.rgGuinier.avg, parsedData.concentration.concentrations[i].calculation.rgGuinier.std, "nm", { + lineBreak : false + }) + "" + BUI.formatValuesErrorUnits(parsedData.concentration.concentrations[i].calculation.i0Guinier.avg, parsedData.concentration.concentrations[i].calculation.i0Guinier.std, " ", { + lineBreak : false + }) + "" + BUI.formatValuesErrorUnits(parsedData.concentration.concentrations[i].calculation.quality.avg, parsedData.concentration.concentrations[i].calculation.quality.std, " ", { + lineBreak : false + }) + "" + BUI.formatValuesErrorUnits(parsedData.concentration.concentrations[i].calculation.rgGnom.avg, parsedData.concentration.concentrations[i].calculation.rgGnom.std, " ", { + lineBreak : false + }) + "" + BUI.formatValuesErrorUnits(parsedData.concentration.concentrations[i].calculation.dMax.avg, parsedData.concentration.concentrations[i].calculation.dMax.std, " ", { + lineBreak : false + }) + "
"; +}; - if (selected[i].subtractionId != null) { - if (selected[i].macromoleculeId != null) { - if (subtractionKeys[selected[i].subtractionId] == null) { - subtractionIds.push(selected[i].subtractionId); +ConcentrationHTMLTableWidget.prototype.input = function() { + return { + data : { + "dataCollectionCount" : 4, + "buffers" : [ { + "bufferId" : "422" + } ], + "concentration" : { + "concentrations" : [ { + "concentration" : "7.17", + "id" : "7.1699999999999999", + "bufferId" : "422.00", + "bufferAcronym" : "B1", + "rgGuinier" : [ "5.12723" ], + "frames" : [ { + "total" : "0.515705406286", + "bufferBeforeMeasurementId" : 15045, + "sampleMergeId" : 8176, + "averagedModelId" : null, + "I0" : "183.457461646", + "proposalCode" : "OPD", + "averagedModel" : null, + "bufferAfterMergeId" : 8177, + "framesCount" : "10", + "bufferBeforeMergeId" : 8175, + "averageFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_065_ave.dat", + "bufferAfterMeasurementId" : 15048, + "rgGuinier" : "5.12723", + "subtractedFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_065_sub.dat", + "firstPointUsed" : "17", + "chi2RgFilePath" : null, + "abInitioModelId" : null, + "macromoleculeId" : 649, + "code" : "_4.0_7.17", + "transmission" : "100.0", + "timeStart" : "2013-06-05 15:43:54.348723", + "bufferAcronym" : "B1", + "quality" : "0.853011", + "shapeDeterminationModelId" : null, + "expermientComments" : "[BsxCube] Generated from BsxCube", + "bufferId" : 422, + "isagregated" : "False", + "dmax" : "25.63615", + "bufferBeforeAverageFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_064_ave.dat", + "exposureTemperature" : "4.0", + "subtractionId" : 3377, + "conc" : "7.1699999999999999", + "rapidShapeDeterminationModel" : null, + "experimentCreationDate" : "Jun 5, 2013 3:30:30 PM", + "lastPointUsed" : "36", + "modelListId" : null, + "framesMerge" : "9", + "rapidShapeDeterminationModelId" : null, + "bufferAfterAverageFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_066_ave.dat", + "nsdFilePath" : null, + "bufferAfterFramesMerged" : "10", + "experimentId" : 700, + "macromoleculeAcronym" : "L9", + "i0stdev" : "0.139364435146", + "sampleMeasurementId" : 15047, + "measurementComments" : "[5]", + "priorityLevelId" : 10, + "bufferBeforeFramesMerged" : "10", + "substractionCreationTime" : "Jun 5, 2013 3:46:31 PM", + "proposalNumber" : "29", + "rgGnom" : "5.40297914939", + "volume" : "475.302", + "shapeDeterminationModel" : null, + "comments" : null, + "groupeField" : "L9 B1" + } ], + "frames_warning" : 0, + "calculation" : { + "rgGuinier" : { + "std" : 0, + "sum" : 5.12723, + "avg" : 5.12723, + "validNumber" : 1, + "totalNumber" : 1, + "values" : [ "5.12723" ] + }, + "i0Guinier" : { + "std" : 0, + "sum" : 183.457461646, + "avg" : 183.457461646, + "validNumber" : 1, + "totalNumber" : 1, + "values" : [ "183.457461646" ] + }, + "quality" : { + "std" : 0, + "sum" : 0.853011, + "avg" : 0.853011, + "validNumber" : 1, + "totalNumber" : 1, + "values" : [ "0.853011" ] + }, + "rgGnom" : { + "std" : 0, + "sum" : 5.40297914939, + "avg" : 5.40297914939, + "validNumber" : 1, + "totalNumber" : 1, + "values" : [ "5.40297914939" ] + }, + "dMax" : { + "std" : 0, + "sum" : 25.63615, + "avg" : 25.63615, + "validNumber" : 1, + "totalNumber" : 1, + "values" : [ "25.63615" ] + } + } + }, { + "concentration" : "3.53", + "id" : "3.5299999999999998", + "bufferId" : "422.00", + "bufferAcronym" : "B1", + "rgGuinier" : [ "4.38278" ], + "frames" : [ { + "total" : "0.497164741078", + "bufferBeforeMeasurementId" : 15048, + "sampleMergeId" : 8178, + "averagedModelId" : null, + "I0" : "160.023229462", + "proposalCode" : "OPD", + "averagedModel" : null, + "bufferAfterMergeId" : 8179, + "framesCount" : "10", + "bufferBeforeMergeId" : 8177, + "averageFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_067_ave.dat", + "bufferAfterMeasurementId" : 15051, + "rgGuinier" : "4.38278", + "subtractedFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_067_sub.dat", + "firstPointUsed" : "36", + "chi2RgFilePath" : null, + "abInitioModelId" : null, + "macromoleculeId" : 649, + "code" : "_4.0_3.53", + "transmission" : "100.0", + "timeStart" : "2013-06-05 15:47:04.630129", + "bufferAcronym" : "B1", + "quality" : "0.742825", + "shapeDeterminationModelId" : null, + "expermientComments" : "[BsxCube] Generated from BsxCube", + "bufferId" : 422, + "isagregated" : "False", + "dmax" : "15.33973", + "bufferBeforeAverageFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_066_ave.dat", + "exposureTemperature" : "4.0", + "subtractionId" : 3378, + "conc" : "3.5299999999999998", + "rapidShapeDeterminationModel" : null, + "experimentCreationDate" : "Jun 5, 2013 3:30:30 PM", + "lastPointUsed" : "61", + "modelListId" : null, + "framesMerge" : "10", + "rapidShapeDeterminationModelId" : null, + "bufferAfterAverageFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_068_ave.dat", + "nsdFilePath" : null, + "bufferAfterFramesMerged" : "10", + "experimentId" : 700, + "macromoleculeAcronym" : "L9", + "i0stdev" : "0.1499898017", + "sampleMeasurementId" : 15050, + "measurementComments" : "[6]", + "priorityLevelId" : 12, + "bufferBeforeFramesMerged" : "10", + "substractionCreationTime" : "Jun 5, 2013 3:49:42 PM", + "proposalNumber" : "29", + "rgGnom" : "4.40615474861", + "volume" : "372.656", + "shapeDeterminationModel" : null, + "comments" : null, + "groupeField" : "L9 B1" + } ], + "frames_warning" : 0, + "calculation" : { + "rgGuinier" : { + "std" : 0, + "sum" : 4.38278, + "avg" : 4.38278, + "validNumber" : 1, + "totalNumber" : 1, + "values" : [ "4.38278" ] + }, + "i0Guinier" : { + "std" : 0, + "sum" : 160.023229462, + "avg" : 160.023229462, + "validNumber" : 1, + "totalNumber" : 1, + "values" : [ "160.023229462" ] + }, + "quality" : { + "std" : 0, + "sum" : 0.742825, + "avg" : 0.742825, + "validNumber" : 1, + "totalNumber" : 1, + "values" : [ "0.742825" ] + }, + "rgGnom" : { + "std" : 0, + "sum" : 4.40615474861, + "avg" : 4.40615474861, + "validNumber" : 1, + "totalNumber" : 1, + "values" : [ "4.40615474861" ] + }, + "dMax" : { + "std" : 0, + "sum" : 15.33973, + "avg" : 15.33973, + "validNumber" : 1, + "totalNumber" : 1, + "values" : [ "15.33973" ] + } + } + }, { + "concentration" : "1.75", + "id" : "1.75", + "bufferId" : "422.00", + "bufferAcronym" : "B1", + "rgGuinier" : [], + "frames" : [ { + "total" : "0.5538035065", + "bufferBeforeMeasurementId" : 15051, + "sampleMergeId" : 8180, + "averagedModelId" : null, + "I0" : "146.927428571", + "proposalCode" : "OPD", + "averagedModel" : null, + "bufferAfterMergeId" : 8181, + "framesCount" : "10", + "bufferBeforeMergeId" : 8179, + "averageFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_069_ave.dat", + "bufferAfterMeasurementId" : 15054, + "rgGuinier" : "4.27139", + "subtractedFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_069_sub.dat", + "firstPointUsed" : "33", + "chi2RgFilePath" : null, + "abInitioModelId" : null, + "macromoleculeId" : 649, + "code" : "_4.0_1.75", + "transmission" : "100.0", + "timeStart" : "2013-06-05 15:50:09.395824", + "bufferAcronym" : "B1", + "quality" : "0.765999", + "shapeDeterminationModelId" : null, + "expermientComments" : "[BsxCube] Generated from BsxCube", + "bufferId" : 422, + "isagregated" : "False", + "dmax" : "14.949865", + "bufferBeforeAverageFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_068_ave.dat", + "exposureTemperature" : "4.0", + "subtractionId" : 3379, + "conc" : "1.75", + "rapidShapeDeterminationModel" : null, + "experimentCreationDate" : "Jun 5, 2013 3:30:30 PM", + "lastPointUsed" : "62", + "modelListId" : null, + "framesMerge" : "6", + "rapidShapeDeterminationModelId" : null, + "bufferAfterAverageFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_070_ave.dat", + "nsdFilePath" : null, + "bufferAfterFramesMerged" : "10", + "experimentId" : 700, + "macromoleculeAcronym" : "L9", + "i0stdev" : "0.191914285714", + "sampleMeasurementId" : 15053, + "measurementComments" : "[7]", + "priorityLevelId" : 14, + "bufferBeforeFramesMerged" : "10", + "substractionCreationTime" : "Jun 5, 2013 3:52:31 PM", + "proposalNumber" : "29", + "rgGnom" : "4.28379338749", + "volume" : "356.326", + "shapeDeterminationModel" : null, + "comments" : null, + "groupeField" : "L9 B1" + } ], + "frames_warning" : 1, + "calculation" : { + "rgGuinier" : { + "std" : null, + "sum" : 0, + "avg" : 0, + "validNumber" : 0, + "totalNumber" : 0, + "values" : [] + }, + "i0Guinier" : { + "std" : null, + "sum" : 0, + "avg" : 0, + "validNumber" : 0, + "totalNumber" : 0, + "values" : [] + }, + "quality" : { + "std" : null, + "sum" : 0, + "avg" : 0, + "validNumber" : 0, + "totalNumber" : 0, + "values" : [] + }, + "rgGnom" : { + "std" : null, + "sum" : 0, + "avg" : 0, + "validNumber" : 0, + "totalNumber" : 0, + "values" : [] + }, + "dMax" : { + "std" : null, + "sum" : 0, + "avg" : 0, + "validNumber" : 0, + "totalNumber" : 0, + "values" : [] + } + } + }, { + "concentration" : "0.91", + "id" : "0.91000000000000003", + "bufferId" : "422.00", + "bufferAcronym" : "B1", + "rgGuinier" : [], + "frames" : [ { + "total" : null, + "bufferBeforeMeasurementId" : 15054, + "sampleMergeId" : 8182, + "averagedModelId" : null, + "I0" : "0.0", + "proposalCode" : "OPD", + "averagedModel" : null, + "bufferAfterMergeId" : 8183, + "framesCount" : "10", + "bufferBeforeMergeId" : 8181, + "averageFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_071_ave.dat", + "bufferAfterMeasurementId" : 15057, + "rgGuinier" : null, + "subtractedFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_071_sub.dat", + "firstPointUsed" : "0", + "chi2RgFilePath" : null, + "abInitioModelId" : null, + "macromoleculeId" : 649, + "code" : "_4.0_.91", + "transmission" : "100.0", + "timeStart" : "2013-06-05 15:52:58.133705", + "bufferAcronym" : "B1", + "quality" : "0.0", + "shapeDeterminationModelId" : null, + "expermientComments" : "[BsxCube] Generated from BsxCube", + "bufferId" : 422, + "isagregated" : "False", + "dmax" : null, + "bufferBeforeAverageFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_070_ave.dat", + "exposureTemperature" : "4.0", + "subtractionId" : 3380, + "conc" : "0.91000000000000003", + "rapidShapeDeterminationModel" : null, + "experimentCreationDate" : "Jun 5, 2013 3:30:30 PM", + "lastPointUsed" : "0", + "modelListId" : null, + "framesMerge" : "10", + "rapidShapeDeterminationModelId" : null, + "bufferAfterAverageFilePath" : " /data/pyarch/bm29/opd29/700/1d/data_072_ave.dat", + "nsdFilePath" : null, + "bufferAfterFramesMerged" : "10", + "experimentId" : 700, + "macromoleculeAcronym" : "L9", + "i0stdev" : "0.0", + "sampleMeasurementId" : 15056, + "measurementComments" : "[8]", + "priorityLevelId" : 16, + "bufferBeforeFramesMerged" : "10", + "substractionCreationTime" : "Jun 5, 2013 3:55:35 PM", + "proposalNumber" : "29", + "rgGnom" : null, + "volume" : null, + "shapeDeterminationModel" : null, + "comments" : null, + "groupeField" : "L9 B1" + } ], + "frames_warning" : 1, + "calculation" : { + "rgGuinier" : { + "std" : null, + "sum" : 0, + "avg" : 0, + "validNumber" : 0, + "totalNumber" : 0, + "values" : [] + }, + "i0Guinier" : { + "std" : null, + "sum" : 0, + "avg" : 0, + "validNumber" : 0, + "totalNumber" : 0, + "values" : [] + }, + "quality" : { + "std" : null, + "sum" : 0, + "avg" : 0, + "validNumber" : 0, + "totalNumber" : 0, + "values" : [] + }, + "rgGnom" : { + "std" : null, + "sum" : 0, + "avg" : 0, + "validNumber" : 0, + "totalNumber" : 0, + "values" : [] + }, + "dMax" : { + "std" : null, + "sum" : 0, + "avg" : 0, + "validNumber" : 0, + "totalNumber" : 0, + "values" : [] + } } - subtractionKeys[selected[i].subtractionId] = true; - } - } + } ] + }, + "concentrationLabel" : "7.17 mg/ml ,3.53 mg/ml ,1.75 mg/ml ,0.91 mg/ml " } - - /** Refreshing Primary Data Reduction **/ - _this.subtractionCurveVisualizer.refresh(averagesId, subtractionIds); - - }); - return this.primaryDataProcessingGrid.getPanel(data); + }; }; -/** - * getAbinitioModellingContainer - */ -DataCollectionWidget.prototype.getAbinitioModellingContainer = function(data) { - var _this = this; - - this.abinitioGrid = new AbinitioGrid(); - this.abinitioGrid.onSelected.attach(function(sender, models) { - _this.modelViz.refresh(models); - - }); - /** It may be abinitio models linked to the buffers **/ - var abinitioIdList = []; - for (var i = 0; i < data.length; i++) { - abinitioIdList.push(data[i].abInitioId); - } - - var uniqueIds = []; - $.each(abinitioIdList, function(i, el) { - if ($.inArray(el, uniqueIds) === -1) - uniqueIds.push(el); - }); - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, data) { - _this.abinitioGrid.refresh(data); - }); - adapter.getAbinitioByIdsList(uniqueIds); - return this.abinitioGrid.getPanel([]); +ConcentrationHTMLTableWidget.prototype.test = function(targetId) { + var concentrationHTMLTableWidget = new ConcentrationHTMLTableWidget(); + document.getElementById(targetId).innerHTML = concentrationHTMLTableWidget.getPanel(concentrationHTMLTableWidget.input().data); }; +function DataCollectionWidget() { + +}; + +DataCollectionWidget.prototype.refresh = function(subtractionId) { + +}; + +DataCollectionWidget.prototype.getMacromolecule = function(data) { + for (var i = 0; i < data.length; i++) { + if (data[i].macromoleculeId != null) { + return BIOSAXS.proposal.getMacromoleculeById(data[i].macromoleculeId); + } + } + return null; +}; + +DataCollectionWidget.prototype.getMacromoleculeContainer = function(data) { + var macromolecule = this.getMacromolecule(data); + + var disabled = false; + if (macromolecule == null) { + disabled = true; + } + + var acronym = (macromolecule == null ? "" : macromolecule.acronym); + var mm = (macromolecule == null ? "" : macromolecule.molecularMass); + var comments = (macromolecule == null ? "" : macromolecule.comments); + + return Ext.create('Ext.Panel', { + width : 500, + height : 200, + disabled : disabled, + title : "Macromolecule", + layout : 'form', + bodyPadding : 5, + defaultType : 'textfield', + tbar : { + defaultButtonUI : 'default', + items : [ { + xtype : 'button', + text : 'Edit', + cls : 'btn-with-border', + handler : function() { + var macromoleculeWindow = new MacromoleculeWindow(); + macromoleculeWindow.draw(macromolecule); + /** TODO: update when save **/ + } + } ] + }, + items : [ { + fieldLabel : 'Acronym', + name : 'first', + readOnly : true, + value : acronym + }, { + fieldLabel : 'Molecular Mass', + name : 'last', + readOnly : true, + value : mm + }, { + xtype : 'textareafield', + fieldLabel : 'Comments', + value : '', + name : 'last', + readOnly : true, + value : comments + } ] + }); +}; + +DataCollectionWidget.prototype.getBufferContainer = function(data) { + var _this = this; + var buffer = BIOSAXS.proposal.getBufferById(data[0].bufferId); + + return Ext.create('Ext.Panel', { + width : 500, + height : 200, + title : "Buffer", + layout : 'form', + // collapsed : true, + // collapsible : true, + bodyPadding : 5, + defaultType : 'textfield', + style : { + padding : '0px 0px 0px 2px' + }, + tbar : { + defaultButtonUI : 'default', + items : [ { + xtype : 'button', + text : 'Edit', + cls : 'btn-with-border', + handler : function() { + var bufferWindow = new BufferWindow(); + bufferWindow.draw(BIOSAXS.proposal.getBufferById(data[0].bufferId)); + /** TODO: update when save **/ + } + + } ] + }, + items : [ { + fieldLabel : 'Buffer', + name : 'acronym', + readOnly : true, + value : buffer.acronym + }, { + fieldLabel : 'Composition', + name : 'last', + readOnly : true, + value : buffer.composition + }, { + xtype : 'textareafield', + fieldLabel : 'Comments', + value : buffer.comments, + name : 'last', + readOnly : true + + } ] + }); +}; + +DataCollectionWidget.prototype.getSpecimenContainer = function(data) { + return Ext.create('Ext.container.Container', { + layout : { + type : 'hbox' + }, + style : { + margin : '10px 10px 10px 10px' + }, + border : 0, + style : { + borderColor : '#000000', + borderStyle : 'solid', + borderWidth : '1px' + }, + defaults : { + labelWidth : 80, + // implicitly create Container by specifying xtype + xtype : 'datefield', + flex : 1, + + }, + items : [ this.getMacromoleculeContainer(data), this.getBufferContainer(data) ] + }); +}; + +DataCollectionWidget.prototype.getSeparator = function() { + return { + html : "
", + border : 0 + } +}; + +DataCollectionWidget.prototype.getFitStructurePanel = function(data) { + var _this = this; + + return Ext.create('Ext.container.Container', { + layout : { + type : 'hbox' + }, + style : { + margin : '10px 10px 10px 10px' + }, + border : 0, + style : { + borderColor : '#000000', + borderStyle : 'solid', + borderWidth : '1px' + }, + defaults : { + labelWidth : 80, + xtype : 'datefield', + flex : 1 + }, + items : [ + + ] + }); +}; + +DataCollectionWidget.prototype.getFitStructurePanelWorkflow = function(data) { + var _this = this; + var macromolecule = this.getMacromolecule(data); + + var items = []; + + /** Adding all the pdb files linked to the macromolecule **/ + // if (macromolecule.structure3VOs != null) { + // if (macromolecule.structure3VOs.length > 0) { + // + // items.push(); + // } + // } else { + // items.push({ + // html : "No apriori information added to this macromolecule. Apriori information needed for further analysis", + // margin : "5 5 5 5" + // }); + // } + var fitStructureToExperimentDataGrid = new FitStructureToExperimentDataGrid(); + + fitStructureToExperimentDataGrid.refresh(_this.data[0].subtractionId, _this.getMacromolecule(data)); + return Ext.create('Ext.container.Container', { + style : { + margin : '10px 10px 10px 10px' + }, + border : 0, + + style : { + borderColor : '#000000', + borderStyle : 'solid', + borderWidth : '1px' + }, + defaults : { + labelWidth : 80, + xtype : 'datefield', + flex : 1 + }, + items : [ fitStructureToExperimentDataGrid.getPanel() ], + listeners : { + afterrender : function() { + } + } + }); +}; +/** Static method **/ +DataCollectionWidget.prototype.RUNFitScattering = function(subtractionId, structureId) { + var _this = this; + /** Add to Workflow **/ + var adapter = new BiosaxsDataAdapter(); + var workflow = { + 'workflowTitle' : 'FitExperimentalDatatoStructure', + 'comments' : 'FitExperimentalDatatoStructure run from ISPyB for subtractionId: ' + subtractionId + ' and structureId ' + structureId + } + + var inputs = [ { + name : 'subtractionId', + value : subtractionId + }, { + name : 'structureId', + value : structureId + } ]; + + adapter.onSuccess.attach(function(sender, data) { + /** Add to Fit **/ + var adapter2 = new BiosaxsDataAdapter(); + var fit = { + 'workflowId' : data.workflowId, + 'subtractionId' : subtractionId, + 'structureId' : structureId, + 'comments' : 'Sent to workflow engine' + } + adapter2.onSuccess.attach(function(sender, fit) { + + }); + adapter2.addFitStructureData(fit); + + }); + adapter.addWorkflow(workflow, inputs); + +}; + +DataCollectionWidget.prototype.getSuperpositionTab = function(data) { + var _this = this; + + this.superpositionGrid = new SuperpositionGrid(); + + return Ext.create('Ext.container.Container', { + style : { + margin : '10px 10px 10px 10px' + }, + border : 0, + style : { + borderColor : '#000000', + borderStyle : 'solid', + borderWidth : '1px' + }, + items : [ _this.superpositionGrid.getPanel() ], + listeners : { + cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + + }, + afterrender : function() { + /** Getting Rigid bodies **/ + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, result) { + _this.superpositionGrid.refresh(result); + }); + adapter.getSuperpositionBySubtractionId(data[1].subtractionId); + } + } + }); +}; + +DataCollectionWidget.prototype.getAdvancedTab = function(data) { + var _this = this; + return Ext.create('Ext.container.Container', { + style : { + margin : '10px 10px 10px 10px' + }, + border : 0, + style : { + borderColor : '#000000', + borderStyle : 'solid', + borderWidth : '1px' + }, + items : [ _this.getFitStructurePanel(data), _this.getFitStructurePanelWorkflow(data) ] + }); +}; + +DataCollectionWidget.prototype.getRigiBodyForm = function(data) { + var _this = this; + _this.rigiBodyGrid = new RigidModelGrid(); + return _this.rigiBodyGrid.getPanel(); + +}; + +DataCollectionWidget.prototype.getRigidBodyModelingTab = function(data) { + var _this = this; + return Ext.create('Ext.container.Container', { + style : { + margin : '10px 10px 10px 10px' + }, + border : 0, + style : { + borderColor : '#000000', + borderStyle : 'solid', + borderWidth : '1px' + }, + items : [ _this.getRigiBodyForm(data) ], + listeners : { + cellclick : function(grid, td, cellIndex, record, tr, rowIndex, e, eOpts) { + + }, + afterrender : function() { + /** Getting Rigid bodies **/ + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, result) { + _this.rigiBodyGrid.refresh(result); + }); + adapter.getRigidBodyModelingBySubtractionId(data[1].subtractionId); + } + } + }); +}; + +DataCollectionWidget.prototype.getTabs = function(data) { + var _this = this; + + this.subtractionCurveVisualizer = new SubtractionCurveVisualizer(); + + this.tabs = Ext.createWidget('tabpanel', { + activeTab : 1, + plain : true, + defaults : { + autoScroll : true, + bodyPadding : 10 + }, + items : [ { + title : 'Solution', + items : [ _this.getSpecimenContainer(data) ] + }, { + title : 'Primary Data Reduction', + active : true, + tabConfig : { + xtype : 'tab', + margins : '0 0 0 20', + }, + items : [ _this.subtractionCurveVisualizer.getPanel() ] + }, { + title : 'Abinitio', + items : [ _this.getAbinitioModellingContainer(data), _this.getSeparator(), _this.getModelViz(data) ] + }, { + title : 'Mixtures', + items : [ _this.getAdvancedTab(data) ] + }, { + title : 'Superpositions', + items : [ _this.getSuperpositionTab(data) ] + }, { + title : 'Rigid Body Modeling', + items : [ _this.getRigidBodyModelingTab(data) ] + } ] + }); + return this.tabs; +}; + +DataCollectionWidget.prototype.getPanel = function(data) { + var _this = this; + _this.data = data; + this.panel = Ext.create('Ext.container.Container', { + width : 1000, + layout : { + type : 'vbox', + align : 'stretch', + padding : 5 + }, + items : [ _this.getTabs(data) + + ] + }); + + this.panel.on("afterrender", function() { + _this.subtractionCurveVisualizer.refresh([ _this.data[1].mergeId ], [ _this.data[1].subtractionId ]); + }); + return this.panel; +}; + +DataCollectionWidget.prototype.getModelViz = function(data) { + this.modelViz = new ModelVisualizerForm({ + height : 800, + width : 1000 + }); + return this.modelViz.getPanel(); +}; + +/** + * PRIMARY DATA PROCESSING + */ +DataCollectionWidget.prototype.getPrimaryDataProcessingContainer = function(data) { + var _this = this; + this.primaryDataProcessingGrid = new PrimaryDataProcessingGrid({ + height : 250, + width : 1000, + title : 'Primary Data Processing' + }); + + this.primaryDataProcessingGrid.onSelected.attach(function(sender, selected) { + /** Refresh tabs **/ + var averagesId = []; + var subtractionIds = []; + + var subtractionKeys = {}; + for (var i = 0; i < selected.length; i++) { + if (selected[i].mergeId != null) { + averagesId.push(selected[i].mergeId); + } + + if (selected[i].subtractionId != null) { + if (selected[i].macromoleculeId != null) { + if (subtractionKeys[selected[i].subtractionId] == null) { + subtractionIds.push(selected[i].subtractionId); + } + subtractionKeys[selected[i].subtractionId] = true; + } + } + } + + /** Refreshing Primary Data Reduction **/ + _this.subtractionCurveVisualizer.refresh(averagesId, subtractionIds); + + }); + return this.primaryDataProcessingGrid.getPanel(data); +}; +/** + * getAbinitioModellingContainer + */ +DataCollectionWidget.prototype.getAbinitioModellingContainer = function(data) { + var _this = this; + + this.abinitioGrid = new AbinitioGrid(); + this.abinitioGrid.onSelected.attach(function(sender, models) { + _this.modelViz.refresh(models); + + }); + /** It may be abinitio models linked to the buffers **/ + var abinitioIdList = []; + for (var i = 0; i < data.length; i++) { + abinitioIdList.push(data[i].abInitioId); + } + + var uniqueIds = []; + $.each(abinitioIdList, function(i, el) { + if ($.inArray(el, uniqueIds) === -1) + uniqueIds.push(el); + }); + + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, data) { + _this.abinitioGrid.refresh(data); + }); + adapter.getAbinitioByIdsList(uniqueIds); + return this.abinitioGrid.getPanel([]); +}; + /** * Edit the information of a buffer * @@ -29163,258 +29163,258 @@ ShippingWidget.prototype.refresh = function() { }; -/** - * Widget container of Specimen grid and samplePlate widget - * Depending of the sample changer layout it may be displayed vertically or horizontally - * - * @param args - * - * #onExperimentChanged It happens when specimen are modified - */ -function SpecimenWidget(args){ - - this.width = 1000; - this.height = 600; - - if (args != null){ - if (args.width != null){ - this.width = args.width; - } - if (args.height != null){ - this.height = args.height; - } - } - - var _this = this; - - /** Specimen Grid **/ - this.specimenGrid = new SpecimenGrid({ - minHeight : 425, - selectionMode : "SINGLE", - editEnabled : false, - updateRowEnabled : true, - width : 900, - showTitle : false - }); - - - this.specimenGrid.onSpecimenChanged.attach(function(sender, specimen) { - _this.experiment.setSpecimenById(specimen); - _this.refresh(_this.experiment); - }); - - this.specimenGrid.onSelected.attach(function(sender, specimens) { - if (specimens.length > 0) { - _this.specimenSelected = specimens[0]; - } else { - _this.specimenSelected = null; - } - _this.samplePlateGroupWidget.selectSpecimens(specimens); - }); - - - /** Sample plate Widget **/ - this.samplePlateGroupWidget = new SamplePlateGroupWidget({ - showTitle : false, - height : 250, - margin : 5, - bbar : true - }); - - - this.samplePlateGroupWidget.onExperimentChanged.attach(function(sender, json) { - _this.refresh(new Experiment(json)); - }); - - this.samplePlateGroupWidget.onClick.attach(function(sender, args) { - /** Clicking on a plate * */ - var row = args.row; - var column = args.column; - var samplePlateId = args.samplePlate.samplePlateId; - - /** is specimen selected on the grid? * */ - if (_this.specimenSelected != null) { - /** Is position target empty * */ - if (_this.experiment.getSampleByPosition(args.samplePlate.samplePlateId, args.row, args.column).length == 0) { - var specimen = _this.experiment.getSampleById(_this.specimenSelected.specimenId); - if (specimen.sampleplateposition3VO == null) { - specimen.sampleplateposition3VO = {}; - } - - specimen.sampleplateposition3VO = { - columnNumber : column, - rowNumber : row, - samplePlateId : samplePlateId - }; - - _this.samplePlateGroupWidget.panel.setLoading("ISPyB: Saving specimen"); - var adapter = new BiosaxsDataAdapter(); - /** If success * */ - adapter.onSuccess.attach(function(sender, experiment) { - _this.samplePlateGroupWidget.panel.setLoading(false); - }); - - adapter.onError.attach(function(sender, error) { - _this.samplePlateGroupWidget.panel.setLoading(false); - showError(error); - }); - - adapter.saveSpecimen(specimen, _this.experiment); - - _this.samplePlateGroupWidget.refresh(_this.experiment); - _this.specimenGrid.refresh(_this.experiment); - //_this.refresh(_this.experiment); - _this.specimenGrid.deselectAll(); - - } else { - /** - * Can we merge? We can merge when specimen are the - * same. So, same buffer, macromolecule, concentration * - */ - var target = _this.experiment.getSampleByPosition(args.samplePlate.samplePlateId, args.row, args.column)[0]; - var specimen = _this.experiment.getSampleById(_this.specimenSelected.specimenId); - - if ((specimen.bufferId == target.bufferId) && (specimen.concentration == target.concentration)) { - if (((specimen.macromolecule3VO != null) && (target.macromolecule3VO != null) && (specimen.macromolecule3VO.macromoleculeId == target.macromolecule3VO.macromoleculeId)) || - ((specimen.macromolecule3VO == null) && (target.macromolecule3VO == null))) { - var adapter = new BiosaxsDataAdapter(); - adapter.onSuccess.attach(function(sender, data) { - _this.refresh(new Experiment(data)); - _this.samplePlateGroupWidget.panel.setLoading(false); - - _this.onExperimentChanged.notify(experiment); - }); - adapter.onError.attach(function(sender, error) { - _this.samplePlateGroupWidget.panel.setLoading(false); - showError(error); - }); - _this.samplePlateGroupWidget.panel.setLoading("ISPyB: Merging specimens"); - adapter.mergeSpecimens(specimen.specimenId, target.specimenId); - } - } else { - alert("Well is not empty. Select another well!"); - } - } - } else { - var specimen = _this.experiment.getSampleByPosition(args.samplePlate.samplePlateId, args.row, args.column)[0]; - if (specimen != null) { - _this.specimenGrid.selectById(specimen.specimenId); - } - } - }); - - /** Events **/ - this.onExperimentChanged = new Event(this); -}; - -/** - * Return vbox or hbox depending on the slot positions of the plates - */ -SpecimenWidget.prototype.getContainerLayoutConfiguration = function(experiment){ - var dimensions = this.samplePlateGroupWidget.getDimensions(experiment.getSamplePlates()); - if (dimensions.maxSlotPositionRow < dimensions.maxSlotPositionColumn){ - return { - layout : "vbox", - specimenGridWidth : this.width - 10, - specimenGridHeight : this.height - 260, - samplePlateGroupWidth : this.width - 10, - samplePlateGroupHeight : 250 - }; - } - return { - layout : "hbox", - samplePlateGroupWidth : this.width*1/3 -10, - samplePlateGroupHeight : this.height - 10, - specimenGridWidth : this.width*2/3, - specimenGridHeight : this.height - 10 - }; - -}; - - -SpecimenWidget.prototype.refresh = function(experiment){ - this.experiment = experiment; - - /** Removing all components **/ - this.panel.removeAll(); - - var layoutConfiguration = this.getContainerLayoutConfiguration(experiment); - - /** Setting new width and height for layout vbox and hbox **/ - this.specimenGrid.width = layoutConfiguration.specimenGridWidth; - this.specimenGrid.height = layoutConfiguration.specimenGridHeight; - - this.samplePlateGroupWidget.width = layoutConfiguration.samplePlateGroupWidth; - this.samplePlateGroupWidget.height = layoutConfiguration.samplePlateGroupHeight; - - if (layoutConfiguration.layout == "hbox"){ - this.specimenGrid.margin = "0 0 0 5"; - this.specimenGrid.width =this.specimenGrid.width - 5; - } - /** Insert container depending on layout [vertical|horizontal] */ - var container = Ext.create('Ext.container.Container', { - layout : layoutConfiguration.layout, - height : this.height, - width : this.width, - padding : '2px', - items : [ ] - }); - if (layoutConfiguration.layout == "vbox"){ - container.insert(this.specimenGrid.getPanel()); - container.insert(this.samplePlateGroupWidget.getPanel()); - } - else{ - container.insert(this.samplePlateGroupWidget.getPanel()); - container.insert(this.specimenGrid.getPanel()); - } - - /** Insert Widget **/ - this.panel.insert(container); - - /** Load data **/ - this.specimenGrid.refresh(experiment); - this.samplePlateGroupWidget.refresh(experiment); - - -}; - -/** It creates a dummy container to be inserted the plates once the method refresh has been called - * This is necessay because we can not know the sample changer layout before hand - * **/ -SpecimenWidget.prototype.getPanel = function(){ - this.panel = Ext.create('Ext.container.Container', { - layout : 'vbox', - height : this.height, - border : 0, - margin : 5, - width : this.width, - items : [] - }); - return this.panel; -}; - - -SpecimenWidget.prototype.input = function() { - return { - experiment : DATADOC.getExperiment_10(), - proposal : DATADOC.getProposal_10() - }; -}; - -SpecimenWidget.prototype.test = function(targetId) { - var specimenWidget = new SpecimenWidget({ - height : 500, - width : 1000 - }); - BIOSAXS.proposal = new Proposal(specimenWidget.input().proposal); - var experiment = new Experiment(specimenWidget.input().experiment); - var panel = specimenWidget.getPanel(); - panel.render(targetId); - specimenWidget.refresh(experiment); - -}; - - +/** + * Widget container of Specimen grid and samplePlate widget + * Depending of the sample changer layout it may be displayed vertically or horizontally + * + * @param args + * + * #onExperimentChanged It happens when specimen are modified + */ +function SpecimenWidget(args){ + + this.width = 1000; + this.height = 600; + + if (args != null){ + if (args.width != null){ + this.width = args.width; + } + if (args.height != null){ + this.height = args.height; + } + } + + var _this = this; + + /** Specimen Grid **/ + this.specimenGrid = new SpecimenGrid({ + minHeight : 425, + selectionMode : "SINGLE", + editEnabled : false, + updateRowEnabled : true, + width : 900, + showTitle : false + }); + + + this.specimenGrid.onSpecimenChanged.attach(function(sender, specimen) { + _this.experiment.setSpecimenById(specimen); + _this.refresh(_this.experiment); + }); + + this.specimenGrid.onSelected.attach(function(sender, specimens) { + if (specimens.length > 0) { + _this.specimenSelected = specimens[0]; + } else { + _this.specimenSelected = null; + } + _this.samplePlateGroupWidget.selectSpecimens(specimens); + }); + + + /** Sample plate Widget **/ + this.samplePlateGroupWidget = new SamplePlateGroupWidget({ + showTitle : false, + height : 250, + margin : 5, + bbar : true + }); + + + this.samplePlateGroupWidget.onExperimentChanged.attach(function(sender, json) { + _this.refresh(new Experiment(json)); + }); + + this.samplePlateGroupWidget.onClick.attach(function(sender, args) { + /** Clicking on a plate * */ + var row = args.row; + var column = args.column; + var samplePlateId = args.samplePlate.samplePlateId; + + /** is specimen selected on the grid? * */ + if (_this.specimenSelected != null) { + /** Is position target empty * */ + if (_this.experiment.getSampleByPosition(args.samplePlate.samplePlateId, args.row, args.column).length == 0) { + var specimen = _this.experiment.getSampleById(_this.specimenSelected.specimenId); + if (specimen.sampleplateposition3VO == null) { + specimen.sampleplateposition3VO = {}; + } + + specimen.sampleplateposition3VO = { + columnNumber : column, + rowNumber : row, + samplePlateId : samplePlateId + }; + + _this.samplePlateGroupWidget.panel.setLoading("ISPyB: Saving specimen"); + var adapter = new BiosaxsDataAdapter(); + /** If success * */ + adapter.onSuccess.attach(function(sender, experiment) { + _this.samplePlateGroupWidget.panel.setLoading(false); + }); + + adapter.onError.attach(function(sender, error) { + _this.samplePlateGroupWidget.panel.setLoading(false); + showError(error); + }); + + adapter.saveSpecimen(specimen, _this.experiment); + + _this.samplePlateGroupWidget.refresh(_this.experiment); + _this.specimenGrid.refresh(_this.experiment); + //_this.refresh(_this.experiment); + _this.specimenGrid.deselectAll(); + + } else { + /** + * Can we merge? We can merge when specimen are the + * same. So, same buffer, macromolecule, concentration * + */ + var target = _this.experiment.getSampleByPosition(args.samplePlate.samplePlateId, args.row, args.column)[0]; + var specimen = _this.experiment.getSampleById(_this.specimenSelected.specimenId); + + if ((specimen.bufferId == target.bufferId) && (specimen.concentration == target.concentration)) { + if (((specimen.macromolecule3VO != null) && (target.macromolecule3VO != null) && (specimen.macromolecule3VO.macromoleculeId == target.macromolecule3VO.macromoleculeId)) || + ((specimen.macromolecule3VO == null) && (target.macromolecule3VO == null))) { + var adapter = new BiosaxsDataAdapter(); + adapter.onSuccess.attach(function(sender, data) { + _this.refresh(new Experiment(data)); + _this.samplePlateGroupWidget.panel.setLoading(false); + + _this.onExperimentChanged.notify(experiment); + }); + adapter.onError.attach(function(sender, error) { + _this.samplePlateGroupWidget.panel.setLoading(false); + showError(error); + }); + _this.samplePlateGroupWidget.panel.setLoading("ISPyB: Merging specimens"); + adapter.mergeSpecimens(specimen.specimenId, target.specimenId); + } + } else { + alert("Well is not empty. Select another well!"); + } + } + } else { + var specimen = _this.experiment.getSampleByPosition(args.samplePlate.samplePlateId, args.row, args.column)[0]; + if (specimen != null) { + _this.specimenGrid.selectById(specimen.specimenId); + } + } + }); + + /** Events **/ + this.onExperimentChanged = new Event(this); +}; + +/** + * Return vbox or hbox depending on the slot positions of the plates + */ +SpecimenWidget.prototype.getContainerLayoutConfiguration = function(experiment){ + var dimensions = this.samplePlateGroupWidget.getDimensions(experiment.getSamplePlates()); + if (dimensions.maxSlotPositionRow < dimensions.maxSlotPositionColumn){ + return { + layout : "vbox", + specimenGridWidth : this.width - 10, + specimenGridHeight : this.height - 260, + samplePlateGroupWidth : this.width - 10, + samplePlateGroupHeight : 250 + }; + } + return { + layout : "hbox", + samplePlateGroupWidth : this.width*1/3 -10, + samplePlateGroupHeight : this.height - 10, + specimenGridWidth : this.width*2/3, + specimenGridHeight : this.height - 10 + }; + +}; + + +SpecimenWidget.prototype.refresh = function(experiment){ + this.experiment = experiment; + + /** Removing all components **/ + this.panel.removeAll(); + + var layoutConfiguration = this.getContainerLayoutConfiguration(experiment); + + /** Setting new width and height for layout vbox and hbox **/ + this.specimenGrid.width = layoutConfiguration.specimenGridWidth; + this.specimenGrid.height = layoutConfiguration.specimenGridHeight; + + this.samplePlateGroupWidget.width = layoutConfiguration.samplePlateGroupWidth; + this.samplePlateGroupWidget.height = layoutConfiguration.samplePlateGroupHeight; + + if (layoutConfiguration.layout == "hbox"){ + this.specimenGrid.margin = "0 0 0 5"; + this.specimenGrid.width =this.specimenGrid.width - 5; + } + /** Insert container depending on layout [vertical|horizontal] */ + var container = Ext.create('Ext.container.Container', { + layout : layoutConfiguration.layout, + height : this.height, + width : this.width, + padding : '2px', + items : [ ] + }); + if (layoutConfiguration.layout == "vbox"){ + container.insert(this.specimenGrid.getPanel()); + container.insert(this.samplePlateGroupWidget.getPanel()); + } + else{ + container.insert(this.samplePlateGroupWidget.getPanel()); + container.insert(this.specimenGrid.getPanel()); + } + + /** Insert Widget **/ + this.panel.insert(container); + + /** Load data **/ + this.specimenGrid.refresh(experiment); + this.samplePlateGroupWidget.refresh(experiment); + + +}; + +/** It creates a dummy container to be inserted the plates once the method refresh has been called + * This is necessay because we can not know the sample changer layout before hand + * **/ +SpecimenWidget.prototype.getPanel = function(){ + this.panel = Ext.create('Ext.container.Container', { + layout : 'vbox', + height : this.height, + border : 0, + margin : 5, + width : this.width, + items : [] + }); + return this.panel; +}; + + +SpecimenWidget.prototype.input = function() { + return { + experiment : DATADOC.getExperiment_10(), + proposal : DATADOC.getProposal_10() + }; +}; + +SpecimenWidget.prototype.test = function(targetId) { + var specimenWidget = new SpecimenWidget({ + height : 500, + width : 1000 + }); + BIOSAXS.proposal = new Proposal(specimenWidget.input().proposal); + var experiment = new Experiment(specimenWidget.input().experiment); + var panel = specimenWidget.getPanel(); + panel.render(targetId); + specimenWidget.refresh(experiment); + +}; + + function WarningWidget(args) { this.actions = []; From 19ac8a7c4ee8e6363e16b732b95a017fd0de52d9 Mon Sep 17 00:00:00 2001 From: delageniere Date: Tue, 31 Oct 2017 15:54:49 +0100 Subject: [PATCH 32/38] new release 5.3.4 --- ispyb-bcr-ear/pom.xml | 4 ++-- ispyb-bcr/pom.xml | 2 +- ispyb-ear/pom.xml | 6 +++--- ispyb-ejb/pom.xml | 2 +- ispyb-ui/pom.xml | 2 +- ispyb-ws/pom.xml | 4 ++-- pom.xml | 2 +- 7 files changed, 11 insertions(+), 11 deletions(-) diff --git a/ispyb-bcr-ear/pom.xml b/ispyb-bcr-ear/pom.xml index f68ff45ae..b727e8d4f 100644 --- a/ispyb-bcr-ear/pom.xml +++ b/ispyb-bcr-ear/pom.xml @@ -4,7 +4,7 @@ ispyb ispyb-parent - 5.3.2 + 5.3.4 ispyb-bcr-ear ear @@ -15,7 +15,7 @@ ispyb ispyb-ejb3 ejb - 5.3.2 + 5.3.4 provided diff --git a/ispyb-bcr/pom.xml b/ispyb-bcr/pom.xml index 7ec9b4460..b8c7744a4 100644 --- a/ispyb-bcr/pom.xml +++ b/ispyb-bcr/pom.xml @@ -31,7 +31,7 @@ ispyb ispyb-ejb3 - 5.3.2 + 5.3.4 provided diff --git a/ispyb-ear/pom.xml b/ispyb-ear/pom.xml index cbbe85d12..54fb40673 100644 --- a/ispyb-ear/pom.xml +++ b/ispyb-ear/pom.xml @@ -4,7 +4,7 @@ ispyb ispyb-parent - 5.3.2 + 5.3.4 ispyb-ear ear @@ -13,7 +13,7 @@ ispyb ispyb-ejb3 ejb - 5.3.2 + 5.3.4 @@ -26,7 +26,7 @@ ispyb ispyb-ws war - 5.3.2 + 5.3.4 diff --git a/ispyb-ejb/pom.xml b/ispyb-ejb/pom.xml index a2132d7f1..9fb47fee4 100644 --- a/ispyb-ejb/pom.xml +++ b/ispyb-ejb/pom.xml @@ -4,7 +4,7 @@ ispyb ispyb-parent - 5.3.2 + 5.3.4 ispyb-ejb3 jar diff --git a/ispyb-ui/pom.xml b/ispyb-ui/pom.xml index 0b3af3d9c..625170c04 100644 --- a/ispyb-ui/pom.xml +++ b/ispyb-ui/pom.xml @@ -22,7 +22,7 @@ ispyb ispyb-ejb3 - 5.3.2 + 5.3.4 provided diff --git a/ispyb-ws/pom.xml b/ispyb-ws/pom.xml index c05712229..d85dc8df3 100644 --- a/ispyb-ws/pom.xml +++ b/ispyb-ws/pom.xml @@ -4,7 +4,7 @@ ispyb ispyb-parent - 5.3.2 + 5.3.4 ispyb-ws war @@ -12,7 +12,7 @@ ispyb ispyb-ejb3 - 5.3.2 + 5.3.4 provided diff --git a/pom.xml b/pom.xml index 1d979d091..290b587c2 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ 4.0.0 ispyb ispyb-parent - 5.3.2 + 5.3.4 pom ispyb-ejb From 148eefba133cd58cde7dcd333a9bee718cb3ca5d Mon Sep 17 00:00:00 2001 From: delageniere Date: Tue, 31 Oct 2017 17:53:46 +0100 Subject: [PATCH 33/38] modifs for analysis report --- .../common/util/export/ExiPdfRtfExporter.java | 127 +++++++++--------- 1 file changed, 64 insertions(+), 63 deletions(-) diff --git a/ispyb-ejb/src/main/java/ispyb/common/util/export/ExiPdfRtfExporter.java b/ispyb-ejb/src/main/java/ispyb/common/util/export/ExiPdfRtfExporter.java index 347bca985..a823fea25 100644 --- a/ispyb-ejb/src/main/java/ispyb/common/util/export/ExiPdfRtfExporter.java +++ b/ispyb-ejb/src/main/java/ispyb/common/util/export/ExiPdfRtfExporter.java @@ -753,71 +753,13 @@ private void setDataAnalysisMapData(Document document, Map dataC Boolean strategy = getBoolean(dataCollectionMapItem, "ScreeningOutput_strategySuccess"); String autoprocSpaceGroup = getCellParam(dataCollectionMapItem, "AutoProc_spaceGroup", null); boolean existAutoProcSpaceGroup = (autoprocSpaceGroup != null && !autoprocSpaceGroup.isEmpty() ) - || ( dataCollectionMapItem.get("AutoProc_spaceGroups") != null && !((String)dataCollectionMapItem.get("AutoProc_spaceGroups")).isEmpty()); - + || ( dataCollectionMapItem.get("AutoProc_spaceGroups") != null && !((String)dataCollectionMapItem.get("AutoProc_spaceGroups")).isEmpty() + && dataCollectionMapItem.get("Autoprocessing_cell_a")!= null); p = new Paragraph(); String [] bestRmerge = null; - - if (indexing != null && strategy != null){ - // Cell 6 - parag = "\nIndexed: \n " - + "\nStrategy: \n"; - p = new Paragraph(parag, FONT_DOC_SMALL); - table.addCell(p); - - // Cell 7 - p = new Paragraph(); - Chunk chu2 = new Chunk( "KO", FONT_INDEXING_FAILED); - if (indexing.booleanValue() ){ - chu2 = new Chunk( "OK", FONT_INDEXING_SUCCESS); - } - p.add(chu2); - - chu2 = new Chunk( "KO", FONT_INDEXING_FAILED); - if (strategy.booleanValue() ){ - chu2 = new Chunk( "OK", FONT_INDEXING_SUCCESS); - } - p.add("\n"); - p.add(chu2); - table.addCell(p); - - // Cell 8 - parag = "Space group: \n" - + "Mosaicity: \n" ; - p = new Paragraph(parag, FONT_DOC_SMALL); - table.addCell(p); - - // Cell 9 - parag = getCellParam(dataCollectionMapItem, "ScreeningOutputLattice_spaceGroup", null) + "\n" - + getCellParam(dataCollectionMapItem, "ScreeningOutput_mosaicity", null)+ "\n" ; - p = new Paragraph(parag, FONT_DOC_SMALL_BOLD); - table.addCell(p); - - // Cell 10 - parag = "a \n" + getCellParam(dataCollectionMapItem, "ScreeningOutputLattice_unitCell_a", null) - + "\n alpha \n" + getCellParam(dataCollectionMapItem, "ScreeningOutputLattice_unitCell_alpha", null) ; - p = new Paragraph(parag, FONT_DOC_SMALL_CENTERED); - p.setAlignment(Element.ALIGN_CENTER); - table.addCell(p); - - // Cell 11 - parag = "b \n" + getCellParam(dataCollectionMapItem, "ScreeningOutputLattice_unitCell_b", null) - + "\n beta \n" + getCellParam(dataCollectionMapItem, "ScreeningOutputLattice_unitCell_beta", null) ; - p = new Paragraph(parag, FONT_DOC_SMALL_CENTERED); - p.setAlignment(Element.ALIGN_CENTER); - table.addCell(p); - - // Cell 12 - parag = "c \n" + getCellParam(dataCollectionMapItem, "ScreeningOutputLattice_unitCell_c", null) - + "\n gamma \n" + getCellParam(dataCollectionMapItem, "ScreeningOutputLattice_unitCell_gama", null) ; - - p = new Paragraph(parag, FONT_DOC_SMALL_CENTERED); - p.setAlignment(Element.ALIGN_CENTER); - table.addCell(p); - - } else if (existAutoProcSpaceGroup && extractBestAutoproc(dataCollectionMapItem) != null){ + if (existAutoProcSpaceGroup && extractBestAutoproc(dataCollectionMapItem) != null){ // Cell 6 bestRmerge = extractBestAutoproc(dataCollectionMapItem); parag = bestRmerge[0] + "\n" @@ -878,6 +820,63 @@ private void setDataAnalysisMapData(Document document, Map dataC p = new Paragraph(parag, FONT_DOC_SMALL_CENTERED); p.setAlignment(Element.ALIGN_CENTER); table.addCell(p); + + } else if (indexing != null && strategy != null){ + // Cell 6 + parag = "\nIndexed: \n " + + "\nStrategy: \n"; + p = new Paragraph(parag, FONT_DOC_SMALL); + table.addCell(p); + + // Cell 7 + p = new Paragraph(); + Chunk chu2 = new Chunk( "KO", FONT_INDEXING_FAILED); + if (indexing.booleanValue() ){ + chu2 = new Chunk( "OK", FONT_INDEXING_SUCCESS); + } + p.add(chu2); + + chu2 = new Chunk( "KO", FONT_INDEXING_FAILED); + if (strategy.booleanValue() ){ + chu2 = new Chunk( "OK", FONT_INDEXING_SUCCESS); + } + p.add("\n"); + p.add(chu2); + table.addCell(p); + + // Cell 8 + parag = "Space group: \n" + + "Mosaicity: \n" ; + p = new Paragraph(parag, FONT_DOC_SMALL); + table.addCell(p); + + // Cell 9 + parag = getCellParam(dataCollectionMapItem, "ScreeningOutputLattice_spaceGroup", null) + "\n" + + getCellParam(dataCollectionMapItem, "ScreeningOutput_mosaicity", null)+ "\n" ; + p = new Paragraph(parag, FONT_DOC_SMALL_BOLD); + table.addCell(p); + + // Cell 10 + parag = "a \n" + getCellParam(dataCollectionMapItem, "ScreeningOutputLattice_unitCell_a", null) + + "\n alpha \n" + getCellParam(dataCollectionMapItem, "ScreeningOutputLattice_unitCell_alpha", null) ; + p = new Paragraph(parag, FONT_DOC_SMALL_CENTERED); + p.setAlignment(Element.ALIGN_CENTER); + table.addCell(p); + + // Cell 11 + parag = "b \n" + getCellParam(dataCollectionMapItem, "ScreeningOutputLattice_unitCell_b", null) + + "\n beta \n" + getCellParam(dataCollectionMapItem, "ScreeningOutputLattice_unitCell_beta", null) ; + p = new Paragraph(parag, FONT_DOC_SMALL_CENTERED); + p.setAlignment(Element.ALIGN_CENTER); + table.addCell(p); + + // Cell 12 + parag = "c \n" + getCellParam(dataCollectionMapItem, "ScreeningOutputLattice_unitCell_c", null) + + "\n gamma \n" + getCellParam(dataCollectionMapItem, "ScreeningOutputLattice_unitCell_gama", null) ; + + p = new Paragraph(parag, FONT_DOC_SMALL_CENTERED); + p.setAlignment(Element.ALIGN_CENTER); + table.addCell(p); } else { table.addCell(" "); @@ -1136,6 +1135,8 @@ private String[] extractBestAutoproc(Map dataCollectionMapItem) int indexRmergeMin = 0; Set indexSet = new HashSet(); + //TODO select also no anom + for (Iterator iterator = scalingStatisticsTypesList.iterator(); iterator.hasNext();) { String type = (String) iterator.next(); if (type.contains("innerShell")){ @@ -1209,8 +1210,8 @@ private String[] extractBestAutoproc(Map dataCollectionMapItem) private Chunk getCompletenessChunk(String completeness) { Chunk chu = new Chunk( completeness, FONT_DOC_SMALL_BOLD); chu.setBackground(BLUE_COLOR); - if (completeness != null && new Double(completeness) < 80 ) { - if (new Double(completeness) < 10) { + if (completeness != null && new Double(completeness) < 90 ) { + if (new Double(completeness) < 50) { chu.setBackground(RED_COLOR); } else { chu.setBackground(LIGHT_YELLOW_COLOR); From f93b5d6da20a2ff1496110e51f0f4754c5ed634c Mon Sep 17 00:00:00 2001 From: delageniere Date: Mon, 6 Nov 2017 16:36:18 +0100 Subject: [PATCH 34/38] Explanation about site customization --- documentation/ISPyB_DevelopersGuide.doc | Bin 207360 -> 208896 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/documentation/ISPyB_DevelopersGuide.doc b/documentation/ISPyB_DevelopersGuide.doc index 65267a340e0591758ce0c0de5773e13591b99997..fee6b6457c4e83f84281775fb38c6676a9b2ee73 100644 GIT binary patch delta 11125 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

L8>pAIcLk;*y`c6Huuu=mfpH*Gcyu$ z%4a%3OkX1ZEfzqHs2HoP3~27|^Rvazy5W+glh$EI=gXD`T|$FuNi3ZtL&p3mSGw-j zo^;#nv=26Z?M*kFwO{_LPj(UnnM)WlmjJEu#MQeT?_YiL?^?d7C}GNP61L2zFL58c zYv~YFem4tWU=QR!LRU7ZZq?<>l05){khShMLDt1ObTo@&Ft&L0IOeZnJSJcw29^W7G+@R;$8)ld8Pp43a5d((+HID|LP?Yf|Z7%U1II!{Q<%vzFDR z_Zq)3^DRfN_l>OD^nr!O&Dz#+I?Kqk@%tp>H=8xJdtndnzd1ho?~W8p9ip-Ks;=rA zbzB|AU)5)-J5No6RX^5tXWb6+97kVcbscM1xrcY=zFNl`=UcZ;i_p#y-5R$J5AWKz iMbofWjl){CYTCG2)6iC3TQ!a779P>XJ+8iWn&sbYTN)Vv delta 9452 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 From e0e4952bc3933a071f744d8da726c9acf7e67188 Mon Sep 17 00:00:00 2001 From: delageniere Date: Tue, 7 Nov 2017 17:08:56 +0100 Subject: [PATCH 35/38] modifs for ananlysis report --- .../common/util/export/ExiPdfRtfExporter.java | 108 +++++++++++------- 1 file changed, 67 insertions(+), 41 deletions(-) diff --git a/ispyb-ejb/src/main/java/ispyb/common/util/export/ExiPdfRtfExporter.java b/ispyb-ejb/src/main/java/ispyb/common/util/export/ExiPdfRtfExporter.java index a823fea25..8a4dabd52 100644 --- a/ispyb-ejb/src/main/java/ispyb/common/util/export/ExiPdfRtfExporter.java +++ b/ispyb-ejb/src/main/java/ispyb/common/util/export/ExiPdfRtfExporter.java @@ -753,8 +753,7 @@ private void setDataAnalysisMapData(Document document, Map dataC Boolean strategy = getBoolean(dataCollectionMapItem, "ScreeningOutput_strategySuccess"); String autoprocSpaceGroup = getCellParam(dataCollectionMapItem, "AutoProc_spaceGroup", null); boolean existAutoProcSpaceGroup = (autoprocSpaceGroup != null && !autoprocSpaceGroup.isEmpty() ) - || ( dataCollectionMapItem.get("AutoProc_spaceGroups") != null && !((String)dataCollectionMapItem.get("AutoProc_spaceGroups")).isEmpty() - && dataCollectionMapItem.get("Autoprocessing_cell_a")!= null); + || ( dataCollectionMapItem.get("AutoProc_spaceGroups") != null && dataCollectionMapItem.get("Autoprocessing_cell_a") != null); p = new Paragraph(); String [] bestRmerge = null; @@ -1117,17 +1116,19 @@ private String[] extractBestAutoproc(Map dataCollectionMapItem) listString.trim(); List completenessList = new ArrayList(Arrays.asList((listString.split(",")))); - LOG.info("completenessList = " + completenessList.toString()); + LOG.debug("completenessList = " + completenessList.toString()); List spaceGroupsList = new ArrayList(Arrays.asList(((String)dataCollectionMapItem.get("AutoProc_spaceGroups")).trim().split(","))); - LOG.info("spaceGroupsList = " + spaceGroupsList.size() + spaceGroupsList.toString()); + LOG.debug("spaceGroupsList = " + spaceGroupsList.size() + spaceGroupsList.toString()); List resolutionsLimitLowList = new ArrayList(Arrays.asList(((String)dataCollectionMapItem.get("resolutionsLimitLow")).trim().split(","))); - LOG.info("resolutionsLimitLowList = " + resolutionsLimitLowList.size() + resolutionsLimitLowList.toString()); + LOG.debug("resolutionsLimitLowList = " + resolutionsLimitLowList.size() + resolutionsLimitLowList.toString()); List resolutionsLimitHighList = new ArrayList(Arrays.asList(((String)dataCollectionMapItem.get("resolutionsLimitHigh")).trim().split(","))); - LOG.info("resolutionsLimitHighList = " + resolutionsLimitHighList.toString()); + LOG.debug("resolutionsLimitHighList = " + resolutionsLimitHighList.toString()); List rmergesList = new ArrayList(Arrays.asList(((String)dataCollectionMapItem.get("rMerges")).trim().split(","))); - LOG.info("rmergesList = " + rmergesList.size() + rmergesList.toString() ); + LOG.debug("rmergesList = " + rmergesList.size() + rmergesList.toString() ); List scalingStatisticsTypesList = new ArrayList(Arrays.asList(((String)dataCollectionMapItem.get("scalingStatisticsTypes")).trim().split(","))); - LOG.info("scalingStatisticsTypesList = " + scalingStatisticsTypesList.size() + scalingStatisticsTypesList.toString()); + LOG.debug("scalingStatisticsTypesList = " + scalingStatisticsTypesList.size() + scalingStatisticsTypesList.toString()); + List anomalousList = new ArrayList(Arrays.asList(((String)dataCollectionMapItem.get("Autoprocessing_anomalous")).trim().split(","))); + LOG.debug("anomalousList = " + anomalousList.size() + anomalousList.toString()); bestRmerge = new String[18]; int i = 0; @@ -1135,32 +1136,34 @@ private String[] extractBestAutoproc(Map dataCollectionMapItem) int indexRmergeMin = 0; Set indexSet = new HashSet(); - //TODO select also no anom - for (Iterator iterator = scalingStatisticsTypesList.iterator(); iterator.hasNext();) { String type = (String) iterator.next(); - if (type.contains("innerShell")){ + // select also no anom + if (type.contains("innerShell") && (new Integer(anomalousList.get(i).trim())).intValue() < 1) { double rm = new Double(rmergesList.get(i)).doubleValue(); - LOG.info("rm = " + rm); - if (rm > 0 && rm < rmergeMin) { + LOG.debug("rm = " + rm); + if (rm > 0 && rm < MIN_RMERGE) { + indexSet.add(i); + LOG.debug("index kept: " + i); + } else if (rm > 0 && rm < rmergeMin) { rmergeMin = rm; indexRmergeMin = i; - if (rmergeMin < MIN_RMERGE) - indexSet.add(i); } } i=i+1; } + // select higher symmetry if (!indexSet.isEmpty()) { String spgTemp; - Integer spgNb = 0; + int spgNb = 0; for (Iterator iterator = indexSet.iterator(); iterator.hasNext();) { - Integer index = (Integer) iterator.next(); - spgTemp = spaceGroupsList.get(index); - - if (spgMap.get(spgTemp)!= null && spgNb < spgMap.get(spgTemp)) { - spgNb = spgMap.get(spgTemp); + Integer index = (Integer) iterator.next(); + spgTemp = spaceGroupsList.get(index).trim(); + LOG.debug("index : " + index + " spgtemp: " + spgTemp); + if (spgMap.get(spgTemp)!= null && spgNb <= spgMap.get(spgTemp).intValue() ) { + spgNb = spgMap.get(spgTemp).intValue(); + LOG.debug("index : " + index + " spgNb: " + spgMap.get(spgTemp)); indexRmergeMin = index; } } @@ -1185,22 +1188,39 @@ private String[] extractBestAutoproc(Map dataCollectionMapItem) bestRmerge[9] = tmpList.get(indexRmergeMin); //outer - bestRmerge[10] = spaceGroupsList.get(indexRmergeMin+1); - bestRmerge[11] = rmergesList.get(indexRmergeMin+1); - bestRmerge[12]= completenessList.get(indexRmergeMin+1); - bestRmerge[13] = resolutionsLimitLowList.get(indexRmergeMin+1) + "/" + resolutionsLimitHighList.get(indexRmergeMin+1); + int outerIndex = -1; + if (scalingStatisticsTypesList.get(indexRmergeMin+1).contains("outerShell") ){ + outerIndex = indexRmergeMin+1; + } else if (indexRmergeMin+2 < scalingStatisticsTypesList.size() && scalingStatisticsTypesList.get(indexRmergeMin+2).contains("outerShell") ){ + outerIndex = indexRmergeMin+2; + } + if (outerIndex > -1) { + bestRmerge[10] = spaceGroupsList.get(outerIndex); + bestRmerge[11] = rmergesList.get(outerIndex); + bestRmerge[12]= completenessList.get(outerIndex); + bestRmerge[13] = resolutionsLimitLowList.get(outerIndex) + "/" + resolutionsLimitHighList.get(outerIndex); + } //overall - int overallIndex = indexRmergeMin-1; - if (overallIndex < 0 || scalingStatisticsTypesList.get(0).contains("innerShell")) { + int overallIndex=-1; + if (indexRmergeMin-1 >= 0 && scalingStatisticsTypesList.get(indexRmergeMin-1).contains("overall")) { + overallIndex = indexRmergeMin-1; + } else if (indexRmergeMin-2 >= 0 && scalingStatisticsTypesList.get(indexRmergeMin-2).contains("overall")) { + overallIndex = indexRmergeMin-2; + } else if (indexRmergeMin+2 < scalingStatisticsTypesList.size() && scalingStatisticsTypesList.get(indexRmergeMin+2).contains("overall")) { overallIndex = indexRmergeMin+2; + } else if (indexRmergeMin+3 < scalingStatisticsTypesList.size() && scalingStatisticsTypesList.get(indexRmergeMin+3).contains("overall")) { + overallIndex = indexRmergeMin+3; } - - bestRmerge[14] = spaceGroupsList.get(overallIndex); - bestRmerge[15] = rmergesList.get(overallIndex); - bestRmerge[16]= completenessList.get(overallIndex); - //TODO format to 2 figures after . - bestRmerge[17] = resolutionsLimitLowList.get(overallIndex) + "/" + resolutionsLimitHighList.get(overallIndex); + + if (overallIndex > -1) { + bestRmerge[14] = spaceGroupsList.get(overallIndex); + bestRmerge[15] = rmergesList.get(overallIndex); + bestRmerge[16]= completenessList.get(overallIndex); + //TODO format to 2 figures after . + bestRmerge[17] = resolutionsLimitLowList.get(overallIndex) + "/" + resolutionsLimitHighList.get(overallIndex); + } + LOG.info("bestRmerge = " + bestRmerge[0] + "- " + bestRmerge[1]+ "- " + bestRmerge[2]+ "- " + bestRmerge[3]); } @@ -1208,14 +1228,20 @@ private String[] extractBestAutoproc(Map dataCollectionMapItem) } private Chunk getCompletenessChunk(String completeness) { - Chunk chu = new Chunk( completeness, FONT_DOC_SMALL_BOLD); - chu.setBackground(BLUE_COLOR); - if (completeness != null && new Double(completeness) < 90 ) { - if (new Double(completeness) < 50) { - chu.setBackground(RED_COLOR); - } else { - chu.setBackground(LIGHT_YELLOW_COLOR); - } + + Chunk chu = new Chunk( " ", FONT_DOC_SMALL_BOLD); + + if (completeness != null && !completeness.isEmpty()) { + + chu = new Chunk( completeness, FONT_DOC_SMALL_BOLD); + chu.setBackground(BLUE_COLOR); + if (completeness != null && new Double(completeness) < 90 ) { + if (new Double(completeness) < 50) { + chu.setBackground(RED_COLOR); + } else { + chu.setBackground(LIGHT_YELLOW_COLOR); + } + } } return chu; } From 0959c267dfc45606ec352b66d6b97c6777b8e771 Mon Sep 17 00:00:00 2001 From: delageniere Date: Thu, 9 Nov 2017 11:25:28 +0100 Subject: [PATCH 36/38] documentation updated --- .../documentation/ISPyB_DevelopersGuide.doc | Bin 190464 -> 192000 bytes configuration/standalone.xml | 110 ++-- configuration/standalone.xml.simple | 567 ++++++++++++++++++ 3 files changed, 603 insertions(+), 74 deletions(-) create mode 100644 configuration/standalone.xml.simple diff --git a/configuration/documentation/ISPyB_DevelopersGuide.doc b/configuration/documentation/ISPyB_DevelopersGuide.doc index 8d05a3abcca0a29ed93bdbc7d91d6f776dcc55b6..be213ba1110737808cc66d9465bb0ea5d81c1b6b 100644 GIT binary patch delta 15465 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 delta 14206 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 GUH%I~dtLni diff --git a/configuration/standalone.xml b/configuration/standalone.xml index caaea833d..ed7ca4b1f 100644 --- a/configuration/standalone.xml +++ b/configuration/standalone.xml @@ -83,7 +83,7 @@ - + @@ -106,6 +106,26 @@ + + + + + + + + + + + + + + + + + + + + @@ -153,20 +173,28 @@ + + jdbc:mysql://pydevserv.esrf.fr:3308/pyconfig + mysql-connector-java-5.1.21.jar + + **** + **** + + jdbc:mysql://pydevserv.esrf.fr:3308/pydb mysql-connector-java-5.1.21.jar - UUUUUUU - PPPPPP + **** + **** jdbc:mysql://pydevserv.esrf.fr:3308/pydb mysql-connector-java-5.1.21.jar - UUUUUUU - PPPPPP + **** + **** @@ -403,18 +431,6 @@ - - - true - - - - true - - - - true - true @@ -427,23 +443,7 @@ true - - - true - - - - true - - - - true - - - - true - - + @@ -499,38 +499,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -564,6 +532,7 @@ + @@ -624,12 +593,5 @@ - - - - - - - - + diff --git a/configuration/standalone.xml.simple b/configuration/standalone.xml.simple new file mode 100644 index 000000000..8b8e63f37 --- /dev/null +++ b/configuration/standalone.xml.simple @@ -0,0 +1,567 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + jdbc:mysql://ispydb-prod.esrf.fr:3306/pyconfig + mysql-connector-java-5.1.21.jar + + pxuser + ***** + + + + jdbc:mysql://ispydb-prod.esrf.fr:3306/pydb + mysql-connector-java-5.1.21.jar + + pxuser + ***** + + + + jdbc:mysql://ispydb-prod.esrf.fr:3306/pydb + mysql-connector-java-5.1.21.jar + + pxuser + ***** + + + + jdbc:h2:mem:test;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE + h2 + + sa + sa + + + + + + org.h2.jdbcx.JdbcDataSource + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 102400 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + jms.queue.DLQ + jms.queue.ExpiryQueue + 10485760 + 2097152 + 10 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ${jboss.bind.address:127.0.0.1} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 0442a303af5f8041ffc978f83e09e0e831bda58f Mon Sep 17 00:00:00 2001 From: delageniere Date: Thu, 9 Nov 2017 11:35:18 +0100 Subject: [PATCH 37/38] cleanup documentation and configuration folders --- .../documentation/ISPyB_AdminGuide.doc | Bin 34816 -> 0 bytes .../documentation/ISPyB_DevelopersGuide.doc | Bin 192000 -> 0 bytes configuration/documentation/ISPyB_wildfly.doc | Bin 35840 -> 0 bytes .../MariaDB_installation_CentOS.txt | 131 ---- .../documentation/QuickAdaptToWildfly.docx | Bin 17293 -> 0 bytes .../database/ISPyB_DataModel.mwb | Bin 208613 -> 0 bytes .../database/ISPyB_DataModel_4-0-5.mwb | Bin 177110 -> 0 bytes .../database/ISPyB_DataModel_4-0-5.pdf | Bin 181455 -> 0 bytes .../database/ISPyB_DataModel_4-0-5.png | Bin 1188494 -> 0 bytes .../database/ISPyB_DataModel_BX.png | Bin 547230 -> 0 bytes .../database/ISPyB_DataModel_mx.png | Bin 1027413 -> 0 bytes .../database/ISPyB_DataModel_mx_autoproc.png | Bin 255751 -> 0 bytes .../documentation/settings.xml.example | 304 --------- .../documentation/standalone.xml.example | 597 ------------------ documentation/ISPyB_DevelopersGuide.doc | Bin 208896 -> 192000 bytes documentation/~$PyB_DevelopersGuide.doc | Bin 162 -> 0 bytes documentation/~WRL0004.tmp | Bin 197120 -> 0 bytes 17 files changed, 1032 deletions(-) delete mode 100644 configuration/documentation/ISPyB_AdminGuide.doc delete mode 100644 configuration/documentation/ISPyB_DevelopersGuide.doc delete mode 100644 configuration/documentation/ISPyB_wildfly.doc delete mode 100644 configuration/documentation/MariaDB_installation_CentOS.txt delete mode 100644 configuration/documentation/QuickAdaptToWildfly.docx delete mode 100644 configuration/documentation/database/ISPyB_DataModel.mwb delete mode 100644 configuration/documentation/database/ISPyB_DataModel_4-0-5.mwb delete mode 100644 configuration/documentation/database/ISPyB_DataModel_4-0-5.pdf delete mode 100644 configuration/documentation/database/ISPyB_DataModel_4-0-5.png delete mode 100644 configuration/documentation/database/ISPyB_DataModel_BX.png delete mode 100644 configuration/documentation/database/ISPyB_DataModel_mx.png delete mode 100644 configuration/documentation/database/ISPyB_DataModel_mx_autoproc.png delete mode 100644 configuration/documentation/settings.xml.example delete mode 100644 configuration/documentation/standalone.xml.example delete mode 100644 documentation/~$PyB_DevelopersGuide.doc delete mode 100644 documentation/~WRL0004.tmp diff --git a/configuration/documentation/ISPyB_AdminGuide.doc b/configuration/documentation/ISPyB_AdminGuide.doc deleted file mode 100644 index 7d126ce296a4790ebb7cabbbf47dd9aafe4445e4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 34816 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 diff --git a/configuration/documentation/ISPyB_DevelopersGuide.doc b/configuration/documentation/ISPyB_DevelopersGuide.doc deleted file mode 100644 index be213ba1110737808cc66d9465bb0ea5d81c1b6b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 192000 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 diff --git a/configuration/documentation/MariaDB_installation_CentOS.txt b/configuration/documentation/MariaDB_installation_CentOS.txt deleted file mode 100644 index e4906e6bd..000000000 --- a/configuration/documentation/MariaDB_installation_CentOS.txt +++ /dev/null @@ -1,131 +0,0 @@ -INSTALLATION of MaraiaDB and Connection on CentOS 7: --------------------------------------------------- - -0/ OS: CentOS Linux release 7.1.1503 (Core) x64 - [root@srvdb ~]# tail /etc/redhat-release - CentOS Linux release 7.1.1503 (Core) - -1/ Make sure that you have internet access: - [root@srvdb tmp]# wget http://google.fr - -2/ Adding the MariaDB YUM Repository: - [root@srvdb ~]# vi /etc/yum.repos.d/MariaDB.repo - [mariadb] - name = MariaDB - baseurl = http://yum.mariadb.org/10.1/centos7-amd64 - gpgkey=https://yum.mariadb.org/RPM-GPG-KEY-MariaDB - gpgcheck=1 - -3/ Installing MariaDB: - [root@srvdb ~]# yum install MariaDB-server MariaDB-client - -4/ Mariadb stat, start and Set to start on boot: - [root@srvdb ~]# systemctl status mariadb - [root@srvdb ~]# systemctl start mariadb - [root@srvdb ~]# systemctl enable mariadb - -5/ Login and View Existing Databases: - [root@srvdb ~]# mysql - -6/ First Installation of MariaDB: - [root@srvdb ~]# mysql_secure_installation - login: root - root password: PPPPPPPP - Remove anonymous user : Yes - disable root login remotely: Yes - remove test database and access to it: Yes - Reload privileges tables now: Yes - - [root@srvdb ~]# systemctl restart mariadb - -7/ Connection and set password: - [root@srvdb ~]# mysql -u root -p - Enter password: PPPPPPP - -8/ Importing ISPyB schema into MaraiDB - Create Database and User Account: - --------------------------------- - [root@srvdb ~]# mysql -u root -p - Enter password: PPPPPPPP - - MariaDB [(none)]> use mysql; - MariaDB [(none)]> CREATE DATABASE IF NOT EXISTS `ispyb_config` DEFAULT CHARACTER SET latin1 COLLATE latin1_swedish_ci; - MariaDB [(mysql)]> GRANT ALL PRIVILEGES ON *.* TO 'ispyb_config'@'localhost' IDENTIFIED BY 'PPPPPPPP' WITH GRANT OPTION; - - MariaDB [(none)]> CREATE DATABASE IF NOT EXISTS `ispyb_db` DEFAULT CHARACTER SET latin1 COLLATE latin1_swedish_ci; - MariaDB [(mysql)]> GRANT ALL PRIVILEGES ON *.* TO 'ispyb_db'@'localhost' IDENTIFIED BY 'PPPPPPPP' WITH GRANT OPTION; - - MariaDB [(mysql)]> GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost'; - - MariaDB [(mysql)]> FLUSH PRIVILEGES; - - MariaDB [mysql]> quit; - Bye - - See - ---- - MariaDB [(mysql)]> SELECT User,Host,Password FROM mysql.user; - +--------------+---------------+-------------------------------------------+ - | User | Host | Password | - +--------------+---------------+-------------------------------------------+ - | root | localhost | *DCDD362A8D61A6EF15DBC1FCA1D97E0B0F6F134B | - | root | 127.0.0.1 | *DCDD362A8D61A6EF15DBC1FCA1D97E0B0F6F134B | - | root | ::1 | *DCDD362A8D61A6EF15DBC1FCA1D97E0B0F6F134B | - | ispyb_db | 127.0.0.1 | *B659BF6BE0C776E1949529F99ADEE9D7E9061468 | - | ispyb_config | 127.0.0.1 | *6560351E4A775F27F05DD50F9E6C06BD79008276 | - | ispyb_config | localhost | *6560351E4A775F27F05DD50F9E6C06BD79008276 | - | ispyb_db | localhost | *B659BF6BE0C776E1949529F99ADEE9D7E9061468 | - +--------------+---------------+-------------------------------------------+ - 16 rows in set (0.00 sec) - - -9/ Log into new data: - [root@srvdb ~]# mysql -u ispyb_config -p - Enter password: PPPPPPPPP - -10/ Solve case sensitive issue in linux by declaring case sensitive attribute in the '/etc/my.conf' file - [root@srvdb ~]# vi /etc/my.cnf - ... - #added by IC to tell MySQL that table name comparaison should not be case sensitive - lower_case_table_names=1 - - # Disabling symbolic-links is recommended to prevent assorted security risks - symbolic-links=0 - -11/ Import ispyb_config and ispyb_db into the MaraiDB: - Before doing that you need to export ispyb schema and data from your MySQL database for instance into 'export_ispyb_config.sql' and 'export_ispyb_db.sql' files. - - [root@srvdb ~]# systemctl restart mariadb - [root@srvdb ~]# mysql -u ispyb_config -p --database=ispyb_config < /path_to/export_ispyb_config.sql - Enter password: PPPPPPPP - - [root@srvdb ~]# mysql -u ispyb_db -p --database=ispyb_db < /path_to/export_ispyb_db.sql - Enter password: PPPPPPPP - -12/ Test of MaraiDB: - In the ISPyB server: - 1/ Stop the ISPyB server (Wildfly server) - [root@ispyb ~]# systemctl stop wildfly - - 2/ Update the url and credentials to connect to the MaraiDB - [root@ispyb ~]# vi %WILDFLY_HOME%/standalone/configuration/standalone.xml - - - jdbc:mysql://pydevserv.esrf.fr:3306/pydb - mysql-connector-java-5.1.21.jar - - UUUUUUUU - PPPPPPPP - - - - - - - - 3/ Start the ISpyB server - [root@ispyb ~]# systemctl start wildfly - - 4/ Log into the ISPyB web site and test it. - -That's. diff --git a/configuration/documentation/QuickAdaptToWildfly.docx b/configuration/documentation/QuickAdaptToWildfly.docx deleted file mode 100644 index 1f99b727a40687c6ade694b60c03a0844b29d5d6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17293 zcmeIa1$$gOvNqagh#j*XGcz+Y#mo#bGcz+YGc(7`%*@Q}m}6%9dY?0U_ROAn?jQKh z>Zi5Tl3uA+ca=(|Dz&Tx2q-cD8~_Ob0Pq0}vDPX^Kmb4?7yy6-fCSbMw6=0Ev~u{N z=xSqVuSMfxX@Qpq3QU#*0RC+Mf7k!R-#~rBm{}hkyzoQdyZ=n%vUn?5VgE>8v>~R+ zGf)_lw9uVEe*1gp9vgij5k1q$Al}rv`xGR-|IBKd1r$PqGw~@Jcam?swka#+%F;fy zw*flIP6FK~{|JkZ1yg$mx+Dt_9brRX+=K$OOh$f&KZ@!jAc`U}TA7Gr5FJjyD;)jz zi}t0j#3f>LLh-6UECj7=XrS22X_^-+5duu@S&L~!oj8dAoqCv%W2eK3jCiL6+4q+7 zo|dmJqOcLTk|mTwAWp4tt!X7F2KoqdZPoJwDm)Zb?V76csKu6FZJx_!5!F4q zZ)#Llxv@!(Y^K1SC(xaTV$G1>T}+#ahek;L7t z-0*kAA(w!xc(fS=^L}#H#|H>N_TR)4Cl;gW^0P+zQ~JJqisuhILkoLanm^M26U+aH zW$|Agy&|sXQ!HWlF8$y9XFB9od%opM)9O#JeOrNm)DV+IT3a?-dVk|uUIx}W))gI_ zor|A#v(FH5+)mK?jgh1T8`c4}_@v#Z@zU-BNDOQ*q<2}g*@a0zyf=CqB@`nc^$%A= z37f(Ik9`bDndnA3pb)x0DE?wfKt46EPgIwmWvjIGkmALdlS;p^X!c#h9iog|xHAa5 zbrRhR+K9I%nUOjsx_7fyt6%XXzL_D?jQoxY!-9^MI3rfC0vh9Y`^<@3epm58Xdo3- zE>tM(i-(aWqWb$$*daPE%O$>A{f`}M%nU95n@=V6pYl=H8IBG1S6 zfDGK<1n=|lYd{j=2y5i~k59>GYN~7sB^bgC1C6iwwU=QDsS#IzZT2aNiN5lkB zf3HD?mq0R6WB_B`X?mj8ZJs%X(pY%lvuW7FkEOsP8(jh0By<_E4E@H6&#ei9Rx5)V zT1oSF34^){Jb1~D&qyJK_qtoV#{K;5|C5czme`fVz5)QHI6we|&ldl~M*qq{XIkgh zyKERAUbAm-2(wjA(wsY$)5#n6U&$OOC(e`&4@DkK_ldw~No*&b#8(#HE{!>TgOH)2 ze1Tn`fzp<1-r7H2mr1(bZ&n=ZNhMRI+Q;OTekL%TrS{i$>=+)OCT|Ya#SOAuQ|-gU z{YWB7Nql#`d+2)O=&TMwZ(iO@GwIbw)8A^Rie!7B9W^wHst!-NSS2BjfN8cuY?1^A zS?fQb&sA)iQ<{>6AlDu8w-$c?YEMb3eH18kEBEy0|A)ui-u0A7p{`QC~e9E)i zsLRQc9nwfSVam8)i!6IdV79ww!FL@SZo@aTbPLrg-L*rwgaDx!Z5W14558Lyd)^fj z+rTWxmd0{@i;5sq#aPLDUrw4nvYnP!^XO)3lewd@BUptL^?Ht4p2$o@ke$B1 z+UV`IV2wR>qkRwrnN3s4nn^}I(R4(sqSovws_Z9EHk<0atH7k;as0$1cAm-iSYC=m zLoiyKzO=@heaTZLBg3Y}TWzUL$|0Pbo#fmyj$uq`bG^&GB`-w{C5ow_`0Ji4u&!1| zPl1*}QC0gHqZnlS z_Y<;A_I%%gM=6e3Nyw2=BwIsBsPWq`R)mtZaU#&>?Ys#l{Q1Al4O!$cZY{I{N3ZHX7!E}mW#8;R(zRT>ptI&h|?`nr^+60UX6 z3u2uvW3J*XzS!ydxo{hVjg0Rv-eFUr9YJn<7i5h1N)Vw3B9!9TtoR6dGa5xKzaB+x z{7qL`!-}=c*my!%eU+`!nIl&)RYgn?)>UdiQWn)Qxr$kAc?9B|M;vK15$aWEo)-a$ zn7t$Bw@CR)*$>(R+bSV7y<&BCQ^h@lDPt|~m+PlF?13FrpeSQ$UGe+!O^uG{5m!5u zr*=8sQYvj>tL%F-`!`Xp9ByCz6c=Fnxkg$E;6=LKVN2@0aJe!4Pz{oBElQl=3?px8 z@>1bY-FstE7EWhtPAk$pH`>rR@%hJ?l?E#K6W!_8?$?Ad>(V~`USOxZvzD@_&PWSW zlXh&2`m7exdab8HaLQhHYP^;~M;Ju6WdkwyF5>#IDua+U!DA|QVJ)adI1#=>$n@wN zJ#loWJjA>H&7jNyriHS*RW)Eerm54+``(Bdes~V)-u~qhIDUL@heul8SBaY$cxw*k z4UN&C7K?8MEYo_Fg?qfn(6kaD`*fPKUXQlJN5yl&f?y1Jr+2TS>q-H{C!ro%fnrz$ zPsQNWNQsH)zNax*lGei;>t82V{0G^lOXbuOi8b z6HJNL?>X?I1ovZurRYY*Ic+QmQ5;l<52Q5$3ig#R`zP2=kHmYqBwZ-$m}XlyRlQH` zMv1B>fo0HY=V90CZYTR4esi;oASpw@&bo{STAVq_m}e4UBC+aQWYDVh4!*J=Eauyi zvLalJYj-kOjG!-cedzSE{Ssj;Pn@+{D~QfwO-V5dXyX+-30g*?7yxS-6iPSDJDO7p zhoyHg`;t(&S=Q86+{Re_Idm;oBZN(YrrUR7&WqC`ef{WDnnOZ8`Zw0ex1aN!Lm#k+ zM-X}-(JP8&n=Fkl2l5&^yEOdvjnw_IV(q?`P}nuGT&FGeoUE~X6}EAPH!;;s z&vCQJEPdufYii;f9|SRl{T|nJ5qzmEu+b{?*)G==P9@2!kleZx-OF=iPGFq~zGN}M z0TycgpN3g3OLcZl;m^e#v;4*ODw;fxPN7nB2Xo33b&b5Lpt3ZbjihLPDyJ`VbqG+- z33k;I>B)0pQe3BQo@0#6wSsFNDCP=}3sS!{x3%>B;`KP$%T?6v%`Y(ToO8io%-^P( z-mI5@1=nk^;)f9`CTrxXtc^GplKgh>T~bLaK05lcG3eoF$R%gEV8^C|Y%`nO*Kt8Y zwO6?RU=Vr-Pv>O~C;GaWjy~luNrMyXC7_6DLlsOWy1jh8?|g3HWQ#lOzf>Fjy*Bph zOo@Quz|AS5%{3N@B6CLf#GS^`ZnazyBXYeYX+WT4xsGmN5gTKdW#E?__wdiW{D$(1 zy9rPCCwt>QMR^f}q*Z<#4%{k>Lm3#Y8JRn@qe{jlBe!l#(n2&|m!OK84(N=#7uslF z7(5*-dY!}CbnLv{TyS4CMCB|-xm+8ZEkRjD1DRvsV3@DySPX3Y5B9@bJ@o$i;6pG` z=vX&a*s>|S456opG6&%fE4-q5^V{JlD+;aMXo823yZHTrA;F>WHi}oWZ`X-o{@HS? z{t6H>TteNN{h$cHOAG)A@C4geLfx@+Dxzs;^?X|EvP^0U>;?fI%B19jlGbZxr#slh zGL3<(uUbTqDccqJIs)C}{#b;s>vG+5GzqZCrb$d@o!nb3WbBZ19Z?Z|rjl z{Y4w?xqG~GHAxOixe0|E$K#0t;*=%1#_3t+#Z!vyvh!5S_ap!qc((a6mRpH|#m~cWv#Pt!v;Y<#O3BJk4wBqKhD6PPMN|0yr2LR>u?k6 z%La-yrklGgDps2W_Htw{TA?Za-Q9_C7LNsyk$!W}_{% zkBYxbw-tJdY3@rQek##LgS-I7=@mw9QwY~4hgqGc&O6YJ^3@*v_17>HBN0lYt+3d@ zwH&JnG10bo&TT7L>g^dKf7@e*v#hAnZD3x#UA#xPUA!fK_{gX=c0b)*QCl0&rRuLs zB}F2Q2O_N7^Xh?id-=QpUg~PMs}?T3U-sTHbi5-d0WVwgpO_Qq zYTg1!9q-S_U#MRw(!B%b}!k+2kzd2>agEE3I{X90;Jjjt~^9yI*8y+eC#)Dx5amjx=qjy3|H^- z1D5^++`22N_v5&)hSPy3Z~t-Aq^UolZHnZ+MT+bECwmyNoq{+|A*L*2j?s5fRQAED z<@Z?cerl#$L|;^f#1=?shD6&AKgoklv!zJ`_!%5C0X>r+FQR3Sr(_<*D<+Mo*US{I z6a8m#S~UHIh#E^`l`q5ckGB5Yk_bZ>xVjU_1bQi^AmZz{%|rvLRfFSp@`F#2Vhg}6 z!O{p6&u-l$TQrd@H>892A??r23p3uQ&RCf4rYeyN2z33MQ}Q@jMu^^dL^nPqM(*k> zHL#H|^V9@9C5MO-Y)(b4-^O5f(kAW}jI_%$8>Lsnsc0$WWcCdXFIUull`}@*!NAfF zxi@xSi9FQe7ppG0K9CP=U95|?)}T1D<+$F3gDM*#eU0ZM-*i9Y?EPtuR{kna-5I##M+T4u?qa9F~T+(4-1Ul!BN;TzM1Cg)@Lm$3Kyp1MQ1)A zd5lz#K>8PkYcO#72vBLCl6zd6s3sVBSwp-?eiPq&?%kB!x`KBtitmBudaPkGF-U5( zpg8bUSy5aD|8RczuYAO$5^IfQU7Bves+^k_Skv$%LKWnN^D+kkRRxrS+cuwCqrNC&bRq190FpA8U2bA z8C}`&goq*Y$8@{%J$4k23rK|B@J~zDQ0c8ps5=722{u>Ok@G897X%N;S?9aJYe-IT zufA`Gv0pEI{MYS^q=w4ftIwTxSxf)`6ZD3(%Gx{=9Sb1PcL@|^^FrPV7^ zCq6>IO`W-Q=tuX&C>8?aw%MXwM~&kV{kw91%=T}v81;v`5qz~8TW z(cD6NnfehEpb*|{)wFiJ2<-*#N< zu8-Zz;Ox9E9PfwM9u=#0sy*+I+4!|l4CxNlecGj-NHA2-1eUmv5o{TbS2 z^a*Ah?IOOqzyz^_#`wB$hj|5=!D{py4{upeW5sX3L|~5c&Q!6Sbv@ z5Eb(SdH_~15kB-WHxEHSzf-#W*iZL)G|lT=fhKY@{SybV?5`CTX$V@m^!?6-`rvu&)h8#*S_ce8V+m^Vy7i^ekwuQVRJl@j4z3J`yPcSOuu?GS_;}k zDKv%ul}!!fLL)(S$IH=jt01>R?WCJtN_#O1JGX)2o&rh^-*%Au=OY4S4=V&i z>bdoaL@4(wp6dM;av;jbo3pjAK2MyN))jIk{xh)>jV3nre<yiw>bkWGX|DU50cL!vKVHmF?+n<%IKV7C~?KV7QI)>Sc4;^F+ z{aRqfml7y8cB5o9N*V6DspG7^SSA76C{!R&rbU$>F=}xjM3s8{!Fjj+sUyYRIf2K?vzXuhi#PJd;jGDeirRFiBMOB*n z;yl~Hz<)c&vUJrf2@ymSCp>uo#lni9n*PGCW|9a$*TrJ4H8N9SZOA3b-@`71Jq-^9 zohdEs7vq_@_5^Lz&`x=n7|S^NBAHtyS$E`vg|N7^&{lQ+Aj@=4(n1Jh#ba{M?g#PjLj88Gho34$gCTXOfyOpdu)o5tAj_jJIc_aiDGncveitpzaJau zXsNr2Jvg<)Vb|1BqnZ&vCeSzf^1*S{gB{cH1+9=?VRl*cqF1So@vRH+Xn+WNeE;8F zK;3CGwv^xifZC^m_~+iFy`h7Hsg<$)ANrwQ)zW&61^%5!=cDKM5qr`j1-jIi(70zm zBkaQqh-t)3$XFrYxV@sj_ZR6}MbhJjVP)he_$Qp}&CB7vaFy&6Gh1U#mDNML;~y?# zfu?pE2@`h**FU3IC{$@$wOIqJEc9uTABr?O-JVWqX`|97Z@H;XQM>bwSscAu+K*qH z>a?AoB1;{ebv5Py%}rH{+^-Hxr|NBMf^DOg8z-IXiApP5KSPf~5BdkSY2d0>B&n1g zQ#sdGJb5ivmft$ZCvL4bcd`f{>WOQfa#^H!#WgtTx@()!GSGGx^%>r1DK`@4J;dSm^~G;#?H{)zptQ1%#d`ktLmx{NCo;Ky)U~ z=If_P9F~aL9$Yvf9u$MzUs`G@(f$xkZ=Qw>vR)cqEOH9%oDQB)SVPy3K(L*~KBnL! zxOj)Smm2Zt8@Sz4ykg7WLMtfN%4gY6H(9{;2W60yFCGSceGu)=p=XO7?%AH7B4@yE zS8)^Cyi!D_Tf(j0ALmxRv`<`1Ww=;~dwjAGtV@1D3J1XSfeI5G3bU~a`xEsc3?ffb z7iP$4dqg~TqCu2c@#tpc+j&R}`Poajrsb?d#v+8v1&E8MJBg8016xI{xRy0qO8(PSq^5h;U`B$+u$^F2>3>bTA zC@wq!K*Am-DUUcm0`O1#eR^hm)G=&C7y}6zGHB&xVthc+)z|pD8N@c z9M`4<(-mPMGEimwLge`+g$P*1E=>^vY&de2`P0S7=U1`2Tq1;M#5mD2k3#5%E>VMm z;i80S_PmhO&!K|&HeCZ@q=*en%DsJ4*=+h;d@ba-QF!$yD8aJ=jtbMrg!2#-dO#N- zD1uEwgvANC;`2+PqR2Rb0X?REgG@UF!?Ny(nxrh47ump`m8uF&Fb5{0oZA~eVLeV4o*|HbA5a)U;8ib z*p?&BrRv$V)^tr6Raz}&>b5RcyEK&#?xZf^_SE6JpwF#_aNEoobPf84p)kFF zc~rwF%itPgq?*Dl&ZLS=F|o`orw$rp@Xqy1B7vNP7g?z!PEbeW-W>65t(Db^Dzvlj z{fL#8{$=H09yj*|svwygp@=lz%C?C<+wHfy-2IsFQj%~bFYFY(fkn*RiTKu<;D^b% z4{Z3_5Ek^tsVl&%-c|C7v=xSy)fMWvx#Oqv5kzWu{I1eU&-A8QvHPjfSrfv|Q&IKP znx2a^$IJ&RR}@+3EDCa-Iz926lB9emG|IzViZPY^Np|V%A?r-?$7lG!e|o)8vz(@k zK5Kl?0RZGbtq*5IJ%xW!E@!DDR%>+dL6>09UxO|}g~GxO`k`XL$)O9rtkyfL#)zqv(dF&D2!qM;-ji|Tyy~kW=R`Ip$7ElHGgw1yEQxxrxFq>B z63S9*<7HA*Lp6Tc_FOPuOM``L-?PCLBqoqo7~vWfmL=&jQsv-Enrdh1VUhAXOnn9Y z=ec;LYm|DvloF;EgZZ0V!LM6`o&|OA98!U}WDyN++hQ;Ju?Tf?+^=zqae9P4FHqeL zK2*WkMdImtR9wK>Bi-S`M_K#>m*>|UCPD2O!jN(q2B3%Muc|6Hs#Zw(uOp$a-8@tm8s8k{Y74c@a=Cx+ zP994OSQswS+O@~ab*a{MT=V2!8O>z2goEh7&`Ie?e?5$i<0~MIt>WVuZ!U4F*&kkF z9jq_U6Mcc3;f1u20R;|50E7@B>jQW5faVf++}|re;`OSf1wgYY!}XF2vlHY3qH10O z_8|%5f~|e@5psNr=mOPvfptHA?5dc8Llv&HB#Mn?ZYL(H~dsljU_Lz%;#&@_m9~+XkJna~H z`te`AvANZ>M5wR;z&CdQ0Pat|w0Cf|F#MnO&UmJV!a4`y&{mUKm%n+I6E38^zwbPL zu}`-z2mTIdt?S}4hDf;O#4}>A*K?wQ>tI%+MN@_BOB@}}!&F@B2XzDO-j>$~df+Bp zT0z8+asEi#)o^~z6bLGBW@IO~Hf}~lI}@QTp8(EW-3+CaXqWfn%a)hBYqrr(d?{|A zkhzCgZx(gqmBY6k-IJfDLbJ{ug8EknVWbEn{X_f5bOa;zo$S9|>+#VwYGVw42G3nR zx)jih;X?nYOp)QW~opbl(xH7HgK7Q*_{f{-Q0U+CCt8 z2tcOi-68*BcqwG;5RNRKA~SgMlWZL*%Espy75?ecI$JL*#*j?pM}0q1^$Z04E6@Y) zEXqh9RU&Tui;(wx_PqBJ*QEUA0k@dvAkIK67?GZzvpr6+1yN@%(Zr;J2u7FY7%xWG z)m%mGlOLFc16g#+yNbt?e}HlRas9d-xrXc-YTRS#y2zeCFHIS>TzV(>u#`j(H)ibq z_Kqe7&Pf!?he~VP%16Q1c6=U%A^h@)uwlfg#Oat3zAT!kF}NT0?2SZI%{(E%$RE}< z!8-lI-J!qT$rSNA3(IBJy^DGTNjljuOZ7t|x2!Ia29Xesjq~ReI<9<0&Vs;zA}Kis z%~7W>%`v^>sot}wNhqLunVO%E=OQCKuPh}CWOyX}WiK-8HhL1D zyv3J=-1A1fq(UVDn+CYKY0(0bg~IB`mW+&Br){fW9CKDQuua~TAvoO1LCEpj2YgYj z9dqKFIAVHVM?q@Xa(KVs_x1tI(1vnEn1=9Jn1*tQKkq1?cTlDw_+KVb_)#-hf++-S z=nqnIs!;c-_^WFz8}!I_>%g>C1)n{h&Gh+~WgepOOsXRh?FF$gD?`t%e9KLTQI41xyn&KBj5;wv@Yb3(bFI=9onHicI^4&SMR8 z#jh5Wh6+Af+QwqWZkGMmuDY}I2v7li#h9Lx^KGP1uHwT+=OcBq6U%1VHA5}iJ>?I- z^rY9~G%HV)LNvp)>)3F4a+uR7Z7p4+_8Sh^5tox#HT9hJQmN2PSglS{gFRB2!<1v) zLhpS8Tx)W$t61V-qpLFkRcdh@@%)U7{Gi@RV`|6JXsOmqqdTx`H9=!$yp-&gSt1-J zhQn{htfr5Lj)F*XN!)NGS~qn2v`jXl9>a**{pw2B3I}o8ab007=5Y*4lnd~0#n0E! z&Ca4M{+tfDh^TE>YYY5Bs=rz^_}95MJ+E<|M3PeNbk+g1E=gCz=nC0TuS3qQuf^%$ zS>Xh1UXJr?_M*kX$Y*ivDF)Dpa9FYZ4DkcwBUoiF-m^=@q+;7aPTAADMez|F1UxY& z3RE0eCKCGtXtCZ{6+y_CKZta%+hZu@xH){4q%n)0uD9lKvrc&Ir$$n9X<^$m-{7Bb z5HR}_pB?94iaLGPbEVxuTjdJfSkyoibSk!o4$saGiN(P7D8|r-6qCr~N*UxSpOfSsp>KwIz`r_93kH&c z@nl#Fb9>5lq0;YqDnsv(cK6bgIURRbwVt|Hp�l9;?;)nd*pJZ8{!3cBGazeqqvm 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

QNLId?LsvR6%~&Op`XPWH$-t3x{H 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 rR_5 diff --git a/configuration/documentation/database/ISPyB_DataModel_4-0-5.mwb b/configuration/documentation/database/ISPyB_DataModel_4-0-5.mwb deleted file mode 100644 index 64fe90bf8ec9f2cd8669b7a7f4e17eda306bb347..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 177110 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 z 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

2|Fr|r|Aq;JxNH;uC;Xb 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�ylR5D%ij&kC=EnE#55?h;bkdFG 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%0D#r15xJ7^W}iVR5)Gh=JwDJ%0!+?Z`#lNinF z4}BxFiu6Alp4OS|mOO7%-sF*L8`-FQ$kdQNwqP_AtKF2aYyw&L)%cc*&s+f=-9$h4w1_^VX}Kix^Bl+_@@8#}o8--w1u4rV?PD`|;A{C@ zX#dClhuap~Z%J9DbxpprF7A5il}!;u@S$UoB`SSZ%;$T}^ZEhBJJDyJa~Q+0eUERA z`USr6Tfx9prHkE<9&T08Aj_Io*-Q2=>_H8ucdf_L(q|sQT&BqTFm|Xh&t}XF(^+9I zQ^UmN%v6v!u}JHY^2QD8Bmuu~27J967vAvzBcT!YtaxBPgbH$bz+YWKE?I4a~ zl6o^-`L?#?yhXx1n}j>p*(ABWTxcaxtOeGH-ab?%F}&+>B}t>zN5m5aiiVg&-`S_1e`6qj#OcKGqP_~b*O7DXQ;IrN zwf2AM^t||`@uzs%pD!E@2tLEYaU^}q)f?2`bn&Ck%>(0Sn58d z;acbFs+!KG-7j3bDI((?-5l!q30;rJwhpq+eWc-}8Q8eKZMmS(C%~b1kE$!s$X^_T^6v zv$(6uuIBu@ljgW^ac|P2di|?9U&v>!a*WFUaN53S;-FBU2LNEQzLWsY4Kb4*;key$bi47=< zYpZE;5!O?*3yGF-qit!lFZ<&|>HX-VB&%~>eaB8~(cN_0UIcu|F~eM`k(e#MdFHXe z4>JQbQpjUemo$5uX#0_G~fD3{lQyqUQT7XD!t{T6n%55YwJ&yd1JpS<(s|J zsdi(vGe&QfSy*bWvbk8Cb>o?j*G7zsnpQd4H0?&cD|+7hFh}RTnt>;z%gjs*&n+uF z5ckH_Fr8BngtORx;s6I19A(0eYR}0c-)qmx%62N7lauWfRQ8#bbIhwCJ@a_q!=2%7 ztr|K6%?Inw%8GH)W z`j|GoFI&Y#Gu%1h8r{`(Jq3y5)a(_I&{Jzml`w>_$0PFK72&FF#~iGq*~Q9orya=o&C z>5yn#GYT@#^~jZ)@7vSd(Uc~Yj*=6pOFeATY^ZSc#1&#|VSt9Bk3dwE-7MrEFB`8vxUf!%QS&Y7Qx4hbKhWJ6bK^xpdd}8eSIy@fpY0TH$(^>*I&WN#Se@o zL!oGUhceAIv$mIAc=iW6dstdoot5W`?@f#K>t4S4#kPj-W@FUAf(QN8<+^l<#^Kla z9TX3#!|n3ehHb}6i;FE(RKhaCAeokAZ2tr3>0Pfko~;%V;l7AD%Uc^n7D`U?IW%aKUZSK90ADc8+@Ln)_xShM*#)!nQpx&%c1XM2qi~|Equ)nn;K)A0igRkr=n7D?=kN;-N3BdzdLmP zM<>EGN%{YJ5P(sdGd!CJ-~O z;i(V=$adqOVM4!Ycp?E%_9kiY8#qJoGcgVa_QWI&cuRnxWU@x2Qh*a>vPQ*IU`NKJ zwnQ2Q7)%fvM8Y985)Ka>EtBI&I2vI}93Ce4O^%~NGy*u%;OECUNCX1Rt(&9~K?i}4 z1fdbZ7C>qg;JQI*G$Nn_PSJp&2B}e~$o>%_Fc4%Mga@Y`qAf%OrW~Y3!y)rR1mi{a z1>_I7g(kNp;mFhE873CaYVL-rmQOx9t;i{M<4(<6rwGG z0{=|_KNIGX0*H^3H8PC`NQ+anDeImLkpNK=5eLDpE2KsRw8+UC1qWshp;2)pB62Q) z25)8f`O#-84w_D*B6CH>6Ttn4Xp5)ekbR*7uNE>_R2nqRUH~^b^5;Q>DSMC#k>PW8 zQlDu!9Bev8Xn5*`I6jXJNTobk_;zG*qd4$61Usl!hK2C~EAz+I17k(OrSkpAj^ML- U{PB|u3=JG6vN}4}&Nj0D0Y9@N7ytkO diff --git a/configuration/documentation/database/ISPyB_DataModel_4-0-5.png b/configuration/documentation/database/ISPyB_DataModel_4-0-5.png deleted file mode 100644 index 7d7c649821592c206a054a9a9cd3572f2d714523..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1188494 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%0D}$8xnCxsP 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%0A0UWanU1*gHjTuM zLYEC2i_xs8b^Go6GcfoI>!NZ{fQw0*0`3+-q?Fwv)4NO>y-sWfgOLLIzh40jI(D3X zr3IS~hW@@XIG;9WfBiaJ@>q#-eoJtxN@Z|YEfc$6zIgv(y<@4Ku;!_8@s34xBRI%_ z-;|<~=4191Rw}|O;}_MUfSyvM_eb-?kGF@wi@@GK@#rQn>?wUarZT}VY%-4306Fl; zgIxv|*1df;5D13RRcw`V*eJ39*^ky46)?>Bv`afCn9c1wtvC(bJt#7(s+|d$zdu!@ zhzOUxm~zE>WH;M9tCq&ZN*tqv}^XYLu zgNIpNM`E2Ru55!iNQwkB?muWEUzmAm1}!+eTMl-EVP37UGaO)ByEj`~P5u;xir;S( zIDPzk@mTXWPc9M$k`VCiiPI@V#8xo*O$Ir_W2MD2N&qI~=XdiS?V*Wx8OF{A*n{4} z_%7M%@*|~g`6sbcP2wMaFn0i#GHUNnGM-VQW*w#cB z?1ZwG!jOCv(;gb|na2}m=C}e(hn|wln$65KdOl3DeU2s~w)X7!Ac9e?&QStMY_J=k zg;8wmR@gizhp_*r7Tk2`$n6^rmq+M4v6fhoCc}Ovau63=3y)SSjtkka2)tZQ@@=nTtov)cr`?gse|DF zussA%%>!oaB4N{j=k%sRG7g z@&?%&CsFFpDCg?!W&i-N<-+$OSVH2KTJn~($6mnd7CwQ!#@MB7r9_J&di$-n_!vO5 zpTIiNit#(8GXiBq!MRUcm;7IvuSUQ^jVTxA06w~^GafxOI?R^iFSJ406Q5!lU#%sS zOx@HmtPUt5x9D|Q7EX$bOPym~{DTnCD5V!=E#f2sYMySUI*;VR(CBXU5lPa;H`v9t$=4I9)9tJ0J`?DŽteP`n>fARMh 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


VL0Kvwl$SE#E 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
8D-A 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 z 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âtsh6gLiq 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

6rY2D_g{Ec`_ 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

X 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⪚o<4(BV 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
%G6YE?<-fX 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

W|qbL-H+Y zuPBC1I8%Y+DSS^`SM{IQU=p;(l(Ieau?d}j?BA`9Ccbz<^T2hB;-0{57}z_G`$%2d zz1wkco&jFNV3=lS3uYKH82w?rFHY=?3%iE%Kc5h4z?s% z*{S&LgYifSn)#f8o1Y1rpOCUEjD|^rUgfXK9th`1-C`gk@xxuRdh%}zkaeS9lMiP0 zCE?&T3mG%w#3fg0HPXMDPmdf#Ijv;dzNEf(U_=`!74b!vD?@E}%ggW;^QuBr+g7;! z*3U(rx(=rk!WKlrtm+Nu^YD=S^+ zl0fvT7gK?-X6!FTA-9x%6;egDtWl=1=iPbJM4gEqXv^F(xAMXgCMRZQznp^0Y~o9A;=S6tMVku!Y^ev;j(LA8H`;mw`K|u7HF@R+RVc+D+aK(H2oIm1 zH#7n!Qb*t-hcq++uqqQr#q*k(I_fIgb&!Z16tEF8MuJb z(z_*XoK-b{vTWEK!d`y>IKl;m0y-hmI@5kErLj8}S~&={tpz6oO)1D5RJtb?{g<5p zMIyi$v9ZruA8S`@vrzhVCp2bFn-uI$JRfAcpd$$$5^JT>Z*uQ+c-s-_=V~Ih3=| z_DbfLGL1}|$N1Hns^=20Dw*p_AHd-3m$Wv}Z}f$7h&o{4UbM zX)9cp-|Q%-ipexbnT~DyKDo)W@^-Gj?^pI}y%+ppn`5}d&I$Lo(*=X<0=yPmT`bLP z#!4)sqp~dAS?^B~y`fin`RboV)mdm+Pss>W3o7KPNJ-H-anhK|_>A(XNZQp12^zrE z1AtLiM--nRVI6+Qjku6L@VdTHA)T~1onvc3r72ASo&4R93)A}GYuwUCl48c-LigrS zVtvII@syP&9gmLSs#hI=F}h+Cg)7zl{h^ZQ`t5t?If#J*<2WV99j2gI?A9JN(L zwqegZ;b%RpNaWUjeYf~?$1FJoMcWfvzwsT+xwB_$NsivAYg|0bC8_`HfK?FFRX!o) zS7&emlEx~uw%CMDK!1>Rmkwk6`OSxFPu%eczh2RcP|^*J(98xmD~kjrv#t$9R9r>4 zPNcTE?znPMVZ=5nHMy|Mn#)HjV=Hr$rPva?9aqfy3d-4JCvwyjwYw*6@Jf%$q>U$~ zK%R$WmUS#Xi-#m&pMvzL!)sh@F%Ps)VY69P zTl&S_3g)A!$#MW%t+L_ z(RTCJz~1nZnljBLJ-g-g+78V_jZuL}g;!RF`w%wqyTesIO653#o)uJl0FU zlcUv&-W~35fn)?e@pP#1jdt=E?GN?!XEWPcB~bxR2ECy!21mZdro3Zxq#c_!H8mjMGVSFYbJTG->gjvNJ3FO>A_TkR_dr9ybl+;v6>D!i zLLg@_eC8y1SnJLY)gL<}v$+*EbKk~_>-G*?(LK?)((UO0v7BbGWidW+A`4U0+lu`( z?!z)+#!=MNLUEPW!$>JF24bbup)Q~odd(y1TxtG3P9BGKYU#rrdtPLo%TeX~j77I# z<@c8j|EaQ+{(Uyx)+bGU4el%7bmR|v^}apT&B_SSj#BNFJUVW{ME!SVLd}Uip5yaf zqRU8#2gbva!F689EO?+x?nDFF7_)Yq|adJW1>E;AE z0W$s8Y(4RWnmY>eY4&G(O148>_Y$r*xNe!x@6ARF9F-Hf8oL}Z9o?Ja5HO#ztY0$m z&HmxoR}~(dlAP&qHNf01Femv~(&|7)fBZv>8O9@G;RgyOF^>)-jtC zu|9DKq9CvziKqcwUO~59!~A>QTZc*$&eEiwrM;(|2+!mw)t5jQ>zM7%(~SHsYomIT z^>_*{TZ;2*74?4}{b`GxF7S`RAzx2qTIw+yd}s0 z`a+XrYx$RHI@8gf%7MoRTINTGnozF(VKp-sGqY2glo|POhfIT*9En7hnTMs+dCgwv z6JFR$t~m zeh1J0e+S))jK-RMTTnOdyFdaVoF_-b7ZTc8IIAK(#XBP-fWXP9*+6tNGWWH44*eqq zjZYuUs?qmT(&OP9&M&H}+QA9EH(d5T!l2lJM%0A4VP?js_*O()cCiB|SI3N{i5YW< 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 z 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 WezD2o diff --git a/configuration/documentation/database/ISPyB_DataModel_BX.png b/configuration/documentation/database/ISPyB_DataModel_BX.png deleted file mode 100644 index d0f63bc274cc4b6e0848fc514cad7cf286039ac5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 547230 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

N-oZX`0xQw{hOGApz_3LbUuBsEyiOgv) zGN+F47pm>FX203HlF&KGbC92rX9-`CQ304RjlLluEF(D;v4;B*pBG z;L|Jrsji|zkgk9Z2L)%8s{yk@)nybE{jpZYzPljaF(R)eCnl9*sx+$abSD0-nJd(t zR48qR>quc|4bl2s^&!7@!j z-K9dYWZ#^fE}!gJQxLXR6?&&$pVCzDIp|NXEq|j2x#+)f&F5`_*FK;2)cx69*DPU}mwfj` zkwwzzB7Ku?D@-!IlXOe$!swE7ZO$>*8`n`<*PyQ@v)B6ecCuM-q@KYla%^wgK_{AR z)qv5Z0oHElDVRX%1;H!y;*51E=pPrtcl9UvZCCzmKp439npiW14+dSiA5|H&Kkc!U1Y{OJJzB z;2ATk(Ul!4-pg$9G}1ebn9`w4xD9=euD>K9+1$hpP|@T6@uK~59!48HnV0EQJZ;u> z+O+s-MGu7N6zmk;7Wi1BS-vcwB}JI1JHLD=kjQNCuKuP=v4IHT0Iis!13!NaWj{~- zR@E1>Y?pXg!`A=5czibtf`CAJd9eR>byYdMSd^56pHf?KK z?KstC+ohU}lS8zAaZMaA*R|X0dH5Y0F`c|IZWO#Ls*F-slq43h8t3uz^)ExsD8RkV z`ZUthac}Pe3aIhxS2sGfQ%TikVRXm$XdR7;8nRG8J@ zQVyGc+^VP)u&DxsHPD9wxIR`N;j;a$)VF80`it#k>8C3}V%45@d!D@RhXqm9j!Thj z&MnOab|fkeX?fw7)eAg}g*FL4e4B;OejEHyDlj{xXRqTmqLed)sTm7e1<9`(S_Wcqyf~5(>-Ss6TC$ z-lsX=l&JxN=s&%iflX-#nH3FLFbpjkDYPDX;rtZ*`eIaCLITqTodj*t6cMxJSN}Y5%JbDxJ3-yEQU8PJgXs8;hs6b9j&%u zc3|<0$WNMbs9ARw1&;|asKomv{>0HuV!rR>&}upq%n!&KW!3G2?aj5*{gcWwz43Oo z<>6v^1KZ~AyRPs0vogtY<=m_jF@-XQ|I!>~0~e48_6m{MoW%V%apxmSZDD z;w4Y*xT_t)6JLO4)Vl>NaVYNVH!5>BurXXS#{{(5~ zo}kw;$XupAW8M#}iyaac$n%`poNYy+${fXdhsMdNasTj;&>pl@jH8Tk*iUzpPlQqNH{lry^jz;7E`QMokgg#u0*CrP$LZ^z%_%Vi0{&GlQUH!Fz4#MyCX)k^ z1%!CQ%57QYu>eyQwr;M!a>iJf5qfnKAWK)TE$V&~kgUXUo zv!j6jO%G3c0Zm*AH&4913`)QJaK5C#mpD2XmIAqa)#_Vc3U)$`U&p_xYA_scwBu~k zDw8s>U)}3t#%z&nnMf4uw>>*CuP@Beavitt5Pq1X3zr22j+kpy2~M&R9c4C@xe7#0 zpiS8WI{MUKwOJSusLfu1YO_DDW)-C(0w`_B=lvadocG>qJkzxsH^!sj-#$HB%dJu8 z&NHnk<<9e?6Gw%&V`VG2M2o^&@6(<&CuWhD!y18Pcdcre#e#*!;^aldbN36ZM`tFM z%?dCSJ_gXUB_amDQ6<%o8IJBd*S^4!1{#q;TK{Jm`hP-1=TSoOkH=is;)Oju9zUj5 zJTPOu9QWhu=gg6B7*_9hBob_N1!ss0KaOAxvz8vmA3zcYq`nVXoc*6(Fu(Zi@?&xIDT8&t44TBV<3#sGMd#bPRjVb_ zVN)G}-P6)~YD%E(Y2jXy8^``Y_=ayBzlwDH$NoFGI5@9fy}C6F4S~A{<2{g*DPD+OFJlBEW<;_hsaM*OYbDvZllJ#qXW5d@RgJ*=kfUVb zVG-!Cf9Zi)K#BU4uu}&{7WoNYJ1jFOfkS7ghxKPo`??=a`&OTk4)}|2|Kei-uRjIx z`u~r=NE{%|-JT#)YM5tblqusa6`UH5?oe*%%l69V<>!e)k7}2lk2?hpc7E}ZesQ~)jR94%)&}-*(2e) zJf6Jo-WMpNEnVW#le@15gJ<EC;Xj zY?-3y(Tq*r``fk!T6w;r&b0mwx4w&4kD5SgDo_MQig)rh&tVWlxivKMWBnxhWt<5f zw8i2ax)L(<{WR;>wc=RE-{mlsUW@R-Ie>dYTp4}O`yheeuzXwR@Qka4lR`9 zo}D0$0uh_sG1*y4h320{f-jtb9>zx$V!(07fAf2(k-#e8VD z7)j#cBQH$f%dGyYn~k#o>NfVp->6$ym~pof!!PTDaEb^q7-YcorUwMr0*J+Q#4=bv zMLQWkI(2U7@YcE5b1@Aw`-;%mRur2sABUak(}VAmlSD*B7cN|2bhmkj6FFv9{aJ=&A@B-3t9Jpo`KD8t|81Qh{ zSZJr{k;MxftC4Jy954>=?<>prq;rtxp?TtBgr%_X^+SH5dnIBVAa6 z3ns)pQB}Z!j1Z|`LiPtfBrvj4`N$6|nL%;Etok<%U_lQ4>kS|cMRjdIKLBs3Jm(I6aXe!Vkh>yRBTOGc93 z?tdDtksfF6GmD$eL&It3o_{a4TXOh$sW*1pG_C;^MiKYc^OdOdoch7KAimL_)D{*W zFhu}g1Ym7aP$FZ^(O}VR4Xq6tj}C|6H`PzCkt{0C8g)eDqQAPvSO-@g_LXvgM!bM2 zOg+>!G@-3D@~E2DPrDF4Ujj;<0-1frpV;qTJX_ZR^KYB|sYp&LSBiw6EUjn-8Nh`5 z#@USg@$jEmnwh!FcWiGL>{JQZ+xrR-Yx7zt`aI)iZ~GiO$6UGA+Q5}5f zpv=nJI#e*@6F{SuCno$ zko)Q%vxeehWAn|dbYo1c?F{WBjZCK&q`Ix-D#>kX#}ZfHcji2FTI3|o)$B}+T5i@o zd)2i;Oqw02@GXJwYS!RU*vD^zH}^^2*7^m*x}Jd1Qc3YRtD_r}pt~Cq+J(QW*$LD6 zu{N5#y9j|msMF00Ot2XLh-s&2PDvrO5$uAFm+W@E3L}DegTIt|o8we{oEic^O9}3A! zpQTyfZ3+`8DDea=%Qv!FT*nQ~?OA}@r~p|ESWkg;e#+2=%7u~J zLojb?f{BB~LkBQh<+3_VCz^lrpyO;XUC6N!ZEh)8K#1|Qf>&@X9H;1y4p415o3Fu& zv5RP1@{PrSM9j{eR2AzKS+{MC1IhA8mt#yY~S5;(iYTaPQ=aLB+wG{koWB9 z3nB4HZ#IN)ATUV#*Z$eF*zI%w$J*`NFugI&=f{9tB9V!kC~L zNDmAVu6H6$EHV3~VxG?LKa_M4C5^U!UbB-BbA9$zPd|cP;JuvOEY}4Pt}0)2(#0aS3aErGW0M2Qz6#_OMhxMXe ztlibX8S&phh#$PWO<@g3NxreGv$gy@vsNsgmAjK$Yj>lL+abE}5I*neJ6^`Ao)<&~ z`NbM>Sv@E*hh&5YEy(#K;@82Kd-{133kPd_l}DGka(V6$e+y7fuHU?qlknVBO|1ipy$?*HCCQS+tOv zVZQJYObCKT*M&5I(RI(bW0$g%=!1=--kjUhU+n$}b->73GY)cGMxBkUwX`&s#^0RA z2UCT?U$y!G72P1DhY-d0indHp_>5|yjdqX^XH{dQ`jL1Wf8|-bWV|vv*7G=~1UJBq zQBv9u7^y8(b6PEB)@v;t18-+y{|F15sZc&duS{tA(h1y?Pa5Ii)p`MjVS`x^ET$X;lh%x)@wYC{#oDaCy=ioWN>(s32)}jpzDiY9u{WJoST0 z8=dL7HbL_)%p;5`wX}55@y#HPkE}2p>SbN!?KLrN!9qnH{HR=5Yd`{(CG{rzqH6zk z|HIG0R{(%f0EUH_OMx_WMB4(1JgmAvf2mKk9QEq)u z$c)1HTqn!HDlW(OAb}#GfF5lYY%gXQ=j%Oq0qSl-0tMM-=p~ejiMEKk3y1)-(nmEl zeg_9%zI;)$FSUbfro;A999%ani9gic`W$J><>{#HabUKfHcz{4S%Q~aqV#x_KgiC5 z=mww8Q%|Gbsh}$q= z3kZh-Uw|`d2L>5`a*Vdno%<428?O}~i)V?;&?S8KwUjMaa4%{vk;kJiM!1h{&hu=l z%KlxA;6-`&jmc^-;^{(V*-|Sd(P)mm+rdM762rCmw*o;Ar?tkr-i3M>W$$;$ViY?$ zS>C6}xC6@+m$U??aDYjL*7C0rjPzg2phFnx-385$J{bi>sIXds4q0<9<_u5?RVegE z|FaTWFp}>1w-iA?Rx1wYblLc9J5;iY25l_wvCotI4Uq^u+F>56&SZE23aU6%C~kTC z4zT)=+d*`XfFlraEl;Glm_stcS!EEU0mPz(C2mV8sT!0wMk)>`+e(*s(#NZxp0)%9 z43c$FJ~!(MN&_4;lBbV5%#q7W~XA?oBQtvkDZ-8^+zRE%Oy6mE0F6 z&!cwLm)Pl|R@u9(DC*5xm0Yh|lRnHqy_YX-Es(nW@s(vgQ}1YL++*`cS{mODFXd+H zr`&a@z}Z#4(lhX8YNuScTqt)a4=0#-3Jj+V;_*LQzj(IO^@c*}6RUpcfcsDG6#J4( zy#?`t!FMGJOqYVgZ7MeNe7^i;pYh{ zx|5`U{^T@WXcTx3<;dFkZ*jV<=o-0$v$IFfpMRyLym2chTgx((@sZ3D1!GEYQwA4t zjYw!>$Wk9g=Mh7P8O4G9(e+V(5S{Sv&o-hiPaCdZTs`z%I6vyt&sjAX+dndkt>?Npw=N)|e_fEEBK@{z4OUBc1!h$yJ96rD=o69xKgn)%&PCPf6Lka0^6#)t}M$oq4mp< zI*?bU<0(OC@|^cw)F!XHcP}BVUmDeok7mbI9Q?mknAr?V^^J!|pwSQ|d4H&<=P)7R z#}CsbcXJOzjpm_6RZ?59J83f}_)IohchK5kG{SB)yRN!~TP=t0T5P{U#uOT(@jz5S zIW$|Alh;NQ1>07V;6rDO%g`)!>h=4?zF^dlrS3g*$y5dn4mHp`C|2xIJ)huO$FaYp zbLLX={wTO`Aa9505k2jB6b4(r1vlpr+s5kdbym5PTtc#+j0hw_vMG!P3$|$o|374= zSvb3!4iwB4OJ5|b-DBrX#J$U_h&>uP3)7}{um_%g{ralANSHekW0%SKI49LLbuXUF zCp-Iwn1yoYifA2J#O$F43*@2cEwqEn%rHC71VY-_O-Mzw7H4-C@$p|XLi#i&W0-tf zNAcPat&zVIHTjX>jSy6=Sm8`NOqY_0ZJ6rbNBU>j*Mr3b7 zviF`xLT2`MjO@KPzt4S+zN2r?>-jzXS-rT=eLmy5-q(A;pAx85l|Y!sYy8U}23{9M zqtt?DP+l*}_#j+ptk5TnXqznNXevR%sh-s@b}#-UQ-Xh6dP%_2w0U$cZYA~xaooae zly49`Z_3s!j5bTa*k+e(oIjIiDI%FHWPx^fAJZG&(EBYvf$4U4x~jg5w% z9u3c*4z!XT&HHoiS42E|%v8?T@pvE?FsFV#bT8+nXhS+K|Ee-**&P;0!`ji zV?sOE)ODZVu)NAUF*6&>$5f4p*+*RkBWk+8TF>GQj$Lc6+q98@%j%Bnmi7w_AEC)X zZyd8%H9i=p^=j~C2Vnd~xAhE6&(3%dLOvr|AR?=wDksOnpG_%Mt8$%~FdD{|H1d#}*JDs4sr(ldLU*ZWiJZ>L~(iTL1mES%?0>A#16^0e)_ zBzL7n5)R(J@NYMe(b8yLRD z5v4Tv;M3<1z994C5w`;L%Pz=b7K{i znDUas=mCCYfq^7>89mWi4L!#Z6h`RE#W%E5-~B^Hw%T<^dFEJxt;+Fu>Z@WbLSU<9 zwQhBG(Y`pwHx#}d+_oMqEt8jR9y{~ys+NXc4!>I=8pv0oSWKI}mc0^{`uG@evKjy} zY;Em;L8;akAC&U+4Ej|_nCf{u6$_1twkC{2=A0aOnI%xa`I`%E=Zo>EJ4};VISRNu zJ2O`pxNfrt^$*{qgq?eyb97<9dgo~6UEH~9@|)uBPtEinamq-y`yW?j@n{^ab&p>V zblzd&89nH1?b7%u#)LMJ^iGyWDu7qgrINpnv1`N22vKss*h~H=YcDQ8G@1bgv?+wo zgoR-sKh8|+D*W=yHhFh-$lUmR}h2$v0o27Hc>p^0HHrGnvuqHq!urE8p>{-$g|ez&!rn{7BH zX!w*2ZKI)zRcc_{l#Sbgc;>~^uPUNn>8D3@N3pIza({z)@PCRafP_tmtxGK$l~!iu{!Ca&$rtD{x0oO{uEb6I1V1ZwZeN6hZvniJzj zB@Mj*;}qn@iR9D+{I(rWx{BwhWBhx%d2F>rg7It@veBJCfI@}!AR_lrC2i;ti z3^dm^R=dmzpJJ%IsI|hfx)KcBylij22iM)%ix~i?*7Q6xP{9a*TjU^;oes2GB-u#A z+vHC=`4ty~RWOLCKZTVA{?hdo|5w*HuB#$3HfAhshN$A&Iv?=Oi==6c87~NoLBLWu zgZ2&3e@ei$b~u|w;S4n$S;fa6ZqkSoQa2Z6_~}H>c6;1HrWyu3K0_Sv?j0HyrbQ*= znaOH=&_~ellqA%|W)ZQGA4j>j9d4LMAFK~ryS+MjHL0Jur+xJD zc!zD7p8;!lW3?N}624HZ;F-6R`t{Q0;&4XKwRbqZ^EN-OqwomfaMYVzC3x#f&%Cl9 z^Pvw(Db923eQ<*ayr_88i=qW24_fL^fDf-~j-@4Y4PD`}`@tP9VNf54f7iT|Hz_ZI zCNh8}9-nRB0CdFPvgN9~LE|A{_rEtD$0su<_qtE+UgO*5;L<+kTidUsSiX`^ap9PA z`r$N#|1W&BXX@=gpZpB_uCt~lo0p1bPAn%ocrE|yvjPQBxX<;0r5Slnw26#l!O4B| ze!%cO_Io-~TU9kmy5hn}33K|us(z$7HygpUayY#{&Y{SVBIJDZ^Kf*nRJ;1XVrRS* z4$EJWVmY$W5md%lDRn6{wOcyriIsR(dQQWNE9W`;s{VPd)l!}lXM+umN5}6zpb6K_ z5is2OwJfX<-(EzEoO(Z7L4yUBCwjN13vU7(B=wxU31mq@2||l0uUt8Q6@Sc8_F_;P zWzGW#>^%zr07Vv#5(E0+?Pa!NoZcTMjJ{GNGpynE2g%Dc*c*L>2v>X8la2@;sg`P| z<^9;N`Cs4ajo#h3zoNo1w4=uBFg-7Hy#7*+_%QeE^CfEv4$quDr^R+6SBG*{`c?gF z)~|*5_L9wAHr#q1umRxp=2Mws(r3uCkFmlBTtdjh;s-O5HnZyUsN>6&0mGl(Jn$n) z&*4r3ZVCzy+-Ka39FZIpoAhaP8|&w-USH?K*osBp2gB78`}DI|kN5i*nG(ac$xCfE z7cYFhpzo+2=XlgzXnK&svl21v_Hy}q0)nntlV74g`fytjIoS)*@ye^|hHV8*wKYWhdc&vZVbU#10_0 zr6h_S1|TWmoF_&01$An9qX(Szes>f4FY&?XyH=v7fRdTGCwUaRyCj*h5e?SCMI02f z0q+qHQI8xmbFM`xmrN4Q|3O7X->fMytc=DhicENoiDf9_xkI*Ee(5Cki{aK+jU3(8 zrD}aCQ8i^^=gCU8<0Hhe^uPrlp>I*10T85xZ=N2F9elvrMoeFhBfh)B_+$u5sRG#w@1;UbC?|lv5~L~oP(p%3XzZ$}ii`(W6L7ixpnahFNCZo*gXD#>FFRkT>LH~Y@>c4<+*#vukKw#^|QN1Q^YQA(O zQ9$B-kcm?3Ub$bYCWrb~tTgr3J8QE=^$$kFNgR#m`&QH!hTZ1ADYjQT?>23er^FJ+ zUMG(^S}qFu9s#$D%#P?7a@NZ+Z!$3cY!Ilks66hta5+o_UubJ2Tgm>@ZnFP9)*ak0 zw2SJ?U?U#!D~w}8{O$So}$3(K$Xp0n@zw66%dO$=|TU1;A~7V2^u^WB|FR%FoV zcX2$ZLL8g)Md*7;+gEdS9oGheA5(ZHCxX~{pM%RhL}OtGaWGDPLJbcz#!!*KQz4MV zPF1BNX7+bn$aiCLoo{E%9KwZ;dNX@;U}BCWVMDtcI7#6tHq*eeV_vNeI_)sCxmavW zw;mSEruz4!^Gkg{z^<09=4uViR*d>0kg0G8`+7+lWuDVqK~;E2Y!p;@ z|6yYn78E4asnRQUmf&zd%G_HY8WhI0x&!%R4ANENoAC_Y(smw8cS6}?AVvWd`v<&9 z_cj=s1+qa^OOqI+5?P6!cNPIEk&S@_Q+XuZ?~4p10IuhWa#{Rz1=4sIB$^1shR!$B zyG;56SEQ+3P2}!0LpV=}jim5_z^nT+xhv*mC!?WjD`tZGm)OWhcWadn+Am?_GP{zy z?zb8ct9m3mFQg9~3iYsXC?PK-M;R;6C6}#Cy_M4gUja+ou4jjDJ1yMH_Ws?+o zWW>~+Rs=~hW7!U81Kk)@&5tOaiw3tKT=E*uoELKLfc1c;ZSA4Dz+i|bBD7)~ZSxlMlNpo6HM?qd|$cED-ePmHnGqoZsvBg)& z8N*%0Q~GgwbbOhXr0;*O!uWtQeNhj><<3J~0oSLv&%mH|;=j^=Ds=&u7P<1Gp~B+a z8&)x?pVE1x3mS3M%k7pvOGa%k4TTxJesp$3RgUVVMINXYD>T+Y=9h{;IJ#mZRpYa2Jk@}$ud+xeu5QB2JR?2`DNp*mlDOv-a$(g#%3Sv zAcwjZrz*_Zoin-#gF3yf#|d{(r`APiOM_F3LP~&5XSJvAc);U`CX7u352w~I#J;t7 z@kxxdZFdEaf<4au2`Ay)R-TuDTQn_fY2fp@Pkri7`UfgU@XbFP8pax?^aS#=x8$jY znQv)mH#-_*I9WfGkbQa3x+|XIW@h8W4wJo*hCI%NsR3k9O^tPvwr1<|WqSGhnx4>@ z3&LIipUI4laR46;+Rk@)aa%)c&w+QEthU9RF@*bNoF!2!=<4VCsYmWWyKh>JY0TO(Pu|oGNDIfsPL`O(O+In$}9n>DIzzi`|?V7yviYmSstmwDbx1 zMHeazP)DDzFeZ}4DRU_V`kw|sRROizV`Th%m#VfC(Ndp9?xUD3p}futpoC!ft(2*u zF|dVgz=>rWh}pCw@9ynTt|UCudRZAs#nh4M7CD>WSpgF`uX+>y6xme;9D3=HeFdGpdFh&6IuD zM51p6AK&5+oeqH%Q2K0OXY)beF<4}pfGkC$0f7Nv)CyRh$dNl>dCufN97+=f(90Gv zNT|w+1P|{=#$SYUse;pOaY0@oGm_%qFxTnrg`(yDs#l%~H;;Hgqm9)(_JQ@-*>Kuz zIJ|75KGn@>^2bWt@DYVnoM2@|#iHQ5q(v3kWYL>SJk1J8Q(1Vr^H`roy~yfyIzPX~ z?Iw^~v{e}kEpUarwB|@d;iq0&*~IT>zO*sF180%5FK9)j?m45D^cmnCwO?_s=!42l zy8Ka+Yw6%d`0sJ`@1o)|XfWI*AW(Xg%)DZM5?dQl0W6OIDE)ETssO8%K>vO;wxy^# zV&enSHqS2OP-H>lp~?`+5otu3XJA53^Q1kqq^s3l45He3Ov7x&dAlWoLy&m!X^V+Y z8`d64#0OFkb$K^3Q~DhC?tXU$#|vvKy`1qx(=Zht4qnJ4Yf(;5B!gBggWWy3j8(^C zVoPJWoOf-N5d{|@-2mlxgjEzN{seja8yM5c`RLx<(()a+9c|l~UQsB?&My9-xB%Jw zb&M3gh$gbM3y#*kWys=TK>QyOm${i4t7D{rF*$VS3b5rztu7#*wc$vGFTD^S#Z9`% z6b3*41dKq7x9MM^s{1n_j0-fMx{@eDXnHhCVS`@9$Q)y5~OPT!f^sh|GnQpC9rH(-gk7R zFY4d`D_s{{o8Yg&G#}LtsK2U!kpRhe{)6_y6k#1yw%@G34FJLi=p_t~rfvj>B|? z(D8+!13~NA9|=1MH=&_;1^+@Gefyx=XLq^pX(5LkE!8v^K_2AJUgCC*ZB;8ohMiEhH2?<{_> z6$+j%Me?_&i>8E05*%(^LI_$M^^7;Gv6*O`j70=7{B+HU+B0gC)Yhqrv~KQC58`Fa z&AMWnucp|;C(>&o_V&(_U+E)C0>!3O+`b#~C6jU%*}42%;_s=~`ItQFMrP+6Kj|6c zG51pY4D+#UcQOzVP%{I6r+QR zG8$g!HbD>&Xj6U!O6CR8sBL69fik(n7LwdNY@q|$G2vsC)n&caOHH3sSY39^`KymZ zx)}V+1sw_t12W4$nf|=NUNo@DP_$p=YhYsjLZ>`nt-2&K5o@T8Yw_7be%$^SL)oI= z6{Ra3ot{1>vg2O!=T*lL3s*ystEICRO73$Fdn^2*{u=Rrtj?iytX6E-i7e`Hp((Il zq`}c7V?j{kth+(&-bITMB9cvrSkl1SZh$GxlO1A9(xJ==A~~? znIq=`F5T4H7t_~~Ega~vkOa4B_|-^7Gc-`qfHT9_)a&3rD4t&bVMBU`!KwczOY-H> zN+&BT_WKy+sg+s6tzsA#4vi+HGSLDz*c#?$;%oiv#$Os6-!egDbj$P=LrF`jz+VlE z=Kimq!d-Y09Vm|ch&}^;Ey^<)&?+$zkYxJr_bLh=jpE;r);I>59-u?`L!QxJhULK8 zbS>VW$UiQ3AX<-)SJK9$&GN|bQ{^L{m_wC0vQ$jF^a{gI=SZ7;wj6^rn*E0sxf6M#Ags1t~4R*U2;}?{RiGVuTogoe5o`@tYthq+&BA{2cRTBx_lR z3WixC9M6e*>{rA?I6H|^IA+kSd8m5WKG&?k5S3+*6b%-xM!-Ks(f48qTCe{r#FoFj zq9U4CvS99kU*Ehk%RRq*HsjtV@*WTBDm;?9V>*S^ zTyB9^&reuPz!Zj~bI?@ALw%)RA2t<{CW+1}WWT0aEqQ6{Q^-FGF=%%w0Xv#i@!4D! z^lk_VX_Z}?jcn2U|Z`IUFH4T!+9wcd@9#hu2%m81; z@aKIbN{>@;GZkE}|;-a2lPSn0i%6-vT3y0j72#ilFP7~9m-SEg&z#+a` zn{*5O{#hW-nYO^V)E3>J=dw@~Gi1NLxHu_qinOsAEP6F0F_G3dJ_wu$zB~kTp84j7 zf8P>6dW$}LI<-3j@iFdeZ6;G7;9TQo+);~m*hP}Hz>=VZEo2f`bXHv-nvoFRbUA5V zqBv=Ym9!M!jaouvWvX;JaK3F+oXofh*uKHBfbE+hZ3$d{mPcdT6w8nbBE+Mzuw(~KjS!n*S4|#7-kO)Kf!5MTc$VJ+G@m5eJ6%5-lyW=sc zP;nPT0yIIy(NCoP#N`PtyJ%>=_PhjHOpP(WR)v_xa)4*b+eJ{RJ0!t>q0EhY8z1dD z+F7)AKjOan={A-Kbq~-H0;V;R%pC#WuagAIadFZ3>+DJ`Op5wNCHryQWG*syz16z- zr3=rpm*@c1AgW`z+z9mLz`Kf9{*u36Cw@*Z4xu63QXEtsj{6Sck0`Jvd6Kr z$F&i%HsK(@iWZ0-ON^+HRi$Oy(X+oI!vXQrRnH*-mW)KI?n^+F|1Jogub6d@hnH7? zr?{jfoLefmpmJk&HTP1OI~^=D?lB$k+oo=O1lXrQ=Lk%%`8vb|!)ND5_PS_P zR^!347)yCuyf{0DuP7nfN7Vy5u;_1GNP~4LqDkOHb(0E`w|{ip%%GLQXjiQ|tToPF zQc>O?t*J@%_6CX;La{j25z=3MRhz=WY^Qls-}EDh$l82|Pm=$+32hSOz%al&PR1+}-3)eu|N_Mi%n zd6m`1^mcNx`Oa3%J!{czxRqWem6&Ampw`cp$TZG?>V@{YLTj^!cfsWO^Q*1n0(_{X zlDkPQz?0+6Y&D`fnuCLQt^>w)zK*EDI|0 zRoa&O!*edR)y~V+$3%)4K3B*2=+v~Bn>dwZRPP1*KD>qPaPwNE4B>s5d2j>+8&QVz z{B&#J>UK6}ZwA4;GgUZqyneh<->$sTu>b9FMPt}r>T1c%cq*IVVPEY9MG;S{a3BR( zS{w}Ruzg$ztxQW?%D)eBw1nGC3`*f+fl)k8=A+Kb)2)6!J{n6aLR5QWJz0)idXibV z`@Q{zqaQo<{iKt-l_$S8vKP^lXY8f87vY$^!P&mGCKz%~F+tJ<*_2Vs^U7e5z8x!& z-7l2)-~cUI*XLn-%ZYJW2xkH{r8^Gjg3?mbT&tfF#5}?Q8%FbrFuE0;1+I<_?&9K- ztKGd4WsCORk6Z0O{i2Eq!^~X@Y!+aiQHnwpckPd+HxGE)HA?T?ahlvaw{p@@y)V6B zFQ_gjvELF?zoWgp-zz?pofb>LmQ_GU+_wrm#YQSeh}GW8VSDgFeE^Z*99Z*z-C=0# zy939rNb%Kj?$cF2(gh--m8fXvqg;*EADJ0X@EIn)L8tlGL1&gZfaTrIEoiA|dtaN-!r(h-% zu%3!Rs0uny&;Q(fn;jR8;1{%Cw5M1WyvO5Uu|1NS9OvKC&^Tu9G8pM8yq;maoHtr# zEm>cihZ&b9K9QD|a!rSCTj-#Xjaw8@9A4l4P<=Biw4RLxauZ{LB1l>}JN6r74Ya&5 zK@onQ12!|`#(NK>YXDJNF%e-eFb+p-w6CB_Ar4{_c@x?LzSR0-I@gd!P1(zRd@s9g z8+C^+jV6IP5$(9bqiIm=k_h60g2wn_G(BVh=k7j@YA`eB(G_f}KdmqkVjS}nyQv<& z(|B_Y58XX71kUn-qT>v7%I{wu!#+zKgZ&WWEvL>-py!^kk9jqJ^~r1tDZ?~Ez}}^O zd|><`$4c~G{7z=X(gEv@d)8YNZm;g#$H*>&xX5~=TT>qfg1Tv7TjD22YaJTH2Q`t( zvvi|ZDLmP5z)mU#XB#CA%4?$)#>kmH3{^E~><)&8b38#o3kMJnnHt(0T*=?AkEW8V!rDT}glnCwjl1K4`7 zuRoDPq5#7iUK{}T{t*A)V-J9*tKI)lR5RUD_Oa;yv_hKV(WQ%_fA!iYq}2s@hZ3N{ zw)azyD=phy(9X~HRUdTv+1Zg#fkP2J_sV5{OSZ3QDQ>hv-|ED>l;N$bq9O)jZ?0Uy zWpSwfeeDO=gQ5>3wkdNkN#ey)tx1-2-!<*ze&uXPNx?JOyZbG#QK~KXdF`E6yIlSy z8=c$)mFloaRsDz7kp`ttmK8j5NiL-v0}9uGFX;h zM4Bl0X3Y5=mL{e=UBvX=Ud?vK=1L^$3(mSNbow93I!UVz<=OAglrxCc&+zTjFEo#F zJ@`yAD)+8`XxXt|9M4WCheee|%-Be+xneMOOC`=uca^~`pQnJKx?7suPaKTb(#KQ2E*Xkoh$$rJVsiVgtP!msR^1nYv+a1qR~8;$oUFl( z`$3Z~fZ!{sEK{QQkJAqfH`d_{ZP9xNOG99-Yin!gVG_oP8=v91oMTAK-6PCZdd;wH zd4Jf_%oJz&F%R)DG?FiN`!DoQ%>GM5GSFOhnkKDG5&cP;+59Dz+>^}xa+sf-dNuc? z?1DtNRO|SPlc@Mm$x9C`KcR0xwk>&}fml;E#$Y<6T2BV!n`<#=iXV2gQ!e#|0we0t z{rag5Yit@Zy|;*}&2;jx=YSfW7q7pGTW& zpYe+}bYWd0D*M}A?fvA6hZA&3`!*R#hLTLg1x05=n7(Uf>$G^8kyb4JEU%h!>MAS^ zRDaQgq>!p;P4ErGS$S0tiFk!tiGGuBf=s{{;6}sUDP|;poe@@iBRry_U^TsCpo-_S*J$MaR(T)aj)vn} z&juSs)%xLbs=gP4mzaKa`I62@oT8A-1cxK`eyMTW>GdxRu$U)N=cM}DhOfnyJNo`=}8UW{&Ah@{vKDW|ewW zq%xw5ri#q4l8xQEk_~%*-3GVAkkuK#vv`7R=j#|}hnI2@cm7Or;6g!$$&YGr{yaOP zFT*h z^K%R*<>w#aq0WV&SW;~`{=lq27elOZY}G69Nevb|j0^ONhzbVvlQ{v6N-=OH2EcJg z`L8znK@jT~B%f5?r_G{5`3i8YO_X=)=Efi%w&;I?0CVuVs89bYvMxg+U}Xp5dw&JJ zEiI#h-nesLHvRmWwDsBCEc!=qvy7QIrJ|DZO~vhUILQs|TM|>e#LUgz!da#@`Aq~8 zeb1Xm#XjOH+?Ff|ykIgVdw?G^`O%kj+2j`p=8b+4oq_t6%h@asUnxrnxew4-HvQ$BXDXefkS3Xl|RVk8wuv#!z zR{nm*s#gld6``#lZzw35J4u-x1h!xx%&t?JExpA zD;_>s#XuZ8{J7+&x;i(icW>z!A_xHi(icwz&8x;m6&l$ZBexR z0&FId7)#A?TuB15aHjzV$(YLS)ZtiqWvL6HWkG?3#BoDiazl?shBf8cc})@nf$#7| zgQxDoV8&o!$o&(%BRSEgvF%m{*KU_I?FMfO8}ftW-nA7|;*;g+#FN#DaX&WS!jr?C zA2ky*<0%N=K=|jxsT5!I#^1m5Z|^gIC4 z=b!$IIs}Wnvit{0y+HBAHQ1VS$1{tIC(j`>t7rIQTNKyx8jS>wd>bVUrwD%6y&JNn z&u`xIwz04mE%#MudeNvZ)|U&{B3|SYO`~$GH#y1Z^B_AvlZQvEz_3#qLC=?Xo3Q7B z@MX~C5F?u$k)|;*`=c?RODBaEm0NBi#Lw|pRLG!?*6d*#6grKIq~_Va3;*HOgl~W- zNeFIm1u?IRVr7Bw?`!l+5|l84l^=Pd-@GqK{c4?Qp`rIjz@_}#b8%@J-57B#L-ibp zqx8)ELI?tIvcNuF%<=$KCZHI`Y%X`5nM6K79^b8C8t|*_8Dy-<(42{HZcQaXTq*i@ z$X;2x7WB$nSrq0P)n+3+)I3Cgr*B*!R(-t+)m+?+vj8Vj{e=@HL00R^@P*5&&cbbrDGDS%{uW>(zW=nZP7T_&!9?Ma57^1H6n{ z?<_HM+&&)y^O=>sh{7l2O`10;-FmvEMNqRL{7nWQn~UIe)+)(ipUrzvicx}*SB?Bsik-xAQ{?Inh)iSTxWrCo-%;Uc!oY=@eFGR#tBE2upFS6XHanN#6{7S!4N^%7YgP$VNR?I}b;U3!axKSl`N9 z2*SGl&E*rsJ6sI6C>FW*VR}H+!~@Kl1+Q*`Su^XeS@WqOEz?}b^CyYWb0L=}^u&2I zl$+#i7dfRU%OwIM0fvPX^aaA_UfwV{f0hni^~z1x7eHrj7E@c)L~-8L{MC~e7=4EN zNjO#Pw#PO3(k$v_yUi;C+?MNgwY3it6NuAQ8-pFAW+-8q7;kiKpvd6`6M7>5{vUus zn6hYcOE8}^_e}y`;PemCP(p_1g5TWM8PZ3ZSFGa>x(L;i#m$82LMVS@JcC5Y*T*;H zIi=1JXWVmt1gr)CumP;V2@Q=8DICQ+d%o|xCLKS9>@zP#oLayqW0qS`UT*Xgo;Uo2 zOK{jlfv7n4a9C%qblaK|>y?b=r9aSQi|g%<8;!GjnHfdIr1H2qxG4%xytHtdK5)M` zOWXFX>S5@l%2!FtoylJMLN-UDb^5%<5aWvlEYWidnA*iyaX|_n_ipr2&~xyv6#<$E z^(WL2PwqW(g@niJW}yd*JqFYpY^Pj#MobI-;PnElXHaV1%%I}U-L_zRTbq*e2C!pv zB@V%@yGZK^#ao}0p;Tm3Gyp28vL0N#vgYZo=gp-=PW*J{vn5(nIUlZi^3`<%iuiVr2~(?ybl+>!*4SdVeNYI7=g%)i;V^Ozj+Q*TUK9F zi{XV%$=94+$a@Hr>QDF{5b!tmC_C9Wr&ZoFFA-M4U})K9rdz=%l4phGKLyi#hEev$ zu~Q-QUu1F~^5Jy3C;7$8C+h}@4dyMRS7W&wN0nc_2}UtnLO*~rVz13GU#yPn2Xih) zGw{4L)+evn9u;Vopct-rmEYk;x4~TeQBxs*h+9R_i?F;IstVN~UpP`_1)lsfw;8&w<~Qpq<`7!=-{_AN8vKD7>F& zeoa}wF9iMsdneMd?@+p=nJC7UsrE?`w!hMKC2t(=^SRPeZG6&3k!^H!7Lt3vSk&s3NGQOhR12BsbfU00rOJ#K(e|1^rmoveeFUX?;&bw0#=yT-bJu9 zSE9L{L{!hU;v45UgZ*u{&f!!2qVjg`J`8qZwA`7RoSaF;sE zq#YP|-)W1N>G-CLY|Bm6bnoIrHbkUl)qITRr#|Jmm|b--J)H&ljEeNGF?sRa_XLnx z5E__tiehBbcEXFm(N25zj^$=L&4|bHm)TcRRZdoYh)g2jRL$_=HhhR;KkE4SxTB*3 zP(k@Q`eWM$7L&b`o#}`c&wKjv_ytJaCaUqliU=unDSgNKvToYz^EDTN^0NtOTEr#= za8_1hGd@Jtle%;2FGm7hW*3h-;k)Q+=*oAftIcI{F|jDgZQ07&N&r}q7U1n-b=PRqpd!J9_v|BFh$01x1^xi z5;pR>8LGZnoKz)ukwd zU)Dj)Z8j&WV^MccnDF-06&Vy2Oi2)J7!P<;wUS@y6V9(AVW#k}aEI)tnZHD zkvbY8ed~JibNR$#f76g>lm58UnnPnLLut-QMTWEG$X;#!OE^<*#0VzG;~s|;y0V;{+1O{?cXP2 z?8)hgc$cQ@%}`XLC-{gV~j1-%Iaehh&=Vft#&hUgDQFMY5Rd=|{z?4`-bM)r&HR z3>fa+@>XDQC&_Ahj}Ubo=_I;{cR~NiwuxP7eqGc#q7xVQK)YewJCVkuusSH~-UD%z z(ZPS4??m&kh83mA!=LN?)oyz=)x)EEixC|9&g{Hd##ATy1}Et&mt-OYuu`1HThsK3 z()u{OZ<(BYy|JwE#*QQ>-_D%!lQy->c=M37c5W^t%MljV-cPN~gKTFBL2OEo3N zjX?G7H^b7NLc*vgb_3XBeEEEDrRr!_R%Y4dwShIvp+?4$SaJ_Mb(jMe_^o`qJ54b<`$Sa-WUxL z^PyHIQyMx0m_(rYvT95Tju;m3t*ZO3nOu4OR&yvA&#q-%If|%b|J5?cy@i^yaoyGK zL+67mHINNo+-!$3EUcrw8x#~2(L7c+YkHf{Q1birgg1U5vO5M>k7TGrQCrB0vL~HN zM`u#I$>Uv?dCc|HCkgIG!yT^-|GZ`JP_vgfe&>F7uS0r;n9p__}BqI{{b?y@R7fN zPmDw?ec9R@O^r7)kz{w+)9WQ$@3^H7XtjqgUXvwgX6@&w5A_Qv-e_;WIW#p^$33dx zg}vRwxILqi@hmb(2Cu`>^J(NVZJ(LTxcy7~H+8+IG%V1$puxT0iEFqUQzpvZAa*&h zn14kD%dXmP$6KT*b>6e0d>wpm?j6^C?<3Rfk`s^ z5QrYk&sUr@h^+m;eV~#dRk2Ra(OYGq$M$d+hobGHI?m;6_8{%AQ1wXw0~lVimT?co zfacYl_;)fj1U1A)Dp61M*Zqqq!nBwZ1X4We2AO&WFpEDQR1mM+4M;!VoB$^8xM!k3&HX?9O?N5$*-PnBdUTCJri_Glze!`;%aWPNmxg@YIA2| z0t$WGvGfiQxeR0ev!Z~QDN-d)kuZ6Nl%Zzn2XA^FVAAW`=q)skF*%*=9UmP;1;Dq3 zYV_RV6*i)C57<(pxs2V?TlSe#yJS*wR%lWV#!hC2j+)IvhTSZj(0~I+>$XzmjXkAd zx5LW)hxszi*8U>SVr@Kh-oxK>tBMF68`z5SaVO~dvMSPpI^-n*Amxs7u2W6@fpY=d z;~r>IgZQ-ufj!?J3QQK*f2|~p5oFyt9!!2|^^(jn_w{aTF(Y{r7MQD^?!K&EC9^~S z76xz~_EnTf8f8)7BNhyxyrzH7!p6is^>~#o{(J&>afX?EUbu zFmHYcz(;b4zOG*xvjfI$rrYm!AAs^9W4%$+^W=wY2)L2T(jzkgr(95Yo+Ar_X|m)D zykMSyz6*@6XLbJgeNn?Ce#9|%Qh}fUKKA9$X0@Y^_v;PU2v%e}ZKSz zYvs1aAX&Tz;1}P|hoIIkBz$gub~^ljMh3ME!0XJV0!xb0HpvUJFxm$gvbCoLZc;f$ zBaQ<3e43SKS7TY+Z_d+- zZs)Ek^KVCKe&1_4;7BJlFL-bPsK={}`%&(N5MK&#p0OEWI~%u{&r;>diWxzIB4G?` zT!2wQR0(S=G6iiOKrAwonSTn=;zh11BI{b!O#|$ux9&v{6ML^YEbazzX+g!|?CPpF zk_DscsPeJGZ?&W_a^d}yG?@zN<1w;~9*hML`kD9!tMwt?pxUIlIscj>r7rKH{{|)S zO4vUBq;=W^2tafJY8ZC9yw8BeDHR)-40wQcGnuW1D`5F54Q%6`tGph?f8Uj3{WCz| z6BnlZ%Px9vxb$==hq!>vFf^VtrpzkW)XhbpZQ5f*I~OV5yZ`Og$5+X)7X`7EWEtP% zUaUC1?A3q8LPL7Fdb(;t*W%nY(GpxMC8J%Z-!V%=wwORY@e@)@beD|t{heu#wV7II)>O*B)&SW1nnvtk9qb?5`(Pu@iC9?qr2{jJ#SYt;LN zd_r-meZqEq!gq8mjsij_+{1_su$ij}=Y)qvg4X*--_YFuFXH=G@q#wek-E3HtyT{B zyvy1%vn?&>hKiO@^bchW(h(jpKV^Zh`|P~DcMDc9^;<3pT(^Pe333;0KQTz3ye=i< zA4AVKW~+UR^OoQ8jA#W@j?=|fFb0tev!c0O<&*5?PPX#c6eAd11D59N5IfpRWIkRwGA+Q zh!7BTQ_tB<;b$xus-j=; z{#Y6&;uPtu^rS0hj)n&@ElN<@rFe%TxFiNuqA=?(NJrT!an%KHuFyXB3e9w9g0avY z_G^E@#l`Kp`&h@K?B+F<`|(%M&|i}#$SnhS%D2rt2}jVJwTGU5)(OA}Zwl_IXy?Y` zh)6tY-UE1#+|+QY+ME|^|b^=#ea4;Tu({tbIq`^C>P zs?t-MUs6tdftG|7o3idZa3p|H_FvHYuaeiJafSYU+sr?Gzf4}M^jtCOagE9^XNfGv z0A~|e`vkx2e{Uged@I=dZ`Z`4g{FAfxfK(*O;x3phD6~nPG1rW4lXj%jG4%zL#Kgp zycd1Wf;@Z;9#dX+;s4c_U&RRwrFw0db!j_P2h2}5THnn`mg_fU`kp*VEvyfA2-G!^ zTx_$|Um0c3xe@{IJah#H8ZWhYw6n9y2;E4se|TNtdeo$u!k+k1A)~1`O^KEr$o9kO z??X16Ags><1*xvnUM@EBo^_V`74ZeeO~q+M-JM7Kjze3@FKq%Avff4yX#KI=r-qFf zCh2M{NMSAoeZ4=&Bz?n_h&gl9kN@^(Z~78oR|@9*$qbj@RSBRB_uqKep}Qy=0Puuw6fn@;jJSRT&y5!EJ?jhEqk|Alu6el4ob{J(AjZFXhfP zDI0hHUvD1Zu`7Dk*;oF!dVug=>Iavz`Oo#50~m(@6b0hs@mCaxDKnG=GKiNj1!0E9^&)M(qUrn?0vg6n$z~BRN?v5i z9#%-M8v~(?rw8%oSs4wg-%9f6@-q4Ahkra4!1*S6gI1TF`ZoB(&fe)T_2(|vzsh@R zz+dv|zi4N*ctdlw=`njzOWrZ(__|fQTbv_g`VYEZHSBz*^UsXdoYeJJ-dX7>jN~b{ zj@L^GP!q*7`c8MQHQ%!!C3a~Gw$wWH!Nrt^biSxreY{_Obe_gYxfif133%FdZ5Y9! zIdKt!=}rFu(?e}=PUun9G>Ip`gmgh(-tRX&poKU+6~6^|vMBu}Ek5Pt=yzW>&zE4Z z^L-2K;2Hl{bf69abM1r9NH{LK_xsShO!`Ok6tV4O3`A^>n3jcyQA0w<9|WQ%f>(Kl zs?}7A=@X4ZjF==KTt+qU-bgq#3$GN&apowKW>RPQf0TW9 zJeTboc1a>5BlK0W$rjle*_+6o$=-Wpg(!q5WJlS)_MQo4%ibe3zZ&-1?T zKhNj+^he#_`@XJo9LISc2bgxiKT&H}X}@+E?!}LS{DrV5wK$@Px?vo9*bjIVpA;d$ z%^2D$)Vb*O3NWaDfAd0PoxT<6C9yw8=;wIhPK981P5a!*c|EIQ`5g-oM*s#r!7~JG zZFFI0oM*ZqH0Sk_Sp`}6RO_i0NH6fafka&@06uLQ((|e?;=S-8k_KiKrH>vve#er- z38b1ko}YUFB>;b<|1+{{^J3{90-1REx*y2g^Y=M z2vb5)+2j#Gbn5$`+~x!%>P?4|1=Ue!?-}P1=gg1y!Zl+lp2Mt>F~7Vz=c&WfE(tUQ z2^yZWbrg$yRDYd|%p`61SA2pvi<|4W5>S|s`_ad;#|%B&pW*7sA%(pEP%hU#3@r=H z?%Jud^rD!hU2V@)t4I(7&EVhAs1v1i)7cEok{(j*x#6x&Z03GdQKolJcs)vykZ6O_ z?i!~9w4BnR58C?MMX9FIbg&zxYB|w$jdwa=Bk7pq@Hm5qe{2uMu zh_yrunD|l$wFA%{JZTr@PP8CFTYa|3QLFB61h(R5ep!<%)EV_d9v{Fi2O0_>NO+wH z&=dW-jL>Mm3}HI7151nJk{zsSWZD}gQ*|$@8pf{{lSD&A!5`6>c$g5Eawjb$WO2rJ z_J>%wI}x4!r!x6&Nf)^NwXA|#v2xat6y?F)uA}aTCVNSasP1&pYY!F`TqYZwBY3EQ zF%jg@Y<_!Dp&}~?{kitK+@yX^_D3CY@o#&YIhygqO_&E7QlJ;oW~i zC^*7>sXYGS^!Nl_44q7Go99YagbT~%3Axjnh#z>MH;mu;nw4%rmKDx{E@fclz)sC7 zNc_XF`)3jJKTT(F%atPORKD7t2z%28P08s&pD9^QmjwdF$=IF@_`s_XSombJfr53pI9xPyXI0T!3i9^E++7X;TCC`ss9r8_ALvxBW1j_V~p%8Zb} z!vfg5gu=lIMGvBw=gQm$bC;}(c4pRz@^NoO%B?n=!zot(GRd) zud1p05hS%d)TUfn2)bw7s^e@iyQW)d9a-af8J?E%E}SHs2H8)4Y(*FG`{leDB#^lME`=y8z=^*+aO~P!A5M$9C>wN-&Bn<>MqKGxtac>`><6GG6b43! zzvEs1(WG`h?rIyhue3ZsY{qI{6lvBWGcRH@I{HkHc|@^s>(0zGyt2MkFU|X=Q{!bP z^4ITd0v`pDj@zSW(6UQK?Q)hCU;mc10c?PZlfRb^hScm@88DvSwiCy=i{Uk-RaRCr zb&}xgIhZf5EU!!^!OmODbCq+|qhP0`-KrrTMDY~B!MuS8siCk$bdS@+v$0Ta50Zp@*z5fMTQL+|BiO#BQhVqwXjU_{VjLJPDu(dX;LNlkC6?NTzxAMtn=xR$ch}$ zdLJi^V=XkFA&_hCg3|*LsYou5@qrXPM$Loi?VHgY&bkjDCUBz9H@mD^5>UC}HjRH6 zezNJ7&)7dpX|a=JFEr#D!Zt+B?Rkg2THvGHGcj8ub#(#|dd@(D1Y z5#WrC>?kTcyZ*l{LM+3Q`+8fvqp62ad-UwUK6d3?K)Tv*Fpcj$P4 zeHyGc_urK8_t=0Lx4e%X?6ru;8=u*;J&3*3Bv-ZFc0=rOsDc`K`vb@#@7pLS&e@`5XwF7EObPbT1Im0R&9v5JE+b1| z@lfQI7gDM$5&q*%a9P8Gt|D;aJU_5VoO2D5l`@-K#?2QD4h(FhPb%qBE_{31FRI*MuW zUz~a$)R#tnE!w9H3NGjEw=Y?FVC&HFRy?pXG4HB9ANz#d#I#h+jm);Tt~skNh9z{t z&1YR4QY@O5coknL9g=dX$wVeeTP(nIAB*r+_~-C@EjfNP|LCFvZki_?VtxtdA5N!8 zT<7HBaobu_1wc$R&alwuq{hUgxlC0;#j%Q>+-ol^@aPdWRNR_;G<0b4fDP$Y`}i|!tHnK`=WbC7L~}KR9B-V<1Z?S*oHot zuG1K6alF6&oinQ&xzc_orQQ}nRo=$h{0mCQK=ZoqVm4ojww{f}FlCdz6}3F@CjA2e z*nE-jg$DWc6989R$j``Egh9}J2@WUX=>qGS(gsBcKkT9?7|q1Ig?U~`$5Td0M0%Q_ z0ojar&tZ3>o0f0*7T6~67H9b!yyP@@{`S2wFuQ&AVcv{a7CM1QuQ)aG^Pau$J(!p@XkDg`jy6ivQY%q&&h=?2xszXgj~80?`oCLkcd zigdXVff>Y88v>h>fAZ9j_3B^FHbO@+n89gulwH{?-vlHOmJ&9l)4rfb*U7FF4dq-u zi}&(;jC2pR(ovX3&a#g=3XOEfl5Z-);+x2UN|B3L?yS1Df?d5PZ5V57{e4^~(WEx=(*t?2YbrqtNpsw^es$L|d`m=e^`Au&`k?+l!XuVN%X5=}1$ ziP`n^w4fq0Q4kKA+^U6h929O*+~iZp*XzESCxolGSZvVd?Ou3V6%om@Vhl^+@B4 z<%DiM)on10V(G_P8d;_JSQ?#UEzEaFt&k`3&%^QSxvetF94>DMfY)vc_^eY2+j9U4 z|LI@%EK~6aRQUoJ=MIy>^^sam9LjLp4oX2lg287~@Iij2r(54rgH! zAfLUC&Dpwt>#7y?n3%7DQV|$#505xlidf61(_e;2LPHvcJ<8u~(=23izB`4MYUF%Z zTTlCP>YQw1b34!x8Bk~tQcm&`>rcoC>~;f3g?X*Nl6N6Bzpeuaq?d)BFe1L^Rvj}t zb}%0yFje1mEM`lzjQmtDp~T6|F=F1JglUBP=id)CUp!zC=Whht>5qcYbWaI@_e2nv zB)5|gd#fjH02^^=BK7w|szJw%%Oy?S3u@B;_seA2?Qm<>J%#@WoBF_FSB9`tx0VUK zrO2BYHzaXSM{Lhq`!V_>{*rP!-=M+J{1sPi2vyzS%_57GFP&LeHGKWTKwW7?{c|7rg;0XVibR z0)Mt&^-r~4uchbFpdV1PZB^nhEWc6eYUg6Cc4f88T*pQ#9=+MBHVl8XaAy=F@|2BR z#I`ib1d-)0JnqSU0I2sUAT3VtxawuNO0AE-L74A zFxAkIG(9c)?3t{T6dfI1_aY8OlaA`B2v8=Yi=^`)9~k7vSk-~EsTc--5|!~ee`UD` zmf#RrT(MlIeO~!STCn{*HN>Arf>FnRG}DTDR^sGxk)+HwP(cA31m;%=*^9i+F{?x% zt5OeoqRoULcXmq zNQ*`?vu`SY_QeTYT<)M*ZX9jk5@hISn8pR@`1pO3o^m0iS=YWC?jO0MX6JVW>2k>%bel0?Z!}_0~99sQQ^}dQBaicdHT- ztQ#+7I~Yo_87Q!=+g-?b|J3k-!&3TK9@kb+UVdJQy_>n7h{e*)LtG?g?^v!!(}jRn zdp1QV@szkX$y*UCxM@x91_T9?BLLswlj+M2_R&`G9{7W$jkgl#cN9XeVAfE z9Tm(3Wg4RSDBT15bD)YpHU054Y%^6*061@QN(y{C3D--yn(Ix0K~8Q+lDv;aShDt3 zqjG-ST4>FEzna|AhLa9O(0(ri%}+nRF+B7+nR=oq5%%AR$rosl93IF3psyv-Ve{lC z0|{CDe8C4&>u>deb+4_kCFD1K_HMw28+e3a$a4U!0Z$lXsQ1(j@_zb{KEviBvr4~? z!Zv7KW9~1}4=gq#1*wd&%OG8<$m(FVL(h6(nAsD!!~Zy-Wn5jMzLFKpf!(s0H0JzK zq;Q_H|Kj8?dB0s%gB;TQ!Rm_Q)PUtY7?v7Oqg5Wfgv8L&&d9(pGezrGlf5?O9BRDj zGeCX}nX>6qxIa~Xnt$a}=EuW3Spu{Xg8tXk#3|Z*fzNVTqJR%)N3%X4uRHlLIX;A* zo=(~;9&!Nv(ZNEt6O9NDmywa-y>o}$;2ECmKP*&Ho5t5R+B_p8r=LX(IHI0(1%|U* zSKuhAt*sZv&~Ic$;wXr2upIJyZb(akb4_vuS`>cb9d>i6HrcPQ-HZIw+HRm(OG6`! z$Ki*=9x*26NtzQu0#OMQ=!NK6HGb^65yT7e>CU!kY@phCS6fTUV9yp9a?(04xB zb`r7zVc!n%=II7Yrup>P$l$+ISUCj(IRoYy+qfT|pa;c}4bnUs)DbUk(!ROjX3w-D z`K+d5^f`4VxDsYLjDh_}LGQYB;ro&2VM}py|KefOr77t%dKsoM*Cjf6GMUQ%^SjHD zgo^qiU)@eMbeQQ*#-7YJb5LE|)BF zuDK;T$K9G{P2!U6^@?**w*%r+8{7edf15@7ZL8)H{cCD0B0RIV0#xL4SCIE+_VShE z)^|g6(3rGUqc=xi*3efd4zME115zC1nA6iiBS7qfhIi#g$L@{a99+SO=uUS;ShW46 zThV*bO68B+`EjVc`OeH=kxx9IGaKp|i71&%yr^OYPc$gO_|1u|99a1}3F%hARE=8q ztrLdt! zHPud!Jx&u4lfnW`$^kF#0c_y23Zx>Z9*e zyl0Arj>h4|j!ES){^hv9(qlF#96Gp9`0-AE9w=Vad>$B``knB*iZGcu9;Ks7IN^#4;w4KW2`6c@i9lWl=c;w;hL>k&RtpUx9+9$0D7@O$V;-W z#RXD=mL~kwM}<+#bK~+UU?!m1Zq=4f5iEB?b}Xa_02%g={9H@rku`n#KWyt4} zHF>n(=eu3vuhRnvRNnmq@4JD3LjxMa1dmK~hSW)l zkXi!CPE7Pk%lOjOOkP$NX}gJ$nmZkS&+QmK%tJ>B<`nLG|>4aF>tk;NY$#JephINsH%S}B=)HPIf>HOZ;r zRJM!|ojL?FPUiumWl=P;4^O_o82LYv36r%svYrsJMF`v1KNj=XU)(az&;D@G^=+AX zCIUD;=dnF|T}A?i1;EI%5C4e?Jg7;(qA6a)L`oNvm%R^$`K%VIB6+VP=~;~r4tcyi zb{RVk+TmPV^i0$UZyNHGeh~Ef@<~*G`{idW_|?~g4OAICX?nc#iI#ha9UGP?%q1$0 z>~Nox>WrgU4wadMy?aRv2aq|NtB90EIaO%AQxflBCtxF(t%8aXD_q#G5=Mt~5a3?t zI*RN1@h)nBlID*`FRhn;SyP%|AR2}FmRrqhl#*HHRk@$R$tC&WI0t68uu)#$w|Z&< z#_tfI@kQXe*{zF3yr<+fr*82KB%EmAmulxsi_HJ)41LWLRe7 zh!6`iDG-7Z6k%0>Aa@{Ob>gK#<_*1x*Fb*j&2u&|Hgd)-L8RYrgCuSAQr^G**6?2( z(y(vMV7ekOa}dTC0{y&nf1QvJ7yfv>0pvRlY*Q{voV<;H`vKuMb`zfpo&z;;d2}xj zqKB$D^_M=+ZGnq$2qoe~*}LDjmCW=VrTXRjJk_e3SMT&NkNJ8&rsy)+Q=OgO&h`zl z=Tlu~mAXULPm_;ly0~*WQ1*aQ#ipt%7KZD`u4@N;Aih~l48%Pbn?;D>{qynu=VLLK zq#bCx5!g%@c|W{&7Xi>yKixMDDj@<;n0K*Q>Y)4p}`-Zti49gMDn(pT>1-F@Lj z2KHi4SETu=0+R6pK<5zfLH8bk=bzubL7?($GC~Qr^9I6 z^Ce7cCWD&M{ECU*mtLgh_PYrt{W9F8N=2D7>Iv+w&L3>N%v<9aId%I831#*yoVN63 zn=yJ3c0Xwi!etlBMA*m^$(FL&1M)W$G!5;1?%|t7ghpSB;^S)bg(l!A6fn{M>m&H7_g?a9qsGge!D)I?t0>1!ban9GmvPOd24Xv1S`$NG;00-aw? z*Qd~mD-^$QLCfF!F%R!OR>QbI9|Ok$Bn}L!u~|GlV^j54Lsk7)z`_MgNb;Tz#N4$# z-4|iu;W4iyDp$(V_ttDwoyC7g&=r8OrGkyBJo8WziJyu}} z!i&RXst6t@wUkf~QPpo4|5(aTpaG0aAct))Bvm6e#0@I&*fKx;NQt;b4GnJg7nyIb zjDS>K+Mr`f6@;xs=Pg{B91BoFT9f4|t$~LwJk4^6))i|KT#q6A%gzO)i~H0D`uu)IDftby?Q5 z1=|)mjH_&S1npxrv>KVg9>+dqLJxT<*+OBP8f-vWDGFbWOU1CrY`@f8+}wH8xHz<7 zCAHk~QIJ_aMq2Ro<={xlvh#v3q`Mb#yUZSOxV2)xy9L-->pRQ)_w=ktEKCSc&PSYPuG=y9z^f)E^t5ucd-(9{ z|8g#1bHk0Z9W#71KDjD$@`>Bn0xH$4+UibU)8@YHYWQ+@4QH3PW2)vrsq_R3$}duYn0YT~Erx9RvAJ$#;%v$!mTz6GFEtaTXl_?f|&@Irzu!%?;iHz+p(QQ3=>s6o_6H*p)|H zVp4e)ahdzSn;o zE>V9VAqX6&HvRBv0FacSs7QL7v0ZFk-40L+9x zm6j5EZ|ey#sS`75s9iqQANUE>f*LR2s)B7jR0IETr)CWCBI#rnVv^aa*)lBB+`JUx zs#=WIwc^}0RQi>?uajUkQ_EbRrX*wGlW=Sd{D8Vx<|(61D39aE4{K#rRd-Rl9=M=f zlW{>LH3-c=$&IWvS9;=39-)$foNr@GQA~7j7zh7k|rRXZ_ks&3XX!bWe zw7aSht^Gn>n=*^DF^;7?mYq{e50i<3g+nfZb~x`#CD8%1a=KrDllEw`WN3@uhf4NKp-a56waEO$Zx|3Yp-{Y1MbWr`{U#xSF! z;w{GtCBgDFQgBdP%k!i`Fu4fgTyM0)OxB{?B$Y~#N*=oiZnRe4L9jaUf#j{XtST(_ z*<%;W6MMy0>?4*o^%?s#30nsP^0%M#v1VosGf~%aoV8UsyMc)A;I{wJaIg(X2MY>< ztwDDnptNhypny>?htd(}%!=p4{+5RM%{T!RA^-&jl;kLr)sVp{k(8AD>5v>C7s6Dt z?dY(zy&W7B_NiFYHD-(@T_PLT#&79maq`lP9@XL|M3{8Ps&wz^tLOE@Z!P0>+dIE1p%T z;DN}E)-&<`qo9lsLcpb+5itFc<@4W4#9|`L;?V1g=sFiY!oksv)j82`1UiLFnK`u% zWjWI-g3iTT(W7_UvlQ)lZ|fNilXu$CSiI{JiuPw6_wvSJmSpaa8y`?_>1!q_GKdZ@ zE<8dTRAWg}nIe^>kj`G((Q3cTUBj+Tkc;+F7fY+0qdir#YhM6q{mZE`4&a__QM-P< zd5bdHZi#BtnlyTI!7@vD%R|kx z1F$?Jv?j;h{qe3xeD2>B;t!%P-NfYcu=`Bp$LGQ0_WrFSdaEx`pFd$0ZBg?ut^e8Y z#5l2kGm`~D_`FPfo8hw}AZK|IvWWFZJB2SnwLvUr3|g@zT`k;9P8lSRy?l@Fv7%LE z7Y1@3dOPLc5edBDd2$*~4o_Y|A-wUV>xINOBHk+d0j4P|$M3JzZWx9K{vAER{2#UA zWw0M8iK)>wiwS_zE%r;t9v(Z{0LYzkU|?W4A}T(-*Edvqnb839@{9rx39uptqnADg zg8Uxf0X4W>eo#kJ`Pn~Ot)3xhN#@-{oWGuCaoyuPOk=->aX7=^M>9y>jSqM&{>HKw84f-W zz176C8Gocv=O#qsE0Qa`98H=P-tf7(Z?%$rc&mK1id*B&rTfF)snf@8oloRXiMLbC zGkj~=5`_C9ln?Z5?u2Z`@H zgMgs$;nDI<3J&mfls=w(9e!Z(5*sZFJ`dh>1AMz}WMd)Mb05|seWTpg@dlX}-78Tg|15L-EHeJu%|);G z?+jzshL09>DF+@$s%BZp!x?K>XUNKFL|=0y_eEWjc0{rI!2`@$=a^$!;&W`kFD$iB^Hrv%@j&7x5U z+E@b$@td9*&?ti~YU^1#!F*X0ytxgC{R{0eeaXqa+Z$4{vg)g!vl2`r9}M*QqFW4e zR~3wZk*NC0CEc@RG`GgmnfF1LrIFzh-gp>&c7>Luj{PXt35qONM;gXyf*J6G3Xx5Jm#`C)VD~XYWUdp&))~ZeVpdKed3`9cHG^#%d~Z zc~aqaMJ;+3b;>m%exyrT#}ODC@cpA>TKA%(vTVd+kvISwwY_pk+B#XW_zgAUf6LAF zQ-{!P=|*g%v(8Y_NV%h|jEn(;w>}oFqbQkPPEWWBg;~oL`Xw`_uKs9!F6KrKrj-2H z)f-*u{B1eKH*@(aIg-_vJ-AgGEWO|tD1d>@Yf2Efq0kE*wAjBO+dxwNceqE&@~Sea z-SvUq%TL4~QeR~5E*|4YmWz7bj_i2zOntox%=fEu4fZpP^B>*1`l z)2&#fc_ugR{1ibny;tw~JZk8Hc&h{_Tj3=akbF#N-?RM!G}X^)8*oDZlU~b@6K?Dh zVvoaITHJSLk-0>lN#_k8olOgh8fgw_uh`^DcjqyU?EOIAB9p?l#`;0$xOa%=LCRh4 z0@qu4{e+p$%~Z`Jb2?yZFClY`or>iI=F<~;3qrvQ6Hw%VFWf z)esTdhf;s;H{lOY=(cxXub?KAV3wmZU#my{RX`{IET9iY>i)VWCR3KPQF!-V`_!Sa zsaiQZ)%9T^9y6X9SM8m353DcqBx+;z>LVQwkH)HfvsYDNxmr2vr2{oM&b6U7VO8XF zmcqjksNR+O`I;aO;bS^PEjzGJdl$_VZB@N9QjYvm`(MzZ)Xw$OIv$*wJ5*=FFt9Xi z&{V&s6)%YUu^P&`ifDd>85QvT%rCEx2jFF?b3Ve7VLLOb~(Hn%MR9Y{_Md`lKH zoZpoae}wd1_rm&&s&ww_j5O!Uf#nxPZ-{nq^Bm8ODs72;8g`Pu+Y(E|SmI}?e<#Il zrKSlzd<-phfM3^#QL|RtP|v68u0FNlz$G^F!o{q)qR><-=4++DosD0BXNjVu&@7Ny z|0yBhM4=feVM)845RKUJL4Xdbr~M3F{?_6%eYHGIB;dz=d5Y$Kx_tZmiXX}zh?mcG zVm95s7q}$8)K}LugxMu_0&Qe4P+wurk1fg7qs7f(6I{d;9)VvJ8k*GO>-Kr+vR=v) z*(xOFfOoT33Klb7!EU4~kaLu;f7_kienklnH(XkmzA)EJ+(rlad4?dnf>|AK#|HgR z=dS@ucUG8ta=?b)=e~EM1x4Dw+S1ygaW*R5#abz8x^kWDZ@uroc&f%uf09J` zTqC|&0Y7+VscJl?+th|Zrfg)pXiZfbzR*l}dk2hW!+6BOI=N?(+R1vH#l3IYuy_P-7YBryh3 zZx#f}LI@ewA>{xEUj*?f%1C?0IAWc6%iQ-{(uMtX0`U?u+CNP1H%=M7-*Z=IgHAnO z$btm4c=N}LDy^6MNh3lH6}|@Ib>P!PH7apRA3=)Aw(AfNa?X?Vv}3?IIvGwlInWK` z@^4hoF~G*spMUEwNQ)nk@2sU)SI!NQxAMN)id|5-_)GdDp=$-!g zH~A=^RRt=gknF(S#N?nb{`3VK%rkdcuE7W=m7NUu;voj<){w=SPRnZL(5K+5^} z^^6mP1K%hNrZIKWlup^L2Nz*KD5M;;qN%LIvd4Qmp|3y_1iPlMN(i1uc@i&Cv*yxk&z()vroMTEWkeoY@^Zr$IroK54hp} z^{n~+39G!M^QIqg*<4Uu6rY;9ypR2S1Q<&=wuWx?8hCxH=`tWxLcdLM$SvOecA9Ry zNk#^2prD{V2JTs%4nPr)DEmLa;XmJK?}vBci70=v7QQN{J=8_K>1yN&-(2^ruw z)KZy9mpsw3N>r!TP(}YIEE9y)_@3Xonsb-6V81+ehn6p=1wQ;*huwoCQO|bqY6J^O zKM##w&i8U}!*ZMV&PomQ&9B(@LCa4A*>B?l-@ji~4zV}uT56IJ0=V(`J7D+#)+1Gj z3weHkDH8fyf@dsWlYS0MV8N2<2z7#+4Tp++9LFGoip~6WM<*#9_`%BfK-LZ8I9!{( z@9$fyz(w*uZ5?oq7&tw>S*VK4#>&iRw_(~60J5Zjl_YV}BuNOtsMZ%qyLTjI$ zL$m?vu3FT0^e03JbXc@)v=!fKbMjId@V?QO%=-mRuV>;@=As=}&k=K%H zkPhcKWAb2UOdgowfp=>;f77;HnNzm*GxUDfxyjj+U~sw3@&u$IZdD8Eh`O?jiCCQ(7GeTi>i7xH@ZRXj#lrja&VZ z6Ev{IL+1ml0pdqY8>LLa;j(rfewiQl3fvdoC+fe-1 zlLi(S@zt)ojUgeD&CO5;@#c++ii(j?@ne00jWl$;^;Pbd6I?2$W)$%pO?H;+7 z=SunY)zw7Z{hGH~MP=ajOcyqg`VMFdqCdFy)oXcgO38O7IH+p_%x$cQ{tDz8Ux-m# zeSsO|%bGxLWphbr4EfH#6)q)D5rbTrI(o|2Q$&l1r6pz0Ji82I8pvttU*|r zllkrR{#M7pC(J7z{s-ozA%H6(HJe1JUR*VZyY}|@sB6-XFMfYD<>ttyHx`k<|3N%_Tksf7BO9Or=5|Z_eU@_I?F`;gLl3c)|slNz2 z;?W5jSyFpQws`r54-`a09Y848%o?&mzTAgM}pn*P*JJ9$s0kxGrLLgh9am0!K8 z2;h>W6J=w3`NYigit@GBH6*!oh78U5;-V5O>cGnfHsS|9;|F;-G-(ZR^4}@GhVf6a z8ATM(fIIeS(FU&Y-0)qfYU6+V{Krh#I5zg&(!#Kvk?9A=qh0nkH|OQj>WbrvZ~46k zx$d`*_wl!5aW1YnJ1qnN!T7bB-O^4??V~qy%kC4+!BOis*I#*kNcL9eE-%7rdJ5Vh zAY=XnE$$Z!br{^%ayQ3(Xl0}v@IB_RC6?zh_iPiVTfj))t~xiNO55J%AW38Z5=8;6 zYAxFxdE?Jw3Xk09YJz&)@#!Q@-c{yZEMB+r4%kNuma+M8KKr`DL!paoub+oYT86+} zY5RD=vPZe7M!l)h>A=Fnd)KB;*fUVIo_*~TUgVEuWdKCis^DHJoO0OVb}5f1ypn}C z%u=N%Y-llDQCQ--_%2DEh;F!I?`v!6B74eMwewL^px?mtb|R-^zM~!wwd(b6w<%|DjQ;Tgn=#hNR{CIxnfHeiQ!GSQ7a70kQAwTvdNfPT_zkFl#b&mD=jbP zF3tcPESR|<2TVM~_z+|72W0`FAs`wu8|P~KA~;Fb=u9@axL>?s6G9%}>9?Iiz11&QyHW@BT1 znX)-U?m*@5byE3rWIR{otwG(w7UzF~8OM9GAiq)+KifUWZlfj4(cN};Ei&U(7g3eF zjr;1(aPF8}$^P2#c+G%gO1L{0GZC40^`>#{+^9nzL%v$qxEg{}k_GE_%Xk=@)5V)* z)`r8m#{;77xwpTIx?atk1%8b$Y#E^*157URBL%1&|J5CW3b;cM=1xh+YD>e)Ozhz^ z+PErO1--qmd!^b&5!<#LU+K;`IKY$kaVag~I>+z7Sa`0XwS@Q&Qfn_VDKi(@SWQci zvUp2(5rp~QsMN}NEn=RGA<5Z0GrDx3wYsl5F-NLz^`$oo7}k6=0xB>A%y#4+x+@-ZxL_|+f3QS#vNqdX_MtP z{!FxA#3^2AnIRXe<;lG~QmWXvpGQ-yWpF_N-GTSJ+BD8+hSJDDNsXe)_s^mp)~gpx zO%fuU6kM;4VlkH#I^F=J*E|X+2d4iJ1#zY1=tNpe9z{HGt`j#8Y$0c3hcqN^BLB0X z|Mhn49L=koc09cv%SF*6OA#I!IQ9iZX~+P|TNM*nP>=+eq~SnQwqa*{CIvZIxhWUP zZr9K}*vuVJ7GYfGv6-J6xZ-=IZKHyBb1%(wXiL^|l%@2zZupg8bq19;MaIIOn06wZ zv9&KsX|JffioQ`N{=rdi3XiubwTk2(a1kzN=E#%jyB+NVXM2-1IFNQr$NUzjjwH)I zfm!h^TNi9>*Es)XhKj@b1NONWL_^;W0oh?GViAOv<#a8g_8EUKSRVM@iD}Em>(w37 z_(cg~reix-mO!c52H^xSWw4}ZaOjk96n=)^q@5v_I-i5uyt5CHGt@K?EQ9ZszBA3B zjCfVl`hn8K#e~54+VLHB0Y7F4{g3)FaAc$?fDpp$G*n}X&&asM&9&bw7_s$2lYnRa z-m!Xw!&3a9%fw)KWf$KV4<|EkUk5v^*Z}x^Rc2~&dn-9)g!30FxDdwg<;pslB8f_M zXpC)+Zo=ode6qvjpCvKJp#{CHO1>5pTJw?ArAzx#k`2SUfatB!NygFYujdCY1@P78 zd#GP0^;W+rmUJGh-2eb8Go$;xCY&5>OqIqX-O$`UZLOj&n`gdf!kP{H@@R+@y95l` z9F4YRd^W8wOi;Ag?U~%(*fu*f`syfLvGv`IrG4wJG3^+BiY%u*4U|?+I$ef@%k4IdR(8;)qZGWvmH`OwB*Vt zgEUmceCL}DA*hi8v+TRoE(?)g{l0b#tXgoxsJ)2}w_<1R zv~30R_~gw?6tE+~nJnyNdjKtlg5REV-ZEaztJ4@$(xa#55@V#atf@>2fl%9U6 zJD!$B9_2+Af%eGXS66`RV*sU|`hXTF2xYw?afIJmABBX#u&b0471)o>)HM*e9n4@i zos5tA`vT^KG6rULA8lh?F&oKsv$f7HyQ|)o&m%k7v|T^1@M9|ZL`YIHRe<*97p!MQ z)ZQMqZN9PNVjf2Luso;J0E=Mba$?!+Fnfu=? z3}9s_?PVUOcn}!cc4%`auvxc`6Ajzd(?{iRRsD~%1V0pWGjxkT&f-i1C0@4@6tl>IU!>v^?4b|V(>Xdw$tXKAhbXU zEGV3mQcmcnBL^eM8yib=t0Uh&W$;xoZM7~^RohHfGAb(MEx2}1ynn7hCi7*{m=d#B972@t8~jZ8J{xo6&DrtP0B~H`1oX!4Swf zM8wl!I26TOw_}0f&(hkIOBWGgT*cl@fC2lMV3Ec*GfJ%#nQPVVzdj)e4a@i#`u-Cx!W_kkSX09q8dT}sjrJ6Q8o2WShH zO8SW^5KbOgv4P0A_moehr2=K1p>w)%#atW&fAadp4|+}mik2mA>wZM`@eBRd6PIE? zS5?~|xTt@sF5jKwt8j9fM18c($47T3XUBPGo;`!X8(ydHqO{PPa*^HWZ6g|G=n&m` zYSz)C9!+MY2Nc~pB~SJ-DCh_E6ZIO$xkT%tYLZxrU^g+DO9n34cmsQi-}qw+)G8|g zJwjzN)p;}BxPq|NfUcAq)VL5xbwY$j$oa5cKToZ_3trh=7Ln_>uhn>2#Ye%n$Ryb2 zh^dpIHMQN9iOgW+w3n=3b$etDtb9!h9xFnF-G|N=KcU+?Q|LW_N+}IR2S?#u*F!;> zcnm{NHt;7Dr?8<2Qp8K(+2KC%rTyu&Ca7ok_!3{-O7ok14?_n+SU8R5;pgNNRCT9; ze0)}1ZtEJPGRVW=^y}X!y{P>Y^@M)ZcakU4EzfL4T%eI*fZKr~Z~nJ=KQxd|L{}K7*d86{Z)e|kxa#P|m~Pvvx#<3;XR(dbbVhP)l2NNm%WWiXK7h09Q`v}v zY&^I5py9#vR%HF9nbPEeaS?i4v}J5Doko$Yyj*GLz~xV$&=D?#N+cPEN#wo2^dbL% zgd$E%LL#Tw7=9}(T|)N5CVyGTYQyJIZrjy{epqC+TH)IVxG=2UfR7MI?er`V zf{ltYps$f|nIhR6%|bLY%88EoP5DhwPKWUWIaNwRdbKc?9OEGcA|VTd`GkH4dqS!i1~GF zyUM2nZ*MNLclchoja5KAJN7uM7dM}zo_4WMrOMn5wsb(jRM}`hT7uO+5mr8zf>vI= z!96)Ny<@52JUlQAac*Pf+JR*CS`NR7(gi9MjGDd)O}S;s=V@U-8O2hpNfFDa*M?+zZZ->tjfGMK%N3?&EO@+@Md)wi0fHJ zpX0m%Rm|r>06^uqHD6vQY5Q0SEXIJ~6Z&Luo`(4v3WSl8(t zW4AZ6Zf&F3IGCUaX({!hq5JhDkjXYgP*N(H4Y6@@&L51*jD;auiA7V+e|0jjGRn{_ zFREj755^F`n^Nn;ltCo|~F!3c|y%kEWX z(F*Mzb?Zj*tN`y3OrR(ru?{kd)RnsVc0-5ttr4PA5x%()^X9X!%})b9LPw?Z^qhbY zIYW4x+WoLW2mWvs;f}~u<&L9%$K!_4SHjmrytb*n=P_G<(`{cF(X}@^-rer)?ykJf zzA}(+=<2HR_!bu`$LFHH!1vi)-}BPu!a|+A$p`mLS*duV;hPCQE9m7q0}7Ya4}J4I zCsDbcIU6wK2)idb2R#NE=wytWuU;@=OFv66GRDDSk$`s0Hg(QAB8< z{3CY@;qDMB>kU=z+BJO&5oah0OP?MVr~m*w%9Hq>!HarU9TVyGt5=vyb{;)Qy(yxl zG&4IZ8x%ZNZJVM(sCIYhDay*iDiHwzm+Q`2OUsLB7#+FMxEVby9ZcnO?BnWh`Z*h@ zE{)Uu_pKk&^-XfkhzWj8$%UtoF>vgpkYx)-6O?kQP(A$7ZbC-vH zDJPo2%(Q1sq%0&U^icrSel>E+tOPDa!}u4{mT(eyK4t#)2dPm;l|9qBuja~lW*2Wv zVRC0kiz=lCO|h5T47Le}OGw!Jn&k{z%WiU^?-dP(^aOg~6m>XPF5O9wh#r5Q(vER){I=9T zaRuY(sKrNtG&|$|tzu6>=>vs>h|Bb5QR!a9kLkAQCiJF5ZQhJYr=<6I!3hN^$*8L= z=s6aqss>QhzFu?dOi$$Wnf9x|X>2`BKK0F1Rhh^{u4ZOt(_72B_H$&SK@F4~L-`D? zg1qX1pI$3jTCi4abiZqnl$QxcjT<`qSUE*H(vI8M=H?wIKLB|SZO5#wZymC+&YR7oCS$kwI82RrO?Ra{ z_g;sk6m)+li0UN)BX=U~w`b|BDoHYyxb#;Y#ZB$guYAXGo?THAK9}~^Ur%@;t$6(D zS`8R8H*AZ+$`IV=tckq2ru6momED$20&D(oR;sADm!rbePU4-e`|HEKXcb(oY0$0D zxRLgC7y?oG-%2C&*Rh><*s}7?&Ob0F1Um*4zux=A{r$06kH_u{MZr`@6O#%a2j3E8 zgtXIM&9i!Venh>y8`9V^48H#0*gHjH7U$jf!q zYVx~(d{A+rx@txLm|3SpV_}$?O@Md?>pn#+kzU9m>e%xbv6jNi32SZqTyoLl#ON7Z zSn7&~{tx7Y^GG*yaNQKHNS=>B$0*LpgXd&1dzLDa&}yvYPtw`jXXn`NUzv?jd$?l* zð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≻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

$Z{R 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

T(#88`f!H3`*g z*J8Tuf8+W@kClW+pG?q)ljMH=N>?2@x-Rax*dTJD*ke=3pJKgjPEqE!qB`(B zr^}<1f@>~W%^eN;%yV;hh@Wsq?5z4DK3#sFBN03wI73$oXfgDP;FjGQZqZI#Tc3#b1vs)6-jTY2Cf=>f+*DNXQ4Nopnl~*u!c~RwrxI#t#I$`)S2u9~y`o z5@@QFPv06tD0ky`*Xb*|j(qrdQ7@Den*E)J|1@Gt zP;l#I&ifgbm6|zh9FNIde9Yg>idZ$$BN^szs(DKjdySU&=@r-$yq0`gT21s{6Azpw zqY8Mr_Ia^g4k^31_ffXz_rwX;@E<0p^(i`@SUFa{u@C$^`7_Lkhbs?ez{d~p1qI2U zYRB&KRuLS1engsO*Ile5ZTFK8z611Yd=+ZQ3q89pX9UYkY+)}OHPjr<8Vi@&`BMDc zRcAT5H%Sf3BZFh7V&=zAxI?&(s!4p{x%}xhX&>p_M5tk2@E6%8!Aa^4&p4i&UtZ77 zK3Onk6VJZkQF6ffdIiS#UfxeqsM<95zHcS?M!wY4@l$Wo@E$p;Y8!%Hs=S$<}Y5{k{5PK$~5h zr<#?;H&`A2_9I*lxxuc;9C>&ADfH2FV#Nc(6W_Ij6;7#vN{`0Ne5V2kxo`YhE@xkj zf_ynv(jws;^i0Kt_;@jhL+XC3>MX&lVE=g}pXz<=Z7-2V%WkKGj}G%ljz=7x_i|RN zvgrncN1`|xcBta(Xt;Lh5yy9ZUmi7crew?sE^Ikvov^^}HJopwt%p^}-!-?C?IB^M zjE^6GmBz=idxER`Ga(V%u=@;;Ujs`<8V7e?(kEzIIc$%A#g5^_S44gcC)3;1him+_ zkoH+Gyi2TI+2S5Le+u(`U1+-&EYH@mWut!9Qc|u?*K2HRt*>fYX_?qA9v>-#I&@+k zD%LDE^kMlX$Ja*8;#>J0=-Ljxl;>!LE;=;a-7cL~6HJ-yi_i{c>Y=q%G#$0MVuO3r zgFCOwsHV6P7R4jAneRCN{qPx~kskfPUz)wtI@0cHPBSP zDj}~D>+`PIiJ&mwDkM?b-$#G5?n#Kbd;0_5$I~lMJ5!3Tw`^JO*aZEq_+4%M(#rZc z1Ty12f@`_GwgWS^p3KjlHe}5<)%HVmmSKZ-yuS!6A#8ydHDez$)G#kQRyM@*r(<6Z zroXYT*-Q>V{fzEg5ocJVBaJs{mp9Zy)Y9qV?1{NuU3>-wD{nov zLa0w^>`FRM-ex$pj4KvIj}HxDv@wl#CkqyQX#Hbjb8@gRrQ%hm?Ggn~1&}Y-JNNe0 zwQMQAJ+!0z5+;>7!nu=pxn{gWe`QaY{&1ewR59Gi-Z9C+idAO~a}#TGe)+-o>W8!7 zd*7BxELom@_N)R|)b5zHZW(`D zOFmz&y8qJ3-+}zG@%@LVweG}BQL_V8Jx>5Dsls_&8D1!J^u3d~Q}Ah{Bs)_g-`>4f z-h(*Txm?j{1bVD8Q3h*NFb1L)6em5*RfR_E2%O&m5An_h}!;M=Lz9MQu?F>52E?|ip zUg)j3S#b4E>Cau#SrbKtg|Cj|HrKaqUAq>cN?sA~(n=ocvdeDw8R2+kEwtLbXx^bQ zyv%UhAjxb=`o8a%_n5h|FVf9P9CRJaZ_jzWo17L-lw(My%OA{euZ z*=H!L^#Rw2yiA?h&k614vlCxiF?7kLEpY6$6Gx@@YrLLcYcp@%>c3p?bY_cwBx~x2 zioUwrY4?3m5ly`fRu8{iKcp109(T}Wo$GSw@a!(dt|uH``}Z0O&$+z5jGATdNzF2jCDcsg5wtEsN2fkG9{cYd3N)(_=#Moi-U~dOv`>AGvXW6`^zE#^&*? zVEo7D`@yGXrF;bc{^CVC=3FHBW`s}RZ%1ytnc#r%E?HIHSSd7G1~}ud(s-#{kd>4> zo^?1mZTN683Wio~-q`ql7;nB<%%v_*VZD#)kr5&)$>Qt*g2Op$I(WU zXa&WtcjxRktLezBtk!fLUaV!qsNS0T!*hp-KR-p%b0@mHyW=sI{$H)Scs%t<@kF%g zFFAykB;N6>$rRdUiNyjLVl_=afMBuf{H(Ba@$+*yX;A{$x9nays!?!mVFkzccE}*w6=G0bGtp2s;FdLG2y{1=48q~edy^>aj ze4`>GllwtVCi+v3-@Li+`l7XOX-o*h{%g|l>7Mk3vn{|v=0~&Sz8aXkqsVS^Pqs(? z4nIFWpD5<|GFzM&ni!l~OQXydBfE06dD6PuUi%tq^~w= z-x7w}m9dG5HWlrJL?%DZ@?uC@;X+C`&0qpn`^{?h@v@cq5sbP2^VdB>g9;WzedPtT z-n+o(Cf=J0pHiXzuhwvDB8QLMhzFIKWh-3-Eimk*QA6HIzczMZIy-;Y=4_9?KcnZL z*IBc>`=y(F*PQFCv$9mKns)r`J&`mOx$bc8|u_BE>7b8e`rQ`Ie$dr$mtB&!Qtx|UfvN!n_I`go z+Hs)!z)&XlLI3KsZjLcP=^S)u*UZC7$C0>PZ|*)e^fX0^XPLc3M}YFH#&^Hy;3?xO+CX!pzbQ{|?@AeKVT<9py?A+C% zK>L@QF@rX?zj@l;8NPic|FezPDn7GqOh({HmwV!l%A568!CyzCd5(MmUu<30p$>T$SyiMk9zAET1m3fWk=CS$U14G2rVSu5>oTJG!^PM*0kh|K z=%aGk41J}yCiT-FzjCKIS_S=ol)ZIWl-<`qN`um(bSVlbF?7R#5(-Kfgv5ZfbT>nX zl!PFX5`xk-M^fFM1jfW*)-#4yac@p<0&_rBNpo^!6V|KLBa3+}!5+G~Act#((* z1_N8XpgE3hL&C4Gv>W9m$0QF9L8)kT^5t_apDaXA8AtYqM^2eZ3mkTd;FjqJgS%O& zX$v-q=jJ@CWpo^U0~I+bgcVsSqT6c)`Pfp#YFp4LX1qit@bWPGP&wrj8a?8=Ip*QQ zGj`1tz4e4iCIz_vjc>Ojn4y9-Gxlt$jrFhn1?`;L#AxPK+Xc$_)3lL3=O1;?j{u z2R)()$x=A}s&!@;B|>e!2x>jeyErNWf5=x$@HKDw?`=_rCyEDm6soAW9;V!Pg&1dL z?uCZa-i=*cnkFWG{`&QUuC6YV#*VaZQv@%}z!D!Khhq3~nsl+)9%>>@a#mMPX{6Ff zvb=dS0Tw4gLzA(ERnb0Jp9%pB}XLWuuQ1(e4cr$)f6bbBwlX&0f959zC z&iC`mt((4g@p^LU^#T97@J?2R%rNkou`aVn{atfzH5EEoWk;Yt3kuOntU@Q z_bkP=2CItla`Yek;Lg+1#p@3yhh>$t41ogM^|sXE-csj0&>FF(<>FvqUQP#c4rs+K zfL7J>lv+**EDWL!AMxKz@vlGhZ}m7RsI9NZ6#n-<5klsDu-y{DSj@@5>2IdX=ezc& zI&r11>-r(*jICnenX$Xn918sPPjlu!JZZ>P1f-;VfJZ7BNUqiS=@0F^MnFL>jJ=Gj zB*|~&TWz}2mQO{P4GOe-CMfypYiG-;zh&6?k)gEKI%mja>1C|@wqhvB%N8tNZ8YZ_FSvl(UXY(= z^{Z#k;`q=0gyYg&2V>N-w=Y-@UqSo&leLC%iVrG=()4gs4;-wA`hE_9@$*&b`#BbD zlM`l!`&SE94*noH)G_H2XUYau7d#rbv2%-2~G;S55EZEHN0?k4U9dvSdI`|EWa}29Z4|^xcljH># zlsP1|oXn?P6lM!HT14KYH(_k~V)f&+(ckgl+2su#9FzP#t@(g^zLngCxJ=@JFJCe3 z*n=+Q?vco~=1=GgRWuIib})@F;7&8~{i;Xr2}h;!HM6ICxGd*jS;J8JgPgIb*Vxhm zcZ~No_Y4PSy2F9A9p0qbh|Dbm8Yg%{oSIR$iz#~O-49K*jmaKRV|Jf{{VUG)FDM&R{~)0-*H-WHSWSq~-scE-UAW^#$c_0P`v`MN-?!@DZUlgnwn_R=*u2%>|y7@an+xO7_SlY)+!I+Yv zt;-!VFtFMffGPM^@SU87>iUfdkdLr-B&b06Cy8SOZ|N_kL_m-4Q?U^eTki-p)7C3& z;@FSxf^pmm2@_t7)+1+XegBnUjh^n0j+Gf$yS$y0Y^P3%AV_gV`S}Jpw^sISK6~J$ zQmES09tt<~%Rc_X(_NKl=Sv))T7A5?dBB(V?cPjq%vu%$F~73iaD3%?6)q)3D!tn(|DS ze~H0*dc6s`^L|cCj@x>{=~$zQ71yccvp=2l#H`DHS7%hcV#&n;VKb6Pu3)%${<`9W z3%qP{a*Aqlk5{kZOIMwI|4|eDWcR0ZhKdI!%``BuAd>3*-GdKXtqrP>O4DFO3bTKN zjg8G-8c-8Hk34xwM8jpusMPk6rXfbexJa<|^bKf{p}j>va?#8_GS`%dGhZ+yR7Wc` zY`>KmM#)Vn*e&yv;s*PvGtdf>#mRfpwn#JUK;!-cqHEw65Rlg^-Trm`r>TSKTp6+z zP;6tR4; zh2S{GG0<8j0J%KL|3O$)r>qc-9SH>jQCMhPvHpTEn~$&Aq0Es?6cTk`k4KT2*Zf9b_h5$M;RDwiNccX#Mfy?3Q`8j-q&sF?7T;J(M{+ z$nv3Sv_#nCR4vUCpK5+xmiKRlqRHQzX4iN1?uSp2vrtvTS5z2$X7 zKkdO5d3NtZ<@Bzi09qZW;fG~|xA!sQJ3h|@AMmGo#gr$ZZ(s1Mr^KRfGYrUP;k+U| zE;%0RI_B9YegKLQ9Znj?c5*mnRONedy!5a4{W2YP-=p2`04Z93dV)gIFOIB4*smh^ znW(iw5)3wU2H<3<+qsOv9c-7WeIr38l#G{W!{DJWG5ql8uX14jM(3| z<^!8i;#o;^Jb6Y!Z@!!K4WRbE{QeEULQQrTzw}g>aQBLP@u#MFDuB{{d_5ot3(Zam zv2P_K`w6Psf5+d-tQ-UyIM#6SUy6-Im^nL8oOk)knO&t;3^OC3lg?I}1X4`wQv;pF zXpYl^R>?g}>5$I=ur>Ouh63Wh-!)(2aeidGdkY_~B-X1(xN%h&-JAUbv?n~=L;xn( zz-yGsX-2NmMo{dri1iTbu1wN%xBsqHedC$fWde=fdTl37tEh}lO#DgnB+dd#|BPM& z^X}L2DE;OZv(AW}u$xot!4*L$o533>lFzTn=*JQGLFhVp`veQ7tIfvSwh1cJG^>Qp zMC^EpAk~70d4QS~HoQ}1#^GF(H$m=umwhmY7pIsjiGV|`69EwH;9lONEWa}v_O-FQ3i2Y{BIPtHVoUqexC95WJy1iZNZN@Gs=U(-S3%jMjq?+J}b3RT0@ln?`90}H4ZjpK>8%(WFBXQGsE4A7)BM&_n#fiXaO8(& z4L`}CAOPwl3-JPAJ@^?QYndgVk0Uh*G&D4Xx0w)yn6ZTCnozEH$z=8S_qTZJ0DnDK zs8BWe@)II6r0MAD)jNWkwBq?P8)?@E%-HSKL!7^A1f*{l&(&Yq>Lkv}S-zXq)8pZ} zaCt;@jmz8HQzRblpRW+bGno%_C)*z;C`1qEs2U++7|-4ph!Hl(imUwW)wdc-1>B=M>A?bSy_% zf9qxk5jv!#0>Tz!wmPM-HG6}W^5(xN<8%c#DPg!5tM`kU$XgnITOBNUxK%_wkGTBd zu+PCz&gBb*x2UGRPc}l*qAPc#WosX2W9C=_ul+p{w3c->+uHz4PQxVHRP4rlJ74XV zhEjW%>-74Y#?o5->yZrZLka7ds_G{8A2Y#TLkl7@L|d(AR(e4dYpYt7$8NxLFyNWB4c=nR zmBc8g*u8sqneQgE2%=r3qKdEv6L}lWoJGWNF6rc}8q)yNh6>9`e*ccK#HJ&p zu_4of`Rvus_^?S2of%^O$nAt?$_bQ6NZcHjz>B3m9!WZdg-ujI7P34j>{Ugc+VGVc zg@)Hr0kn)9S&K?gnkEQw;p#;Xq3j=*;UyibLQZ+~(rlGKa;|dBKh}=0Oo?>J_i%q` z%&fmh6b2Zu``+JXZPJRoxbSBJTgA?rmj;r8HuMIQc#a!>xS7?png>i(`Uzqp!_eOReQB`7Xf3O#sY+M zl1B+L*AJO(GGbqzzaw?XZ4Kmhn_i$BNHzC6$pk1KTH1Td{#$p>guthL63MvVHyZ!U zXcYK&7ng6mhK~B+Jo=9`TAG$C?x#LN%n1}}?u~SbNd*Yi9^yuS5ffwge{3H;%uI0M7m{ah1DZzDp5jBc7J4(Ug zP7{ooyv14e^bj+z%JCb(7iN>b3Hb$_c&b*V zT`>%pdn32a(+B%iZW7}hf&ZJ5q9NMpp+P#oASi7Oy`B6m=>|wqYO9aKY}QoExVvQj zqt$1lP%Uw9F){)hy>g=skOy(V+uY*0AvA|B50e>&?K?W_Ep#6}4dWOGnsvfa{w=S2 z-^B8y*;I=BSJek^xK9fvBTZr-)(2w3U^>sg6XT%>3KYpo7m-YoC?t}`&L)b;`Q+7;Xy1j4iQ;EQerON1X&xRz+X5ashzgctzpG!W{1xgu_=U;jct zxDZoPPXJ9c^Yl6P>p?Py|MP6$z2t*m&`j=Oxg*=Jb9-wCHPFl23moti!@oKndYA~I zD$N23@x@NZH9x5^RKktDPUeg&JWKTlgjBvJpdHwrrZ#I5x{i>f(!R7``+%%Bt~mDS z2sA)5hRjTO$ZROs=D_3~Vt~MDSf<}E`7IcasqZ~p%HVW#K5$9%zxIQbj6Pdmm-}QW z^FA5bGl|EHlS#d%F{VSLKwIdZgQR;JEkUn-n19==xy~tdGvIUS=g;kYghU>1jh~WH z)oHaVO6VIGAsexz<4@?%DQ9*$$qKd|pk|mYcEgvxLtBl%+UyM@d@|jPi~dtBgbip9 za|^b_GMTX>Uc3esv(f~`Dq7s>=U3Q{i8?o=Bn-bF`sUF=0T)OrlYjTZ#}>O+%IdqE za&f62bFnn2Ya6&*+j5~AGsYo%-zn?#u}L7dfYp1uAk!Ai!?taW6^M47jNBC;$f3I6 z-U*XY8d_b|2#j4yPhpe6MR;A_N-)J;0?L;uXkZ}7;V&rH^+Qe>{_4dGE{)c73@*(? z?!bO+fsyX>+e~S$gG-EYh0g8A4aK~Hl|r;S5B>x0B%csoIJPHBse@+X%}IzV-ZhoW zQ{H`dkC{_5Q%cwdTz|lv<$8@Em{usOKkmKRdrNO%Y~?&K#dx{0`l~igEFQO<%fByy4hd@}2;278!&->ps`#YzLv3j(FkU$1 z@d3oKX?&oq*|ZNgN&a!%R8OFX$WZcv+KGkyxQg8)anS2902f&wt57*q#z8#Oxa_pM zI|8~p18IYiO^tJ!vaZz*>vqr48^q~Q^){7aRSPli%qId0S5{V6`x=;P;CkU_d_~aD zYD_v305g5{O#WSzGN%WdWlMA`S}HH3T$pL-0(18Lp8az$Qm(DZ4EY*_Hh-Z4c3+c* z+@GYqoo#J}B2^YL?3uZ11T~RKBD<-&$6vkL5eu4Ige#mZKbuG8KrelFkE^Tnd@7zf zwfrHTnn#!IdJp6V*D~9e*0$LDPyUju-JY*B2o|pRy$uQp_JH6iwS8J;Q0mx+Z41mz zy0cfqB`z*r zy^_(0gKl(j4PVjQ)4OiTPJpW9e0cr7&%q8K1>R%TreUTlu|keJRN?o+>%3s6o`K=s zNUolNjf*>oFy4A&e{{fod!I*E&`p-WzQqnZ_qhrjatJ3$E1Bh1w+|QdqL*7FOnBnJrG= zCmuA~2_|;pe4YnWJZKu%1lHj^c!Ny{n0!fbH54(svl7qa5X;IJX5juB_ zBf|l4Yi6mEV1y&51C-=C={D#bH}(UBwlO@pwr%V(Uhvc5Hd}DV31U_k0ER*q=blm%q{C9=9i*Fk3-K&grU-O4| zlFO{AxodLW14%p`$^i!VGR<9PA@)oLy0wl7+?E({hdFRP#UTC=t8q*7iUSiC2z1?}Xd;0CbJ0+n|&@5QiK8G0LO zxzrIp@B25r0zdjGxnk5;wl>_JkD|X&NJA$;_xj~ev24dn8Lm6Ak4L(qIef0Peejw$ z*J>biI=I)zWh$4fIt#uh${F?xhhMf%jq|t4ol+T~3;cel3=7;Y%PN>kylQNzAZHInz-7id^7LEbr{Fc&55E*z7e>cI%dWNZ9AQ^vQ-_ zw9fd8toucR0=LGzZhkt0bV=Tj6`MJz1CR}WT%4eE9N+#nArvvGp zz`(4(N)2j!QSLYORUXt97AgoXn*q&a;ktLJez#Yl9!`p=7`7f(He$)$=qbhZBx=85aUsgAT?DrDkH9@^c$w-wH57V!Lkuw!%yfK<$`(s?X{>P4ZX z#G6r+rEKHm*v8E<4m4>63^J$YJRE5pxjnlG;crIfI?OgO9$TrktqEXR9|O6=Dmipt z{OqXV`iA!|+kKleEvA6YSNH9(p=tY}bVc@`_WYt3v@Wu1ZH~uc0Ut`L0)q-yCQK!T9 z?w!|HAm#yHjMI#WdiXwn3ec>jKL$lv`lWTrw@H!tkpaZi(Axh&Z4Xx_b}@*9SF_M$#t`?S7IZ2$h9tEk7$FL zHLf~hz8)3f6Od(6y+JyMm({FB4;g;By8Ges^m|8!ESBpkpuR?!o~+iXax@&nclIli z&hoSXw}BO?1Ur==0@-sB2s6aObS`L^dgxI#z8K9e?ye5>Hy6!)!#$VNK8+KuYoX;3 z=4bCKfend{r~3M}B)MyKRivu)DbR)dPX&|I4dWW?=Pkpxf->&T*?D@gc(FAggXg%% zQMYOlzz}$LEy>SEk54dS9G2>UNT?BdY+&H3Rp;+8-^1j%eT*6oJfk}+f}Ul|og~BO zMRc0N;)K8b=M1YXYcAoOsV3lTBa{WuBx8J`85x;o@yfMLbR_Hht75*erso;KGYA#gqZ z`k=4v*W)8m5|~$xW=O#IwcwYIZyy?Z|Gs7k4&2ZHw$ce#k2nmm4(WE(hB-cpdx=9_ zHbWkcN$#yA6j8Ch_xm%L{LJsf_ld^&VKj%J5p_{AkViQb`Ek@R#fxVxw|x!FzMZ%A z50g{kW10_E1K$0c&nAej6N=b9LA+Bx8Ucu&bv;2EGJhTpEj0%ZT4Az#x7caxSLLbjVv% zNzL06^zk(!Fd)IchsJVG;KL;l$NZJpDCL|1)?Rcphi-FG&MZJ*dNJnm>x&}a!xRMu zW6x&)pBFi;(VI;gIxsN;?fFm0vy*j^F3(c$*c8rKfPzX zSCI0*go2XC8-Bd=FOIpHek=#v)Q(sMbU1wj&M{35LtWz6!|HXlf0pV6RiQY$Vx0-;$l;l#(EL<2o#G{s}2v2H=g_#FPFO?c5d$SxPi zC)5GCAmR&oIc{!osd#U?0KlvuK-gpU{u8*teV?CEaCky~JI#M>T-ZRuI{w6>GymDM0D6A=3O$dI;`VxgH0GM#b`i*Y>g=o?z~8+5j@J#< zh_R|~uWKBy6)H9@iUz%s@kmBDuRtM7iP0>uTQi#3B zZ}&&~JVU3MQpf2XCI~0WRHFqHxL;U(bpk%kiImEv!;;M~ohww@jWmx-JYHb$FcH31 za^P&c1?kUcg_NolLTXVZRDshyf8MY=G!feKPMUYf_@6l@(7w3y6&<;7uRfkc(>x2E z5UjwD8oqYO`2Cl!(rS>Ry8n_EleT;0N|#^~>_n@#{m*d!N4|bI;@YE#ql?4voU%*S z{X-q*uy4tiRHml7q3YT^#q5FQr%<~Mpnn<17w_4oCTi1x{rc{m$#Ff%_T=?lYN%RB z^?BnK8hxRhsLrjhMH&)rPor=-+`LBD(Vy;+5EbS6?0@krre^Ij$+ zz7uYgG+gSw$3nz*O9Av5Vnkj@H>12K3_QHR{Ipjer^CL2#bC9)zW}kewWZ}rGWeaj z@YifvJ$40ueG^o^soBY=TXf3LZqob}J2N6RkFd(sN1se8R*zo|C=Ij?Ap<(4J_k;; zeH7?k@nHK>oV2Ymy+W#=;8u8iZ1u2k_2ZUH)9Sf2kMV-|-@Ue+0jf|yby6huOnLgw zuAJ(FJ5vZKA{Lar1Uepy;27@(TB8DD`_^D36(SA0T5c;DrxZ=XIf8ypPI z;t)4?ogN|$6Z$IMGFGwXlm{JO1r9c}ps#_cU0i2pXMu5oWSs4}0wl)5`Y}OW~vGMcJooPYDEV?%;AIro%F-?bY%A;~s}Q;`cQC#QLt z9L|#)3Y?5g!E%gJ^{CF74uy~m9ih@d?MAE*tz}H`fmfRA!awjtA5>WG<85uru2JCL z?}i&AID7HZ`)J&q0d2K$dKme23!v9M!z_{Q0?OE%<2%D|GRAJqS3`!~?bA`2^?+Nm z=bi>@+Zd-Q@t}d!9;CuL+<3zyJaB0j{b7B|NC*9@;+ZoSWB;tf*5xKZo6nG9*4eeD z>NIb*boXVtBeaUHyc;8?q9+v!d>T7nQ!56WyP5;BX{7c2VC=xjN@LE}u^mmJ-cRv! z@ZysY$l1HJCsTC*t#dg!53k;G%SqM%9RGZs9_%4X`1pms*hssV;ldKLv*pKllNDrS zU3GVpyw(R@WZ2WzKaH19qO@-vH-Xnb(TAMlvC@@$Yzv3V)58s@b2OX<*xcS=4`6A7`azew7tq10|7<8x3 zq92Su^@j&9o5AF-%pP?tZ!a2ubkIHLPWjOH`@q%5aY5nhgZumc(EN4;{^Jc>lR|?|KT6byMqQ<4{sMOWkrhKC;0bWJZ1n4ZaTWnrw6hH+ZM^0Kbm!~U%xI$ zR;lAzIZ0CUrj3!XW09os4a=>U)CN1VN=}21;~d00**3X^xMRyu*B=0S>!b}38H`#6 z+~+L1-$j>V2{QNr15&R>iuMj~f8mfYwK(Qm-jtzs)R=qKfl6^c*oSNt`kn|PJu`;} zh$qrAs|TiZNKS-Og z6*i_gD#R$NZ2Dkx25;QmxgtThFw|kv1o;J{FXR}FGLgi`>%J?jYCBvO|6;TCGgMaM zZI4)TqB>+hmvU@!SV-`nY@AyKaLFXx(m0g@;7qoX)(^8M=fDw5?*LeUeqT1dOWmPV z`3L6RvHKy8AvQ31ikTU;2D80aq+t%yBpwbE&AGm0u1a`Ospiiar^YF~$g<y zMHTTvu#+q5FN3=(XvBAi>*M@mdgAgmdwLO|{14uV072(+=+64LD$d-L1IU-7>H7Px zP$s$m&W)9{E`Ju8bH2Z+lq~}*Tz+*OC1xWwo^jA=8vA1#M6vnpq?H`p$S)IB$z;nkL11{5j!3bUR+eoQ{LzvR)eY*haQF1~$2sR>;mU z&ewzQKBsTby8iSaJ0L5G^mtv4!Xs>f8EtsNIO@yG-3G*KGIs`m8o9;t@fb&LykN6O z=il#D7U|h>?%rsCo^|`$!}7A6aPxI!RFv;;)6+#dbYMn|v%s5$Ww*1b%3a?8M&fc0 zXDqOZV)~qIRFaKzM#m68qGj4+@!0#C033H^2PV)84GF`STwnIO^(%y!yv43Q7jQ8r z(G@(>9}VV|=u`TJp%ou__|mt`sp3q0aVFJ_J-D_>^RrPx$+vG0#1bEUe2M9hsm~_S zT=r7MOcs!yi+0En#MhZx67(QG&lEzAE`h99LWqH08DElXS!3Zs52nj85+mLL~0e zDH1Y1^%C`HQzxG)ApfgP-sLYZ|xnY@F-0axZUzM})8P_TNjsJ{gE*ie6Tw z`3JZgbv!0yA+`AOx}OSoQzRWS=z8r;2-cBB@789V~ zKVd^W%*3mJ`cX>?3mP>DEw3fHu%(9&%aqX1t$=6?9ijQkO;qHau>ePzBHU*+{o-%D zQC9xiaG8oK*wKsTXpRGU%qdC$@Gyn{vtA?MAo1-T)c^%b|CLEGY5q~FnZ+0Gyx>r! z4s1-$T6CCFhcuDW9HGkmdje_bPQhY3e-$||`^`o#`W4q6wu!_G9qmy4dbpINRfP*n-UX6Spr$QY3LpkM&o&e60a*DYW zqo(rBEmx~mVAxicuV4=bMjqa%%~C2ixKXlxLAo1?TSkwyWZ`InylDu zt-9*oxM#9|6Y#hN0(R6Z+^S!gm4fHabqTlbwbgV19QQ(rkAC3A5Bjr?Nn9p=r?Rp^ zkYv-pZwe)ziq~V4tvj}UxG+|rzjAD2p3zmc{V2kSG0;kM`Ym2bMw#A#xYW3S8p{k- zh|adZoCJ&DAKIyJ4k(xzh5)S!hDQsPDu`_E03H4KngM7UF4E=;n9y;;;cTJXq)G!i zWk!w90hikvNF_8taj#O|1Q62~Pt7#An0skE16AnBP5L_1b^>YFl_;QP#&j$YOsLRH@Zt9- z)Pg1c4CgG&!5mp&psVa5UajWOx61P=p20CgUEMz)h^y9=6L`n~!Ou3&x0ltJJ8cZX zt$m8x!tpAA5l?#AAT63#{@=5sOvujR^h`|3wr%g5(*o$q@e64@c00Im%A-uVvJ?JY z#7ob#0aeXg;FqwA0u% z!cep}l{#lJn{_`}_AO;dLo)Av`*uj}=H1_Yd8uc}~V;r5rwqqVv3ieAl zZe9PO^%Bg&NyEexQV*Cw(+W63GF$|22ATlgU4U}r&9MA`^D7tq^(!M@A_2GZGOdMsWB#5unGP{Rdh}#x@d-_f)rT+)rsdo(-r$ zhGALfBs$QO+rzN%zBvS-NBZf<^_`2X5=cp8EC+;bWtCQu^-gKwr zo4xh*^}T8m=n@LL0}llHPx$yA9*O_~1Is+Jx2w9a=<_#{{Ea4T|05|%yrQ-Wz0+q_L{9@yTa{l{_bTVS>cEv9wYu0I@-go^G7Sjfo@uv#?E{LGmV?T3GPDY( zK!59goRfQnS3>6w&x{W+WT?gU=ranh>LD-r+eeMahM%f8dq2f`+Ed(olWC8MLUdex zLZ#mGL!t|RTIZ+A(;z#7a2CVYD;wv&jWyNJuU}OE)_?kKZRi6qhc$IF)70$Z^Emj^ zMhMvRv@=z}_IKy#gqr+L<#w_evW+F0GW!`RTvm0#{b+n%+%JH_ zicL@{C|t4wXbI!Qj&cDSyiH6?)UhVZYu2+m_2zo5UL87}&A%d%bJf4b{vo@Ldd=(< zG};r95+x$QhTrk60_RAfD>wXK*G0L}e5E(h_5Y+ranLGZ&8YBeuOr)D2z0ijqWECs zpGcAH+?A;97G(UK=D_~j0Uhd&&Cng(FD>vKe4d@OLD;BY;8E#&!Da_uKdBsCd1vd=TBWlDhG0weQ^5 z*4B2vU(B$Sr0n-Cy8a_a5=G}Psb-_5&+3hk?Z#AO2eo@@dbjz?9O)(^uk(=_mTwc) zv2pY2OK$0hPHpReis786o$<1&c(Rr=MJ*$oMI<3G8iM^!@rxOl->=XQqY*87QgDn!A+=jEP4 z&q={_ekIUisA%LU4hBi3P)K-n&OV8E93P^ zmDM0~#Vjnmng58|T38O}{~BVnKsr(E^(s@deFNo`fU8Cb zwZ)Gevqe;PfH#E>ko1Lw(aOm+=PIgeXcRUyO!0Z$w)th{MvP6;Lma6n@`fnmA3JpG zS%!j*Jy}?c-VV?jHMuOSIsqQS+1BN{vi1g{a-DtS~*}=|b z$6q%!8^5OKCEFBkz#+-?oM>D z!v1s{B|U7n;$uR6!lFry`CCUvv`Hwe%JTX^L;OVR{@aR@(DOx8df?=av`t)jp1m-h zKB$!^++0ATjDE8gK#=F!_ZBGUX?0S2KPfK#KT52xieSsx2A8WrF9=UDCCFvBd8oGA zv+5q^4-S(g^qfJrAiepl74SNnkvpJVQ(!_w;FC1K{&`F`Ixg}v;1RAk-}lgs0tG6q z@UE2Kj3j2w9pr?uBAeGNgp}UzY2fUde|!=#h^U3$g*3e_-CAhTVP5ctPbx{AvPIvh zeYc>24QNYI$9pTX6?iOBJTX{lUGvKN-8Sff7fZ`bfX8rjSw-?h58vf*gI9BSmWE+` z>Dnis>?mI3l|9a39&YHk?e8?gY4++yOrOB7qJiY&)xdJmxu z%$@Qlm8`YT7%{DpK2N7eCS^TMK7&#=!dAVV=VDNLC-?6(WOvg^?Xj0l9t7YAyoGub zs8796yt*-m-uW!Yl5%IuUkOQ9vV9IMe$B&OORI#JCHE}}F-)AswS(?;S4cLl#Q&{`lhblf@k&=kUppA^Z%Kohy=lCZK6S6 z_bnT-d4`|mZ1yaGmP8B|@MmH+=9sp$pJd(u6Gb*tG=E)og0r~fIX2H^Sh&IA-;z5m zfTDuFnvu&}EA)eKf(SV#*p=d)(gU#ChhlI&=%bvEYk~~3_7cgp<+Xe;*I2tx$9biz z6p#r&&5{|<;IP+Zhw7P}Lx%fH@6)aNBO_gRu)2M+<98qp1=@j_MeIS*WP+u?5J#}Y zDST{!^l89XNdsKv?kT($Gyn7FV#Ofq#k`$qz{?$pk3FrV*euPIU>c^zlB;6k=i&oB z?M5rng!B58sHk2Kzz=kyoT7`ncK^DT`q#NS4J(FQPYew+AT!6Ug}au%Zj9NgpLbon zp`8UD&TQ|3^o&5io0u$>kjlXlm25NLkK>1soDicIb_?FSajuUj%gyJ83B4gbFLW!1 z(M{46upUgU=2sG`MWgj!GVg({&K(^m1+IunC(B$h_&eyk~yf zOuK(tEv7v^LM-BMm&RG#tqFLrCS}9IpKoOLSDaKv%}82{+LsSOs!m-tVW&NX8UvQr z%CAuY*-#yV4#t`cqk9=Y?EKVUaSZHDcM~AtUj2Bk0UU-W{eKy!z3cy|>XFJswi9FRF0cK|uj z5y9uAV65W%6w04H3mzxdWM-?qVo`X2vor5y3jM9V!53e80cWdg2Q|Iwd!dj>YlfST zZEe5aWMJ@HRbTKs4w$iA&~O=y3esy?n6{gEx$eh3pR?UE(3-H!e9vGCs)$jVoKO;5 zHz=B)l?xx{p7Js>)Zf?&y~+=&^~`~OM#g|yT36Q-e3t)@d3ANEGjdg&N1t)BY@=1; z`!R3#=c5_XEXl*>$-0l6>E>j1Tud;^SSO@Qm;29s1uVZA*g6OsYqqu>xMXb#J~WkM z%&Bsh|56-s9y7^c+M-LxYAQdJJTb|_`T^h~&57!bzzknde%yE7#Y-w3SJYAOU(f*! z#+--r0ZkL2#fd700=0_}r~c`v2I%-7#FX)_Jz>jiFn@UP4a4$Kb4Ixl5sGdbw4mbN z$2nC*Wj&#tjb7TE!pkw&Ee(AnZ1(!=U095V84_v!eV@3F;iZ*XbFPHqZ~ zSXIYnzR6D+dBM_{12-b1X3OQLj0|j{APfZ5|0Lo~0p<^xHfKcGzLWD!w#yr{+do)a z;0JIDSzy@ATsh}bea6ahbZAiGHimL?;THd`^S%)$2>-|X9$(O zHz!KbpoNS>M#`4Wu}(xpLuMjUD&ttk-r0nbb*xC{F|udh>rHj<&-eTI{jNXmdfbox zc%S#UuGj1NdXCVHB6*hQN#B>3t#;xhTztGry1PF&XMeD50@R9Vh2UUG_c?=w<#yoe zyfeV<-~0{~lZ&SJ*E=mobUxMWJ_|lZ?_2T!f7l_lt`M!#a3%TnW6E7#KaA;Rs2GI1 z|EoIHLy)gp{&2P-!;o;4t*J=eOTy<4CGeQSaaYA_aIES8t&Vp9Hs~?w-CWc=%r4E1 zBCU#)wc61E_GChyDbGO?8oG%-@%h)uM;H5>B8gKuS~s%6L^?$28k^WzP2RArJKMDq z{Y%%M@olr#`-NyTt)atcLv1jIWiJ|%K_THM17RY_7c-1r?BUCNPTCvH=O@9BhDQ|M=t;!voPF1Ybg0zm$>@lxNH0?xi{-(>sV4`SK z)4iJR6aqRWg2+4flP^C^@m_-2*!wr#sBpH}q4Kq7uTfRhr9o_S@Mo$SMPkurJlv?+ zfJ&e2$0|(SMd$&#wfZ2Oz9e0r1-Jh&vlC67pBDI@AnhTugZHz4I7oP3y6<-^lEAq9 z=FIk+iffpKGIOLSdPu;e(>j%agn3xUe$CW(Gg}UBUaQ=)Mft%bn&2jcGYJn*o!*`J z%LGcV%r>@7^!Ut$(*Y8~?DF?#zoaBiIJdv+Ey~C_OQ_4`T34VGav+o_`OcyOEGV*3K)ZSiV{xaSeaF_;)<-#vHd#VEJIuXS z)zJo%eX`0yUF?8p1`g63PYRlqJ1IlVcq28bw<#_Hst1N-ys1JilDYfPF_ehKy<|489 zVEGo=lnG*0T!@2-Ad#AL1AF;ZKdN!Ty4<4U+Qx1pfp>XIhHITyp5eE!P@7SMT+63w zv0lIRLenM|B%~C?Tw?E#D`P6O3#d5hhXlyIG1S@FyQs}shbN+*NKNs(YuwSN+IA0d z;)>-C^(TgiCr`1p+A~}(-;!@EJ%6Qn4S~Ll+#6zP^L!o#BZF>x`oCA@qd}G=Obsip zzC9HUpKZ@nFp+i_^;T+EMXBo^!{!p!IiL!1>~Sy2u=Xo&9&>t7LwB6~MotdB{JW9< zyPQ#s?slTxT_j{ST18#S6n@u)>MCvGhr~#Da0`z+9qHD6i^t!Z@7B+r;Nr+X&vIkC z;pS<)7&N6?BGelh-^a7AN$H4y5B2CVLEv&}|?FH4`;%sWfCYaN%(m!Kp=y3y#!QD&ED`b5h?Uawt z7~tI12JgJaB&m3a7SONU38OSTCBDc>ns@WA6?GV8zsjo%Ni$*)n<4MUtr5RK^lTh7 z$S!jLKy^1MqSmZxs@~`MM|Gm;{zMLmD6vFzr4trMFq}B#4F>M^Djw{@wj*(aYt`9} zmN^tbYlH=+<*V~A@i2Rf**7Wfc&s~nQUNXR=U#s~*oo)h3uxiz=VSTT*_FW4h-w$& zvl6EQQ!aHsSeF)yIK?Z@xH?Gid05-6Dh*?gvt4rKrfm?B;JW3w(iq3rz$0@;f1g8! z%e{aZk7*{K6*EPeLlrxU36Y4fQ^4LFBTlVsOiTfek)IC~Hjg3eK_CUnEBXHR-9&5n zUZNa^BjyopRC=8^b@qUrkGbK7_{MFi1=7zNDR3eOP#x3~WLo!W@SF&`Q`=V>C(X_+ zd8*Ha+6A`@CJ^6e5R$uuYn<^Eh4#-9F^#z3Li?zp=SoU+k@&$He-m1BHRjf#hG5xL zC#etuKP(mn52ZPwm6D6HI|3f~{+f`D5W66B0-_0iAFM=Ox_aqQLIh7lg(27YS;ZL3 zlJQF|)^rWt>|dv`X(;vMazl*_*b#)T{;IWFZDb=hncC8-!IR&Vv0+)xfcklVrJ4azj8%mY>!vgxnLs<1 zEWxBhgv^9FCW{txq1BDkjHf5&1dvLC!pO}*A(GG)8Of>iO++JgkHBp%uWZ2pW<*vy zfW_hN1QG6~Y>md;+cg&6$6d$=CR4ED)m4xm>2&R#lu7vvV=^41usv9CY8K?P1D)VB z>AlkwKqn;-q#)6sI8pbQ2q**X)7oGuT*{u9GSQYt$^=1=$?qOjIdcyjNQYw7c?=Pa zXlIqg>LaX*4zF|A;_Mrj#iNpwvM&quj@K$i&<>2hk3Q4AH&{8S$R?dtB^D>08@OLE z9@{S%)vd>fA;4}%r~UPXW)G#Dvm<{%y)Ewh2NWSO@y^EgquOh<%rM+)I2%Ow>QQ~K zg=5fj+3-knTvP@fDKhMF1VK%~@i}X+^;bS{p3~wPF-IOUAo2oMv8q_IB2K#;3KH@D z#Cbe?c@5@m*lB)1(rKqh)EIe4~REq>v|!==wF|Lw|{V(rv}1kJ;d(7+%%I;CdOE@-zd#V=31DVum5 z3=kGGhjhm~pbn#o9fh6*7QuO>l~?B4AAH>iVz9SSdJ}Q6t2$VH^a=yVUGraekNl9| zt4h=Px0%shxpBgYQd+Wd(oJ2jUa7)C-l!{VVk|$|2NgAO4z>%!tHlfv7)JMdZy8<{ zEK+qiSund=JXl$~<O5Ttwk zW5HzFd)~@D7WBz4w{HF%*7Wpt5L$d~{VDV>gr2bdC+6uQWFT*{c$jUFI-};DXT6KS z^lBBJVQ!L0ggB|_Vq47D63dJ0|M)iY;aG~a_+Ntj8Fa-Fifg&&&-gM7nq?M+;iHaJCivwA8h*BY~_!vG+ zzl_&+%Ui&1>YKA_uHBJE=IuaKL%9yI8)IY#{M%L83^s zwYE<$mM~s%=z!Ckg#PWfM(Y4f(H1_@{Q*c4#j3?xDpQr}%DXj+aAS9LLfux7 z=WadP;dr~cnqTv>{pZ69Z~*YG+wdIB#je303KvEz{k@O)X6@xV zc_IF?fe^@Ec z%hGz79doOUe5KyI6}BP3mnjz7i)1MX+%oTfP)C$tuYmRFprA*A5hU^Zv?$f%K#Mqh z*UNTcxZ*c%`tx0S1FD@}TfBzy56yS1LeHeGsX3Vo;EZML`o!b^UST&#yL>$pR)53q z{Gd&6I_vq>#Ou#Tk zRxf)}6Kz}56Ghe^=ghTq+kU9@D%Npi9#1RKmUhk@30&YoZXk z{!aui&g;=m9uYdjGKuIQof#c81LS571s6@IPHJc93T6>fCK#n1v5GYjfV7taZL{o@ zrv96)NblwSZ$;cm{MqmQ36}Pk;WptPl&sN@fRY96C(Juxw`zh&hXS8@o*IP)yG0xw z(D291z+O68B237LZVdKXA9OgW-`RW>w55)s|Qhqn?V)%e;EqL`7T}B-`r?u}k$*JL$vyi)n0PDNiMd`_aJ2DE;M2&D*xN>3MmV zBDvK>lXvHC?y~U;UU;^+w>UOQiQH__|C;16ycDx^+7I|5c};YvZrgbzL7ph-v8KoG z(QrPN8}ZHGzG*X?!il4`Vv;-~wrM^8135NA0z{I;6APjfl^_J+>ySRLNy8Jdp-JJc zRUK$#)Ca~gjhTj*6n%@SITgE+8k+Hs=htuTW#QSu&9~RiNwkwJQHR;sE4+K`NWn)qdI7W6+4=>L#k`EI@1x_cONbuSV+)fgbSrTdHTPA+^*fDY?%-1H-Ob@p6Akys+a2z ztyl~*!h|@8gURSD*LLf;_ju%nT%W{VWpUE4rS;#?5Ti3_jea1m#bkbdHRoY4OHCc+ zGPmzyVC+?c7c@kXU95s92X-NL$r(n~?9`&L-8^aA<}g z@CB!fQr*g6ZVKly-+ZYJtuqr2Vz6mC z&<1_%1BREqy{$=4pI#b`Wo*20NyQ^u*>?j|9usJ0PW{qsaU4+7pBm;{do9O7znd$^ za$7yQSOW)5H}KGO!>rZy&%=#vp$^lwv!Y&E*qK4DA1v%&Z`0Ivfa@ORX zBG}9#^o@{$Sy8>fuExZvNZ%kJPh_MLRVHj)+zF`}TI~1XBBhZ@>A(3~7i^?6n_f0! zU4e0k^r{;h`GX)QQ$yu773Sr*0u`m4+BHFHRVp4E=&L#ysWd~xv037_b;gx9yO3$K z(f*K}=%14ZzKc6Q@dt1Pr>PHf5j;HAXT9Q&PpRv`thx|Cvxio(10KNhKXR=iVvk50h^p{lM$#u;2=W79W z%)-<0S8VY2r%=MSyOt^KB1TwG93K@1p#CG6-7vD=JsVuD%qg(%dr-$+B*Ebbtk_4N zQ5z?YmXh>JH3>wZbf;&~u-M9tG6XaskM{caQakkGPW7r)UT>5;d&Sv~B=Ku8VY8Qt zk}=M5E1Xu`M{I^BVv$@1*%kqc$J)v+^4I0fqlZqv)|3S6XN6U zBgA76mL&N??E&x5U86KR_X=6NBbCUrSGz*63^6qwJT#IzK6zT;>Hh^3Z>hX#TP-qdv}(<^ZR*ha zBz>XRrUT0Q5oGm71+C@g*znCIoHF;9R%c~!xTO6Vl9{0z-5xyspbAJE;Z^MKmjOF1 z{_`JS_Rz&Wp+A|^i`)RwlJCnX)EIixv9v2;?4(1vr+pqoZfb@R4bI*=*WTVfY}+Y| zJ!j4;w{mO5vp$bqEOs8Zd>=~hhaVNI-M)N$8!fh; zn>2t0-?m8_VdM24{gq>6Z(vs-e7LJR@81mVQBjs8vh)NFGh`7p(=wtxWJjsgO(Ga? zDA**550Ld~60s!bFH@pkoD+nX!qMtRpxbsnQ!S&|umVCJdtUd>PF>!v>$~OH$oSI_ zBvgoPpN-9Cdg(aB+sY$K{!Uc|RUU&UG=3X^q7bK5Niu9gD3q<)h?hhy_fAz)aiJds zu4oe0ncIgHT9D1qPrYN6DBvVn9PnygOAi!55bqAC0)F;sb)z@1MxNq^-7%g|PQtQ2 zAf`%BlwH;??%ZF%l`&Fs3pNFpm+x;#R9W{vIN3ayg5L5*Jq#0Delo;eyhPO=VPmy7Dn8xlDY&N4vZ{#f zbv}DewpxKzo>{(e!x+7!K8M!U=Y|^b1128;%HLHMHi7mF2$%Q2Q2@aHXIc&>=+yZY|q?qa-I3K#K;qHeOo)vwBuZ;^uIH_Ib3Fw$MVCXPJC#(#_sq;w+ zpUPCpfkvU6jF*6ChWQKmA$6aO#4x)Zy|u>O?_2<7RpA>fijV%*s0Mukp$ zU}s9Zf2^@+hu{DD?bzk7m8FUbU&yE+*PFV0DoW8arwP21!Ym09PMmLgqZ5QvT@M7dPZ%ol%!f+2Fway{<}K^4hyoK)QA0gP^YNEEcsxoC%M zXIc*J--TeF#J^cYQnokUJkrES{6RDk!Ga!P@r5a+a53@N;;3!4$QECK6tbqTH*uHx zI)n`O=0^RFZ@-9FNrk4#Ir$`9KTCW7h3Xv!3<36S@Ug+{?$0l1IhuiPVc3jvL{&ff(KczgMd*3;Ki!@O9drf;Yf}c=QLw?|FOTbsWS+UTMy495pMa8^5>4;v*FkFRa`} z?aglR)13z7m+X6hCH!_qbfi-iKGR3zz~>|*G)|W1W5Zbx+_+u?3D#Tf-77J&bpwcN z#v&JGTa)or@n?d9OoH-uUic4PJJl@9Qji2NO7a%P@4$b4%?621tZQ+0fY@UPim$}MCcgGMupw^2 zo;Sd9bnp~~lK^jv^?uohJ8u03a_i-vNQ#o@&HsbseGpWm=ZJ!w*Nx`~Kp$dQc-x8g zEzgvkki0zeDAi#S{(CBs5?sEyfP$3pOvRne_ka$rKz=Sd{Pe}UHSoHIE`Qh7Eq^Ka|T zH@fTn`04ntVwB=^_G}m+0l!gn9wPy5K&UazVc49S;z15i>PpKWT~a8s&>-ah4$5KRC-Hr8 zxuX?na_52Z)ob5Wnva^)Iye=$s8rq%H=}ND1V`ZXN1XbXBYP)+P&duEDztv@^&oBC;gGpY6FPyR>Fjn_q|W?6Fi)0o>=GQxSTdekz+NFA{o30h-s z71B3wpDo_DpCx|2X4gA@Ox}Ure6kIYInm8{s!3s?Gktiej3kh4;}bZh8U2gkol59f zf5;kW3cmj2YO--0z&&vOgO~9j76_Hr)xqz3(<=S&hE;uT+?9@`6HtUgj@2!UljK7V zc-V)B?e2hdKCJx8sx@X)<|8qpfc!o@Ev!d+;~H|v1+(m)>`V=@)O%f5y~ZENx#vLW zq5slW5Ma9M07L64Xsm*mQ~ko?So{T+!x7l78Epyy&0s+ndC4zO14=D=P+0@LuV9sg z$RU7v?>pebvzq(U++zq)`}XhV9`Pc~HR+`h`XmvzVTN$nDNd1m#A|cB0h{N$C$N0p z6Zq#UKIM7oI)u0L8Y>uj5uSz%?Z;IEW4{RaFHhnY$VTqa1-DWdG`74(;t ziZBDYV9%bzY9%ZINOJfqn11#bSlm01ano?Bi58Z*H_xT=-mTxf-d3&$gS7$_=W>11g=F@e!E-qb69u(Lxv?T>D@g& z-pk}6CIvQ+%6HdrrfLInd22+GWL9xb%H-ECG<A?2U%Y3B^gm3_MbB<0Q!&s#diU?6$KQm&C^88Xxj`qioFn>@ zy;q*ynkmT)7@eM)ZWO4F*4Q-hwTb`{`tbggMd zbLzYZlFwXRk3#6_< zf_D;aptJ5vbe#9S?6o-&Tj2%tUm)%3;ZT`}PZV!;8U^$$gev|psr%!r0#_fs3oJPV z{vi+Bw@v`W_MCpkxVO>|Ww_XzZqI7nA|rg2@VPfD4L8n+C<;&{uRVVX@~1EK1k^Ua z=Ely-`Eb10)h2_8%Eh-2lbD@Pv3Z7jMH^0WPq6h-^&t~$SeZa@UPrJ_H&Pb~fW}I* zt|Kj{Zy2LNHz+X7F+=^>~(y!_8RGGWP4%&}t{gN%vA@Ts^`km_NT zQIU%g8F#$LdpEsayoNW5ir(0nM* zs@40M&bcs6dlTaa)t=y_YAQZT`4{7urv#>R7`D>qI$iMO~W zm2QlMrH5qvdFOQm^yBsW?DE{oqdnnoF(5iS0i_L=m;2I2ZI?!oW8P z!Ayt}HuZNo0Vzf%r=4!;?h4^klrw0iO7C%bXOz(hSj`?ULyE#@KDQN-v(emUp0zKr z@8Dt(U=N+G^=8B(GZ+dkYurgGiEHyuEgn2@`PRnjZ~r6D zBkNw+rJI=AlCbacqOrqgFaG+Rfs@#;j7Uhhh&BXMoJ`}AkH}PUat>9`MKkS@*F3F@ z0HoeI%B%hQS&Ps!%0xmI2BN!+z~+u(0M?IwWNvW83<{(M#xdT;UK3BhujcrhWS7k= zmQ1CF?_By5))>Ce;8q00SD$}pGb*WFw3IYpL1Z+;Hpa5;NH80ULTPuDQEk`3{1DVgn$jDKr{#Wr z8!c`de%02c@Rnj76DJ3!%Ewkx4B&N~a0NB{_~ml) z)|FcOVV?z+oISvk_!>)>-gqO2Z6So1YhN!8Rn!$t6bB-8#gEl;GbrnXt#?8JNvHp;DqpR>DbmICG0TJr(d z=w1QTVv@H$^824(h&+-3_Qsm;6Rqi`r2~n;VD86lRHlYf@rREt%GY&r$_BNpL32LC5eSpZ1WqG4pR*SDkkBFQ25XC6niV>gh4~${T}BMkB3?+Aw2se zwhn@1(n5~FDp^~Co*K|sm$d?YYq^%~v6Wv?77*QLRf% z`Kt^6fq74N6?D%7i#A-0UdJN+N|eHLU=z1Fqlstw?B^6M$=!R86dM$Ec?;D^w$x#} z6ct~H>N%bV9A~NvERy`Mfk^lLo1%oTogK-K;{7Xhzf)3Hv6Y+4#~>lH zB1VRFGgY0Ais;Nk6H(TA_c$%x2=+O2qijlKR@ANjJS)Ek&kYyViM@>x6Kz3Sn$ac@ z4{`o~Vw@+)`>fOJw?$NK+mh8rj6!}-de^^zNv~JUTTf&!-btkd6v*?o1m!<0KCVt# zfbWC`>8}yIE+$y5U z@%}nM6U3(Y_KNCmVO4aj|98u%2wxV>9zrX2>KmBE?TR&$uyXU)Bw=6#;dB6(rDf zh`eOp7~lfG-tg75_wFilPL8LO$USwurf$cs0#IhbnZU-drSqSZ;X-N>s>ZSxYBMs{ zl6Krv483><491k0_#4AfPN$qV5;AgfJ~;?npJcF=@sHaho`t=22vo7$$iWaDJS1<3 zbCvhet!U^-S+eK350xV`v91bx_t4+R^3|Ty-tSkD{C;Hdq||qFv+FO6$rAJ;)sas5 z^MHDV$w{0Lvr)>*hiZFaR&wdwegYHoZT4W;=et&96ovAf1mo0B>5Vn`z!Ts-xMJYRG#Gvd`;JKh@IepXNiqWihHVMPWKgt zz#AS2W+bIlqFaKda{DstAdfQom+!1ndQGO--7+&8M=5{N%g9Kj%|<$3ovgsvwMHjx zjV-{Ilo<|gX6rX#1lx1}{`_CW8!wv4#iv(aou>>q+c;r|GFB2y?RO>oV`h@A<2)3` zJ|b+qaKnzSH_bb1h`ICi+P(K=6`aZZV1jtiq2B%pm+Zqn9~U2Aiq~C*_iv)N(|0Sz zfo|)r|LR7Xi(dU46}YS98pJn^6(2^{Yv7y?3REA>%*;^1 zcBJ5w!O&qOq@QIlAcq{KW|uwp4?`A-2OJ8QZsf)x^TXjp z^?A&yO(#>%q4m*3gq<<$O{1zzz$TtzMkzy>+~H+yZeLpyXdLm*cbFg(eQw=ITN%bx z79j7UnxT?A*RRNi0YVO29_dlv{%W(qVi~uYEB;|F2w>U5G6$HOfW`xMqs8ZoT{CJ+ z8ef{Z4|>H^s>k$GZoJv6($fUlg)+wovKifsX;xPU4y`^3lzkLUfE#B6hCp%1zPf1!-o*iinRoO1Z3% z`m)vtdxAYB?1+Kq4a4|Br8}?I>-`KKiW3pi(WM|G?vcXBQQW=9*L2)``?GOh5=Kt| zLm}pu_pM0qV=6|yWGeIo$UeT#YqMsQhrlrL~;ZZ<_I}=GnGtJUBf>2 zDQ4mkJ+rcu-SZ*VUqfr#gJLPIYXHD~pQcv=N>B6{lDyrWK?U3=kli@3hNW~=XY9Cl zmO0uH?_MlGK$1R__rapJ9BYfyQprkNt?r??-RO{pB6}K);0(5)RJ87!X@nJn)d z#2|Q~;u(xs%wS_C%w;Gw-Sk@+Cc@WKn7hXUgR^eeU@}g8bCKKpUH~mPH8MA%qd&-E^q$+aG~h$G7#f%b|KhR~mKdSlSeuF& zw~r^UJn!yLFz&s!Ox&;{9+I&S)t(HrG*gz*4v_k_lUrRF-VEiF{(~E`+5RHA=}VnI zA0Ch9>wF}{06J-riHRTlKdfems`xz)G7mryY3exyfq2m4x}f;+8s6v z?(S}S?D&0waaE1PsC=8U%H}*|Po9Ge&AV7S@_Mrf3-KyR$A7?6zaA|;t#;>IL_tA8 z)AXm;=@}VKt^9KWeCnNIMD~o5%Quv%&yuv{B0i6F#OIZOBXUZo#H4 zW1p9u@7ptQ2qNT{?Rx~vJ3^;%h<#Wp0y$-fO#0n-SV5QD=|FZ)e zJ|91Qmz~;q+*~=V=kkxb4xy)ghX=3RA0Y%7j~~ThE|*H((^1c7#Xt~6`45dr%H5-> zHsW;2+NaHFx}p8#*8+iJxp(^;!-PCCw|aQ}Mc4nmgG)^K$R+%YMvo{`r2Evvw@P1{ z+oyw2+THG*e!wKV1DIr6;Of7^aARTe?kYi^K%4&|9+zxS_5{ZqX$!=e%vU|4#D{8~}{%bLt4?XsI4Tsd|i zcTQzSs7x&^B^z#9FAMb?`q_9GC#}GcAW6+_9$}tQ*(Si)umr^qU^P1b3I4s+1y1qR zf!tKpFhxCo2c$fiyr@xRa##18%VOkQH0Zqp2B}`@icX5S?~>TJ*7&H9GhUnMO9q~W zNNvrf!%P=L9)T&$%)Obz9!1jd!fKx*8w@=I?-T;d--j2xG3{u?@Mud~mQ`7ku=)Wijjf zNlwMDrlI(3#YR1KLns>2DORyC+Oj~b_%DiSmtYKIkRR0d?Kb$*!F#UCe@O9@et2C_ zPD&i;_3>1Mm9iLCL@ixRyy13_w^>wj=*QP1M8RLSSc}4*RlHn|>Ox7H*J`uM%q&Z~ z+JsfbgcAG^T=XYqEf&16L&oYde<=Qr{p1f$=&Sv`W-j{<=>Hgdu&c@YlP?Jzty}H? z#ar#q$=qJbx?^ z)t}v^2@KXL8p8sNnOo>BIo_`Yk2~W|CkttmUAIR2(Fg1Tf}H0>z%F%=3*4qB#rh{C zYg`V*{r8IDrdRp5W6)DMl^s5(4C;hSSi%z)?{BosC*Kgf!oG8lKN&H&>p{etQ z#!;rSGa=N?>%E8Irjs9%o<>GSy{xFHxbuZ~bFRDOlIzkLe|&ZB7cH5*+l&j90>Bz* zKQ^biO27MuILiky{>M_@+Rw|kwRZq3>?r!Vkw^d7DwW>a+4XJL=qmPZALtO=@J9=I zm7f-DvFGsMn^7w9%WAfr*jxSEz77U&zf*@>;6DwV073?={r_in_6#rG6|@%pPFO^# z!3RwRB3=5?`)1dS1WlF5HBS{k6VZ29VwR-mQ)eM;M&M>E@7m|gsov|Vq>J8Z;*0XM zYAOv6xK73T_)(VrdOcS%N? z!yQ1-$M4vQ`(VhzHYgto!t#QU2D;c$Gtcj-&!j-vIw1?AoD_I|Gu5nIBH}N1U{4_; znQxmJw6~9?9ufruHfoNHOmmdq51-ACi(e$YS8T*|`(XRXFb6y^jm_XT@n!qY47g?C zYFdHCq&+A5`}^(?h1RP6R8mL?OpN?9JUZC3CUf8w`!gzXhtG4RST&uZi--BCZDz%V(RTasm3aXiVjtYKh%;2APdfNB9tuoKp6~ zDeA+oT&L|*7U_QMv-m@KtJS3i;JjB2dfb)-9GI1@yp=Gm2wWCWDV!g2dp41R2MV(5 zxpxtisKCj1Ar|yqs3OZ_ZoCIXKUsbd($ZbDkMsX@L#)0a_XKm2M|v2=m`qzdeXc%zi;9QAy#OO0QHi663W3}XgL3)8 zF{Mze>_>Jus6z1K^Eq(9mWro|)2}L7Z}X%Y*R+AeQ6~IC1Y_i&G%y;$?Hp`iKkOZLCpmdG$yzONR6hRuw%S z2@{)Sz0J+vioiELv_FYxQT=z)+N)R3sF_aFy;D7O;!#SefjhCe-nX7lS|-z$MKwd? z?nyMlzcQMYhL$oU*DtJl;)M#VTG3prA-lcUMWHOnq6gw#GjZSiMT>|wS*)npCz4X+ zQ#1zNd7^##3lJW(f@mU;4Xh2wq`(hS>GL2zng815&02e^36G1)#~&4l1hp*z<062e7e}pVDik7eAvz`dXNBq)pg~Ax2AJ_xt z>Mw5ET79$kXzJ{WZDwW`d#dkOZkS-I#&)a;hG1=m#l6Zq&ub&4XI+vpS9z;UcH=$# zM)P5KT$sx_y$`SLPwabD>|)e($_KU-Z=uRxB`7?MCkP95(Lj>m><&CC7CQd|bL@Lm zxaTg2kTbdEupb_Nva(CPI#+)G64{{YiV6cF^ZF~?sEPylh)h|B#UyTV+Kpcax#3{u z*Pf`=DI6n`slhtzQTV(S#^g9u`q{SezBidSh&GL64}6d1h-z|KHwSWyAw(rKhvF+B zu#!zA7`PP@aH|)`R zlB5Vx@`0}LE;}zF+Cvl0?DDYodFH2O@NcR6oG1;iEV*xBp`EP=6?>w z%POgh`fn~h51I{0S!XvU2C|rXqkGh4idDdY$5BP|4e^x5_IsZ+7;y)HWKZPdQsAhC z_uB%)Qf@{hqoq(jJu2(6kW${V^$?U4)*&G#NpAS%bBD71#zjF>)zFajt_S4GTZoYg^k?s3?T4SAYMb_T( zelDI&xm`m8?6?JBX!On6oSXMdfX6i4?KtYdjzFP**7Vs=S7Cl2^9JvkCerv4Px5qJ z3hp>3__OHQVgcuq&!?STbA3N4+a*Uf`Y zBx=_UA`N1r!QHw%k^Deu2NRE~IYnVUb#jCT(^d}whn-``o$|Foi)-fF3FIFdK^?i1 zZX>NO^tmmt`domfCdvP$04vGsze%`oc5N57pq8?5)e~8;d@WkBrLtpepmB{I`=HjH zF1nxDeoA$`;v*OL@Ce~{u*=FjEAQQ43Pg){8vWV{>YiJjk^E**o5np=k9P`k{AxDoNZme_V6)_EN z1-sM%5_%*$I9vRIn?oP_MlKhk@;djdUE=k~vr+VeF1s8z8gKp9WrLZ(?lqL{BPea4 zyXyae=^B)WD`8!vd-x*$JAqXx9F{_FHsgQ}w9P^X-fq_nbm1((B94(?Iklz?PIe+6 za0qKk5uFZ1)mNsVEE&4}mWpq0PuWp>+XiH}4=@*um~V4jqe0+DclrW7t0mW12bb;Y zXIt-I?|9l0OEBHU(F7g|T1S>z^A5n&C;s1H;O!{jIP*HApb|C!t0|-%4j+oBD-NmI zem!(8$JRA@vC?tGLc)=lI8{46=Zg7{&R4;zU6rO%)1Tl4$nM@024OO*n-F6sbr@If zQ#5#8uZxpEQ~QQQiPe^?S6*eMmLyTcS;Fi|Cky?~^Yg`fpJvaUGdQhM;mq|U@;vF$ z;{^6bvjbj?-52R1Y^MuFvqkXo%q_VQv*)wMT^cvVdqUkaQxZwg`<+E&xUGdqW8d@0 zg+QOAB>1bP>{48fVNjo3AwjSwYMSf`S z^LSLtDaL$a0JB$f)WWhQFm_}Kcr&Fu%ez)CpAlv!?}n&=*7c#5xV5x zx-LWqm@BDA+64`WeWQQKwo)IESlBVNfI-om&_^_KUo2lcw3-g`se`Eb=lOa7kQ@R| z2UwIHOXxQ<&o#FHO}k}gCnxaC`7G(18<~Q0%|~gPkNlvYA}GezY`b;JP-J^K*$3-W z5t4cH(E)`O{32zqnowgQ*=Wcyc#p1LcKW4hDyQWrBe77J2C?4-wyAf$v9f36=}E*! zt@lM1+Ex2W5xxam7k6|$ziZ*MO(e@QGQeZ0cth3u(7gYyGwN+`d`8Yip-=3S&JW&L zTta8vDHQy{gz(Dpik=YI||U z#!C~`5x$Bx+2~q5GjVqGoMIoBW)%zOGB2BaP=Go6+Qs;lL(jeEBj;jm9b zJV{s9l+NfXZCt9x)3&-ltWx{J@#WcFtFvdx);*V&%>>kWc`yAz^e}O}OG?4VLVzIj z-VK^1D}Qan`aF(K=8is?=EMc%y*yT)GxLdlF5slyo(gxToCiVK zCv+nT%vS<=h?9DcJpxX?r77O>4m%t&;zVkKJH|nN7rb`^w@RxV=+IPzhh^jT{bxSD z{&$`ek2ovhuy@w$-Y^!ykFyt}j~VOVHZfM4dGIYG%Jx=w;j(B^GS8HLBfC8>Va2Oc zxOum1Qyk!dlae?v zQ3wOV&gg$fqNNFliECL3u;^iPS2`{`-8Vz9WpbGFw7LhKWkG$lTF`}R>xw#A#Xg7a zFy`+2ZnB9y-y|}!kz70&64q08YY$R(u^@rgu`idT5Iyc96=#O5&GR={+RCe4lR0i_ zTfB6~u^~si;T59v zI&468jRFk0#yzqzdtZS=Yaf&RYW*Bfn zn}I-;U@sMGa-9AzZ-uw&lJUfW&<+&4zXQqZeH%@H^*XLu=bWo43~qnCZ(lZj?_UVa znt@Ba>kV7tEPUM(%J7d5j>r1}o~*Ia3_}ya_*E9}PdpZFWpb?+BfwIjSKRtmBc7+F z&^dK4Xv@WMv~h!@rwmamPwOqJOEw9ct!oJcZzMh3_;=iT2qZI(KDvK!ifNx#A4T=HCnqWWBT`MdOX%~ zDojIKiD9BPRd`k0-WaD&-x3bhXu0ja-Ya@<)e0AcK@|?#4=UQ!iPSBT6n+L}<<>HG zG9VO&{dqsEzjzb&^2CSOQdY?F^YZ3x$lIo_)NYLZwC@rlm;KCRPOc+N$1kLZxYE3m zHhyXAt=xOu((yLFhdsl>v_idiVFve{B@#yoATjBkZI@Xa@j?N*>X&jWke0N8@u#yl zbOXRjNB(4-Ed1g38JfolP1i342(u{~JgBi@KXb{*=ma!Oip@ioTbwPboIAm~LBtO# zEqNMiA~5^(qsAUdwOl&zSEX!dG-OCeMq851pZ})l*c#-J>CrnY*-%-)^=)X)&ZKA| z#cvG3Ie|^r6|_kjmX30BHxw*eXu&e%RDJtq?f)hm7h4fHf@fxE(d~`7rS4*&u>Wq4 zf2xGQ0_<GlKuE({$jE@JQq5zkYF-RxRzym>8YT4M+K-sVjh&ORq;c$D<}dHS2PV zPH{N*r4e%NsjzQ+;I0l+q()UAP%+Hao3Zn1GyQN_*%v>i*1!X+-waoChwN`)2e9Y9 zt1-cUBuz%InSF5$J>+EfB3nQupW(U7W1t^T=|CX1AN(Xj*)u<3w+m-Kj`v={W9i*L z4DQ(}#SKPvpv7smE=b>9So5y$8=%jvw-gSgjMu!}zRP2>qo*zd-j-h5_@WBG0!K?cDF;2aOAH%t#R1G^PLH+Tg4mE?;cjF9|L z2&S!Vps0t~X9qMLzp3g`HL{w~!hh!iXZ?Et@F$`(VS0jIFD!lFyJi1e1$noM=VD6L zHw!F^q{3maxGVAED`fX4JY9KswV_=OVevquHEj>7)|r>5*Bi1v<}uIg-HO3eHI8E+ zsOp8d7q-E6c?sB2LUT9iqHWdlVKPRL*HUZn=omEHcUv{Kv9`g<&Di*7Ft6OU^SXyBK{x*4`+J$3%n}&wzgsnNC>Qyw4pGh2QVl>XQ}1 zuuM>{&N&0B;jh&1gEwRU?HIm)^xeK6e$k9k_)f{7-2XI3XwR;`k{+13)Te&UNKIRb zT>Nupr32>>R}Oh8X*Yt-;W%W0pIHwkAL}}*A!}|(JO3WQ*6rgnVe@Fzw_nLDswl))e`G_%_Fbo9VxAoyT|d%o%>su5DGflz>2wQ=;wTGQ zA*^N;tPUFlb1hdg5rnJd|BEq4;L|>X_Eo zOq5Nb9Eb<~^v?nC0R>om&@js*nwFnu5JA#fzYXo=QV^@KVTzuZY4lcK$Q)^f2JUaOD zvEByQd?lU7G=W6<<9;Xc`GSmGb4~K@l}abv$;}~#DDCmtMsH3_sm@^1l9%kjJAg)< zom?Y2-1@^txpMx*$X6d3+SaJ#eqQ9pRO(3veHRAd-R2=db@ov*f+-dEJ2B z?slt$IqTwH2{fgqUZ^);wPPjHVX%L_==UHoz^?z-NO}hv=~|u05Hb1&>k7f=TWxEm zWZ@YAXV)zh;_N2;f{mGdQi11}QziYr$x&fHDMs{aU(`N43@G58OU^x-(?G$i>^2%O zDCPLvZlFv*Qbu&uXn^m^6+#~44sP}o+a>;ciHtO>9omv<9Ka72OJd|zU_aKy%6cM2-m3=EqcCJDt zTe2^sBzuUlkG0jBD@(|dZR}$m>kJZ8WM76ETVx$Wj2X)q-g9)__x;?@``+*KoPX3u z{oy!|<2ZlI_xJsMe*k>`UC{HIum&^bRiH2LePGrBaTIqyAS_$(7^Oa-pTKlAWZXv^CwJpf>zm<-6MpouI&o^jvw*{}b**Ep}NtVfDd>Wyi2 z4_3o85g#PgMPf>))wo-v=2R66`O8h*n5>%DncQIzK*`T}(9+z}*fRtUH7Owg+b zd_8#RYGGN$TttO^^}kOg_9R3=TSFeJyj{<145G&yTzItqBxV%vd%-4d3a0080IV<8<$TJndpQ#@-OF zamKVa#y_ze;7FbPh-%omwi}-kir4!p?#4in_PkgVtK7iweWKj83qfd=#3vRVL|iL# zbf{9l-+I|3j;DUQNwI+7y-Nt(2l3dDro62=d_g*@sf4dJYt=Gpn7k zHj!PADAhn@28UWJ)JI^W{H)(Cja8Jbor`#1E5Yj&eccU!fn#4f1Ns@N&%S;>IHuXa zB;h(!#W@OYM$NWuTz0Y=N6B$3+bSZ^yViFMTBH*mt3O+#0uUbIl&*KL4hUcn>q_`+7BGRz{H$QUZuv>}lFbj0$pJ zeKV;hEUgg3G@6X}rB%>XR*6<*8eDi}oi{=EsEq?Vv-< zcIZ#WcOu7H#|WRVDHy&2=8%jd_{JPEu8)BG^GK!l$*}}SVQsHeF>KQ0uTm0DwQ+4k*t&ThXBw8X|2WzEbT;xfh5bsYs#TobjP%<$ zLH=WhFBmU5vfP+8D#!oF$tA%2FQA9=|Ht`t1eH)D_~#!fY>c%IR|IN0Ji>hfADi!t zoT2G#Wg`sF`&ger^hr(#zr@Qmi&{r7#~OW*_F&)#QKFtB`57i&%-iZL>sf4ES&=G~ znqYOnFOgupS7HDqNIHn8Z3Qsnz_WT3pb;Sh0NT^e1dQ*Dk<0&+^2`72xaVXcCba%l z`?=2`jDw~EQKyKl5Qa%Eg#8+h!(uHx_%Y%2nFvg57{XJ}83sT)K6U8pSi^eTjvYaA zA2Rc(%a(@Qhab82q<0A(--SB6e=^p6i=!OcrjhrxL#R9U&%HWQ0JwZPEV#MtIqO&Z zOH+LB;+Z4ySl>kSJ)QA*w@9gz@3iuyZoHY>sMZ6cHme3r`>1V&Uh$4xv0CaaLvrn>Jggp z5qe>eH+##3A)Wwiq)uxjpk=A!DUC%dseIE+f!w>|0wiY7_>ibUV)zMl2624AKV`hU zE}co;I~}?8&x})8^*>lDpl^SP^nq!tvnf^G2ztEfn}9=_G>ER9tgYyP7J}SMvW3mw zhzKJ5qb_=zfkqnCtA zXnYVxZ$T;m0eng1)`Rs%hv7mau45+{P#-W-d+c^43RiywYIOs$ph<>o4*u(>ix-of zrT=UuBnBBj?ALoRHk!;_%js)3^>6kq_qo*Sm&KYqaI_kZC(T=cIoz*w!G zChB9By$efyBBj>wbCA?Mrr+BJ6jaXscP_BRzqr7fRhkXU$7efkUMk-{BRzq`t14?3 zR>V+`#qj3DF)6rS_-~)}0^>=rs!_RB&2V=YJD@VFS+3L?yIOy|s`W|r;vbP^qh8fG z5x;clN2xmzH>bYjqChc%ITZ@zzoD{U1N7jnlNKJO_@aV(l3DI8*7?sKjnygDNm!%N7 z$1N+lCy|-G1@h+?D+YG^XB0~(QdA?x?~C(Cm^ZH{o-qPBnGlAiM77s8RL0>)Ze zFI%pe5ATt$g*IE(_ulH9E-wC}a8(Q`0v38%dGi<}4zk2M&r|4z{wXaW^#%|w73Tdf z5-Ld(?_YTT-2~Tn0npwOArJ^5BEW$w1MkIdH27=&&*Icgz%#vhZgW&k{H^0BtZ4KX zd%Xe?e`<@Y9T;Q}P239$vo4ElwNcr2t*6ls-aLqqI{n~VA`Gl{hJC*H zzhu=$Ypp%xJx0Z6Id%3f!1Np%-b0g80v=)I|3to}k9@f?&TQ~VGgw3E$6MjOU6vtQ zzb^dlX>4V9^&h*~{E3u`+vc?BG}4 z#mo0-*YO{B^Y`CctuW~SHGM#`?*Hks|Ar1h;+K@gt2!@B4?m4DL9BAM?7TEvcv^6Z zrK*)x01J*IdB*XTB&{NX^Ot)w#5ys9mMBlv385$@X%fF@f74B<|;Afi(tFnY~4V4U7YO5>#+uC{j3)!;Km(|;pSMrW%YT}u0Roz>sc zyACaBbZ3Qwf!QK%*vea$%1ZlxNxqH~NWt1PJ@GY4sHv^p&9~9aN&Dlxg+2StW`Gy% z5ddI-G(kYA4<_Bx%kNF!>6vy{gwUGq8Y}@N1g?oFdGTY@m(5M;nl?Ilk(=2lItWBy zz|p+vUiEa2gxhPX7EZYRg}wHfia+1E0dizOYhQ~_YbimYw6DTD8(oNo{hs-EYb>B* zmQXin_f+|1+eZC(Ze@)8!?E2hS0>=tD6k$ zHw0yPEc_i-FLWk+jn!=EOt?-blHhi>HrB8-bi;;tx{&Ygr>nTHm%env8#70T(u=)U ze6rTmU=)f)$xV|cUjnk^v<<9R&454;0&!3Kf0P5!9w*LfWo5$X%aep^W;SehKt)Bw z3r>~b_TeJ=AhKJ!8Z+?fvuD<>jEcSafPp)YJ}bs%C$r{Sq8rS7=!_1u*xqS3h59nM2g@oU3_UE>#O2a&DIATJ0%Vyg0cK;Ai+qD zN3_? zYqG%`CSg(a#4geHt%FX`_F4~plG2@x4) zWtK@j7-eAZn1AmbZLvpB*aMdHsK7PrrnyFZl}5j)=I0zEFOP=#WH;Gy5*~vCR5{&) z$g3Cb4E+o*Vd*5*07|$H&I7=iGLM#=64l|@Us)F2PSD~1HW zY!$nM54e(CR#Z~rYWHN4+psjC)&Tz)S=nsvh_pA<9rt<@g|#se2fqiDNhD9Q#4WQh znFb=~=1xU-8L1oTXejnbI^5`Bjml7&BiY>^g^E4n!FwLxZ*bA-?=$yk$|gomIZ zX;&ny0+P<-@HJE2F%)iC4k?873)r5|8R~%6oRya`n3&?szdND-en=)}`5>f#;N3Mx zu}Wsv@Mb?+pWOs+k6rpXD3m-Rlv!Z_h=)!R$Lz`b8*{#UTg$s@$DJb3S)sW$LJazOoz~^-yAQ#s(n;m|_pentaGFGAC z?7JXP>$@1!ir9!30!z!42I5vvQ+$2K8t1zSD0}p3!#SZ>oZ=Iut)@xm1gLpv=h&pN z(|QNym8Ql%tF<4QdPOA`bho8?;M7>W>Klc!lF|cNiR65dQP{Rcyy(zZ29Xgf_+;Si zv6Yb$;>B~!nt{2Ib4Pm*0@>AbCm;mSbQ%|E%c?O8G#DFD-v!1{!QluG>HyMre?7}% z?@h2)8yDe84HmBS>N<-JPa!?1)gRIf?^|16u8fzmdTLCpS?1$I{OIH*l*2Ns9A94$ zpJ2sUd&jC0zu#bp4^aXpw4k1Db)dmTdVtsJ!PJ}&-AV%Y?UcZDn@2ZxeGYwAGwA(j zS2CQGRxDbq>oQUoLJhEWCQTY6)!>OIVlt24CK$>-m_}| z-rBGeFM;nn%{IQ}5f^yxG-U>hBQzcEswr=5_r;pO(^HDT8v6H_(Z$2F_;t>ITY2|H_5$qVTki%QQx4B094j$)OVv=KBNF6I@AF&J9Lx6>Cd}7 zNW-!(+I)Xs?k(DfalMDjZ#A`Xt+yl?d6o=Xe}^>=HCo?~*Q_C7X|l;xRLZs^(WN9= zu*^7(+(y*e_|QFt461&flQR$!JzS#u7^-xQ<+465+a73foBa%$yNj<0YdK}w6qi>t zR8{jWV~D`zaTr6LivDQB70LoZ7ubkei$7Tb7P^;x{~QOjLd!o0%HZfhP&Av<&@lvkRTnkUri5k=TWA=r3XPfW*LjtG;SF0%2; zC~Rg*w!#AA_JADv4Nnd}8xPz(4P~QuzZg_i$!{IHR|n%xTh#?OO!^%(E;O9?pGA9q zNhi(z#CYCX#+2q4N*0;KZ*%T(z@@Zsodi7L90*jkL6+FP2&zFz9AjYsSsnxIwB?{Z zc=s=)$NXz%U~Y6r%6CK-6?Pa6U2F*n=zkOJAlO#qJ+6~6^s}v4CX^@m2ZvLR#;LpA zewEcY(KYtWS*$@3K~SIV{I$Fx#m(zl?KI zYPT9%?bUCB{P>fy+7?NE&M#hi=@BE_v^1o+CsP$yV33uWue+~emc4r9%{``^h6*hm zqO{9QG)g1xC!r&qx)DTIkd4QeU13`x|0WaLu5JvuQG3wWfv&L@`<_4DM{&VOgRnN) zRK1(yZi7W6B8bn2lg;DSOX9)@YmtNdN`~+0OXl?KU363Zt@p?3s~Yo6p(2djc||`0WiW1AS(|kE4j7DDqw94R2}*dUKO55-ow{7 zoHP@*luV2zR@g=$j^E*UT{b||@BYSaAm!MRT?qU1hatCyZr~yO@D<-dt`rup?k{Js zygfM0SKG3O%^IsI=Vglh3TtItzZn;PjV17x(V}d`$M_J~zLsE@)=CZeQrrjixcjhP zcesL@q?JqTzL}}5MUb%x7W^gBvWxy*G)L^87-4OiwKxV8j}c;v-z(!KET2pQ@zbX% z+otUwuibZRoaH^HRMOQ$Q9U1I1p6ir1Iqh0wAK4ui!2MuPb8uv!8$n-M#yY>M%0SZ z*I1vRoMJjQo>&Wv;j-NH0hPkcxEz?4QbI;U&q<(L-xPPP{AEeXR{^yXU2!=YXY-Zk zb+`FIpzM)JznuzbUWx0aJ7Ya7OBHP3pfYF!3^r(YEG?d{+!6%{<5Z`Ysdx;47}Gu? zwq;xHit(9WDxlcWKNu~y1pH`C29|qq(y$AzLoHYli1pE1Ht6Vwld>b zSgx>2M5>Vs9Y`bcI0sYzUsLj~2%Fm3tnv241)}eQ0Jv+AR@LM@p!aUT*G__2o=h5a|Qdx4+GW&Hwx5axF(0sMXJvQ zPasTFXBC9rhU}Y#6xG?7xcG^87a_GMKZBJqcY+3=+uLAG=}{Hi6)la@4;ji0> zMRomhMHDlf&hz10h&*w}qN&)>!Ua}0uxNj1m$YdTx1GNCP9>UlFTl+me!A6vfQ4_s z=T)6N4VCdLpY)o9jI*t20ij}_cFU_bg6sI#LDaByJV$*|p9f>Ji-ll<$V|2quIhTm zvxyCoOM3h-6OSg#$E{&l8=q78uHp3tJRPN@z9u+hpVG%RESLqqDC|1U7qaLL>fdWT!NCNoIZlg*#2LrGLl;fRYt!bgp~PPb ztAuW|#JI1p-k+j^Z4G0))>rN1lLd4Nh&rc>?X5HBonQMJ!5R~j%uI3#1|l~*bTo@# z2}Vm@z(o$*$ouUHp^b43t5w}q_+(wlhMy6a9vOLt9k!MSuqBSf&u5l@V>qVLqK1~+ z-K%#uN|gbb5mC19ndLd$g${~?wTOQ)vDSYOniMLF?oP`IBzS3|MfJQb7o97==OB^> z_Jmc>>+c2z1rhT@4lsQ}nuV_&UYb+t0Wzi&&LI(<&u$$I4i%5%i-#iQonOx}x zg)Ib8zUwmS3Dk$|W;=jx<+dF!|9AtfhHy%N2&g!{?gi&gQhv1~g0VHPm<74l^o(sY znSaXXqyp|#J7<3^bh)OqaX71pv|-Mg9}hS=+A{(~_u2UlZJ*a0u~RJzWH;I9D)!yt zxMPc5+KFPu^4BvKkop38U!2wA_PLUh%sO>84MU0s4}8)eUi_o{JmZTzJ_3*GOCj7+9u z&Di6DrzvAbB`u$TL-)O`WL!tzF$wm|CD1#Kq@*yly6`f#7OP7q8+o+S%|mMG9b9Wm zE@33R!Oi^K ztldHvj;TK^2mMMob&4c zIc!i!jluh@(&C)5c<9SCGeu{RJg7$WUpedWQ36K9kflDVI0IuBz8TYj%AO4>QfJND z5(ITtyOwo{S3yphHsEET7wLjl@yIfB#8us7*^72L0(4vU?YCJa+jfQ~C*A9yiNulH ze#@Wll4t$IZ2pY@VCvL<8%;XdAGq_8slPX5ar-bnxO~hbbisS~V%YUuXlyy^`m;2K zwAc86n)klz>A6(yxOHUUmt@H@0fn5Mzw*s~o^)!wYu4P?%XessdSN;mw{Eh%22Tp9 zQyRcHZf|dm>GXyQG~#)uD?$&}{BX*Py*lSAOuiH5yfxH6`-JT2D#<})bl-YX@R$=iJ4r)jq~tawU|_?M<}-+LV<2?pA~fMTvP zo>1=}vd=O{e3v9FRY1B^0B&jAxVMo07$CSnwDx%Lu7X}e=lUK0n+8_n4c?2{LfK9Y z{HlysM=fd(e3S>C2Ny>r52;i_(*|J`!raQ`U;ukzbl@0~h#iH^or`=?E4dJhmMU=F zd>%DO`k6XXV&;gaixCGd81455;nsc*kU>1{M|eYch~Iv6sGQTDM|_BV|MjrZcBdnl z+f>I+ax7Q}q8D%InKqK9~w$DZ*d(@jd zUU%c;rF1{G9PHtlv9N5sYk9*p@K6gIOJ^L#aKvzBXk(m~hdmh&WZVr50R|`qG#2a} zUeasXT}ZF?VN_+{wxETxK#H-AAgs_*44z)zf904p2+C;zM7$5=)Ugvy7JPi;B;K{@a9LMJlxeVJdo~A5SEBmf~jR@p?!mE=Ps|nNM zn7{{c&hUu%`vL8s<4Q>lfL^+|>L_od%TTu2KvfqzQuN(e_s~~lj_foX_&*2d2M*x# z;^ubfIKJ+MZ2~SuLYa|TZeDpA$IND`A5c+l%sT`C|jDffnf1kkL?+}(;w}I$6$~1biY~T0Q){SRm_~JjmB#%cj6XsRS%{4!> z@S`5BJWN*&Lg;0wsp?AQgc-ACnuM(!Mel)aZ6b^x?kPQgCa%uZ*}0|dq|6pbBB*jB z?jr+hrnV_d1G!v@C3}el8eoHH%@&pr)BalGrK!Y0RXYn~rIlZX_!2F&N?iX%yjfUX z3OKo(0J#tP`%;H7)%>X=oACo=xSFEAAELa&)e(O$8Zp^$HT`J)8bRa!z<>?9ns|x1 zD?o?J&u_l-9~H@!TG6GH8BOJ3oa?sQeuYGqe9uBpx+BoPS_cf$`PrcJiups}CA{aC z8FSct)eP<_z!}n$f8cSEJ>c+w8cFY?s;9DK)`sdj$4R)wd8-T_(`9&&GZ33^s!=^e zUKSnzrFjhQs{iud4c60%yI3g(lj=zUWe4~|^KtSX*Je&=;PGo~2%K8iE{l^+isZ*9 zQXzGNW?A>@L+C8y)nLX33Cx~X<@n+$ zj=7uhY*2OoZ>Zf#^}4%!@bM0=mihhZNvqYI=xd9v;RD;9yl~ujwL0R4V@Pnwk212s z`ROMa9vP|d4*w20rT(cUzgx-xS}7CE*6ta+J|c+lai-R@cch3TFM~CP^crWOzH2*m zn^&_N8vR1cwwgu&Lq!-rC$uUyeSo0O;-;PgIzrCpg;l&4zH3q|SnO<&fuz9e{P^*zba{6kZVQYE{B@NGFtZ9kA!LDJg^l9Kk`giAwo!b46^0WZ zr&%hanv75M18%SdU!@8od2gvv^j7Y4AVnq1Hky1jzv>wc*|Gm)8BvL<;ls-F^Tu}Z zwjYd^+obcEO=}NsKNco+sPx8b@mwiw&e@#X5Y^=(MDI|PrImPLPg@;Kf%}YQruxyx zYZUe-!v==drrWWqhE0>eX3G}vMK&qoi|)<@4>iAmnh6;O3FP_6Bi%-qyI6dnV9yOY&FgZq{W<*asd z!jx}9+Q*fghB64RZJd>aDrOLtAS?6_`6>4Hl+4p;<>Qqx|jqQXp7D$BBoR3Hi!u4r~qhGfLzOyZ-1Rsx-*18 z6EgTu73d_?o{f!qC=2x=yk7NaA@;cKNl{R;OGaLeyE#t0Z@ZWEcpPx~=Wv_`Ze}XA zJ-&y}s%?BW#)*sR&w{8VRIqDyE%IRRo739;(WlbR zD7-Su{^ktTP<=Fh!S$tl4KJguEHUAQ7DfsUj5rxq(X&+x8f|3buc?hbKRyB}x9?(I z+`AUy3(E>%`HQbec?oZGW7tI(={_`PMA{J+_64eXkxIv22eNi^Pz%B}-`ze|&L~ywyJ)JQA|MH;wh~quOR}7EnVEo~W7ilqwp#{g`kq ztsd$P)n0hpdc1L^c)Ci2Pu;$yW-Dt^q%79;aK(ir6U8LBbM!qf!o8oi5Zrw?I=B{H zK{fPyll}TwYS8^AFxV3+i$HC!B;1Q{HVytZD7MKcJDDuCijV-@w$!Jje;V_AazB#d zD%WB~=M;m&yV(*y1)tz<6hxN|Ld-8{1DCZ*+vT>JQN;`9hr@%V?2E-wgyKQebr;u* zBIPRwaR`4mXhZ2_VC*h*b3c&JAom9yD9u$iq`6`J*N@eO0`4o~)xBVJ@DaMb2zTA< z1yA3E`gZhPg|0PxD95uy^;LmjYrF~(o#nAXtTIiNoZ_C#N_W5&Ss${RD$h1Y2Mj{= z=XV43Y(tE!a(t9i_~R3?xp?{MUR;ge`joh^{AOVHil~?v;>W0!2cJ1ygkRmvRDZnk z?QV%-#TuD_Bj&pdGcg`RZ)?W0>BbKLaR@VgxEYYQsM>xnv#jmP4B1-_E|L@0FwbQQ zoxx1KM;?T1+xQ+rr@4=xEsvTR8EEr*X5evuF9EqT+ZOCx!Kl9N8?+rX{VY1!c2FIu zkQJ;JYU5A9RX3ZVg7$X3v&Pv%-f@9qD!9P=jpJJCT`AH=g;^UV1}cxT8kes%Emope zcjwS2DO__TUJ%IP2>2SMzMKRCE1*P0v%q(G5)1@SgsQ}RRY2yp%TM^{PLhU=$!_N& zUgC}Vze&_hT+h=nl5O|Yh8efMrhlZ;pJyU+T+5wgZ&fm>#NWkLH;`v{e&DMg(5&)x zp@SjwG9yk{*c=DAUqP$$Y{4QdZuFq+Vgp~uxGJ0K6L_X-I&3RwGe5;``qM4nTaK^{ zC5Wg2f$i&7-wmi*z&lvPxqafj_i8ZN2c@A0yN=+5s_T=$oPer6okLb12b{NWe$=UK@mx#`qo{SW~vMvh17G6wto=Qx_@J zGH*BBdT-^SYxl*bwS~IzZYkXE`c43Q>v76Jd}eJlEU(V@kqYm(w&SnvVfGu(>%(%= zXSBNwBl6NY&24)}PI}L1qkSn;^kub*L_G3`ro=rnf z=aX2MLL&QOR9*n`)Y&1v12f;YMIkUGec%X^_|%(77PeXw zk_fY9hc4PzQ|vd9Z>qRlned@SbW)ZxvR)?=YhBY~KcA_Nb)b1h`WNBs@&TJxKneBz z?x5ZK-YguLI)Eqt+_tUsP@i@T-JS;5PK5^dpomB{e852-^4DfOK{+&ZFw>u$m!U>{ zm7z)uM9q&4n4u-Cz8}nmem}?Jgfz%JG&qz6zcun(_?{2Ub6AHfiWnqd!0OIi;0d+x z&%7RUUhwt-mPmGvBEMVv=GnRefM?`m1Y< z5np_JGL#j%g&aslv08#dZ<&+xG*PvMDz%ypB40Ze?He-DxLJxZBC9gigf)M1&P!GW zq~Yz}yP5T-+6!3Ke5E;=TS%nudOI6#(|O2ksyRSD^dLB0#$G$K>9E>3 zEA?P}oYkazc(DvPr`0<(SBpvQq1fbvyuO2+OA9}EdN*9J2v3C zQI9^Q3Gm^;&Z~U;D|6&-0qX>*B%D7b*{#??wwD(OOQj}{?`a>p&BhWJE2|e_nBL|` z>VX7^GHI@wJa=QUw*~b`T}7R`6WnMU>a$XV3_J*;rYg||vUHjlu7M9BhgI4F;e=*+rlT`-;ySW_u>c%u$H*of`&bAvzVz%tq7;)x z-eahVb{(;b^Y+vFgtU*& zc0?2NHymnudC&2o1NH;v_WgFMJw9XvP)|`d@QbTksTji6xVJ^cuXux&%zWz37VqMa z0nSxDWU?0(a&5Y5%prx;a?q~6)q>i3a9Fa_3@0}Ckg4n2Et!YrI?*3wmm%qD)b!oK zmrBY4>RFDzdKN!?pq&loY3#%$u>76y6l46;{p(oyDIai2Tn^naABm-1YGc^|nIp+4 zalt3}Vdkg_-_J`A)Yi?mitZ0=pOPqHg}GXjtcuq}F-^a}t~tJcn7Sk*AxP4|WYlJn zw}qxydw#EL5-)5ZM}ISc-=tU0TU{r2(plrLsqHNt0RMXRXWM79z6@7CYd;&`SR;-r z;(HSt0&R1JAV;%c|D|zdFMhZSaJByW~x)zqtg?}n| z=VO|eH--<@`J$`rb6Za`UStSU4kDjYrgS!lo0|{e(|T7(*5trz>1@k;Fedd5|B7cu z*%k0;n4kb;Oh^E_v+t~6H7eYumbGaVb2t~ensvB-h*Y1f6U%{kZSFOT3$QElQO9uE zjr||$P^1e$dbHoeR&am0w#fMPbXMfXBjHH)B_)I6iA!+2umOhj*Yu0Ed0~%_@IQCw z^tNkPgn2bI0y^NhSA&@~tRnZ`k0om}DSCEcN<*|-5F={mZPo!M74Y1@P5U$~Vq?B? zDF@qo7WoD1^7!y5??rFQxqfN*pkR{7kDKQyl1hpbn!1BS%>ZF!8iQPIIQ=x4vbM} zg!>W%skq?H_fK2)N~px=faU5oLhcQ+Cs5^11s^PDt%YhqqRc)j65&;5u-ywc4gw7= zn{d91yN3_CtDc_HHa!2e7bsaZet(GU2RAWpbu$*~&PN*t)@D5aKw(z-w;2Bd+Wi01Myt@dl&vGj>AV7=z6q|;a%TZ08 zPBywcKu$Dc`V8i{jTvX`u~T-BgXUK8V9&ds9%n-|IHOi}u(al{9rY}>u z<=|sR**RfxVA@nm;2C&?%k)OvK>*tUNyR{Y@{otY13HCKWLu5p_s>2gWl^gSFo7#0 zDRFN~MaL!7IjFtxtQ*F4zI3 z4K?|x-q?pAESg4D0!c*BTs6GwO)v`wsTa%`y7$I8P3j&O7&h~!DtP&gi>1)Y$N1&m zx@b~f0MsUJO|UY+Dl_z;N1$mnRD?fLa+elz6Fsn5M4-- z^>C}x#mxb`+3NJ0p)hdI1v;9mx}>^=pU_?kyoY%TI)e;ZD9s3HUmacstZ#g%8=SgR zlMzU$bvxKv7Qhu5FD~}&?*ywca)=r3xS<>r1(Gn4&mH^ZgSML}i>f_D4x^BbY-^)z zuX}AxUOuy*qDVy_cZt)itmi$Nw9lE&RItKOZ=ZF>pxp-Zs>fxad-egM(&i&=$M;`( z>zO=zC&inWoyt>oO934e!q|6uKCq0ok*EFMMPA-O0u`1;*xsBHBZJsvWQx5&)`H2X zA$dh@mqYA!)W`hrsP)^MQ~%;%K7FgV5BSMoYq5qLu?ken-`8AXm3I z*32xCzdPC?Y9$`8B3)m4dVZkM(j640{i{X_Wbb|>cldG4$0d9@r51-RiNk6J%#rO7FophmqQ+%S=Nqu1bu`vWwGG60h9CeL%2i+R<61;Q~yDt!

kA$qTTTj{#M;z9ZgpecQb2c?PI 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

{AJqELpRY?2LUITd0KW%P^L) zh8bg@8H_RCTfN@z&-=T4K9AoYzxxk=VD7oEbD!&6=RD8zoKs0Gu>yKY;5BL~43YiX zz`+6((EUS^-`D#;4gn5>yY>YEPqB~dJ#mKezZqFB-REj;|4)uQ4c{=jGdyz6x=M}L z7vI?J9WZ}ISfMDMubOtH=HxhgPNetF39nS#&xUu{xluDgR{^WUDf;Poq19bfJ&Gc!PPFL5f;6Si zp?G>6B5q`9{znU?C6zY6ovXJ!Ohpi}mS1vnWjtP%we3A=yUW=fcenOujcbq=_Jy)g z%Px^ls(8Qs)l%?dbi?EQ)9w4GoHK-{V-lQ{-G>F7=v%;+bpEPjeI38{WfxStOSEzV(3AK|+M}tH z8{9$5YYg%K7!Ztex z^jS;6*7z*2`eg^tDM))+(6>xTpQN|I)jncLVHr_&pk8c<$ot5?9Z=P|H~){J?-t6v z36}mPliO`99&+lfZMy?u;Ti|aLrluHI~|!UI$nhMhh1%KkjCG);ghpBc}oXZ0Cqb= z$8)O7r?xwLiW33{pE}Af3Z&0e+RQQnedWZ@;~|01b;wx3Gja2f%V+5MY0H;DfGGeV zy237vy|&eHXteWnVDfvUW8m{OpnqnNgV(axD1I}=+!7Op8Lv|@-UQ0DvH@&v1 zFa|B1+rKsrE(6}BHDH5YBNPdyea$DKrF1$O5PaUwIYWs=hPv4iLUi%;+Wy}5Qii96 zW0YXAlYbM>fxvO~Dx{3PFHM78?-GMI6LBv0e zM^S8-SWg>1YY6%>_3zPb02$RT_|*<0llZP=X)N?IHcCu%LO_COD;`v3vRfX)a@Vcf zoy7S#{ddO~&UBxP58*0V2rEMMp5!0)7ynGh7YL==wkH{?ES?<~rDWAnic`hql8*;F zf0WRe_9AB72io_|G_<{VBS2b#+)}@@m)CNyp|*-|_`yWSV=HBJB%?b@s5|4D|3jox zlva1`5}V?9<0o~pb8zO@k`@3*KXVBiI{_SJXBEB!THBi!$t<>1v<>fWjfM6@>y>WrMM?pLD!hd`QHt-G}fuRSy2>ANM;Pmv` z(tYu+y{|Q#X#UW&OKoWLDl-{nG>>dgjiZZV<r~m`p~+#Dw3QkqIp|ikEybh2e#w ztN`)ZP#X|qbb%qq9~_n3|5pRDH{023|5t6JaMqvpW5M5mE_2_jpqDQST-X?D_A{6b zI~`PP{Fm3Bo;tY2?p8>1?>~EwW&dxz$Lfxco~$g}0@++pImc(irDdGK9{RWW-2A6< z*RSQz#wq+_tBm9yLAq3H8WTNYR3I|#wG}{Xr%`R+jdW|20jL5Dr9dmX=@3s4TGhGs z40v2IdgjwkDjlFdxak?yFxsuLgHLYhkL2uTMtn6~+t%=x#DO5nsC+KxHMod-wJo~HYX<%27FRJEGdd~<^ULc>OAb-hz-`Hk53 z1Y$z{pfyyGmDNfC?7yQ2+u-n9UI$k{E;r4iyed;)w zS56Fjvyar0d5WHwon4Lh;ln$}UMpt*x^gu%O>Xx-|KvA8c}3x8>4vryl{9@Oy{+KO zW85-QVQHtR!m(F7o?X6~ZDMpr=8WX~)})tLmS@HwnZdi!TC|^EOXsV|vQiHXQS{31 z3&{v_R>OMpdLNl^eZA00hseF$3nE&jqK)5Oe%M2x>fHhi%ekF5 z;XOBf^~=oB{GYw6noS`Rbp3VLJH1PL1)_sG)E4AZY=36& zvpQ663#8LG*+HNVJH~a3Ubk%jriesAH9D%J+>CWV%iyqF-ry zvf=o8TE1mkZVxg;|9R5T+U6UQlNZ~SBzL2sC3~YbKl}OEDkWst=_`UC4|i!Z(Aza$ z@cZp5u7#tTphYYWr(K;${N&NxnoL_Phjr?VZIB;xmB9?!192v!Qy!BumUSxQw z3l3y`C80Qcg%;R$(SmBLtDE!qZT(zh91>PzwWQs!{ZZYCxG^hZ)%IptpZ$`09p_|jv7B;IX+TG8_8TCegBrDZ&6RB$Q6l$qJhIN z4`Szwk|JH@Y7=l zvb|TZ-ZqicNS#vh0@Chdt%ZPab7ao6 zC22o7>UY+ngbZ6Xj)WSCPw5fIiM)HH!RyD>kVn}V&^AdO77B}HbB-OdfmO?-y=k8& z82X!7SZXs~Xt9=r=t|iBx=Q(e8TfYE5XU#KLxgLp#!$Au-nuX>GMzqWBfGgS-AD16(Tn-1Jiw#*8n58toCa$GLlx*bQaBD)-r{M5Ch2;6rrvoG|lDYTSxdgH`| z{*04+#xT>Oy=8aO-TCUYJ1zFIm(@%;U3F;cz9+N{?=_V1!4UnJaaXrc+0gCjgJZgw z>pk9<&S=>c!TW|YuGbD;X?Vbsgi3<8W&C-*2AtC`HAV?mJrb!u zy4&{)&zvMn6*k@O=8g31B$^xgTh+64EFCQ|HHyu#(N(ymz~pUhj=1x9%+@buXNZ+z zr+vj?Z)Z!aK3_yP4_oGq$1%V`ClM64tt<3PJT|~>J`M(5MB5cK6^rgPiylh7CBl-m zx3!utWZ8p#WQu}Jy(w;xMaZ2&h?&HXg{;JvTh^~o-p6Y-xwsl(IKvbLn1j->KXNE6 zh#`xZkbr~||ESjV8771I>AQj^-R8^uQMv9xQR>%CAYy8ArJ|iDnkjqq5V0ckpU+9> z`t9@3T6*Ve9b=|5nBiuKvEcGKr%_ZSUIYQ{gn3j>HJlYOJU`VEVluh?=t@8a6klpu zef>I$?y`i@s4u)gP=uw4zshISC!So@HC^NmJU(elk}klIQnk9D&IGjy88w0h@C`nP z_%45s+Mvm}S|*>`!vDeEqM~Bk(c-Mzi9e6;r(RDos#sHAJ_PKyBNebP4}8}x;zS&e zbRV$V%z+;zql(FQv&teB#7c!nH^?FoQF{_JCT9DwQGQT4`L?}=^QEfgA3}OdG3|6y zpYIYX#gFdHWA=}6&Q(?SX#U*jyKS2iWPu|7NFoA7@3h9v2Aom;M?W0+ z#uos4(WRCt2?BBH{=5>P7ZN$R^I-Zmx+S`biSSCvPux} zdjEY7F5QHwNgmRX*?E4aQH6Fv{pe;OP^LJ3vH_J)V&PbdAAgpXmgau#&#(DK&F*Eo zJtD7J5M$S;tX!Km8BndD{;zWU!0GTsRefh_<-CmRw9}Bz^f|Vy^ChH~Cz=TxSLk?* zfR;;Qd%qTZj;n~xjJ>(n=6*b1IVAF#B6`CrxG4>D8BT8X{KCzE8@$J3*$%%!JcwlHDGAx}^`NaG@89I<46;HUlt3gF)+Sci z|8hsL$8h)NlM5@|fe>9b?8;=wcG+|}{=2Xz*z)J?dsashQ?byC-iSh@S2Zhb$5*tp zWZ*=n2FLDm{y=>xZh85wW4yX-+|Y8CSw(E|e(kg$&K-6u^^1M`%7}or{&Q2{4m%|P z)2M%d;28E}^bAMMs5rzmhILKpxd8d=v55eb#>633&D4(95?fwHG`6Rhrys-{?m^O+``yhF8!X{Qjba zMfSJEQy~$w`nMvULxKXuLUP%tMYx$k*7?~hpft0@9Hz(WKF0P5DgRqjdJoWm^__6lfZJ?(| z_XXXqMC?_8x4tQxbgaqxzixwKtoZyV@Pt}SxusW{wb)H+k2|WBS(0!f1UW5s?#mZP z+dhYU`?JU66RKstToBW`G)t(q)+)UqDq)m2Q-LdOfgqc;Yd#v*#u2)Zzu6MSxZm)0 znMp)2Zuv=?-e6uch;}WAA#q>PN`9=zpbe^ynpX`JU;_=e{MYG*ica$bViqv`3SYLVoT48$3K8;L7^R72K zM)C7JU2i&=7dB@M8wZ$YPM=^hQ5$|H5_Q+ZOP0|iOGTVe%FvosU}rM4&F}MvkH}_2 zb|WbjQIo%`)e(10Xe3ks8eg`|(m9e#5GA;R`^6fPbUtR6yD2TVZUQ>!b&MJHmJg5j z#^~#e8zJi2*2bzjq0`M%z;x`na<2Rei0YBK&wO!$Tk2aczw$Ih+2(yU!px4>d#y@Q z!1tRin}ExiD-X*mdlkCxMV{wItqIt_YT+4;umQTM^|;FpHys_+5sRswz3+NY0bUbd zVrDv}yX8PLPQEH8@#MfKFTP2SQSxeAAH0*_cM(A={Rj+AT*acTO}-@p({}96v4Bnft`=c6E=3zM zU@XVKGvfo8RRzFJefjzge0bE>GWk}wwyQ(MUZvfHg;~v*rlR+bLs=PQdxkB2#9mIb zo~9IeyyvE~acRtqX^Hxl=NTSxnZBppCY@3SX1lW6i2kj`Jw->qjIf~g{g7NbUFuJu zmonGe(35-)OI1~4jo0*7vpNK7<)8hyBil9-!E>uC8ClvexO~$U`4P_@>K_itc`*e; zeOCi}(M>1|6+Yyh72X~$J@QIVi1IJvruLRH1fpoPzv#T8&T+Psk=I)=P`{O&WmC{k zO;oF0I)#SZR7w#bPL5SN&E*|RZY#(&uCda^?z9zZGKhxE53grDFaigjY~~2b)d>m$ z2mDSv5ui2NV_N#?`S`H*4XY-PiNU;0AQj2ZW|Jaq|7gvh7M(C#!%7!SxY!27l=Ffh z!uxqu3jhDU5lEGIJf7HIBSW4mHjxn(=N$<2sM*-+#@8%m)H*kDh!$t+SUlbjwu33` z{bXh+A*Mh>9uFpH@9(S}SLPmay#>i0O2k=y7`<`OP|zK-w0=H)rqB{s=;zlU?xZQ4 z58`1NUn6G_y8{Uo@6ivMonphFSD2!NK)NDc`TQFf$tIM zrNh~Sk$1f%XF1L%h;SJ%HtVn|o8%of%H{!DtEDiNcQxlAZxO*5@+&s9fH`&KHDF@+3629|W;Ne(z!KHD@gidy zt!CFz+d^RFK|*CH;n#zVV2Qhi3Z@9{SC)Q1mfB5)M>-2rs-Zdk)E5pN6P{QS z&YuZqQyrgrCE~aK!%;8|MXXJxinU6t@=(SPnxFX$L^!fH|C^YDBd^j*h5@w+6FafK zS54lHk}b=GG$UK=H^rK`VT_@r8phlE{eYL`HYdX2-|V((W21NQA4w>q|QzDm!{F)PN)04 z*Ci@eao7r1@J=+vofLGNn+Fz~RVx%l(@YgJD5scgwKa}4n|4j9Cn#8C?azAPjL-6C zf6I&C;KH>b70xZoMK`{SRcgLG!7=3pv~jfWocOHlcAgfiGwNQ-Ij> z*ryxM=CbA^_`ztP=;t&&vxwVR7zsaNcj?}Tx7d7uY!e@{3tsot8D8gbIv6!`1#VSNx2C7yDl+*pN*Bl)k!^8ZRp})w$0Kesf ztonL+ajjZ^k_&mcSRQcON8Xz?n{2`)w*~_HfUI`n`Q1_8iZ#q{15J_}ED$*p>iSKs z30I{T?5snc^*K&>-Qvg=$rzZI5XnfVWx2LJP={Tq`-;r`Z>27q^GUc0G3d)_k1tV-$pOGOk3(M{3Zn-vyD=Mc}lKU zaF7+lUw^KSsA|SCzC7&m&RCFmwj0ziSsN6-39uQfIT?iQ2AZo{Tnimc0>|%C&{4Qu z&OwiTWC*TsK5eMndA2QNQ@1Z-X<_qj$4nJ4Z)`F~2?f|n;0jrRl+eCCD_MNu35&bg`W9Xr=9T@w*iD`aAkU3!pC&0|weaOp8D&G^Cqrvu^W91%JW^nQ7k zl7b&vO0$zKHM47&e_}Xw~ktI##Y;$^4c!KzV2>ITj+42p>mS?{%l#(2I*(vfPwT4YJJDwfrw_d*r!ger z=`M9#_QCe1f@u&2YBHQ#ntf=?JgqgT1pe9G;9>9oHbKwXaj)@&$~^Laa7_KBZiI6F z;+HJ+$O{Z-!x^QaSjn*f1a0{yY$$9fgzN*;mau_t|8^P)wj{FZgDSjU5Jcj6K-Xap1Bwu>3?|oA=DJH;Uk3HEDn1o$d}Rf%){rz$PM?uNb3nv z>ob6(r~>yPO}&_Jex@W+F4cbJ+FN6eW5qb1zSBy;P@`p|);4A!0oRuPVW*47+=*w? zo%#Gd`~XFeGPP6uWZ~yW_iH>O67Wufqw5fO*+B`8V@1OCTr!hKg?!g}x$@6V-OrTt zvlIi|wewM&_g@|yy&JXA6Xlg1QMG+KNl!}vzbrIO5#OuMUq0R*HUSj7tQJ@HUiaBn zr@6ztFwpsctN$hpJN(>ORSCH6rXi2p0B-<9C1l`7@o=4(kp zt$8G(G}{0KA~s)w-QUzS@z^O6$1ifTCBu^QP$+k`SIubIu#V^|h0iKaE%{-|;^yb* z=X14q5$XjU*OgtKgSQ^NETs+9%<^QR@TEpZG{tZJ!B#=%&)9+t^FUUdSNNEK5NEZu zOLCP%GN4y}>6=>vNrF*JG=8T073Pt zit!Y{WhgEvxQ6h(NYA^+djyk8QLfE&(ZP3$PE<2PPSfiG{{pm%c=t^`yCjZg*!`}Tnfn48m zgW_qPJH=Z!kmer>HcJ>zl__^(Mh)0KxFpZ7bvIeSncBkIMRy(?K7C_n*?)+!{M285 zGs$GI!=Ae`@~jCgz?b=fHXulOuk+W=6Ffx6t5bOX^ziE;ZN(n(hE=H)u)p`thvVZ3VnrBG8v-Ft zZUtj%xo`H?t{)JX$)mN0gQLn7*Z5gZIPH@TP?%<+eNqX0X`gfw_{z)|r>>e`;t*9T z&-vag^XwjnQ>^fP<2cp{N?=-Od)$ocx@hNQmMNXu*9FJ_^sm; zdP~fdH1JRP6ZiC6ej8|jq+jmq+t-jChMFJHw41zY$=Tq%)JDl6)?XjGA*{skE{dh$ zixRBjv6S;UKJ}_~oo04@Eh}%off~*3{y%`5$|e1cHDEya=R;EC5?F7;MX+nD!4=bd zwaf@LmHgvcK8nuoR&PT>Lc;UrLLB$&cvWKwUmQexR2mzw+r}dC0nJ)@CVPEFeqd*1 z2!AGH_f4Ab?<8QWM=&1mOWkYm?gPW0iMZ?`C{Y5XmE4>br%@mV2*v+!6Qg{dLT)Cn zrNh^cowy`X@eshJ;=+y~*%TGPp9`lgyZ0B8{pGPder}{d`FyDqJnMmO-o_@2 zWV4y5T2(saH5+~^ISiV+0eZ@`l$4{JR<-5U@I|Xg$`aub99X@MVv+G(w)w0yzEBNr z$MZ5UxK1HQa<$WV0KDqx(wiwkY&ZG;PO^)%-6Uk1mu7qDrMV@$rAi$W?zmPrmbdA5 zeWHEP)CsF=(S4f(b;)n{*<%e1Csdo#bkDj?@^(gvm1^1AgxNF2Y7v zcRXy`(&1;gfrg5mL5z7z_-CcBxJ#6HFDp&%oc!u0T z80M-){R@u>M(UJBMSbZk`-?~Wc>~DW%xRKtr2b-SK-VUp}VnQ!dC6d-+su5E_JHXZ)ZHj{*BQD!HV z6$IVe0bjQhCdnXeks)3;%extEVq}y^A^B5#4JVP4)1E(k8p_F3#=iWt1Nbp^`Z&D3 z@_Dvf;*pG8$79F=7qoK7ut=8YmnWbBQmE|^(Km~9pqDf`%E52$t{WE=*A}ke$1=zS;oGolcj1H?UfcV(wp$sDU2KzFm^3Z~0_)_xa zn&;5kd@G6H=j*f&PArlUB&jkygks;VP;=(-x`PStJ(~C_Ls@X%WbYdI^FBLce2za? z0=gR4EZmF*BJ%2&&(82LbqHo2awtUNpS#uy)Krh#+4WXU@CS_@x@}pLRW!p{`4prP z1J7(pyXpdg()_g8GaX;i<$E)CQXqkTN^x4G*(>S(RzT(_AI7F!@xu_G=DdC$A|y_; zKl-+nadrC=+_qBL8)p96Z>QT5;?Q#Nz3+PS$&=0#H9d%iH6*f896`VZieZ4swX4Jl z0P@`8mVO@Hv^y6r<39JT&icuJ0pCOSSZlz*E)yJPIYfZ};Oq0P2tWur8A0gY=H-jz zMK!kZJjjlqzvahy)qLn$B9Y)XynUnEJg~m6!n&OFtLXGLf*{a1}PgcZX)rHw&!pUxc zGtW!^nMQ?&o=JIkXUKb6DA8D*v^lfdEm6A;-u}>p4D`>K<}CRUG)XE5EYS6zg*0(= zuI!}1CWl;&F%4x95NPqDE|9PRKnmHK4(_;SImZOn@a}ukx5nFmn@ps`r)x%@lx_Th z%*T!(bFJC`gv=}STPlq6)1^*^IQL{ID?^kfme1;Wr(jCf#QTtE?spNrX~3 zWm$T53t2_bU`7cl<(|{;-K0}d_~)eit1Qh*p1D)yK}~oZ_PwT`QmtcR$#hw(xU)+Q zg-q(m0eh*DutlYj6*rev)A+yn)-VJP4!NSSs=2Sdn#_kduJdxzeXs?G*kJ?PcZ=!wCh zW`kJQD4L1l5sv5KCCyL2zt8?7X`F)TqHlcclo3rcb5PMYfT`jHG(Q3kpPk*>sg7Xb zAmYu5sX(Q%5peN^*`~0Q;}fM#wxfZmb=Iq6^O20RL4%|`=o-1*4U-H@y|2hnSM2*> zbo2h;nri~mT#)`Z0@|d2MM22>xYDRx(n?IXSjcGiS<|rFKTooq*SF79fZGt1bNg za@#|8r0U^nW#7F;t7rt~m%&u=R`K5DJ)oRlBoWOa7L{-p2XJmcpk;8S9efaZveB7} z%ZFFp3|)=Td<7he(ca|jkJKk&Y|YQ$8;lumH3!~mG@Ts1m2cS)x&>{w9T_S+>Sr@h z>I1fTf!nW&2DUCgr;6cQ%P_}gEBMt$L!&a2u^_aEjdS8t4U;v80S`M$AV?aa2S`!p zVbW9T!Avl5KTBl{f0d5HMaIZ$E^7`U6+*@?vFNJc5K0s zq+&_3-6rCBE5KZKZi2`Yz)_2-o-LEd!k}kv)-pbH5ohij!%0A(@>Gng-%<#zfvdC97EN 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 z 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

v?`BC@+v-0TBDsbS+7Gr> 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 M)&D5IVG;6w0F#J1b^rhX diff --git a/configuration/documentation/database/ISPyB_DataModel_mx_autoproc.png b/configuration/documentation/database/ISPyB_DataModel_mx_autoproc.png deleted file mode 100644 index 89a51ebacfd12e17b84e4e668a219555926d8e05..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 255751 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 z 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

ut8mD3n%_ejJ+5nbj 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�i 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 TkBkrWI<8&5aVhJ9sqg;*Ysn6S diff --git a/configuration/documentation/settings.xml.example b/configuration/documentation/settings.xml.example deleted file mode 100644 index e4996843c..000000000 --- a/configuration/documentation/settings.xml.example +++ /dev/null @@ -1,304 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - esrf - true - http - proxy.esrf.fr - 3128 - localhost - - - - - - - - - - - - - - - - - - - - - - - ESRF - - - - ispyb - ****** - C:/java/appServers/wildfly-8.2.0.Final - - - - - - - - - - - ESRF - - - diff --git a/configuration/documentation/standalone.xml.example b/configuration/documentation/standalone.xml.example deleted file mode 100644 index ed7ca4b1f..000000000 --- a/configuration/documentation/standalone.xml.example +++ /dev/null @@ -1,597 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - jdbc:mysql://pydevserv.esrf.fr:3308/pyconfig - mysql-connector-java-5.1.21.jar - - **** - **** - - - - jdbc:mysql://pydevserv.esrf.fr:3308/pydb - mysql-connector-java-5.1.21.jar - - **** - **** - - - - jdbc:mysql://pydevserv.esrf.fr:3308/pydb - mysql-connector-java-5.1.21.jar - - **** - **** - - - - jdbc:h2:mem:test;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE - h2 - - sa - sa - - - - - - org.h2.jdbcx.JdbcDataSource - - - - - - - - - false - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 102400 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - jms.queue.DLQ - jms.queue.ExpiryQueue - 10485760 - 2097152 - 10 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - true - - - - true - - - - true - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ${jboss.bind.address:127.0.0.1} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/documentation/ISPyB_DevelopersGuide.doc b/documentation/ISPyB_DevelopersGuide.doc index fee6b6457c4e83f84281775fb38c6676a9b2ee73..be213ba1110737808cc66d9465bb0ea5d81c1b6b 100644 GIT binary patch delta 54411 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 delta 64985 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 E0Me6-zyJUM diff --git a/documentation/~$PyB_DevelopersGuide.doc b/documentation/~$PyB_DevelopersGuide.doc deleted file mode 100644 index 37c256e44998fd757846dad2ef52641c0cfe270f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 162 zcmZQAbqx-3V;~W*GL$m7G6XXOF+j9NJUv(rG@^$EMU;Ud9wOes&fv?C$&km8&fo*2 lLm8YP;$3V&Gcp-+fn+IAmVv<$Y7Q%d3qw9b2~aeb0RU;Q7jOUo diff --git a/documentation/~WRL0004.tmp b/documentation/~WRL0004.tmp deleted file mode 100644 index 20ca96603d885eb31c28695f17d63427d0cb90e0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 197120 zcmeFa2Yg%A`S5=O0TO~r83`-E#ViM6IeVBmkl`${h!aLh2(m5P3by1*auNe!mC@1y zEunLb-+$lF&+v_O*ICav z^Bz6@$L(Id`nFwP^V=J@IYNJBjad_|?~6e=TR*I5-|o z08ahy-+w^~{OkJrz1XY0dwSld+uvtIi)8uh#sA43-nYD(MKy~m4y`!UTnzu)yJtTy zy10jTurOhLf_pDcj}k1+nZzgykk%Ey|d39-Q#>p;mhIU z_POxqD9&%@d*1ZTJa1-+=WT<=y6cDD?|Ij8{yTen-Z8yB@3#Xzk6XN#`INu*pT8;d zymKk72_rl&%X4=U_!z>wWF>KM|7Cp2UooHiaNm0ml2;zQ{t?ehbG&k;=T-69{?C=e z`qAyLm}5zo{JBqepZk<>!+$P3_v!NK?vdj^bFc;F(~r+{V&+QwPYM6nU2^hr;j|A| z$|wBi!U>-%=DW*Jq3|T!D6T7htLI%Q0@*}=f4Rd1k65+;<}B-sw?uJore@yNT!J_!LgL_|4Ug z|5w5}pT}K1E+i+fB&N7CcHqj{eL8u$WB2KvFZAi|Yyau=(R~(r-kpDmLi{!5(bZr3 zc-*loXLnra(}nNk*#5Jt>+SE~RL4$Uh0@89$#FtvJi+xW=D_~Lj|swJMz_>)@`b@2#)hjWxjCH-O1LH>pQ%=i+& zI-Xt}Py0tCn(8JswfUo>L;P5>&Tq{mk`4Zh*y4CH_tennNc&X0wkgq)iTkywWPPHc zH66)-ErLop*V?3En)M^^c{haZa#B2NvnM>5h zGaJS^jP%vT>tn4=gqmoMr&_Zv)Ns;MQkNO~ro%p_`>;+pMnXhR4sNtsKs$ZSg+VaRuTx+&GrKxsuH+w9=aoi)Lq zpYlrvmkllP=QsNEkN0d$ZTOBT*e)eajmXxO0PqlSzq@q6qz zING~9Ri9lFBNgfRcUu$bcwO%u{Mq9sSIw!Kw7KvJw7j~t1|eh;i{pMG=~o$rE)dn|qM{l)$VEkgO-Ln^@miEC<*Pa7 z7%xu;PLrZh{}CdY~Rs+#n^Btcx|JlJV%$=B5G}7+zcqYVSj$!=t_DsjHqc zagyJZs7c4t;*m;INt}5y9>>+neFB#w_42;cgKuEMvzVT6c$LSrn66A-sMZG0+&e@(}dnV8Wt=d$O{nmDUK zl-50T6{Wfdv^gw$v7&_fq|)VqlfGbp-yEYtOU4h8YOFx4)>Dk`yda0Er$9T5?6jKg zIKW}n6AT{IMTqJOhDpIqH5N!}sl0tLpTPyfwkF?Y*lP2`qT*(nTT*Epwlpvb1Zu6j z%Rtq6U)~E*$?xGOXs)t1>*y=J}yNpQ<9OIjvPAw8QUmY}D3ajKWYubZ{c99LW?cpNJSyWMr6Y(Vl%Eg*(mjSBX z4i^DVYS+#x5U{n^E(2DBm4HR>#A3?gi)splX$`W=Fx4U@Owl(Dvo%p5NNa6f1vy;J zO@c(*Opr@59S1qw*;^OQnBi(~!zI8O@nmaG{sn?{rq)%kYHAWJ1sc>#Gg5W&0@cNE zXKq~utmY;GQ>s%^OZ-IEUy@26SEv}A#dQ^|S{(Jkq(nNCrGksM6bjKNF5Fk^P1@SSmW6}}qR2q}gMO$liug~P`c&5kw13&r8=t*bcH-XspB zIDvXHE46=&3bMI{Zb1t%6i^dqcU?uGb|(=?HgXY2Q?EeC&iY1n?t6x-^-0KR_TjOn zM4jnYNl&XPold0-6^%2+F2Wz7rZ`d^r*@kvC>30FrX^mRs87_|zLZ3l*SG9AS0kLY zc2VDsP-`6(?L9NrETW)sPTeRi)(kzZLQRMf)@X-xQTL8eqa7u}o*k<_uCV@$u*TYD zfNHD~AVG$#pg@qa9#L-fUwXNwywm^StgtfaNT{JL8sNIb=Vle@lUh1^*v#P7<{dA&C z)&RSVLJe@Vl&6lCOrJPzc2~8_2y1*@g*{S@Z?u$cwQ)u522Tnnf26gvAwxR%A0yS$ zMoSaSbbH4!Jd;Y>Mwhn8@;wVw7d1S|)*G?(=lq%Z?eCA(0tVt92+z zI@_J}Jw`f2?*cnyvsLlV%wS&oOcjXD znRZvPscCC$>ML{%uRxH_yt@ig&6^-e{)TdhB~vJFn)U+qRcEF?(wTZ!(W$8qMhH2b zk~-1Pw9$}iX4H~eaGV=NEs!;5`du{CMycr&SK-(?%hD)k{#`|*=1(+I9dw@ZQO>x# z3Q~=Gu!v;FbTg9NT(~_v%Gq;Qp&ENeb4(X}QEQSGaWbu z*vFTYcPz+J&U(8F*jTT&<85*a1n8``i?-w_wb~=792smO{~15NG#gLWNjpgfU#g~! zA3B%w(E`ypbL}b`W3JLQPc_GV#!<2b>h@92TDuC-SgVeCR5LS>^_x<)xH2D@aN*P_~S*xqgdfLJ5r6p>}`j#3~({lzjMARc_o`=BrAZ zV)Q0;cs`XJ5DA5BC!RFyT&5#hQMySfE%EwTtxiw5LHOoa`nY(O@o-LyU#*X4Ya7uO z+HG@Z&6-{vjT|{KMKNc6ab80^6vomzy(5~@NeVh!4e=<_D7WV+dma1M(w6~Xo9^yB z);BVvt@^O({_yCL9VUUn{b+Mr=DSU*4!IK0bLBF`A)*gOUfctH(axob%;4y-+#s?m z_81qUu@=TY<67);T<$ZOxJ|?kHEI_zQtWh&?;a~W4MXU2vE0xUOU|0jAuZ#L?Wr&c#1FkzXSHj9#m z!2^8Scy4Y8OV6ngjT$TMaM(7=NMy(WCRUqdV9?zaI1W-IUTI3asfB@ahK`t9w3<6m zt|WEBQEp+PG#$^7F((N;K(o|SZPAug69fIJq-p`Ph%yG5DbEB`T7kWl+n~#bMhBHQ zwI*X_(%nXUQYz^nlfgsQRrvAd77`)zA1JSxYb>Q1muzcJwPuKi>DB=Q{Wi=w zZ+eay-?Y>g18A={pxQzcMk1N+c&q6B@qr^4Toy;akIqH*TGUF*l z#nhZ!%ElV3+(`!=3o06_lZ?!PXB(L_ms_nTlwj~MOf5(Ksr8a>e~C_ur?lp_q2@fN z%wx#onzn(|Og>Fm$qSR{L3-3aNW9)@8|sjW2^;5V9F_0Po=jwmmo43#ba21QOqnQck7T}Q=68K@9c+GaZL)9EzQK^ z{*72@Bhws`84AtXn#9i}TZ8GPX_Djh3G7B@DD6x>>kXPxOGGi_$;F9uDyenxVvI_P z*EGE)>)4gd5KGHKhl?sA)5aY>ZsC+!GpZ`f7Y)hO9k(!@N@dO0!O@Y?rO~AktySDT zQWctekZoOkQ(GCerl~W<$t6wptjhS!%u#4obfQHj?{roit3ruQ|HCeoAj?%|(pn`b z*$gGPC6;Z>aH2k*jwfp+pSj6Fm1-@EE5ygmP5H6KsYIP0*U>$MsUAq4uw!!Zy0UCr z3uS=I^=(~dDoLGSq^$f*Giw7Fx?761;xLy=l}=q*HaI%GER&42WExZ1LBj`)pkuHo zmKJGY?phUxZZ+3%5iSl=Y@1cgE=D7z6j~iBg%L;~){mOC7pCHG6KgAH$9t<83T1!92yUsHjIoBEeX_ zb)g$hhIe=mNl3M{Y4Vn96@CGs>PnavgejxbDu4BEWmI#VDVO90Yi`Q*DM_tlad}9^ zqJyHUW%KECw4&1UO|No(Q>=|9)_fUEm`{b`BBV5eJXkw2)p}%(^$jxNt0v^UgG-{7 zgo(#YoXRv1ijAD@!|s{D$I09@nj@~(U*57bR>#2X1;%3QTTv#~)-)$FvREl2p2L&? zp_bG3S!hEunJ;f?Ger{R5+;Ryxh6iY zt4Ql=I*rf_e>KOmsFAFG3dg9k^O7pLq-!XG=*jF{E^JWP(l#*;*G%ZgX^)w@gGP~` zFtvf+q{{&$aCQ4Z-617!e6;bGPBQIWen_YlotC%hqBTIR zOE)cKa4d!}dft)x=I%pXLz+K7cR^NDh_Xe#!Sgh26D++7%rQUoXnZNn4Vk;KW{J*Q zO}AEwyK9v?#Fz+)=}ub271_yYYB6T!WYHS}OXn1XR^U7{H0bok^g=Rz1w}tb6v>?M z7g1Z6N%N($MEr1tcEL;o$rRwk>Jq;tm1?p#scGe|#FJ73)>W3EKJgMU&@u_8j61T7 z4A}EyBfk8N;jCTzQ`VYp8oQ{jrnW*|5^c4XHaZ{J_u^PB z!VjAuHQd}kW9rgW!0E9Xs3gKPP)(X-j(mvL8)M4teH{mqvBUp1*}33@Z)3z0X^D(& zSt{Shl;_TL7^plt#|QG8g*HdbtPHUAHa ztXnek^M!`h<`yX#{maOzQrFhRE88Vl*r-)x9fqnb=|Y`LWTcQ?SL`7=OHZO3TesLWRB zwTxO;mS|8>vFCW24>SE0olYZZx^$#wXo5@p`leVzWr*CZpooqD{o8(N4%Non#)6vI#8N?=l)1+lyIL zkdETAf;c2toR)?j_^VN)%extNP%!xg8$@H)0OSYSaf)eDrPGeIczv8GJC zm_S%u6P|+Zvw{Z?1`_bLk47%#&-!#iSvP%bL-l_|%C(K()!D6M`m9oEbbQ*;>l- zaSb_BFmGeZ!$xAPsXSlm`1Qc!Tf4-FjSWk8ioSAMyAiQPEz*WF-XapxWgolN#p1@C zt?ibXlNeWT{>JchSrDALky9n~nQIyIEUTg4gmqPx=*i~TQt3(1KA?-9sVo^Xh`)9v zrM5Ii#$4Km>zv01Vav^UtwMoI-#>4_g6_JsGo;xTucOHmjGs&MW>KbvVFemNGFCzd zUwWg`nP54qtmYfYNQx{D6}QK&(kh_giLT5|;R-{i+?cX7%xuF&z32qnkTe}b#3<|T zwTaG{oA#un?w0|g26`!yM0{QwwQQDuX^BO~(D!SOWhp`H#b%G2JGW}iOy}-s7s$nA zS?)f2rm|$;x)a*?E1NN+jL4=;sc3Gl$YcgqS646d_bnUV;vcRe>G={1&1U!`2e^&=5VMTcIxyw!zUPJe#HFzqMMc?8@;dc3vu5Z?^ zKcynqLj3|!yFO(TwE`n`EWKu6mT{L#)7Un1ALU&XWV_NSw60prN21sH;eAfXjW8{n zk9mjtLXs*sykZ8cB>9yk`{=-xW~FQ*HDCiSKEM{$oMu{I3|KI8YI=#tLgD!1uB==^# zs2AbC38X|~YS8dp9Min<#9{KA)fkc-#YnMBp@g6CB(xN1&k{o^M=`IBZ#5cLhB8Qy zLdkobH$Y>XtL5Z#94FsW7ESu>V!k(VBza0`9)|Lk?;tJP1b6y`EAjZGG_K)E{3(v5 zBy;JnAeKAd_c=eEs||!DB{5g;4CiTvJ`>YiyAts!3n}{y|2An#O8HC?gQTsIZyDmO z@J8!@g9x`qbDN)+0i1Vfn9MUSb`y3-vi7~P++}njOI(s>k)%04m*a3LJFUoYkCeKT zm^`zD<8VoqQgSH`Def&LNoY#R%XW5NBp8sio~zU?;~JnoirUY<1Soz~S z;~i_`45-VnxtkLA=dz*-hn8VxN?X6;$L#R4)OXF%hz?)L9Yh$eSP5L#3^S5wk|C3$ z46@4d6`81z@m8iCTAS)*I)&B~RlB^P$yD(`K2j|-B4y}38jqz1G8fmHWIVNf?$u*w z7>6eFQ*{=P_x8-YodjlPo*Lr`+VC=DtrH{?Z=>8;+@=j}5+WBOky-Mxm}xhm z5N5p4B%s{3_jS@i%e2r9Hq_c!Qp?-Cy&DBX^XNcwUa-zeKcp3S6~WaXUimyK1$VYZQ8iinhgWZt03zp&mERo?A31dL6e zuJDhG^GbxgyCzk6y^5F_GtHPPqvltO+>Ev_Ecj@?nnxn?_O_iY5|e7BXJl3#X+a?; z$|{*M#St}EGv%ff=~%Q#u7>%OQv+DY!7>NYPW!rdZk{&u3K^Z!F?*Gj`Ew>suv!__ zh5Hz0tck8fold>AFj^&p@shz>XM8dPr`DI(5_h+jm5mK%4&r9en-x^F0<*FdFQdVg zswPW=igzc?RDmWfr!9nrA=s=DIp5elp+su62E|y z(GX{plwsbmK7ILIqTG~#xQ5VMaqd834Chyxda_ER4}r=xdzg1miRmHB5&+duxo?EK zQwI7?fX0|nQZs!ecMTOyFcaDt+n+-&I#Fu0jaR~XhrUtfFLkQi*bQRAt4aXI++75V zYEYga0-4*DdzgL8_y^MS$EWL5H1@>hmW)4~tF5^*~4bFseA%T}L zsw2&6I`T|`h;C?0-6cn^5`)Yw)9sNJWEo#7i*zf=F?A{yM4y|SWyQ({%1DL$GtiGl zU9_Ys6m2MMDP!4C}bNcYMHYkh7`;}|nx8A>KDqfptnR6V**QeO3w`Y0-W-Jma&G0Uh* zcwa%@Jhf(1B2m!Wu~#L9##cGBl1#LukfZ3kTz2lpwsTB=%%DJowKS^K_F-g=cW9Q` z=Ok-$Y#ga1D+lQpqD32|B`yWBo<*TAyD+QNzI?7Df}HG8Z@4zst=_BhHd4AMz+ zGURS`fw3NfMUy5NDQ;cnV^&Mx4-t;J6x70P1N;{HmLhw~M(>a^(rRdgk+qA%%x6}} z*of4~#uZQtngI`~&IvwTn(fyo$Xm!PQ$?>(uQ<>1$qa_BF!D=nPR#7*G^PTkyLZ^< zre?^a(M;n263Id+^#LyDnrvHACUa)-Z!U{|q}Q!e|FVhlQMq*K()oD{ zw&oizIG?=n)i^Wm#24tYm9mLCJ~p+kGXJ7qHW62nkjaaVuF*-CWek;r*>i=F7kxvF zlCPvp`fQRU#^RX*>ElYri8B|zY07MI**2mUn~v9sv~z_er}9%_&-{m?AKtDoBsR8~ z3CqZx`cI`GRkNt0L~y!;beOaMp`>)2k`5EHY0{BzP^PXm2Bp<#83V}->a#LzNV^g; z)vUdqUrg;U<;sd3b@DArrU0aoF79mnggM$4%&!#viY~2A9~d`#YP%Gwm&;F?yOXoI zL~UT^;Y5jT)U@JMOs!WfFLxbLZ5t&OeyRFl?vXZWs#U6D=U!#;6P{^6*N)A&<0Gm~ z>6b|yWexFU8H@G1&QgJgLpdav4T^nMJq?uBw3a+cms|!-K7EgIp0umX%W^@hQC2SD z*qhi$gig8ST!x$zW|tx3@9DeGI#e2|W(G#u@TPv{ZkBfYW={v7+{Mi%R8r3dx_9jIMvD+1CK&8JWki%@Q7wCKPKD66_ZuyOn9FpwYRNml09xfqvDD z@zV$T)w8BoP33dOxFe??p3{5@mXy0b4o&XG309uVTSA&gV*ed{$H4>>&3GQ#CTEl8r}2RdEzfq)rQE94(YIiLPBK znaIQs2D&FBS2gk4nDhB&iL{+_({??=s%>?5X9kRV)fGB$ti5JQDa$;pJolV?VJ=y zT4WnavxD7cBD1J_WS1DTbkwFv`>kd#J+S~b;*$nE=^To`VX~X9Bz*?a6kS4OMv^iE zDk4+uo-6E-X9OQOa@UtN^$G(j6ol#xT{gG*OW~(XYccc`V+nSkfNwd;+py?H7fgWk zlO;ImO(V+jaY+h>KsUj87b9oWVY8!TRz|LTWZk~Yt?397q0S4s`z1v5l%kS_*bGv} z87j7=A;!ca90qM=I3#bFNWEa5uP~*=8x0ypPBOV-i$vyQLGwiv^TiQGs@3NQ6^J;> zPytr>Rcv1&FW5{Qj}&EFlmQxTwM@kBV5a0<_)LE1;twjE`7^5K zjw_owb5aCjkT+9BKs8M~YqWxJ7B)2--<(5j)jPgohvP9eV{j@p@YR`dgs#IlL#@Cf$c5Ig`lwv^xdH=mie-L zJa?MbpC;DbCT3vNQmnw8<;6x_DIJWenl8L?@Cbt@m8%cM9UM_LUPP?>WrnLw6L8cY zXI_D5M$Kh3C8)Z23Du}|MMM@o5xn%1W$^bxe_Z|+5AAG0$EvVTNq#8CXhg@<7}Bpd zEg`H?+6%^Anzwf(6Ya9v9B2B9)k99*nCznFklI63$>us3KOpnXiKV%wPuzE}eY!Gd z7&69GK)b5Pgrlzk887Ne+sIyD*`RDq!^*_$24r0+>F7!snU+F|*glOZ8a?B#pp^3J zpJrHQw=gwXvSaFGa&SyIrG(B(sE^HSVIY-RZj{MtkT&q6YFZcrL6^oRm>s zx=EKxgnG}lG`J;fg6dsRNmItAEyYWv*2h!?MZ;tBYboyw{RuRgrJ~7X+hpU4#<*)GToG z($KwPxHdt4^U{tV;6>=B`~>NlyqndX67IC>*(OMHBII(hlCX^Q`uv(yo%Aa3;uN?& zunkk$xr|q)r`m?JwFi{SdR$$RgcPj8n7JlhC2889s(*EEPL`IH1lK_lY~uv=(upLS zgLJGx>L6`6h%%rZ(v|Nh4<>;5^{9G0T`+?AjXIrD_kKHI@nGlnHaq(vJzQ*%C4 zUj-hzgPgj0w~XSn3Y%pb+*CB80PSrY%MQ{aGK^<8@JxZ}ik;infe@X!+gO8C4zfjn zE&O)XyE+%0pLg9Pryn*((;N!7lJnwpG3f|b$Q9YA#S3=$tm|1!n>c-{I0{-N@Pqm zn~vK7#pbf$^>5SQG1-VpZe@6piClU6T8iE@Ze>eLbC0$sn~^-qNbN7Zt|-X1B~^AWhunEhmKi4I>52#o6eJPlbOFq2n1#6@q|r$t-B^<(B1*T_gdFs^ux9&8 z9+kO>fV2neBj#Rh**;Bfu@%yhj^je9vU^N?E-xx0SmN zCmEhlcy~_UEMC_xIO7SyrkcjLIHRZTXLm>KL5MrG&e>X#wx?;L%xx6?l-WV50!giT z@m4zbsI`R5XsCE^BRXEDX=;^m6zwB0*(hu1>tqU>7|am0-dw6RxUOWVrPqfJ*e88c zy$gmH^62>4!0c|Hjmbx(Z5VX}KU{XAZn3_}EMq))(wQ4~rdD$bO`XmY` zTEs{I>(Y|i-KD9kzf=&D3SE{@gkxeK0MRa5>Or8kurvyBJyHOChC4-Cuts+F_aC(G zk`DUZpd%FQc`C1t+F*1YoqaDZ%P4|-6CozGX)qXQ5*o^*7M1pHw1ea7C21mt+b#3u zl~e{Q7*G?tM^vUUGIo9tj~O}TC4Scs*X(KDOfR!E^c_K4tJPhxs7TzaMgznRxn#Ib zw@FLTho!A#vn#8|c|kx_!IYv3PyAn4kZyp$mcLnXW=1H)gGh|7s?=BG@Da8FJJ;M>H-QW+-ojrp}I4hfdPce>WAV=$X!%Gv>ofHKE5O(Um2bS31gsK2{Rv zwTA}Y6_%DaD=;fdq*)e~nj&fd8xdUd-tox<~XhxTRbr zTy7ep{x4b`Q zRo0Y~OfDc1Swv;VgEtt?R~vR?_127$fm@D?Svcv*s!UZovr|9seqmm66@?96g`!6& zuT{C^u$={#Wd=D(t8NwIB4P3D68nCVXsiv%M%+$M-G~WqjmXNTGIN(1b1@^nx=xJo z9j)vGg=Z%sdA-HV)T!p$aw5${LB(!Hv+;ebBdRm^xjBzet5|MUfriW^GRCGgqHoZY zsBoR6=b>t|Rl_1S><-m0l3|tu&9td1@gug2&$-}*$#B&{V9j~XN=~r#PK~asp+;2+ zAsDK}ocRVtu2=b)8*`g_lbBQl%`{`IX^G0{+Bh>ZQA0^-)5CaN@ptNvo%Lymc9EXA zF)T6i(FkMD2SG9!LrlvKUrI2_;Dph*rTAy-gpO$Y)JKpL~(A9im zZW4l~SKgf`SR%oyY{cWtJ3x~pe{!=e1(PairMQ-qEg3B_3C(Wgm9y}SU7xL4U?T$k zEhOFYhQKuu8i|dNMA<&|r8{ma5Harrd}U z#YUonl5$zrCQvZ*EVGs>f^a3$-Z&+1#x!v|F@IuPGIqEbxJsoZ15v+J8g(pHmYwo! z@;XVFB!!tDdiYEr%WNt2krRL{yEaS0w0Y`UrX*5Qq4k1o>>(`#;MPk8BV~FUA=)8t z+XFQfFuefj&Z!IKSsgBy*wtMI8=}zNYhn$*Onho4Lp@AlCAETvq z*<)Bi={(9dQ)EI))w&&wK-bet;36OvgN~PFn-kKl%{6+0!jo|+c}`Y`n;E1~j8?U2 zzRFaNx(b;t%_YgLhGmLDdY?v#BndSVQLRKo6~)wp9h{Wc3T#U%=WcAXmPF8UUm_{u zB?hD_GIXtD+Q$e}y5bVLF>7YPh(*>2Xq{tL>5}A*WNx*^whR0?4KCWewg?fKbf07F z!)VW#utempwTyd_m~94d6LREqA#CfS06MvA5?K*(ieio<>h&(v_J=D9$!NGmYimO} ztp=wx5w3iUK5NU*1SejVH}<4O)4?Q#`XnIcd8Jm8WIBHN zB_dTQPv)KosZMC=YdC>uZDUFnZv@^&(qNK}j8JA2G{11Dv*xf(^duCsp^4Wpn(Pj1 zCZS=Gsm~LC>a<11cC!2H`>E=)%z9(H(;o@b8|CHn zm0xr)r9(N0j!~fMvnH|RTZU|G+0S5TMXDYdV&Rh&K3b$aL^Gf^E1v`XDYS*F8Q`E# zKF7X)8>Bn$n(SCe;?NoWGQdBfPoJx3oLH-yQ@?$7M5V?fP?gI{pTuq-RzeSy-w#O_@s z7njil@^4Z+K(ZrAaRf!BEJ)G@B~*3fxEZsjSNXFh`NlSUwT<8bldUMVe4ZiZ8PdS^QYtO6kJT|b zm}ugCfRdT9M&D7pzqD_tx@li0O9q7UsnIT@gCyJkmR9L2i8AyS4;SkuN>V(UP2t&4 z=+Xh9V^-I?CFgsdk{KbU;*NF3o$;6tXE`O?{VQ(z{fRY(^3p-A9I0_$P=A zJd*$``3~=Q3MN~^T;vM_?Oj@lv_5 z+G?emy3zBnF*>C;D(^xNvlnxlq}DbYLze-cO4;!)F2$u8lnqtXa!e7|rzlO56%bk^Xq-YM`$!=_Lb8 zT-N>RFZbSm7Aa!NK({-%YoTpcuZ z8H3prjEwxy%a`FBxBF+oU*w4d?P?hxmRiOP$?L<2n2Oa_X(Uwwmde7d+vAE3amzlD z(l21ED;?v;CE7=|$ESNC2ffq@?OqKlt)4o!YT?xCg|gHU5mN5%j9HJ#IW)?xij+5) z#RZcbx3*7KiG*58CIg}CcCgrGqDCPcxh6y!K=pNwgEdpOSHPfz4}>QUx|1W*e~BJI2T)Ckw}JI>b7Y#%GzslAM^G#o-4n~T0kt^7 zZ9CJYG0h`YhKw|>sh32Q#%8rsQV}^?9$R2yW0gKj0L>ZcYTz857Bk6nzyT5e06)JC zPhH2xGbO=pQ}RADH6J&YM{GOHB!yZhm9%+s{W5!a0`?&eK(ih0T$v7^(Pz0K7`MwS z4Kk%UH%e-h^) zyfP@pDU&80c&5_nE#1wV^Kmw+2%yh0nRTTC;b zQm~y=TCuMCBhDtgR;P?T;1j%hnVWa3ZA?)Qn)gcFOdu(e-?)~S@*3=ZWyWrFr52%WF@BYaIWu28PDPOH!*p{oqaWq|aM_gsJ5_C;%89RP+P_L`L zm~+^Rx7sI!SN4ncNR?9I>yK|SDNgm|%X{kF?Oy$J+qsFQR`7l!zj2|Sku4(I-@;R( zU|3t3tT`U5_vu!expS1IF4)iixi4ATY}U5xc_TUJ@$mSPd3{XA6lLN9;i2<(o;m2L zg|k8RVOE0cc(v*@nx)MZz0Fj{;SOEi$f@fPBAB47=k~jB$+M$H`AQ#5w&j#7lq{;| zT=rc!>KX8?jK|pp0LHwfMpFYP5qhB_QkSe?SxDh+`y#2<3O}@|Uxb(4Shqhx#zWZ7 zA=Q$9f*sCcNn0J$l40w3(r5Q}vc*0xa4jbG+GM4i(mfFiZ64UoRA}C2)tg-sXW}+9 zky`R8(L_j7uytaEFMByA>X(ilJzC;#>0nRB4jyQh`@8c1jrXR4;hVJuqLBD?eJzEy zs&N@c4;dl^?>~H5*6<({E;o_Sb`PzF{#-iulvgaJs2OQTQAADG8ZRklvY0_v&6b zc@oXd^l7E0W!*ttUSHmE>EzZlWZGy)EMb$}zLYRaYM|YtBR}vbkuF z7KN-FNi@jFE-qC!l@~$i!lF=Jt?4L}9~~RfNov}cZRe?(!#i_hj~+NiKNo0{Xzoal z$Bs6t+3V)~QWh*XYaf2ZxG;6R5##^Nch#kkkll^0><7EM-H9PIW#l)__05H#U8dq2 zmv7fI=y0=;O3JUDx3D9y*m>ZpWV_6u`x;coEd0XT>EJa(nHBz`g|5Z0P*-b2eKF+x zH4kQj&*-JBdC(q^?nYq-z}ylsNmDNUE=e*QWK$x`@8~PYic_A^h)nBE7mc~uM`qq@ z*kso3@vMtNOpl96658K5FKNUY1+r>_!;&{>HCw!w)vk23z@){8a>;93?JkR7XHHez zx7l4pwqcg(D{-08$RTsYm}v;GP_M4^1iyrcs!c#L@+Y_4RQZ+)UM(nP*+Riuab^Xbl9sD^}ll+Bq;`KvEj2baw z)G+zA%CtYfk>4vD#V@E1?weOY)aK>~ai+|j%VGs4j%(YAj&+dJJNik~;P(oQHE8o) z-*$IB^+QCqk-Xm@H62~~WeWzb<&8!8X;SfYtiQ+Tv>Pl}-6?v|tm+M3OG1ex`{C*z zr-of5cB4Zh8+jx@nDw}nfw%lwnPMlZ5Ud4%y@s8D|lD#I!D!*DvK|6K=V{ zuA(rxr^C{5*41$gma(#2ztxnlDF=~LIgbWE{v(AZD^T4=fT3_P9$CXmGoy)4UOPFH zQuhn6QYQ3HWbW53VU=!^l%>ATYMNBsB_rE9%)rlx@lI)I+bAhc+sm}=!mu;R>m!<5 zsn-1E^K=_JnQIcw(@~WRa&1W$eW^MIH>4%o{V`NHS0j{ANZ0c5GF{M7X!bt+A$e}J z>#kJU8CG!HEZTuUjfcsZT7LbKT8@>vZGJM*(3q7UJd_OxMWk7O(6EZZL;P4X%QNk-dvqfAPgP{NvHHrUqNv*sq( z9uJp$UcSVE$s+0ul~>ry3?4da*wEqpsHgQZ^3(Fe`chq(@(k;Z(~@c#fHuEJD;;CA zY(Un8NRufYFQb0fq~XY&+YAj_U`hIpfK&@^%ZXA^039NhVAOVlb$VvWbMC!*w4Z*o z+npTW*`$%pww7OBZ5C#fb0Qn3x;IP2*^rMz?nHu8%E2J7?hCP8WnZ@8o*XD)urSe5M=vU1J+ zRG7TuM0x11u-NpJC+dzolAIHQDu;BK^d>0=se*XbI?g2Z(!oQ94r4Xu1o_ciibs8F zklG4npUQIegiHj83l*13II?2M40)Q9=FL&Iv-gRbP4nbu+wz_drq*4?b4@=|n)Hu4 zDtL?#L?1Zg3Lx8>jH@CCOGt1V8J2yCgW=X8?pXr2zjfEt7WMn|nWLqw38e>Xt2$|+ zncU@jei;N!p=x}pQ`fQ*8zNM3YDfmJ9%P>b$sr9jnE>U8H6^C4BcTN>QfIFwEj4AqY)m3h)%UKD^5K@I8 z!(>w0E68qK^|;xZTyZ_7v&Ij=U?}YpEIEqi$2EZT>E56S#QiE*5N}>NIt4WOA=Ydv z6^-Qo9=Pd~iJnMbM@DOqCAFFqGRx9YD`~<_96yoGBIxg?nH!e(ncPq4nqhM@LQK{g z;!$xMmR6-AnzbEj^R$ zx*A4XHQB+>cw#1Ap}$_9w-f9NvtcQ;!S!$(+zvbS_Pm{87dQl3;as>C?uC_bKRf_0 zz>DxFSo@Eco_gTUTkgE&@>|Y7eR1P=8V{d&_{^$83E8e+}sk5eS<{bvdKydA)s>*`bX7-s{ z@Y>Mv%B|eOz!<-xp%^}9ldZGVjT0#Tk-cg2*P7e-7zM>1~fat<~AbKEjFW=hpPK9+azmMmA5*GQ!9DEQCpzuLn z&}+lb{GU2_D;((?;e#~=5B?Os@=W-^eBWwkFY-t)Z?ju_*6;0Yz12?UXyxX5)XyBf z+Cz@K9z4<0^BU^f4*Q<{_M1WP*l(gr-d3Ks`93{j`}An26Ls-+7J0n{FT=<13498l z!N1`Ph#<#r!L~2}4uAt;Ae2EAMCOBFFwB6NFbihG9H@qca4f{&1UM1au6be2%C&2* zUvtS1FKPSXB|ltyy_~7FXAUDndvDh3)Xf(yA#Ypu>aks~Q;nOz9fUl^0ea8p+|50G z8{ay*FM2QDWYDy4i{}X4YQtM*?ky1B?#A;JxVmF-J{<1wzKsj_t+ny0z4gN@Y>sEz z+?(I))Y!Csfp5!9*`{X}@Np*PB&LXzZ|EWMcKLY?C1@X$hxT{mm&TqVgJp0MoD8Rc z*zRfYJ-8CCf~(;gxB+g2Kf=@S47>yXg0=82ya(@t*#8Hx9=62~d>h_>{r%Nz%zCVA;8 zT1SeKq}Q=lcCN|ZZX%QIV0+jZc7X~w2*$wSa0DC)N5OaCXb``?0FHs<;RHAlmcjSn zboe>k0>6Mq;W1bRkHZu22Y3?x`QobER^71ThBH>&u;RtrUR-tJs(O27uAUL4Fw&0W z-MaDuZ*9JDwq#;B&O9Ahc)q!GUYZ?Cpgims!U|ZxBYb0<_KZ(?*H}j9`s<4vMFxj# z<9R=Wts-g7{s9-z7Icuv*9|p9*V91kJ_?p%m2$3#aO)ckm zS=SU*YwRg9xB+g2o8V>;Kk#$71s;P{@HjjH&%kPU9o~R9p$EQVGuRw@!WOV4h%e~{ zyiV{52=8T@#Ztv*e6ug0a=f^g#;O`WsiKM#hiR zqwQM9Hf-aWbj>hN7tp%@inhWYB8SId6+8}az?<+EybWvNUD%CwK!4aBM#3l<4Ha+@ zi~(scjD>^YXqXQR;25ZZS~vktgk`V-{_)I%cRhIBGY|gb!Hdtnc}pZ$!Usj+87 z3A+3v;K{<0&h=|!9{Q}TmqNEWpFuM0awD^ za1Z<*?uCcoVR#qbgZE);+J=2#8;HQSU|W#3<94t;M4=qk{`J|vuDpKbigQ;iUAf}e z70>?l*_m?YSUD3Gwvp9vYM8>-zj40(x3p?D#_l_7n{S*1ij!`~J&Vpgj43_o#}zI; zufND+5DbQ)FbwJ-4)t&foC>GG_uzCm1EfE27JMJBgX`f2xDjrKpTTe74!9GZ0s48r zTy^aQ*PeF4wHLhj%NJL*t*U)-hMoya-F-i>y%o)x)|0`Mo(%K$3`SD2Q+oFbMp16T zPi>4;O-V`ZSGc4)FYAuwlcy0y_x3x}vEsDF_@dj5-Lx;o{0{A|BA36S# zy_eu+_!vHcPvJB8625}%=mTyKJ3tvkp&SOo5Eu&5UmOl2U=CD6p8YQn`#)6de?iXv zjYRe)j=~bjE8T9kw{O5>ZH%ULCY!wU6fN!IB-*ilb*_`93AUDHvAB7gqz@I_!;~X{s-=YyWt*q0bYbZ!OQSx_zQI8 z6O5NQOuPinz~p16?K;y1qw$vz-o{AP#IU=0x&DY1pto@y%tPS8_$`nzx(C zue74cEZ6Ame=3!wv8NJO~fL!>|e-ht=>bJO}T>d+v# zVci=qzVYyjZ@hT>ird$nyKd?2>lVnFL*>kZ+pW!S!MGu%SwKn~mt!NR?=v#FF>=a_ zvuOP+&T5Tj_Ax0dkmrr@LY?WBx4X#V6W9TZ*b(|dKiCy^g9Bk8l)+?}0#o5om<}^Q z#(rnPY-oaJNJ0u)VKJNxr@*PO?yYsJ?^}1>I@{yB@4EXqQ*+4m+L%(#(~P3+uQ<&xI(wQ)bLalNn`4Atf04(fa2Z?W+=||6fv?w?;_x?*oCrlZ%>{Psr*Z1qpdzkPRn|n5CnRZuui_?M5b<$|{ zXT|~w_>KVHunR7s17F z30wx3!xbQNLs!9@FFkeNQ@^K6_gh${}cphGW z7hxTI0PEo+_!vF`ndkcqK8JpIshwbF*ah|k9|l4hL}3a{1(r#9)4|jSH?F(voXbu) z=dyF|yHVdT4;}!Rhdi7oNE1iJ#r`#62ti zvEqeuUs$^01wB(AoDs$7;?L;9s@*%&1tY)d=ALfLuQ+=(>G-43-~U6NKTn&BZhv}v zN!gzPXTp!*9Jm&)gX`fwSPA#TgYXbM43EI0@EH69UWYf}O;`)>!k6$Bd=0zM(%Kbv zgWX{d*b{sxe*Mqy_v~7>hnRc1En6eC2lwbKhR&_2V?WbfyY8%hFHZlAj!!Z<>AW2L zIIM@Pt2w&C&Mx@Hp&_b^QoF zg@vTzcIbsvJ`LM%;e&PI+wuirn{t5*P~y!yzyZCc_k10LQ>WXoKV71Xug-a;ARmnnOABjm80o?WGpnx!&6r z;GO>eZEH8ST4B=BMVh@Ko+58j7$Aid@f;Uf@V@CkehJK_uaLO<9Ec7a`C zFbsjAFau`7EI172KsAUznFohM8ZwZDRxo}48}7g8yo=iIzvzaG==(QF-(Sxhc0USk zN~m=2;-&iYlFp=R!i$=FHff3H)xkx#D2tMl*Llrj>aN?2<`-yjbXYs>DKcn-t0yr z8htOUkWt_j>yDK(Go{gYtn2k%xld=(>~8I9q+epf?6&lala-Oi^+sb0NTYLkb||ZS zuUK>&ue0`qw};5#3-}Utp^o1bc7y&<0((Og%3%;pgF|6D%!FAm8>F2u2dW_jE%04P z!(vzhC&9^Z3UtH&7oAfh+kX2M?<^7#VGxKK3{1=(av~CflNPb$5})C2%QR z23Nq9a1}_q?;5xk?t_(ZKRf`Bz@zX7coLq1x8QB~C;SW6!n+`C$M<0!L})vH3$}%C zL$UsU>(b(-dd~ixN!4h{x6M79w8ZoLf6-c7l$?t4|I3Z$7w|Y8`jkCI2HV39up9J; z-C-n*g3)jgjDbp!KET0n2+W5Ca11PjT98qyli*}H1ulV0;WD@au7sxW8uQpPzI+IaxI+v##Mf)CaPm#fQ;AmI?GJd`g zq<L6Yj_I&2v5Umcov=m86$WBUWE1VA$$ZM!{?B<7VLUi z3sxVj4xPPng>C)+YhdxRO__TO$kxc|g9D2RuY3Kijgvre((TZ97gZ;;nw%CW4R248 z!M~v|V>ta_Cm0MvU?>cS5ik;DtY(y8i{%|JO!~_WwnDLnHq;$~I2^#YxNf|AdL7K*?{6|L@F8Y@GIayNfI? zgca~B_%-|n?tr`D9(WaAgTKKS@Fjc&9%FqyU^9^Mzn-uK>;)yTH|zrk!ax`e6>ty~ zeDCW}-UQ#s6(BE{7b8_8odxFJO(>m>lYr63SB<83yFJpGMiiy1yNfL5!QpTO90lKj zqd~@V7r-%aJe&Y0!ZJ7=&VU=?Cb$_Mgoof^coZIkRUl)+Prx7GEqELL3GcuMQ2hFj zrv?`<;ls?m1te_B{kcKKgx9@#pu0W5qU2PRuRp@%ra+E6tV4V$|Ml=W{2RW2evI4i z1Utj7up9IT8Q0$f_Jo6A3{=8cm;e(Y0gK=`I0Mdvv)~7CHvAA|Zr~g!Zv98qP?b@E zTzwO(du+(YNxwLW89#HViK9U3H+3!0UETBc5ILL+KZBpcE$|Dt6>fv);RSdR*1-p` z9zKGP;S-Q~p3mTO=*L{oPOvlV0(-!oFa|22(AvMr>n^53`6kl5XIL@PG*UU(+`9>- zl9%$LTO-A3lJQj!7(3Xcb%eK@$Yd-W2}i+qU_LB>V?gHbj)fR3gOlK7I0eptGvPe= zG5iF64!6KB;D6v&xD8|u@K^9_cp9F8)$so7&+_tb@HW`>D|j8aJ~%Vmw*PM(QM_!2 zntKb#)|BNP!;1;8dwxGp1B&i}6{i74Ye$<@7sy$M{=JKO!`oA2@GQIlFT(5a2D}M9 zn6upsHis=>OV|ozF1I&q4g10VPznQJAe2EBOoGV}gBqxXIMhP}G(rLv!I^LttX=ca zn&18GcNhKacRySE(AqWUt!cAo8iF&O3<#KV+JBTvw?H`=4fxr}jne=VLz#KHKnxq> z)$_EcX#eqz+kbnDEJY6Y!}dONLGT1rGM98P90Jv_0`7#Zm^YgNS-1k8h7Vu_vmF!R z_wZ?0LV45Lc?a)=Z!7%F+u_^t4*n6ovL<}+a`<3%!Gp)cR~`-@{62i}n}P><@!T4| z&3$K^iLLkR(PvT**=WsvYp9dGxb)bQE9Tf^4~T}k`IB$SyK?u|_vAe(#~P+SX`VT8 z1dgGngmcZJH!xe`-a@wd*urC5u+Dc3+fi)p`|txe7tVt#;7Yg(?t;7F9=I3ogOwoq z@&G&tYv5IQ4gLmi!dvhjybtT3kI%co5P@ys+prxJ`~KH%6U5>zcQ-$ma^U32ug;!9(ycJOYowDtH{EUibq%32(zc;T`xFtb-3=SL%q} zpg)X&kuVA>;2;wPE|OI9V7S+t$Rf33aS9&yZId zMYkWk-9;8-LF)N&Aa(sIa4LKsE(EFjF9KO#ay$G6?f_{I{1zSpX&3Z{twGucGhrSi zpb0V{ZHC2gIk>jNI=)NW;Sd-P6F}M#Rd6Pp1>c9e;U0Jp9)U;UF?bF>S^uxsUwh@X zmtK2*{WI&IU%%?%dmmo)n^n)RfBv@hzqsMHYc9QJ{fg!5KUx3%Pfq)U8;*Y_*jM(> zslSljA-6XB%F14mUSHiw@=T+7;hkrHvxL?HJJ8;9%pKHZeH#z`&eQDmsGDkXRzEp= zZ=XS&m4D^lshd5ZpL+c5zK!=yOmS7JaE*)m<-0i}_sfwNq)v`V9p@#Tay{DTVEue* z^k4MzqP>sTPi+7YTA%5qgeJS?N(!?Ju5d>@t7n6-?T)(MAZf2jSq^7K%w&i|Jv2Z9 z7QGGGq4Zy&Q*|v6nyT5{yqwQ_tu6FUJoCTq1@lIxvzMe@WEr@ zg9pP0_l6IC7e4s)f9{}gq~SRAdrxWIsha3F**e(kxjE-nmEb6PZpOFgq@$f{$8CWw zoIa}OF(6Gq_r0O6?}>3bVc|194$0=V5=$mNVUBJ4QjQ7Ewm2J?J^Y}B5Jn;P8ciwvQo!IrAD}JK?oqgunD^6aTZQ;L! zX#Rw)-$e75Bp0c5l=VyLXYA0^)h;GCox#=oBX`fAYi`tglAGE39Q=sGvmXa7|S!gdYJn{euU8FK68G6*sGNFQ}8)7ur8(%z7Icu6{Yxo zxELOWN8nLdFo1djZi1WPzys*}!-?<$lpe@<0Oa|Cyn_$ICX#pXcKAx(GldWG{Kw1T z0G{DtqYz(3#}SPSpLIuIRcgfwK}csK!0gp=V%a3QRK zE8t4F25tn=t7qUjcnjW!1JJ84KK=0hPyhAlhcB#t>hVWbKd}1V)ptF$`l;2o-f-*1 z=bdpLALreA-U+v^Ua~r~IwTKl=e0TSs}0qS`8z$HevO0T^Vptw3w9LRf*pmn&~vNy zTe`uyH&3k1KeuyV4aP<8Qno{*mfE+a3-7uKZOfn1>6E7ScQ>*XW-s4 z+J4M%w!&gq0?Xk&=3v*s2e2NH?OOhOoBv+_@U>T7_~R3Q{Njm6pLp^m-$B#% z@;VYx^OW8_Gayenk6*R(GaAaW?Y^g2TmVJJ^N(<78*`8s z52DQdmt*(ll4K77>-D7V&l#TRb1c z9zTN$?C>D?75o}rg!jSOX?zfUKKK&)4`$6boB`LvuV7a+@i@2@{s>#pY8nkkK_h$~ z&Prb2;QjEGO?&XqfgNCSsQXR;BQ)i_uiAg)$&rpr{-7n!Q z*b15Uf)dyp20|H(fl8PNRd6UwhxxDoYM~Am!Ew+A$HNuy+8=*+?b%_n^Rtt(*M8?;%g^{=QM!c!ACoA=Qo6#}1{IlUSa{6rsCOqYYyZ=rR6)B9dJy<$E7I2CLu| z_$#~#Z^1hF06v4y!NaHbfbC#=*co<#y`TgJ!C;sNN5RpMfJJaJoC0UUS@7Lg!dtKo zK7dc*bJ(0Zy(jDrd%%9MKa@c^jD%4z17^Vya3p*O=0iOsU^)C0E`-bAa=`9yxaVBU zTlO=2?#%2xHx$_?D8`e1%h-+>pSJNgT1$%1z`(Z@p;5uTMHqB9t!6)IlS%n6f(t-o z@DjWXe}?t&A(RfI9RTOTM{wA1bOzpmvJv=kcoMc@WyC^Q3_pY`;7V8t_rp7o*9I)| z;KQ(I|K9_IQe1(!y9%@k`-`1j4cEZUa63E*55eQ`1iS#E3m?Eo&=dJ@0li^s*bcUb z{;)gj2m8Z;@bUNK%H?C-Y> zH|aKLEq+a|#j%8+_`ln}^mY;%4TK6f2*$$%m<6+8F3f`yVHunXr@@cl9JmNBh8y5U z_&MAHtKf0?BRmZsz=!ZPc=(J6d<(XR9iR-NFcgNtOqdOCKl}FGZ$EqU@?~{G!SCMx zf6BBNo%v7des}cS+h5A~XqXQPSOiH(!3FSBxCAbRYvFph4Q_|~U?n^R55vpwXLuD} zgSX(H@G*P>J*gkIfZnh*><)Xt-mniG00UtZjD|_@<}-IY;Xl*K_y02a2Qu2kErsst zmAAi?@nkp@ro%!w7UED3r@(1&E}RFK!R2r@Tm!#>JK%1(2Oft%z@OkHcmv*qci>;} z6?_dnX$Ncpy`c~61UtjNupb-%2ZHM#NSC0K?>Lq2lxcU%DG0lWI^(~}6YVc$JQ7Ah zC5(mna17K#BV?f!+TaBE0h|Mu!Q~+ByZb=ecYlE4tUQ|pKZSQdR@NO0C&Fp)#&h@H z^v3zepI@ico&VhY=ce%AeEHnZ)ZDHf?PyR(+ve@t>Vbrcu&Do}^n-L1(N+)c-9#fn z-Pb&?zqH3kAfx4Q0f@~02_m=8;49b@nfV}cj6n@7gE!$r_y|6ReJZeDm|z`=(>X87`6_#kg2`StLXS2yVaf$8{e-`;8aEjPYvzR66%ij&TUxlzr_gVG#ok0_ zCCF?amSO|41uS{F@86m@jFOEE40D);QR10tbsS+5@xY)geTz7upW9& z#O`4yh(b9Gf*-<<;Z}GB{tADGPv8saRmJB=>(;*g_gCJ2=9Rafd<5_Cz#R|V^}y}d z-+uY+mtT0^S*NtMCi!m>|50Mt*9Fu6t`>RMHWBPx{*|-0SIr8Ybw~2an$4-dQd^hL zS<|*;T?men8Fx9hSA(Mq_FSs>%4cvd>q5-E=4@_phNefJAO&Bw^wiJ0zJ*KXcm>&$ zPqBZA&G@CeN&^T zIuAt7zk$Di$k)lWNE6Ht02(wjQ)Vo0 zg^Q!`LEbZk51tK&_GI|rv4RJff9@~uwju+_dnOl@e%o;~-pAUi$J3i(tXpn-`XQTb zclydN57-AWAA)0E`DH`hlZ`*}n)k8tl7lVnEWUjAcI=_gze-gg&)?c(yVGZXrO)5m z<5<21&-dz3CvtkDu2AK#?r%r&ts;Ao`@t|7VvvNza6E`UI6bID)}jL<_lw|ixD}Qm z_dmds@Di+?#=8X5@e%OFZ2Ay~;p5>aa2KqB&pulB(Ym)^dz*h>dE%8{{q$F_oPFBa zubiDayY}qUj+jZU)vxj6qt|=9&A`H`UM!sIwNu~5zbyGZ{#gF)=)4qG=={EY8&CW8 z-??AT*H3vBFWS48odo~-YmtnEN()b}yGXnp!%qcg4>EU!XI8iJW>4D+ue%j12)EK@ z0TM?`ZaOFEVSGt2GgtU_^G^9(Q?b(zu{5^?E&N8X(9Os-bkL+n*#Co-#;LYsan?{vXc&>I9k1KD2L^8p-Rm8k&z;k!R3(b{HvPm7MTVo1;am_|u7wCP91dZ* zerRR;8K1i%--AHpTnP)|QFsjg41a;@d5i%-Jsfv9<4AB8?0p1%OE?I|z|Y|YcoF^t zFTp?I9a#U)-{1NF*gNk)s=xp7U!zcDMajr2-U^jn8QB^lq=jV5%1kyPQb~l!NM?%6 zG|Uo`oraaYGP1Yd^L2H(w|9Mheb)E>@%?&v+}G>*I_IAAdY$Jv&+|OzHEvUVQ`xJ$ zS5K3plJCIU*E8=G{6B>Sy~=a6w)k6NB0HeQ->0KSdp^j6v1|9wuSvSd`%KAJS`yd| zurF)&0?Ur9T7+5i1{^!FVvTV2Li_t!MwGH@M=tnr}B0;mALS zhjHD<2}df@YM#Hdn}pepE#Qpf@~}g2ge>e3905B3*^=?i1Aqdc2p9sPKnjovWC7Vg9#8_j2D*SL zU>?|X2A%;2r~v^$2v7u6ffIlsU<}v*mjOrM3NSI$)A0$G1~e5NrNyNk`MDiwiD?}V zIv(VA{F2=$BhRq71@XMu-IXUEW*Av zw}aRZBt5ua5JDKV;NmGfPk=mEfW*YkX>E=OeLp{>kHkjol!h8Bdd+tndJi}jWQ|D$ z{d5AvIl;$_4nx*IhoRGhp1q6R7-Q!^TC;N~Iy$s1T(OAKJnOBD(eBSKM6V;j=ntKS zfo{S9M2{F9BD%xqNf2~^=nByhq91JC_=4ODv;kj%eqaEY0%XqqWEW=8>iO}3Bh39a zWE?wW7+b(e;J+yJ|5>J8*wJu=-yhq*az0qupsfc|)iIU^cKqu3LKr|`Vh}+$j1WE`UFf0^|aDKow8}Gyz|ML0}3X zIS1GO0YShDa0Q+Lr9e9{1@J;c5d`!BOCSWu2jsQk`ZZtxI01>kD`342_$7b@paxtA zLV;yq<9WC@5ika>0TDnvkOdS0#XtivGXW19ed``=8f`=`O|XS5Rj{mVHU6~5#K5dy z7iPXj!5_>GrC&mx*+V{mZo9}nq|N^=>}Tt-W@mn7!Jxgb%bGo z{Z9$MZ*+fn1UQE0_rf2h_OC?4u@1i%FeQs#W7;{%YKvdLx&>ii2~doPPO#gFKggK2 zvIN`M?FDHoNINM6Isr;u@ZkX~;66|V%mceGz&)hEC_tqL<40f)h4uG)ef$?gey=v1 z5NIQ7f4>(x*CIh-{F#80Pb>C!aD>m;Avgj$0IgNRjq;=`x`cNNk*>@UqW2l;BW46W z3xp0?kufy^_7kkxe-gNL8?kepz&wEHlM_I6dI~`F>IWdY%?1$t_5+BH$w9Z204;#% zTNXfc{uZbOJ^+aRjX?LNfCUf^qyk@nA>gP!%ryc2z0=j`- zU<4Qiz5^&jm{$V!0Fran z78}_Q>8w7n{X<@}?BM62?e(uLJd6!LJ5>GXMUd+O$h83Y6+W!J*nP`tj9>LFkPlK( zM3AyG-C39^hsQ z_l^NifHVN7&-+6wkMkWI;rGRZqlW*ifTKE`q;V9~hNTqT)5bZ4e?`Dfd1ZS|!^DWz zZ`mLGaRmW&%pA5>-xz?NW#~HnY_xr1rWU>I{#VU zU7%a~i(q$wBY*~Q4!8(70nUIwpk@a5lmR+`9$*021D63ezyr7q+yR1t5Fi}52Sfoe z09-E^f~&yY{qVM*#>wi*H>=Cz5SJgzqr+PDV29+MA1uhEqO)3e~r3-BOqbIT9?AZ7jxm;kmw_1OVr zSwQ~_GyyXJndMJ*9LK2Ft<=hu&jf4%Cj>|E#E$ldso^AzBVZ@?$J-p>_xuO$!PvcW zqs8x%T76f?KXYFPa@Pj;?+@=^N)v7W;kxlgaO0466%Ql=slXE;6UYW~0i-_U0k433 zpcr@!lmQh$HBbk91U><+KpW5l^a3NmcVHYq>eVDL3(Nsa01E2ZI)DTq2Q~pTz-E9R zK5}*b417`qjzzyiDL6&C;HP6r+E8^dFJxlQSaYiplz4%pY!?X}#EX(lA zmW#O;!^#+Gy{mU*{Igc)muVySc&w!RH!JebTAe?zYI~6K^8w<3rvTF4UjQ!wq}>+* zuYoe49QXu$0T6r80dxVAz%+o^heco+SPyoB6hQ0+H9!k&1sDKEfEnNgz5yths@(`s z12=$h-~~_$q+f!u1TYLtjZKX;K7V-aIkKQvYafkc9%EDXQ)8B}OpPHLM*hfG3_M)P zIY;V&qFa$*o)+6p#3BVmtvW5O0AhrYD0t$d3 z5CyQ=gFRSLz{+PWwor*JRQ$Wb8|(yrFZ^LX1=y+KeCIiKNcO)Vtd7Z7+&|z0_yRPJVB3Kmz%F1na0rkFO1rj1fULR0H(l2z!tCrJOM8t9!LP509imD zPzTUD!Lb3;{R$zTe8vHE1xTSvkM>w)rvjXb9ggyKMG=f zXxH|vUVmAgDcB67s^43XpXM2Gt=cbI_1~^LVJraUN*=5Eqhyh`DhikZ_W+DOGhllP zK=c_66aysyqD$Ey`V@49`T!ULMnEp`3}67mIEURtCVwl8V}}f53pgRa7jVX5IKuD0 z^ZO7S15${6I4`h;oPR+;zJea3u`v?D*6Q23(fhi&H^6NXzjyQgnRbn_BQ%i9>iZ?J z#{W3>hYqRlOaL<=3Wx!R00rPEU;u;yVL&xd1AG7)fiFN4Fb>QD^S}atat0FyYyhYL zHh>*a0aSsLfF|G%Tn7Sx8^BHA7ElS)0S&+hU6rVBUaxGgeIi@G zk}$D9)V?n*C@pxA`~C%bEY{1ch+b14-VXi=<{B-!0<213PKKf5Y!n6S25lD|ifO}t zW?BA`HNmnvipcq4+HEOpzXJ%N{gMJmJKhGI0<-{aKo@uld0-A0{DQvz{+NvgyG^z_ z68Pae!ai)@@mIDN*Pp~_4RXq`_zF28pQ|}RC?c=b1N7W4q%f{H_2a0JuoVeiV_>#- zORSMt_LTH~@^^v|!r| zhz2TwMF68qe$b@^APFb}N`Nik1Y`p^eZhAW)*p`W4J!cmJH6N;-PpocY@rETz=_uQ zXCc3j_OB$3b8I+=gCqPtQ|yCAw%0+^LGJga#J22fb9@94<)?lHQ8^jDzHq!|^WuFa#$Wj?j;N)W6TP3p?8Hh2}qth8>Fp zd1o_`E+-NEW(bQZXdWZ4)d5U>p@PuWnP38(dc=U}?ku1UTmXnbU)+EZfYD(NY)=7* zE_pzgh(31%yuc~o46raWw9vWGR`s?pCqCHAP(=d!Sbwt(^CgZnwZECA{qYVSct#e9 zv-%>opQlifcb-b>6rfF?7@cvY!v(OLE8qABLu>S#QsJmphSp@HU{%+?iEZGK5j7GC zxyrT@1F_KmI0h2$mocEX_&E%*?knjcpKB7jAAmd&)zTZ+DE9rbln~#5@!EwUxb`1u z8d$bs3(|Ja0_T8>0K6L=#Q1?-ci9dXx-?gTn+afZn zs|h1+?D&|Pj;zrq|A$uRwzlfp+k$^(bue#Q22UG_`>QKR$e3Qo=H9QvRxR6)Hx~RV z56s&xSL6P8|HQB2Vvc7u?l0}%PtGDU(ng;HUw}?vGw24yj0RVse=dQl`zUntGp1gQ?#~XX`=$mqw>VswFvu_Gf5!eStf&RbQhf|_V==nEf z{mXW|+7tga2BeIH;aDEH>vQZR@=kC;+fNWe1owm!t-fo%Q_uo8Hl%=z1#waUn}x(% zyQ2u^387y{(pyOtyI)84u39@tElXD(H#OMzv&G~?^o8gO(US^*=*S5005E#G3ft^Z z{v1FykOO=I`hiJ+0Ce#Iy9NB)!h7u0-u?FkoX+?jc0M=)b^x-y3z?BOhG#I13H}p} z9C_CFJh1M^_zwJa@X}Ev7?0jaA6-HJ`Y%`&YD9N500)3+Ul(9o510a`0iK)S69AV1 zM<5ue1=in!Yeawzfa9iBVHX&VP>CJ#7F)mx`OgZ)zs)BCJLLBQcC3}{o&TA+S|k0Z zBwK#i_O&ha?*Jh+;J;tbs1ZB*2IvI%K&KLbB%lT$y1oNo^xX>E?Z6N)0!#wa01@ba z9e~saI)ENv23P>3e((UofH$HEVC_c0*~P$C6;X*S?2i zllNkW{KIi6=I!7=ed7;~`loOF0}oMOUmtzE&{Lo*5ZL&TBRl`Ub|2GEa3JMg07wPG za}|IukP18pJ_4Tr``h3T0L?%Ppb-Su^8jDqEg*Xb#tlF@@K-R*%>oHPBJdul0;&NV z8}WMq=W5X?cH0}o7J9ISu0Iq0@H;s9{9)3680`;}#yM@Aqs9@i&Kj{7#K=8>rO0i1 z=qo=fTgc4M$`VoNTSkjCIY<7^}f@98Lo5 z&B!7iA?}HW;yj zML}d~o#74G|V?x&d+%f&}Hy{_J z0tsdmtAm2HpUK(4I*EK0q1>0?GiSjqd@FcCH0@0$xBQa36>Q;(&)hJbuT<*ZGPX})mL6z{-Qj0B7Gt^HR)keOo)Gy*F(35SPkY!{ZeItd0nVO*VL{- z?La;e5Bsc{?GI_y!(QEsgstv{aztdLMx|}2MX%9mfNXmb8at%C@{G77=X!J~`mz51 zPYuS@T&!Ai6H{xD7r0>8njbRchk0v|w7>~;PWM#$(QCnVjp#Mz2+(tj5Fcq-{D@t~ z=mIH!q}-9RP6ko{OqnmhHd5YGz%)<_-qKsZ!i)fA30wpGfN)?6mCsnt*z6Afu=oiZ^?&R`4B<*IfUbn-IfIog#P*{rC^{8%B}94*dW=q6 zL6?1q2Ml_{=+qOoy#QYTTfdVaV{|+N+uO|vP+Y(fKm#xbEC3(i8sG;60=I!6;05p% zXa_ogE}$Fe1^R#iU=SDrhJo41;ct_D?Jafh-WKM)&3l_x_%`Wb_`~qq{ttcJd?p=i zRu+?$Qlup^MKKt~@gtsuhlIBj`2kJ$)IO zdpu_S1irJfUTyJU2i8JY_d*Wn*znzz-pIlUv<==EqA(o)5n>?wRzlW#Hnd_P74#b7 zAnK(@e z|F@O^tBtL$g}$z}z74C1g^hu=t<7#$*ukoAV5nx%S>~RW;mE$n8v@k$NrjW6;azyfCH8e3pa)ro; zb#-m{^ein5O^ndTxb_tpGZO;~TRtmGGZQ^WR!c+HgGy>Dj?y|-)|RFQdbSe$aAx)f z*8B#xdi-$I6cntc7c6aTc!l})^6lZ1H?hz)!$kRU;`~TH6cj&>kwVkj#MS_P5H_p^ zmu(F!^pPV+4o~i|w17Gs93O=m6cMYjiG{5V1%;`N6{{YcGwc49vav&-H#<_sCa_m` zrCeFXS=m|bpp4F;Lu{c;DJaxzb**h#%`EkF%~%gh!%;#EOlIb~dXRwutA(z)0mTVB ziysltMT{JbCHw%cr3EVm?*-O9aAL4f5K&MNQ~da(RXL;}$I41aK|%3jbMwBvh(tm0 zzqS7VZ$EFuPh){T7P}8W39=@D-Cs9?jP%!?0McLg07!q`4in=rin9}-Hj^C`p@aE{!>R@6>KXR={@?b0p#(5~9IVkkE!>%iy!#9Bbj=k(Pkm)UUi@xc?L5 z8&e?qMs&Llz}7ELa~;J#4V3-|j0ggbwb)3MCba(asC^*8%|*cfzRX4NdFFV{Toj*Y zj@Qh^@OkEV%v=E4JKowko-^Nz&ojqs=KJt@=6KCq0H0@$*USa+dFFV{TnL|Mj@Qib z=)(!%(}5H~;n9u4bLMz-qXb~!fJbKnuWl3`Ge^d3cq?{1XO2fVN)UzD%<PF!)a}j*{aCpufk3O6T zK7BY5eEM*B&K!?EoCrRBI1zmMaCpufk3O6TK7BY5eEM*B&K!?EoCrRBI1zmMaCpuf zuWl3`GZ)3D4~OT>@#w>e;?svio>;@%+$)|l$D;#1K-p|nZd$`PTd@~P^hmz zJqd37KbSY~f3pzz{&phzP&oE*^5!TK$9H@<>5r z_s<4sy$K&_+0tZQD3(t}u5|R97*S7JMUvSwDVpgyba5nnQfEAE7=iHxep)O{v&S!s;zW{HR5q~ zbxB;ShipqZhdB`U!X${!5OP;u{17I`7^fN%%z+Tgd!uZtld=Q{$W~9U#Y+NnqO5y( zh+~}O#6~!a{4fW6#m6QiX-FDyenM*1Yw?c@^bLL03

@d$7+K>p!Oy~<)jG{*-RX~cQlI!}>tDMyB_5UX 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 C<7x{4 From 66ef7cb61104b682856ec2bcac4afeb801009417 Mon Sep 17 00:00:00 2001 From: delageniere Date: Fri, 10 Nov 2017 09:14:09 +0100 Subject: [PATCH 38/38] modifs in doc --- documentation/ISPyB_DevelopersGuide.doc | Bin 192000 -> 193024 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/documentation/ISPyB_DevelopersGuide.doc b/documentation/ISPyB_DevelopersGuide.doc index be213ba1110737808cc66d9465bb0ea5d81c1b6b..bf73a7a973272242b5c4dee56682b50b70e54a8d 100644 GIT binary patch delta 17030 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

c#HS5SUZx2hdEe+Ral2z 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 z q3`Q+gf6c6L4qGmFN{@fYOGETO9vb5LgOCZovJ`E4{i~sPq5lF#-@<$V delta 16526 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

WiMtQ+lawpf} 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 &2IrZ85 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 diff --git a/configuration/documentation/database/ISPyB_DataModel_mx.png b/configuration/documentation/database/ISPyB_DataModel_mx.png deleted file mode 100644 index 5822d1aea23a492f51a07beb798f96a43f4dc9ec..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1027413 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�}|XZcA}Re_TE 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%0Abjb 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